Merge pull request #20787 from CarolEidt/Arm64IntrinsicsWindows
[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 #include "softwarewritewatch.h"
22
23 #define USE_INTROSORT
24
25 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
26 BOOL bgc_heap_walk_for_etw_p = FALSE;
27 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
28
29 #if defined(FEATURE_REDHAWK)
30 #define MAYBE_UNUSED_VAR(v) v = v
31 #else
32 #define MAYBE_UNUSED_VAR(v)
33 #endif // FEATURE_REDHAWK
34
35 #define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0))
36
37 #ifdef SERVER_GC
38 #define partial_size_th 100
39 #define num_partial_refs 64
40 #else //SERVER_GC
41 #define partial_size_th 100
42 #define num_partial_refs 32
43 #endif //SERVER_GC
44
45 #define demotion_plug_len_th (6*1024*1024)
46
47 #ifdef BIT64
48 #define MARK_STACK_INITIAL_LENGTH 1024
49 #else
50 #define MARK_STACK_INITIAL_LENGTH 128
51 #endif // BIT64
52
53 #define LOH_PIN_QUEUE_LENGTH 100
54 #define LOH_PIN_DECAY 10
55
56 #ifdef BIT64
57 // Right now we support maximum 1024 procs - meaning that we will create at most
58 // that many GC threads and GC heaps. 
59 #define MAX_SUPPORTED_CPUS 1024
60 #else
61 #define MAX_SUPPORTED_CPUS 64
62 #endif // BIT64
63
64 #ifdef GC_CONFIG_DRIVEN
65 int compact_ratio = 0;
66 #endif //GC_CONFIG_DRIVEN
67
68 // See comments in reset_memory.
69 BOOL reset_mm_p = TRUE;
70
71 bool g_fFinalizerRunOnShutDown = false;
72
73 #ifdef FEATURE_SVR_GC
74 bool g_built_with_svr_gc = true;
75 #else
76 bool g_built_with_svr_gc = false;
77 #endif // FEATURE_SVR_GC
78
79 #if defined(BUILDENV_DEBUG)
80 uint8_t g_build_variant = 0;
81 #elif defined(BUILDENV_CHECKED)
82 uint8_t g_build_variant = 1;
83 #else
84 uint8_t g_build_variant = 2;
85 #endif // defined(BUILDENV_DEBUG)
86
87 VOLATILE(int32_t) g_no_gc_lock = -1;
88
89 #if defined (TRACE_GC) && !defined (DACCESS_COMPILE)
90 const char * const allocation_state_str[] = {
91     "start",
92     "can_allocate",
93     "cant_allocate",
94     "try_fit",
95     "try_fit_new_seg",
96     "try_fit_new_seg_after_cg",
97     "try_fit_no_seg",
98     "try_fit_after_cg",
99     "try_fit_after_bgc",
100     "try_free_full_seg_in_bgc", 
101     "try_free_after_bgc",
102     "try_seg_end",
103     "acquire_seg",
104     "acquire_seg_after_cg",
105     "acquire_seg_after_bgc",
106     "check_and_wait_for_bgc",
107     "trigger_full_compact_gc",
108     "trigger_ephemeral_gc",
109     "trigger_2nd_ephemeral_gc",
110     "check_retry_seg"
111 };
112 #endif //TRACE_GC && !DACCESS_COMPILE
113
114 // Keep this in sync with the definition of gc_reason
115 #if (defined(DT_LOG) || defined(TRACE_GC)) && !defined (DACCESS_COMPILE)
116 static const char* const str_gc_reasons[] = 
117 {
118     "alloc_soh",
119     "induced",
120     "lowmem",
121     "empty",
122     "alloc_loh",
123     "oos_soh",
124     "oos_loh",
125     "induced_noforce",
126     "gcstress",
127     "induced_lowmem",
128     "induced_compacting"
129 };
130
131 static const char* const str_gc_pause_modes[] = 
132 {
133     "batch",
134     "interactive",
135     "low_latency",
136     "sustained_low_latency",
137     "no_gc"
138 };
139 #endif // defined(DT_LOG) || defined(TRACE_GC)
140
141 inline
142 BOOL is_induced (gc_reason reason)
143 {
144     return ((reason == reason_induced) ||
145             (reason == reason_induced_noforce) ||
146             (reason == reason_lowmemory) ||
147             (reason == reason_lowmemory_blocking) || 
148             (reason == reason_induced_compacting));
149 }
150
151 inline
152 BOOL is_induced_blocking (gc_reason reason)
153 {
154     return ((reason == reason_induced) ||
155             (reason == reason_lowmemory_blocking) || 
156             (reason == reason_induced_compacting));
157 }
158
159 #ifndef DACCESS_COMPILE
160 int64_t qpf;
161
162 size_t GetHighPrecisionTimeStamp()
163 {
164     int64_t ts = GCToOSInterface::QueryPerformanceCounter();
165     
166     return (size_t)(ts / (qpf / 1000));    
167 }
168 #endif
169
170
171 #ifdef GC_STATS
172 // There is a current and a prior copy of the statistics.  This allows us to display deltas per reporting
173 // interval, as well as running totals.  The 'min' and 'max' values require special treatment.  They are
174 // Reset (zeroed) in the current statistics when we begin a new interval and they are updated via a
175 // comparison with the global min/max.
176 GCStatistics g_GCStatistics;
177 GCStatistics g_LastGCStatistics;
178
179 char* GCStatistics::logFileName = NULL;
180 FILE*  GCStatistics::logFile = NULL;
181
182 void GCStatistics::AddGCStats(const gc_mechanisms& settings, size_t timeInMSec)
183 {
184 #ifdef BACKGROUND_GC
185     if (settings.concurrent)
186     {
187         bgc.Accumulate((uint32_t)timeInMSec*1000);
188         cntBGC++;
189     }
190     else if (settings.background_p)
191     {
192         fgc.Accumulate((uint32_t)timeInMSec*1000);
193         cntFGC++;
194         if (settings.compaction)
195             cntCompactFGC++;
196         assert(settings.condemned_generation < max_generation);
197         cntFGCGen[settings.condemned_generation]++;
198     }
199     else
200 #endif // BACKGROUND_GC
201     {
202         ngc.Accumulate((uint32_t)timeInMSec*1000);
203         cntNGC++;
204         if (settings.compaction)
205             cntCompactNGC++;
206         cntNGCGen[settings.condemned_generation]++;
207     }
208
209     if (is_induced (settings.reason))
210         cntReasons[(int)reason_induced]++;
211 #ifdef STRESS_HEAP
212     else if (settings.stress_induced)
213         cntReasons[(int)reason_gcstress]++;
214 #endif // STRESS_HEAP
215     else
216         cntReasons[(int)settings.reason]++;
217
218 #ifdef BACKGROUND_GC
219     if (settings.concurrent || !settings.background_p)
220     {
221 #endif // BACKGROUND_GC
222         RollOverIfNeeded();
223 #ifdef BACKGROUND_GC
224     }
225 #endif // BACKGROUND_GC
226 }
227
228 void GCStatistics::Initialize()
229 {
230     LIMITED_METHOD_CONTRACT;
231     // for efficiency sake we're taking a dependency on the layout of a C++ object
232     // with a vtable. protect against violations of our premise:
233     static_assert(offsetof(GCStatistics, cntDisplay) == sizeof(void*),
234             "The first field of GCStatistics follows the pointer sized vtable");
235
236     int podOffs = offsetof(GCStatistics, cntDisplay);       // offset of the first POD field
237     memset((uint8_t*)(&g_GCStatistics)+podOffs, 0, sizeof(g_GCStatistics)-podOffs);
238     memset((uint8_t*)(&g_LastGCStatistics)+podOffs, 0, sizeof(g_LastGCStatistics)-podOffs);
239 }
240
241 void GCStatistics::DisplayAndUpdate()
242 {
243     LIMITED_METHOD_CONTRACT;
244
245     if (logFileName == NULL || logFile == NULL)
246         return;
247
248     {
249         if (cntDisplay == 0)
250             fprintf(logFile, "\nGCMix **** Initialize *****\n\n");
251             
252         fprintf(logFile, "GCMix **** Summary ***** %d\n", cntDisplay);
253
254         // NGC summary (total, timing info)
255         ngc.DisplayAndUpdate(logFile, "NGC ", &g_LastGCStatistics.ngc, cntNGC, g_LastGCStatistics.cntNGC, msec);
256
257         // FGC summary (total, timing info)
258         fgc.DisplayAndUpdate(logFile, "FGC ", &g_LastGCStatistics.fgc, cntFGC, g_LastGCStatistics.cntFGC, msec);
259
260         // BGC summary
261         bgc.DisplayAndUpdate(logFile, "BGC ", &g_LastGCStatistics.bgc, cntBGC, g_LastGCStatistics.cntBGC, msec);
262
263         // NGC/FGC break out by generation & compacting vs. sweeping
264         fprintf(logFile, "NGC   ");
265         for (int i = max_generation; i >= 0; --i)
266             fprintf(logFile, "gen%d %d (%d). ", i, cntNGCGen[i]-g_LastGCStatistics.cntNGCGen[i], cntNGCGen[i]);
267         fprintf(logFile, "\n");
268
269         fprintf(logFile, "FGC   ");
270         for (int i = max_generation-1; i >= 0; --i)
271             fprintf(logFile, "gen%d %d (%d). ", i, cntFGCGen[i]-g_LastGCStatistics.cntFGCGen[i], cntFGCGen[i]);
272         fprintf(logFile, "\n");
273
274         // Compacting vs. Sweeping break out
275         int _cntSweep = cntNGC-cntCompactNGC;
276         int _cntLastSweep = g_LastGCStatistics.cntNGC-g_LastGCStatistics.cntCompactNGC;
277         fprintf(logFile, "NGC   Sweeping %d (%d) Compacting %d (%d)\n",
278                _cntSweep - _cntLastSweep, _cntSweep,
279                cntCompactNGC - g_LastGCStatistics.cntCompactNGC, cntCompactNGC);
280
281         _cntSweep = cntFGC-cntCompactFGC;
282         _cntLastSweep = g_LastGCStatistics.cntFGC-g_LastGCStatistics.cntCompactFGC;
283         fprintf(logFile, "FGC   Sweeping %d (%d) Compacting %d (%d)\n",
284                _cntSweep - _cntLastSweep, _cntSweep,
285                cntCompactFGC - g_LastGCStatistics.cntCompactFGC, cntCompactFGC);
286
287 #ifdef TRACE_GC
288         // GC reasons...
289         for (int reason=(int)reason_alloc_soh; reason <= (int)reason_gcstress; ++reason)
290         {
291             if (cntReasons[reason] != 0)
292                 fprintf(logFile, "%s %d (%d). ", str_gc_reasons[reason], 
293                     cntReasons[reason]-g_LastGCStatistics.cntReasons[reason], cntReasons[reason]);
294         }
295 #endif // TRACE_GC
296         fprintf(logFile, "\n\n");
297
298         // flush the log file...
299         fflush(logFile);
300     }
301
302     g_LastGCStatistics = *this;
303
304     ngc.Reset();
305     fgc.Reset();
306     bgc.Reset();
307 }
308
309 #endif // GC_STATS
310
311 inline
312 size_t round_up_power2 (size_t size)
313 {
314     // Get the 0-based index of the most-significant bit in size-1.
315     // If the call failed (because size-1 is zero), size must be 1,
316     // so return 1 (because 1 rounds up to itself).
317     DWORD highest_set_bit_index;
318     if (0 ==
319 #ifdef BIT64
320         BitScanReverse64(
321 #else
322         BitScanReverse(
323 #endif
324             &highest_set_bit_index, size - 1)) { return 1; }
325
326     // The size == 0 case (which would have overflowed to SIZE_MAX when decremented)
327     // is handled below by relying on the fact that highest_set_bit_index is the maximum value
328     // (31 or 63, depending on sizeof(size_t)) and left-shifting a value >= 2 by that
329     // number of bits shifts in zeros from the right, resulting in an output of zero.
330     return static_cast<size_t>(2) << highest_set_bit_index;
331 }
332
333 inline
334 size_t round_down_power2 (size_t size)
335 {
336     // Get the 0-based index of the most-significant bit in size.
337     // If the call failed, size must be zero so return zero.
338     DWORD highest_set_bit_index;
339     if (0 ==
340 #ifdef BIT64
341         BitScanReverse64(
342 #else
343         BitScanReverse(
344 #endif
345             &highest_set_bit_index, size)) { return 0; }
346
347     // Left-shift 1 by highest_set_bit_index to get back a value containing only
348     // the most-significant set bit of size, i.e. size rounded down
349     // to the next power-of-two value.
350     return static_cast<size_t>(1) << highest_set_bit_index;
351 }
352
353 // Get the 0-based index of the most-significant bit in the value.
354 // Returns -1 if the input value is zero (i.e. has no set bits).
355 inline
356 int index_of_highest_set_bit (size_t value)
357 {
358     // Get the 0-based index of the most-significant bit in the value.
359     // If the call failed (because value is zero), return -1.
360     DWORD highest_set_bit_index;
361     return (0 ==
362 #ifdef BIT64
363         BitScanReverse64(
364 #else
365         BitScanReverse(
366 #endif
367             &highest_set_bit_index, value)) ? -1 : static_cast<int>(highest_set_bit_index);
368 }
369
370 inline
371 int relative_index_power2_plug (size_t power2)
372 {
373     int index = index_of_highest_set_bit (power2);
374     assert (index <= MAX_INDEX_POWER2);
375
376     return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
377 }
378
379 inline
380 int relative_index_power2_free_space (size_t power2)
381 {
382     int index = index_of_highest_set_bit (power2);
383     assert (index <= MAX_INDEX_POWER2);
384
385     return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
386 }
387
388 #ifdef BACKGROUND_GC
389 uint32_t bgc_alloc_spin_count = 140;
390 uint32_t bgc_alloc_spin_count_loh = 16;
391 uint32_t bgc_alloc_spin = 2;
392
393
394 inline
395 void c_write (uint32_t& place, uint32_t value)
396 {
397     Interlocked::Exchange (&place, value);
398     //place = value;
399 }
400
401 #ifndef DACCESS_COMPILE
402 // If every heap's gen2 or gen3 size is less than this threshold we will do a blocking GC.
403 const size_t bgc_min_per_heap = 4*1024*1024;
404
405 int gc_heap::gchist_index = 0;
406 gc_mechanisms_store gc_heap::gchist[max_history_count];
407
408 #ifndef MULTIPLE_HEAPS
409 size_t gc_heap::total_promoted_bytes = 0;
410 VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process;
411 int gc_heap::gchist_index_per_heap = 0;
412 gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count];
413 #endif //MULTIPLE_HEAPS
414
415 void gc_heap::add_to_history_per_heap()
416 {
417 #ifdef GC_HISTORY
418     gc_history* current_hist = &gchist_per_heap[gchist_index_per_heap];
419     current_hist->gc_index = settings.gc_index;
420     current_hist->current_bgc_state = current_bgc_state;
421     size_t elapsed = dd_gc_elapsed_time (dynamic_data_of (0));
422     current_hist->gc_time_ms = (uint32_t)elapsed;
423     current_hist->gc_efficiency = (elapsed ? (total_promoted_bytes / elapsed) : total_promoted_bytes);
424     current_hist->eph_low = generation_allocation_start (generation_of (max_generation-1));
425     current_hist->gen0_start = generation_allocation_start (generation_of (0));
426     current_hist->eph_high = heap_segment_allocated (ephemeral_heap_segment);
427 #ifdef BACKGROUND_GC
428     current_hist->bgc_lowest = background_saved_lowest_address;
429     current_hist->bgc_highest = background_saved_highest_address;
430 #endif //BACKGROUND_GC
431     current_hist->fgc_lowest = lowest_address;
432     current_hist->fgc_highest = highest_address;
433     current_hist->g_lowest = g_gc_lowest_address;
434     current_hist->g_highest = g_gc_highest_address;
435
436     gchist_index_per_heap++;
437     if (gchist_index_per_heap == max_history_count)
438     {
439         gchist_index_per_heap = 0;
440     }
441 #endif //GC_HISTORY
442 }
443
444 void gc_heap::add_to_history()
445 {
446 #ifdef GC_HISTORY
447     gc_mechanisms_store* current_settings = &gchist[gchist_index];
448     current_settings->store (&settings);
449
450     gchist_index++;
451     if (gchist_index == max_history_count)
452     {
453         gchist_index = 0;
454     }
455 #endif //GC_HISTORY
456 }
457
458 #endif //DACCESS_COMPILE
459 #endif //BACKGROUND_GC
460
461 #if defined(TRACE_GC) && !defined(DACCESS_COMPILE)
462 BOOL   gc_log_on = TRUE;
463 FILE* gc_log = NULL;
464 size_t gc_log_file_size = 0;
465
466 size_t gc_buffer_index = 0;
467 size_t max_gc_buffers = 0;
468
469 static CLRCriticalSection gc_log_lock;
470
471 // we keep this much in a buffer and only flush when the buffer is full
472 #define gc_log_buffer_size (1024*1024)
473 uint8_t* gc_log_buffer = 0;
474 size_t gc_log_buffer_offset = 0;
475
476 void log_va_msg(const char *fmt, va_list args)
477 {
478     gc_log_lock.Enter();
479
480     const int BUFFERSIZE = 512;
481     static char rgchBuffer[BUFFERSIZE];
482     char *  pBuffer  = &rgchBuffer[0];
483
484     pBuffer[0] = '\n';
485     int buffer_start = 1;
486     int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging());
487     buffer_start += pid_len;
488     memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start);
489     int msg_len = _vsnprintf_s(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
490     if (msg_len == -1)
491     {
492         msg_len = BUFFERSIZE - buffer_start;
493     }
494
495     msg_len += buffer_start;
496
497     if ((gc_log_buffer_offset + msg_len) > (gc_log_buffer_size - 12))
498     {
499         char index_str[8];
500         memset (index_str, '-', 8);
501         sprintf_s (index_str, _countof(index_str), "%d", (int)gc_buffer_index);
502         gc_log_buffer[gc_log_buffer_offset] = '\n';
503         memcpy (gc_log_buffer + (gc_log_buffer_offset + 1), index_str, 8);
504
505         gc_buffer_index++;
506         if (gc_buffer_index > max_gc_buffers)
507         {
508             fseek (gc_log, 0, SEEK_SET);
509             gc_buffer_index = 0;
510         }
511         fwrite(gc_log_buffer, gc_log_buffer_size, 1, gc_log);
512         fflush(gc_log);
513         memset (gc_log_buffer, '*', gc_log_buffer_size);
514         gc_log_buffer_offset = 0;
515     }
516
517     memcpy (gc_log_buffer + gc_log_buffer_offset, pBuffer, msg_len);
518     gc_log_buffer_offset += msg_len;
519
520     gc_log_lock.Leave();
521 }
522
523 void GCLog (const char *fmt, ... )
524 {
525     if (gc_log_on && (gc_log != NULL))
526     {
527         va_list     args;
528         va_start(args, fmt);
529         log_va_msg (fmt, args);
530         va_end(args);
531     }
532 }
533 #endif // TRACE_GC && !DACCESS_COMPILE
534
535 #if defined(GC_CONFIG_DRIVEN) && !defined(DACCESS_COMPILE)
536
537 BOOL   gc_config_log_on = FALSE;
538 FILE* gc_config_log = NULL;
539
540 // we keep this much in a buffer and only flush when the buffer is full
541 #define gc_config_log_buffer_size (1*1024) // TEMP
542 uint8_t* gc_config_log_buffer = 0;
543 size_t gc_config_log_buffer_offset = 0;
544
545 // For config since we log so little we keep the whole history. Also it's only
546 // ever logged by one thread so no need to synchronize.
547 void log_va_msg_config(const char *fmt, va_list args)
548 {
549     const int BUFFERSIZE = 256;
550     static char rgchBuffer[BUFFERSIZE];
551     char *  pBuffer  = &rgchBuffer[0];
552
553     pBuffer[0] = '\n';
554     int buffer_start = 1;
555     int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
556     assert (msg_len != -1);
557     msg_len += buffer_start;
558
559     if ((gc_config_log_buffer_offset + msg_len) > gc_config_log_buffer_size)
560     {
561         fwrite(gc_config_log_buffer, gc_config_log_buffer_offset, 1, gc_config_log);
562         fflush(gc_config_log);
563         gc_config_log_buffer_offset = 0;
564     }
565
566     memcpy (gc_config_log_buffer + gc_config_log_buffer_offset, pBuffer, msg_len);
567     gc_config_log_buffer_offset += msg_len;
568 }
569
570 void GCLogConfig (const char *fmt, ... )
571 {
572     if (gc_config_log_on && (gc_config_log != NULL))
573     {
574         va_list     args;
575         va_start( args, fmt );
576         log_va_msg_config (fmt, args);
577     }
578 }
579 #endif // GC_CONFIG_DRIVEN && !DACCESS_COMPILE
580
581 #ifdef SYNCHRONIZATION_STATS
582
583 // Number of GCs have we done since we last logged.
584 static unsigned int         gc_count_during_log;
585  // In ms. This is how often we print out stats.
586 static const unsigned int   log_interval = 5000;
587 // Time (in ms) when we start a new log interval.
588 static unsigned int         log_start_tick;
589 static unsigned int         gc_lock_contended;
590 static int64_t              log_start_hires;
591 // Cycles accumulated in SuspendEE during log_interval.
592 static uint64_t             suspend_ee_during_log;
593 // Cycles accumulated in RestartEE during log_interval.
594 static uint64_t             restart_ee_during_log;
595 static uint64_t             gc_during_log;
596
597 #endif //SYNCHRONIZATION_STATS
598
599 void
600 init_sync_log_stats()
601 {
602 #ifdef SYNCHRONIZATION_STATS
603     if (gc_count_during_log == 0)
604     {
605         gc_heap::init_sync_stats();
606         suspend_ee_during_log = 0;
607         restart_ee_during_log = 0;
608         gc_during_log = 0;
609         gc_lock_contended = 0;
610
611         log_start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
612         log_start_hires = GCToOSInterface::QueryPerformanceCounter();
613     }
614     gc_count_during_log++;
615 #endif //SYNCHRONIZATION_STATS
616 }
617
618 void
619 process_sync_log_stats()
620 {
621 #ifdef SYNCHRONIZATION_STATS
622
623     unsigned int log_elapsed = GCToOSInterface::GetLowPrecisionTimeStamp() - log_start_tick;
624
625     if (log_elapsed > log_interval)
626     {
627         uint64_t total = GCToOSInterface::QueryPerformanceCounter() - log_start_hires;
628         // Print out the cycles we spent on average in each suspend and restart.
629         printf("\n_________________________________________________________________________________\n"
630             "Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n"
631             "SuspendEE: %8u; RestartEE: %8u GC %.3f%%\n",
632             log_interval / 1000,
633             gc_count_during_log,
634             gc_lock_contended,
635             (unsigned int)(gc_during_log / gc_count_during_log),
636             (unsigned int)(suspend_ee_during_log / gc_count_during_log),
637             (unsigned int)(restart_ee_during_log / gc_count_during_log),
638             (double)(100.0f * gc_during_log / total));
639         gc_heap::print_sync_stats(gc_count_during_log);
640
641         gc_count_during_log = 0;
642     }
643 #endif //SYNCHRONIZATION_STATS
644 }
645
646 #ifdef MULTIPLE_HEAPS
647
648 enum gc_join_stage
649 {
650     gc_join_init_cpu_mapping = 0,
651     gc_join_done = 1,
652     gc_join_generation_determined = 2,
653     gc_join_begin_mark_phase = 3,
654     gc_join_scan_dependent_handles = 4,
655     gc_join_rescan_dependent_handles = 5,
656     gc_join_scan_sizedref_done = 6,
657     gc_join_null_dead_short_weak = 7,
658     gc_join_scan_finalization = 8,
659     gc_join_null_dead_long_weak = 9, 
660     gc_join_null_dead_syncblk = 10, 
661     gc_join_decide_on_compaction = 11, 
662     gc_join_rearrange_segs_compaction = 12, 
663     gc_join_adjust_handle_age_compact = 13,
664     gc_join_adjust_handle_age_sweep = 14,
665     gc_join_begin_relocate_phase = 15,
666     gc_join_relocate_phase_done = 16,
667     gc_join_verify_objects_done = 17,
668     gc_join_start_bgc = 18,
669     gc_join_restart_ee = 19,
670     gc_join_concurrent_overflow = 20,
671     gc_join_suspend_ee = 21,
672     gc_join_bgc_after_ephemeral = 22,
673     gc_join_allow_fgc = 23,
674     gc_join_bgc_sweep = 24,
675     gc_join_suspend_ee_verify = 25,
676     gc_join_restart_ee_verify = 26,
677     gc_join_set_state_free = 27,
678     gc_r_join_update_card_bundle = 28,
679     gc_join_after_absorb = 29, 
680     gc_join_verify_copy_table = 30,
681     gc_join_after_reset = 31,
682     gc_join_after_ephemeral_sweep = 32,
683     gc_join_after_profiler_heap_walk = 33,
684     gc_join_minimal_gc = 34,
685     gc_join_after_commit_soh_no_gc = 35,
686     gc_join_expand_loh_no_gc = 36,
687     gc_join_final_no_gc = 37,
688     gc_join_disable_software_write_watch = 38,
689     gc_join_max = 39
690 };
691
692 enum gc_join_flavor
693 {
694     join_flavor_server_gc = 0,
695     join_flavor_bgc = 1
696 };
697
698 #define first_thread_arrived 2
699 struct DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE) join_structure
700 {
701     // Shared non volatile keep on separate line to prevent eviction
702     int n_threads;
703
704     // Keep polling/wait structures on separate line write once per join
705     DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
706     GCEvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
707     Volatile<int> lock_color;
708     VOLATILE(BOOL) wait_done;
709     VOLATILE(BOOL) joined_p;
710
711     // Keep volatile counted locks on separate cache line write many per join
712     DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
713     VOLATILE(int32_t) join_lock;
714     VOLATILE(int32_t) r_join_lock;
715
716 };
717
718 enum join_type 
719 {
720     type_last_join = 0, 
721     type_join = 1, 
722     type_restart = 2, 
723     type_first_r_join = 3, 
724     type_r_join = 4
725 };
726
727 enum join_time 
728 {
729     time_start = 0, 
730     time_end = 1
731 };
732
733 enum join_heap_index
734 {
735     join_heap_restart = 100,
736     join_heap_r_restart = 200
737 };
738
739 struct join_event
740 {
741     uint32_t heap;
742     join_time time;
743     join_type type;
744 };
745
746 class t_join
747 {
748     join_structure join_struct;
749
750     int id;
751     gc_join_flavor flavor;
752
753 #ifdef JOIN_STATS
754     uint64_t start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
755     // remember join id and last thread to arrive so restart can use these
756     int thd;
757     // we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval
758     uint32_t start_tick;
759     // counters for joins, in 1000's of clock cycles
760     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];
761 #endif //JOIN_STATS
762
763 public:
764     BOOL init (int n_th, gc_join_flavor f)
765     {
766         dprintf (JOIN_LOG, ("Initializing join structure"));
767         join_struct.n_threads = n_th;
768         join_struct.lock_color = 0;
769         for (int i = 0; i < 3; i++)
770         {
771             if (!join_struct.joined_event[i].IsValid())
772             {
773                 join_struct.joined_p = FALSE;
774                 dprintf (JOIN_LOG, ("Creating join event %d", i));
775                 // TODO - changing this to a non OS event
776                 // because this is also used by BGC threads which are 
777                 // managed threads and WaitEx does not allow you to wait
778                 // for an OS event on a managed thread.
779                 // But we are not sure if this plays well in the hosting 
780                 // environment.
781                 //join_struct.joined_event[i].CreateOSManualEventNoThrow(FALSE);
782                 if (!join_struct.joined_event[i].CreateManualEventNoThrow(FALSE))
783                     return FALSE;
784             }
785         }
786         join_struct.join_lock = join_struct.n_threads;
787         join_struct.r_join_lock = join_struct.n_threads;
788         join_struct.wait_done = FALSE;
789         flavor = f;
790
791 #ifdef JOIN_STATS
792         start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
793 #endif //JOIN_STATS
794
795         return TRUE;
796     }
797     
798     void destroy ()
799     {
800         dprintf (JOIN_LOG, ("Destroying join structure"));
801         for (int i = 0; i < 3; i++)
802         {
803             if (join_struct.joined_event[i].IsValid())
804                 join_struct.joined_event[i].CloseEvent();
805         }
806     }
807
808     inline void fire_event (int heap, join_time time, join_type type, int join_id)
809     {
810         FIRE_EVENT(GCJoin_V2, heap, time, type, join_id);
811     }
812
813     void join (gc_heap* gch, int join_id)
814     {
815 #ifdef JOIN_STATS
816         // parallel execution ends here
817         end[gch->heap_number] = get_ts();
818 #endif //JOIN_STATS
819
820         assert (!join_struct.joined_p);
821         int color = join_struct.lock_color.LoadWithoutBarrier();
822
823         if (Interlocked::Decrement(&join_struct.join_lock) != 0)
824         {
825             dprintf (JOIN_LOG, ("join%d(%d): Join() Waiting...join_lock is now %d", 
826                 flavor, join_id, (int32_t)(join_struct.join_lock)));
827
828             fire_event (gch->heap_number, time_start, type_join, join_id);
829
830             //busy wait around the color
831             if (color == join_struct.lock_color.LoadWithoutBarrier())
832             {
833 respin:
834                 int spin_count = 4096 * (gc_heap::n_heaps - 1);
835                 for (int j = 0; j < spin_count; j++)
836                 {
837                     if (color != join_struct.lock_color.LoadWithoutBarrier())
838                     {
839                         break;
840                     }
841                     YieldProcessor();           // indicate to the processor that we are spinning
842                 }
843
844                 // we've spun, and if color still hasn't changed, fall into hard wait
845                 if (color == join_struct.lock_color.LoadWithoutBarrier())
846                 {
847                     dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d", 
848                         flavor, join_id, color, (int32_t)(join_struct.join_lock)));
849
850                     //Thread* current_thread = GCToEEInterface::GetThread();
851                     //BOOL cooperative_mode = gc_heap::enable_preemptive (current_thread);
852                     uint32_t dwJoinWait = join_struct.joined_event[color].Wait(INFINITE, FALSE);
853                     //gc_heap::disable_preemptive (current_thread, cooperative_mode);
854
855                     if (dwJoinWait != WAIT_OBJECT_0)
856                     {
857                         STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
858                         FATAL_GC_ERROR ();
859                     }
860                 }
861
862                 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
863                 if (color == join_struct.lock_color.LoadWithoutBarrier())
864                 {
865                     goto respin;
866                 }
867
868                 dprintf (JOIN_LOG, ("join%d(%d): Join() done, join_lock is %d", 
869                     flavor, join_id, (int32_t)(join_struct.join_lock)));
870             }
871
872             fire_event (gch->heap_number, time_end, type_join, join_id);
873
874 #ifdef JOIN_STATS
875             // parallel execution starts here
876             start[gch->heap_number] = get_ts();
877             Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number]));
878 #endif //JOIN_STATS
879         }
880         else
881         {
882             fire_event (gch->heap_number, time_start, type_last_join, join_id);
883
884             join_struct.joined_p = TRUE;
885             dprintf (JOIN_LOG, ("join%d(%d): Last thread to complete the join, setting id", flavor, join_id));
886             join_struct.joined_event[!color].Reset();
887             id = join_id;
888             // this one is alone so it can proceed
889 #ifdef JOIN_STATS
890             // remember the join id, the last thread arriving, the start of the sequential phase,
891             // and keep track of the cycles spent waiting in the join
892             thd = gch->heap_number;
893             start_seq = get_ts();
894             Interlocked::ExchangeAdd(&in_join_total[join_id], (start_seq - end[gch->heap_number]));
895 #endif //JOIN_STATS
896         }
897     }
898
899     // Reverse join - first thread gets here does the work; other threads will only proceed
900     // after the work is done.
901     // Note that you cannot call this twice in a row on the same thread. Plus there's no 
902     // need to call it twice in row - you should just merge the work.
903     BOOL r_join (gc_heap* gch, int join_id)
904     {
905
906         if (join_struct.n_threads == 1)
907         {
908             return TRUE;
909         }
910
911         if (Interlocked::CompareExchange(&join_struct.r_join_lock, 0, join_struct.n_threads) == 0)
912         {
913             if (!join_struct.wait_done)
914             {
915                 dprintf (JOIN_LOG, ("r_join() Waiting..."));
916
917                 fire_event (gch->heap_number, time_start, type_join, join_id);
918
919                 //busy wait around the color
920                 if (!join_struct.wait_done)
921                 {
922         respin:
923                     int spin_count = 2 * 4096 * (gc_heap::n_heaps - 1);
924                     for (int j = 0; j < spin_count; j++)
925                     {
926                         if (join_struct.wait_done)
927                         {
928                             break;
929                         }
930                         YieldProcessor();           // indicate to the processor that we are spinning
931                     }
932
933                     // we've spun, and if color still hasn't changed, fall into hard wait
934                     if (!join_struct.wait_done)
935                     {
936                         dprintf (JOIN_LOG, ("Join() hard wait on reset event %d", first_thread_arrived));
937                         uint32_t dwJoinWait = join_struct.joined_event[first_thread_arrived].Wait(INFINITE, FALSE);
938                         if (dwJoinWait != WAIT_OBJECT_0)
939                         {
940                             STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
941                             FATAL_GC_ERROR ();
942                         }
943                     }
944
945                     // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
946                     if (!join_struct.wait_done)
947                     {
948                         goto respin;
949                     }
950
951                     dprintf (JOIN_LOG, ("r_join() done"));
952                 }
953
954                 fire_event (gch->heap_number, time_end, type_join, join_id);
955             }
956
957             return FALSE;
958         }
959         else
960         {
961             fire_event (gch->heap_number, time_start, type_first_r_join, join_id);
962             return TRUE;
963         }
964     }
965
966 #ifdef JOIN_STATS
967     uint64_t get_ts()
968     {
969         return GCToOSInterface::QueryPerformanceCounter();
970     }
971
972     void start_ts (gc_heap* gch)
973     {
974         // parallel execution ends here
975         start[gch->heap_number] = get_ts();
976     }
977 #endif //JOIN_STATS
978
979     void restart()
980     {
981 #ifdef JOIN_STATS
982         uint64_t elapsed_seq = get_ts() - start_seq;
983         uint64_t max = 0, sum = 0, wake = 0;
984         uint64_t min_ts = start[0];
985         for (int i = 1; i < join_struct.n_threads; i++)
986         {
987             if(min_ts > start[i]) min_ts = start[i];
988         }
989
990         for (int i = 0; i < join_struct.n_threads; i++)
991         {
992             uint64_t wake_delay = start[i] - min_ts;
993             uint64_t elapsed = end[i] - start[i];
994             if (max < elapsed)
995                 max = elapsed;
996             sum += elapsed;
997             wake += wake_delay;
998         }
999         uint64_t seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
1000         uint64_t par_loss = join_struct.n_threads*max - sum;
1001         double efficiency = 0.0;
1002         if (max > 0)
1003             efficiency = sum*100.0/(join_struct.n_threads*max);
1004
1005         const double ts_scale = 1e-6;
1006
1007         // enable this printf to get statistics on each individual join as it occurs
1008 //      printf("join #%3d  seq_loss = %5g   par_loss = %5g  efficiency = %3.0f%%\n", join_id, ts_scale*seq_loss, ts_scale*par_loss, efficiency);
1009
1010         elapsed_total[id] += sum;
1011         wake_total[id] += wake;
1012         seq_loss_total[id] += seq_loss;
1013         par_loss_total[id] += par_loss;
1014
1015         // every 10 seconds, print a summary of the time spent in each type of join
1016         if (GCToOSInterface::GetLowPrecisionTimeStamp() - start_tick > 10*1000)
1017         {
1018             printf("**** summary *****\n");
1019             for (int i = 0; i < 16; i++)
1020             {
1021                 printf("join #%3d  elapsed_total = %8g wake_loss = %8g seq_loss = %8g  par_loss = %8g  in_join_total = %8g\n",
1022                    i,
1023                    ts_scale*elapsed_total[i],
1024                    ts_scale*wake_total[i],
1025                    ts_scale*seq_loss_total[i],
1026                    ts_scale*par_loss_total[i],
1027                    ts_scale*in_join_total[i]);
1028                 elapsed_total[i] = wake_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
1029             }
1030             start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
1031         }
1032 #endif //JOIN_STATS
1033
1034         fire_event (join_heap_restart, time_start, type_restart, -1);
1035         assert (join_struct.joined_p);
1036         join_struct.joined_p = FALSE;
1037         join_struct.join_lock = join_struct.n_threads;
1038         dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
1039 //        printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start);
1040         int color = join_struct.lock_color.LoadWithoutBarrier();
1041         join_struct.lock_color = !color;
1042         join_struct.joined_event[color].Set();
1043
1044 //        printf("Set joined_event %d\n", !join_struct.lock_color);
1045
1046         fire_event (join_heap_restart, time_end, type_restart, -1);
1047
1048 #ifdef JOIN_STATS
1049         start[thd] = get_ts();
1050 #endif //JOIN_STATS
1051     }
1052     
1053     BOOL joined()
1054     {
1055         dprintf (JOIN_LOG, ("join%d(%d): joined, join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
1056         return join_struct.joined_p;
1057     }
1058
1059     void r_restart()
1060     {
1061         if (join_struct.n_threads != 1)
1062         {
1063             fire_event (join_heap_r_restart, time_start, type_restart, -1);
1064             join_struct.wait_done = TRUE;
1065             join_struct.joined_event[first_thread_arrived].Set();
1066             fire_event (join_heap_r_restart, time_end, type_restart, -1);
1067         }
1068     }
1069
1070     void r_init()
1071     {
1072         if (join_struct.n_threads != 1)
1073         {
1074             join_struct.r_join_lock = join_struct.n_threads;
1075             join_struct.wait_done = FALSE;
1076             join_struct.joined_event[first_thread_arrived].Reset();
1077         }
1078     }
1079 };
1080
1081 t_join gc_t_join;
1082
1083 #ifdef BACKGROUND_GC
1084 t_join bgc_t_join;
1085 #endif //BACKGROUND_GC
1086
1087 #endif //MULTIPLE_HEAPS
1088
1089 #define spin_and_switch(count_to_spin, expr) \
1090 { \
1091     for (int j = 0; j < count_to_spin; j++) \
1092     { \
1093         if (expr) \
1094         { \
1095             break;\
1096         } \
1097         YieldProcessor(); \
1098     } \
1099     if (!(expr)) \
1100     { \
1101         GCToOSInterface::YieldThread(0); \
1102     } \
1103 }
1104
1105 #ifndef DACCESS_COMPILE
1106 #ifdef BACKGROUND_GC
1107
1108 #define max_pending_allocs 64
1109
1110 class exclusive_sync
1111 {
1112     // TODO - verify that this is the right syntax for Volatile.
1113     VOLATILE(uint8_t*) rwp_object;
1114     VOLATILE(int32_t) needs_checking;
1115     
1116     int spin_count;
1117
1118     uint8_t cache_separator[HS_CACHE_LINE_SIZE - sizeof (int) - sizeof (int32_t)];
1119
1120     // TODO - perhaps each object should be on its own cache line...
1121     VOLATILE(uint8_t*) alloc_objects[max_pending_allocs];
1122
1123     int find_free_index ()
1124     {
1125         for (int i = 0; i < max_pending_allocs; i++)
1126         {
1127             if (alloc_objects [i] == (uint8_t*)0)
1128             {
1129                 return i;
1130             }
1131         }
1132  
1133         return -1;
1134     }
1135
1136 public:
1137     void init()
1138     {
1139         spin_count = 32 * (g_num_processors - 1);
1140         rwp_object = 0;
1141         needs_checking = 0;
1142         for (int i = 0; i < max_pending_allocs; i++)
1143         {
1144             alloc_objects [i] = (uint8_t*)0;
1145         }
1146     }
1147
1148     void check()
1149     {
1150         for (int i = 0; i < max_pending_allocs; i++)
1151         {
1152             if (alloc_objects [i] != (uint8_t*)0)
1153             {
1154                 GCToOSInterface::DebugBreak();
1155             }
1156         }
1157     }
1158
1159     void bgc_mark_set (uint8_t* obj)
1160     {
1161         dprintf (3, ("cm: probing %Ix", obj));
1162 retry:
1163         if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
1164         {
1165             // If we spend too much time spending all the allocs,
1166             // consider adding a high water mark and scan up
1167             // to that; we'll need to interlock in done when
1168             // we update the high watermark.
1169             for (int i = 0; i < max_pending_allocs; i++)
1170             {
1171                 if (obj == alloc_objects[i])
1172                 {
1173                     needs_checking = 0;
1174                     dprintf (3, ("cm: will spin"));
1175                     spin_and_switch (spin_count, (obj != alloc_objects[i]));
1176                     goto retry;
1177                 }
1178             }
1179
1180             rwp_object = obj;
1181             needs_checking = 0;
1182             dprintf (3, ("cm: set %Ix", obj));
1183             return;
1184         }
1185         else
1186         {
1187             spin_and_switch (spin_count, (needs_checking == 0));
1188             goto retry;
1189         }
1190     }
1191
1192     int loh_alloc_set (uint8_t* obj)
1193     {
1194         if (!gc_heap::cm_in_progress)
1195         {
1196             return -1;
1197         }
1198
1199 retry:
1200         dprintf (3, ("loh alloc: probing %Ix", obj));
1201
1202         if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
1203         {
1204             if (obj == rwp_object)
1205             {
1206                 needs_checking = 0;
1207                 spin_and_switch (spin_count, (obj != rwp_object));
1208                 goto retry;
1209             }
1210             else
1211             {
1212                 int cookie = find_free_index();
1213
1214                 if (cookie != -1)
1215                 {
1216                     alloc_objects[cookie] = obj;
1217                     needs_checking = 0;
1218                     //if (cookie >= 4)
1219                     //{
1220                     //    GCToOSInterface::DebugBreak();
1221                     //}
1222
1223                     dprintf (3, ("loh alloc: set %Ix at %d", obj, cookie));
1224                     return cookie;
1225                 } 
1226                 else 
1227                 {
1228                     needs_checking = 0;
1229                     dprintf (3, ("loh alloc: setting %Ix will spin to acquire a free index", obj));
1230                     spin_and_switch (spin_count, (find_free_index () != -1));
1231                     goto retry;
1232                 }
1233             }
1234         }
1235         else
1236         {
1237             dprintf (3, ("loh alloc: will spin on checking %Ix", obj));
1238             spin_and_switch (spin_count, (needs_checking == 0));
1239             goto retry;
1240         }
1241     }
1242
1243     void bgc_mark_done ()
1244     {
1245         dprintf (3, ("cm: release lock on %Ix", (uint8_t *)rwp_object));
1246         rwp_object = 0;
1247     }
1248
1249     void loh_alloc_done_with_index (int index)
1250     {
1251         dprintf (3, ("loh alloc: release lock on %Ix based on %d", (uint8_t *)alloc_objects[index], index));
1252         assert ((index >= 0) && (index < max_pending_allocs)); 
1253         alloc_objects[index] = (uint8_t*)0;
1254     }
1255
1256     void loh_alloc_done (uint8_t* obj)
1257     {
1258 #ifdef BACKGROUND_GC
1259         if (!gc_heap::cm_in_progress)
1260         {
1261             return;
1262         }
1263
1264         for (int i = 0; i < max_pending_allocs; i++)
1265         {
1266             if (alloc_objects [i] == obj)
1267             {
1268                 dprintf (3, ("loh alloc: release lock on %Ix at %d", (uint8_t *)alloc_objects[i], i));
1269                 alloc_objects[i] = (uint8_t*)0;
1270                 return;
1271             }
1272         }
1273 #endif //BACKGROUND_GC
1274     }
1275 };
1276
1277 // Note that this class was written assuming just synchronization between
1278 // one background GC thread and multiple user threads that might request 
1279 // an FGC - it does not take into account what kind of locks the multiple
1280 // user threads might be holding at the time (eg, there could only be one
1281 // user thread requesting an FGC because it needs to take gc_lock first)
1282 // so you'll see checks that may not be necessary if you take those conditions
1283 // into consideration.
1284 //
1285 // With the introduction of Server Background GC we no longer use this
1286 // class to do synchronization between FGCs and BGC.
1287 class recursive_gc_sync
1288 {
1289     static VOLATILE(int32_t) foreground_request_count;//initial state 0
1290     static VOLATILE(BOOL) gc_background_running; //initial state FALSE
1291     static VOLATILE(int32_t) foreground_count; // initial state 0;
1292     static VOLATILE(uint32_t) foreground_gate; // initial state FALSE;
1293     static GCEvent foreground_complete;//Auto Reset
1294     static GCEvent foreground_allowed;//Auto Reset
1295 public:
1296     static void begin_background();
1297     static void end_background();
1298     static void begin_foreground();
1299     static void end_foreground();
1300     BOOL allow_foreground ();
1301     static BOOL init();
1302     static void shutdown();
1303     static BOOL background_running_p() {return gc_background_running;}
1304 };
1305
1306 VOLATILE(int32_t) recursive_gc_sync::foreground_request_count = 0;//initial state 0
1307 VOLATILE(int32_t) recursive_gc_sync::foreground_count = 0; // initial state 0;
1308 VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE
1309 VOLATILE(uint32_t) recursive_gc_sync::foreground_gate = 0;
1310 GCEvent recursive_gc_sync::foreground_complete;//Auto Reset
1311 GCEvent recursive_gc_sync::foreground_allowed;//Manual Reset
1312
1313 BOOL recursive_gc_sync::init ()
1314 {
1315     foreground_request_count = 0;
1316     foreground_count = 0;
1317     gc_background_running = FALSE;
1318     foreground_gate = 0;
1319
1320     if (!foreground_complete.CreateOSAutoEventNoThrow(FALSE))
1321     {
1322         goto error;
1323     }
1324     if (!foreground_allowed.CreateManualEventNoThrow(FALSE))
1325     {
1326         goto error;
1327     }
1328     return TRUE;
1329
1330 error:
1331     shutdown();
1332     return FALSE;
1333
1334 }
1335
1336 void recursive_gc_sync::shutdown()
1337 {
1338     if (foreground_complete.IsValid())
1339         foreground_complete.CloseEvent();
1340     if (foreground_allowed.IsValid())
1341         foreground_allowed.CloseEvent();
1342 }
1343
1344 void recursive_gc_sync::begin_background()
1345 {
1346     dprintf (2, ("begin background"));
1347     foreground_request_count = 1;
1348     foreground_count = 1;
1349     foreground_allowed.Reset();
1350     gc_background_running = TRUE;
1351 }
1352 void recursive_gc_sync::end_background()
1353 {
1354     dprintf (2, ("end background"));
1355     gc_background_running = FALSE;
1356     foreground_gate = 1;
1357     foreground_allowed.Set();
1358 }
1359
1360 void recursive_gc_sync::begin_foreground()
1361 {
1362     dprintf (2, ("begin_foreground"));
1363
1364     bool cooperative_mode = false;
1365     if (gc_background_running)
1366     {
1367         gc_heap::fire_alloc_wait_event_begin (awr_fgc_wait_for_bgc);
1368         gc_heap::alloc_wait_event_p = TRUE;
1369
1370 try_again_top:
1371
1372         Interlocked::Increment (&foreground_request_count);
1373
1374 try_again_no_inc:
1375         dprintf(2, ("Waiting sync gc point"));
1376         assert (foreground_allowed.IsValid());
1377         assert (foreground_complete.IsValid());
1378
1379         cooperative_mode = gc_heap::enable_preemptive ();
1380
1381         foreground_allowed.Wait(INFINITE, FALSE);
1382
1383         dprintf(2, ("Waiting sync gc point is done"));
1384
1385         gc_heap::disable_preemptive (cooperative_mode);
1386
1387         if (foreground_gate)
1388         {
1389             Interlocked::Increment (&foreground_count);
1390             dprintf (2, ("foreground_count: %d", (int32_t)foreground_count));
1391             if (foreground_gate)
1392             {
1393                 gc_heap::settings.concurrent = FALSE;
1394                 return;
1395             }
1396             else
1397             {
1398                 end_foreground();
1399                 goto try_again_top;
1400             }
1401         }
1402         else
1403         {
1404             goto try_again_no_inc;
1405         }
1406     }
1407 }
1408
1409 void recursive_gc_sync::end_foreground()
1410 {
1411     dprintf (2, ("end_foreground"));
1412     if (gc_background_running)
1413     {
1414         Interlocked::Decrement (&foreground_request_count);
1415         dprintf (2, ("foreground_count before decrement: %d", (int32_t)foreground_count));
1416         if (Interlocked::Decrement (&foreground_count) == 0)
1417         {
1418             //c_write ((BOOL*)&foreground_gate, 0);
1419             // TODO - couldn't make the syntax work with Volatile<T>
1420             foreground_gate = 0;
1421             if (foreground_count == 0)
1422             {
1423                 foreground_allowed.Reset ();
1424                 dprintf(2, ("setting foreground complete event"));
1425                 foreground_complete.Set();
1426             }
1427         }
1428     }
1429 }
1430
1431 inline
1432 BOOL recursive_gc_sync::allow_foreground()
1433 {
1434     assert (gc_heap::settings.concurrent);
1435     dprintf (100, ("enter allow_foreground, f_req_count: %d, f_count: %d",
1436                    (int32_t)foreground_request_count, (int32_t)foreground_count));
1437
1438     BOOL did_fgc = FALSE;
1439
1440     //if we have suspended the EE, just return because
1441     //some thread could be waiting on this to proceed.
1442     if (!GCHeap::GcInProgress)
1443     {
1444         //TODO BACKGROUND_GC This is to stress the concurrency between
1445         //background and foreground
1446 //        gc_heap::disallow_new_allocation (0);
1447
1448         //GCToOSInterface::YieldThread(0);
1449
1450         //END of TODO
1451         if (foreground_request_count != 0)
1452         {
1453             //foreground wants to run
1454             //save the important settings
1455             //TODO BACKGROUND_GC be more selective about the important settings.
1456             gc_mechanisms saved_settings = gc_heap::settings;
1457             do
1458             {
1459                 did_fgc = TRUE;
1460                 //c_write ((BOOL*)&foreground_gate, 1);
1461                 // TODO - couldn't make the syntax work with Volatile<T>
1462                 foreground_gate = 1;
1463                 foreground_allowed.Set ();
1464                 foreground_complete.Wait (INFINITE, FALSE);
1465             }while (/*foreground_request_count ||*/ foreground_gate);
1466
1467             assert (!foreground_gate);
1468
1469             //restore the important settings
1470             gc_heap::settings = saved_settings;
1471             GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
1472             //the background GC shouldn't be using gc_high and gc_low
1473             //gc_low = lowest_address;
1474             //gc_high = highest_address;
1475         }
1476
1477         //TODO BACKGROUND_GC This is to stress the concurrency between
1478         //background and foreground
1479 //        gc_heap::allow_new_allocation (0);
1480         //END of TODO
1481     }
1482
1483     dprintf (100, ("leave allow_foreground"));
1484     assert (gc_heap::settings.concurrent);
1485     return did_fgc;
1486 }
1487
1488 #endif //BACKGROUND_GC
1489 #endif //DACCESS_COMPILE
1490
1491
1492 #if  defined(COUNT_CYCLES)
1493 #ifdef _MSC_VER
1494 #pragma warning(disable:4035)
1495 #endif //_MSC_VER
1496
1497 static
1498 unsigned        GetCycleCount32()        // enough for about 40 seconds
1499 {
1500 __asm   push    EDX
1501 __asm   _emit   0x0F
1502 __asm   _emit   0x31
1503 __asm   pop     EDX
1504 };
1505
1506 #pragma warning(default:4035)
1507
1508 #endif //COUNT_CYCLES
1509
1510 #ifdef TIME_GC
1511 int mark_time, plan_time, sweep_time, reloc_time, compact_time;
1512 #endif //TIME_GC
1513
1514 #ifndef MULTIPLE_HEAPS
1515
1516 #endif // MULTIPLE_HEAPS
1517
1518 void reset_memory (uint8_t* o, size_t sizeo);
1519
1520 #ifdef WRITE_WATCH
1521
1522 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1523 static bool virtual_alloc_hardware_write_watch = false;
1524 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1525
1526 static bool hardware_write_watch_capability = false;
1527
1528 #ifndef DACCESS_COMPILE
1529
1530 //check if the write watch APIs are supported.
1531
1532 void hardware_write_watch_api_supported()
1533 {
1534     if (GCToOSInterface::SupportsWriteWatch())
1535     {
1536         hardware_write_watch_capability = true;
1537         dprintf (2, ("WriteWatch supported"));
1538     }
1539     else
1540     {
1541         dprintf (2,("WriteWatch not supported"));
1542     }
1543 }
1544
1545 #endif //!DACCESS_COMPILE
1546
1547 inline bool can_use_hardware_write_watch()
1548 {
1549     return hardware_write_watch_capability;
1550 }
1551
1552 inline bool can_use_write_watch_for_gc_heap()
1553 {
1554 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1555     return true;
1556 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1557     return can_use_hardware_write_watch();
1558 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1559 }
1560
1561 inline bool can_use_write_watch_for_card_table()
1562 {
1563 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
1564     return true;
1565 #else
1566     return can_use_hardware_write_watch();
1567 #endif
1568 }
1569
1570 #else
1571 #define mem_reserve (MEM_RESERVE)
1572 #endif //WRITE_WATCH
1573
1574 //check if the low memory notification is supported
1575
1576 #ifndef DACCESS_COMPILE
1577
1578 void WaitLongerNoInstru (int i)
1579 {
1580     // every 8th attempt:
1581     bool bToggleGC = GCToEEInterface::EnablePreemptiveGC();
1582
1583     // if we're waiting for gc to finish, we should block immediately
1584     if (g_fSuspensionPending == 0)
1585     {
1586         if  (g_num_processors > 1)
1587         {
1588             YieldProcessor();           // indicate to the processor that we are spining
1589             if  (i & 0x01f)
1590                 GCToOSInterface::YieldThread (0);
1591             else
1592                 GCToOSInterface::Sleep (5);
1593         }
1594         else
1595             GCToOSInterface::Sleep (5);
1596     }
1597
1598     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1599     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1600     // It is important that the thread is going to wait for GC.  Otherwise the thread
1601     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD.
1602     if (bToggleGC)
1603     {
1604 #ifdef _DEBUG
1605         // In debug builds, all enter_spin_lock operations go through this code.  If a GC has
1606         // started, it is important to block until the GC thread calls set_gc_done (since it is
1607         // guaranteed to have cleared g_TrapReturningThreads by this point).  This avoids livelock
1608         // conditions which can otherwise occur if threads are allowed to spin in this function
1609         // (and therefore starve the GC thread) between the point when the GC thread sets the
1610         // WaitForGC event and the point when the GC thread clears g_TrapReturningThreads.
1611         if (gc_heap::gc_started)
1612         {
1613             gc_heap::wait_for_gc_done();
1614         }
1615 #endif // _DEBUG
1616         GCToEEInterface::DisablePreemptiveGC();
1617     }
1618     else if (g_fSuspensionPending > 0)
1619     {
1620         g_theGCHeap->WaitUntilGCComplete();
1621     }
1622 }
1623
1624 inline
1625 static void safe_switch_to_thread()
1626 {
1627     bool cooperative_mode = gc_heap::enable_preemptive();
1628
1629     GCToOSInterface::YieldThread(0);
1630
1631     gc_heap::disable_preemptive(cooperative_mode);
1632 }
1633
1634 //
1635 // We need the following methods to have volatile arguments, so that they can accept
1636 // raw pointers in addition to the results of the & operator on Volatile<T>.
1637 //
1638 inline
1639 static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1640 {
1641 retry:
1642
1643     if (Interlocked::CompareExchange(lock, 0, -1) >= 0)
1644     {
1645         unsigned int i = 0;
1646         while (VolatileLoad(lock) >= 0)
1647         {
1648             if ((++i & 7) && !IsGCInProgress())
1649             {
1650                 if  (g_num_processors > 1)
1651                 {
1652 #ifndef MULTIPLE_HEAPS
1653                     int spin_count = 1024 * g_num_processors;
1654 #else //!MULTIPLE_HEAPS
1655                     int spin_count = 32 * g_num_processors;
1656 #endif //!MULTIPLE_HEAPS
1657                     for (int j = 0; j < spin_count; j++)
1658                     {
1659                         if  (VolatileLoad(lock) < 0 || IsGCInProgress())
1660                             break;
1661                         YieldProcessor();           // indicate to the processor that we are spining
1662                     }
1663                     if  (VolatileLoad(lock) >= 0 && !IsGCInProgress())
1664                     {
1665                         safe_switch_to_thread();
1666                     }
1667                 }
1668                 else
1669                 {
1670                     safe_switch_to_thread();
1671                 }
1672             }
1673             else
1674             {
1675                 WaitLongerNoInstru(i);
1676             }
1677         }
1678         goto retry;
1679     }
1680 }
1681
1682 inline
1683 static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) int32_t* lock)
1684 {
1685     return (Interlocked::CompareExchange(&*lock, 0, -1) < 0);
1686 }
1687
1688 inline
1689 static void leave_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1690 {
1691     VolatileStore<int32_t>((int32_t*)lock, -1);
1692 }
1693
1694 #ifdef _DEBUG
1695
1696 inline
1697 static void enter_spin_lock(GCSpinLock *pSpinLock)
1698 {
1699     enter_spin_lock_noinstru(&pSpinLock->lock);
1700     assert (pSpinLock->holding_thread == (Thread*)-1);
1701     pSpinLock->holding_thread = GCToEEInterface::GetThread();
1702 }
1703
1704 inline
1705 static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock)
1706 {
1707     BOOL ret = try_enter_spin_lock_noinstru(&pSpinLock->lock);
1708     if (ret)
1709         pSpinLock->holding_thread = GCToEEInterface::GetThread();
1710     return ret;
1711 }
1712
1713 inline
1714 static void leave_spin_lock(GCSpinLock *pSpinLock)
1715 {
1716     bool gc_thread_p = GCToEEInterface::WasCurrentThreadCreatedByGC();
1717 //    _ASSERTE((pSpinLock->holding_thread == GCToEEInterface::GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p);
1718     pSpinLock->released_by_gc_p = gc_thread_p;
1719     pSpinLock->holding_thread = (Thread*) -1;
1720     if (pSpinLock->lock != -1)
1721         leave_spin_lock_noinstru(&pSpinLock->lock);
1722 }
1723
1724 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) \
1725     _ASSERTE((pSpinLock)->holding_thread == GCToEEInterface::GetThread());
1726
1727 #define ASSERT_NOT_HOLDING_SPIN_LOCK(pSpinLock) \
1728     _ASSERTE((pSpinLock)->holding_thread != GCToEEInterface::GetThread());
1729
1730 #else //_DEBUG
1731
1732 //In the concurrent version, the Enable/DisablePreemptiveGC is optional because
1733 //the gc thread call WaitLonger.
1734 void WaitLonger (int i
1735 #ifdef SYNCHRONIZATION_STATS
1736     , GCSpinLock* spin_lock
1737 #endif //SYNCHRONIZATION_STATS
1738     )
1739 {
1740 #ifdef SYNCHRONIZATION_STATS
1741     (spin_lock->num_wait_longer)++;
1742 #endif //SYNCHRONIZATION_STATS
1743
1744     // every 8th attempt:
1745     bool bToggleGC = GCToEEInterface::EnablePreemptiveGC();
1746     assert (bToggleGC);
1747
1748     // if we're waiting for gc to finish, we should block immediately
1749     if (!gc_heap::gc_started)
1750     {
1751 #ifdef SYNCHRONIZATION_STATS
1752         (spin_lock->num_switch_thread_w)++;
1753 #endif //SYNCHRONIZATION_STATS
1754         if  (g_num_processors > 1)
1755         {
1756             YieldProcessor();           // indicate to the processor that we are spining
1757             if  (i & 0x01f)
1758                 GCToOSInterface::YieldThread (0);
1759             else
1760                 GCToOSInterface::Sleep (5);
1761         }
1762         else
1763             GCToOSInterface::Sleep (5);
1764     }
1765
1766     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1767     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1768     // It is important that the thread is going to wait for GC.  Otherwise the thread
1769     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD. 
1770     if (gc_heap::gc_started)
1771     {
1772         gc_heap::wait_for_gc_done();
1773     }
1774
1775     if (bToggleGC)
1776     {
1777 #ifdef SYNCHRONIZATION_STATS
1778         (spin_lock->num_disable_preemptive_w)++;
1779 #endif //SYNCHRONIZATION_STATS
1780         GCToEEInterface::DisablePreemptiveGC();
1781     }
1782 }
1783
1784 inline
1785 static void enter_spin_lock (GCSpinLock* spin_lock)
1786 {
1787 retry:
1788
1789     if (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) >= 0)
1790     {
1791         unsigned int i = 0;
1792         while (spin_lock->lock >= 0)
1793         {
1794             if ((++i & 7) && !gc_heap::gc_started)
1795             {
1796                 if  (g_num_processors > 1)
1797                 {
1798 #ifndef MULTIPLE_HEAPS
1799                     int spin_count = 1024 * g_num_processors;
1800 #else //!MULTIPLE_HEAPS
1801                     int spin_count = 32 * g_num_processors;
1802 #endif //!MULTIPLE_HEAPS
1803                     for (int j = 0; j < spin_count; j++)
1804                     {
1805                         if  (spin_lock->lock < 0 || gc_heap::gc_started)
1806                             break;
1807                         YieldProcessor();           // indicate to the processor that we are spining
1808                     }
1809                     if  (spin_lock->lock >= 0 && !gc_heap::gc_started)
1810                     {
1811 #ifdef SYNCHRONIZATION_STATS
1812                         (spin_lock->num_switch_thread)++;
1813 #endif //SYNCHRONIZATION_STATS
1814                         bool cooperative_mode = gc_heap::enable_preemptive ();
1815
1816                         GCToOSInterface::YieldThread(0);
1817
1818                         gc_heap::disable_preemptive (cooperative_mode);
1819                     }
1820                 }
1821                 else
1822                     GCToOSInterface::YieldThread(0);
1823             }
1824             else
1825             {
1826                 WaitLonger(i
1827 #ifdef SYNCHRONIZATION_STATS
1828                         , spin_lock
1829 #endif //SYNCHRONIZATION_STATS
1830                     );
1831             }
1832         }
1833         goto retry;
1834     }
1835 }
1836
1837 inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock)
1838 {
1839     return (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) < 0);
1840 }
1841
1842 inline
1843 static void leave_spin_lock (GCSpinLock * spin_lock)
1844 {
1845     spin_lock->lock = -1;
1846 }
1847
1848 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock)
1849
1850 #endif //_DEBUG
1851
1852 bool gc_heap::enable_preemptive ()
1853 {
1854     return GCToEEInterface::EnablePreemptiveGC();
1855 }
1856
1857 void gc_heap::disable_preemptive (bool restore_cooperative)
1858 {
1859     if (restore_cooperative)
1860     {
1861         GCToEEInterface::DisablePreemptiveGC();
1862     }
1863 }
1864
1865 #endif // !DACCESS_COMPILE
1866
1867 typedef void **  PTR_PTR;
1868 //This function clears a piece of memory
1869 // size has to be Dword aligned
1870
1871 inline
1872 void memclr ( uint8_t* mem, size_t size)
1873 {
1874     dprintf (3, ("MEMCLR: %Ix, %d", mem, size));
1875     assert ((size & (sizeof(PTR_PTR)-1)) == 0);
1876     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1877
1878 #if 0
1879     // The compiler will recognize this pattern and replace it with memset call. We can as well just call 
1880     // memset directly to make it obvious what's going on.
1881     PTR_PTR m = (PTR_PTR) mem;
1882     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
1883         *(m++) = 0;
1884 #endif
1885
1886     memset (mem, 0, size);
1887 }
1888
1889 void memcopy (uint8_t* dmem, uint8_t* smem, size_t size)
1890 {
1891     const size_t sz4ptr = sizeof(PTR_PTR)*4;
1892     const size_t sz2ptr = sizeof(PTR_PTR)*2;
1893     const size_t sz1ptr = sizeof(PTR_PTR)*1;
1894
1895     // size must be a multiple of the pointer size
1896     assert ((size & (sizeof (PTR_PTR)-1)) == 0);
1897     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1898
1899     // copy in groups of four pointer sized things at a time
1900     if (size >= sz4ptr)
1901     {
1902         do
1903         {
1904             ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1905             ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1906             ((PTR_PTR)dmem)[2] = ((PTR_PTR)smem)[2];
1907             ((PTR_PTR)dmem)[3] = ((PTR_PTR)smem)[3];
1908             dmem += sz4ptr;
1909             smem += sz4ptr;
1910         }
1911         while ((size -= sz4ptr) >= sz4ptr);
1912     }
1913
1914     // still two pointer sized things or more left to copy?
1915     if (size & sz2ptr)
1916     {
1917         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1918         ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1919         dmem += sz2ptr;
1920         smem += sz2ptr;
1921     }
1922
1923     // still one pointer sized thing left to copy?
1924     if (size & sz1ptr)
1925     {
1926         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1927         // dmem += sz1ptr;
1928         // smem += sz1ptr;
1929     }
1930
1931 }
1932
1933 inline
1934 ptrdiff_t round_down (ptrdiff_t add, int pitch)
1935 {
1936     return ((add / pitch) * pitch);
1937 }
1938
1939 #if defined(FEATURE_STRUCTALIGN) && defined(RESPECT_LARGE_ALIGNMENT)
1940 // FEATURE_STRUCTALIGN allows the compiler to dictate the alignment,
1941 // i.e, if a larger alignment matters or is beneficial, the compiler
1942 // generated info tells us so.  RESPECT_LARGE_ALIGNMENT is just the
1943 // converse - it's a heuristic for the GC to use a larger alignment.
1944 #error FEATURE_STRUCTALIGN should imply !RESPECT_LARGE_ALIGNMENT
1945 #endif
1946
1947 #if defined(FEATURE_STRUCTALIGN) && defined(FEATURE_LOH_COMPACTION)
1948 #error FEATURE_STRUCTALIGN and FEATURE_LOH_COMPACTION are mutually exclusive
1949 #endif
1950
1951 #if defined(GROWABLE_SEG_MAPPING_TABLE) && !defined(SEG_MAPPING_TABLE)
1952 #error if GROWABLE_SEG_MAPPING_TABLE is defined, SEG_MAPPING_TABLE must be defined
1953 #endif
1954
1955 inline
1956 BOOL same_large_alignment_p (uint8_t* p1, uint8_t* p2)
1957 {
1958 #ifdef RESPECT_LARGE_ALIGNMENT
1959     return ((((size_t)p1 ^ (size_t)p2) & 7) == 0);
1960 #else
1961     UNREFERENCED_PARAMETER(p1);
1962     UNREFERENCED_PARAMETER(p2);
1963     return TRUE;
1964 #endif //RESPECT_LARGE_ALIGNMENT
1965 }
1966
1967 inline 
1968 size_t switch_alignment_size (BOOL already_padded_p)
1969 {
1970     if (already_padded_p)
1971         return DATA_ALIGNMENT;
1972     else
1973         return (Align (min_obj_size) +((Align (min_obj_size)&DATA_ALIGNMENT)^DATA_ALIGNMENT));
1974 }
1975
1976
1977 #ifdef FEATURE_STRUCTALIGN
1978 void set_node_aligninfo (uint8_t *node, int requiredAlignment, ptrdiff_t pad);
1979 void clear_node_aligninfo (uint8_t *node);
1980 #else // FEATURE_STRUCTALIGN
1981 #define node_realigned(node)    (((plug_and_reloc*)(node))[-1].reloc & 1)
1982 void set_node_realigned (uint8_t* node);
1983 void clear_node_realigned(uint8_t* node);
1984 #endif // FEATURE_STRUCTALIGN
1985
1986 inline
1987 size_t AlignQword (size_t nbytes)
1988 {
1989 #ifdef FEATURE_STRUCTALIGN
1990     // This function is used to align everything on the large object
1991     // heap to an 8-byte boundary, to reduce the number of unaligned
1992     // accesses to (say) arrays of doubles.  With FEATURE_STRUCTALIGN,
1993     // the compiler dictates the optimal alignment instead of having
1994     // a heuristic in the GC.
1995     return Align (nbytes);
1996 #else // FEATURE_STRUCTALIGN
1997     return (nbytes + 7) & ~7;
1998 #endif // FEATURE_STRUCTALIGN
1999 }
2000
2001 inline
2002 BOOL Aligned (size_t n)
2003 {
2004     return (n & ALIGNCONST) == 0;
2005 }
2006
2007 #define OBJECT_ALIGNMENT_OFFSET (sizeof(MethodTable *))
2008
2009 #ifdef FEATURE_STRUCTALIGN
2010 #define MAX_STRUCTALIGN OS_PAGE_SIZE
2011 #else // FEATURE_STRUCTALIGN
2012 #define MAX_STRUCTALIGN 0
2013 #endif // FEATURE_STRUCTALIGN
2014
2015 #ifdef FEATURE_STRUCTALIGN
2016 inline
2017 ptrdiff_t AdjustmentForMinPadSize(ptrdiff_t pad, int requiredAlignment)
2018 {
2019     // The resulting alignpad must be either 0 or at least min_obj_size.
2020     // Note that by computing the following difference on unsigned types,
2021     // we can do the range check 0 < alignpad < min_obj_size with a
2022     // single conditional branch.
2023     if ((size_t)(pad - DATA_ALIGNMENT) < Align (min_obj_size) - DATA_ALIGNMENT)
2024     {
2025         return requiredAlignment;
2026     }
2027     return 0;
2028 }
2029
2030 inline
2031 uint8_t* StructAlign (uint8_t* origPtr, int requiredAlignment, ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
2032 {
2033     // required alignment must be a power of two
2034     _ASSERTE(((size_t)origPtr & ALIGNCONST) == 0);
2035     _ASSERTE(((requiredAlignment - 1) & requiredAlignment) == 0);
2036     _ASSERTE(requiredAlignment >= sizeof(void *));
2037     _ASSERTE(requiredAlignment <= MAX_STRUCTALIGN);
2038
2039     // When this method is invoked for individual objects (i.e., alignmentOffset
2040     // is just the size of the PostHeader), what needs to be aligned when
2041     // we're done is the pointer to the payload of the object (which means
2042     // the actual resulting object pointer is typically not aligned).
2043
2044     uint8_t* result = (uint8_t*)Align ((size_t)origPtr + alignmentOffset, requiredAlignment-1) - alignmentOffset;
2045     ptrdiff_t alignpad = result - origPtr;
2046
2047     return result + AdjustmentForMinPadSize (alignpad, requiredAlignment);
2048 }
2049
2050 inline
2051 ptrdiff_t ComputeStructAlignPad (uint8_t* plug, int requiredAlignment, size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
2052 {
2053     return StructAlign (plug, requiredAlignment, alignmentOffset) - plug;
2054 }
2055
2056 BOOL IsStructAligned (uint8_t *ptr, int requiredAlignment)
2057 {
2058     return StructAlign (ptr, requiredAlignment) == ptr;
2059 }
2060
2061 inline
2062 ptrdiff_t ComputeMaxStructAlignPad (int requiredAlignment)
2063 {
2064     if (requiredAlignment == DATA_ALIGNMENT)
2065         return 0;
2066     // Since a non-zero alignment padding cannot be less than min_obj_size (so we can fit the
2067     // alignment padding object), the worst-case alignment padding is correspondingly larger
2068     // than the required alignment.
2069     return requiredAlignment + Align (min_obj_size) - DATA_ALIGNMENT;
2070 }
2071
2072 inline
2073 ptrdiff_t ComputeMaxStructAlignPadLarge (int requiredAlignment)
2074 {
2075     if (requiredAlignment <= get_alignment_constant (TRUE)+1)
2076         return 0;
2077     // This is the same as ComputeMaxStructAlignPad, except that in addition to leaving space
2078     // for padding before the actual object, it also leaves space for filling a gap after the
2079     // actual object.  This is needed on the large object heap, as the outer allocation functions
2080     // don't operate on an allocation context (which would have left space for the final gap).
2081     return requiredAlignment + Align (min_obj_size) * 2 - DATA_ALIGNMENT;
2082 }
2083
2084 uint8_t* gc_heap::pad_for_alignment (uint8_t* newAlloc, int requiredAlignment, size_t size, alloc_context* acontext)
2085 {
2086     uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2087     if (alignedPtr != newAlloc) {
2088         make_unused_array (newAlloc, alignedPtr - newAlloc);
2089     }
2090     acontext->alloc_ptr = alignedPtr + Align (size);
2091     return alignedPtr;
2092 }
2093
2094 uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignment, size_t size)
2095 {
2096     uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2097     if (alignedPtr != newAlloc) {
2098         make_unused_array (newAlloc, alignedPtr - newAlloc);
2099     }
2100     if (alignedPtr < newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment)) {
2101         make_unused_array (alignedPtr + AlignQword (size), newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment) - alignedPtr);
2102     }
2103     return alignedPtr;
2104 }
2105 #else // FEATURE_STRUCTALIGN
2106 #define ComputeMaxStructAlignPad(requiredAlignment) 0
2107 #define ComputeMaxStructAlignPadLarge(requiredAlignment) 0
2108 #endif // FEATURE_STRUCTALIGN
2109
2110 //CLR_SIZE  is the max amount of bytes from gen0 that is set to 0 in one chunk
2111 #ifdef SERVER_GC
2112 #define CLR_SIZE ((size_t)(8*1024))
2113 #else //SERVER_GC
2114 #define CLR_SIZE ((size_t)(8*1024))
2115 #endif //SERVER_GC
2116
2117 #define END_SPACE_AFTER_GC (LARGE_OBJECT_SIZE + MAX_STRUCTALIGN)
2118
2119 #ifdef BACKGROUND_GC
2120 #define SEGMENT_INITIAL_COMMIT (2*OS_PAGE_SIZE)
2121 #else
2122 #define SEGMENT_INITIAL_COMMIT (OS_PAGE_SIZE)
2123 #endif //BACKGROUND_GC
2124
2125 #ifdef SERVER_GC
2126
2127 #ifdef BIT64
2128
2129 #define INITIAL_ALLOC ((size_t)((size_t)4*1024*1024*1024))
2130 #define LHEAP_ALLOC   ((size_t)(1024*1024*256))
2131
2132 #else
2133
2134 #define INITIAL_ALLOC ((size_t)(1024*1024*64))
2135 #define LHEAP_ALLOC   ((size_t)(1024*1024*32))
2136
2137 #endif  // BIT64
2138
2139 #else //SERVER_GC
2140
2141 #ifdef BIT64
2142
2143 #define INITIAL_ALLOC ((size_t)(1024*1024*256))
2144 #define LHEAP_ALLOC   ((size_t)(1024*1024*128))
2145
2146 #else
2147
2148 #define INITIAL_ALLOC ((size_t)(1024*1024*16))
2149 #define LHEAP_ALLOC   ((size_t)(1024*1024*16))
2150
2151 #endif  // BIT64
2152
2153 #endif //SERVER_GC
2154
2155 //amount in bytes of the etw allocation tick
2156 const size_t etw_allocation_tick = 100*1024;
2157
2158 const size_t low_latency_alloc = 256*1024;
2159
2160 const size_t fgn_check_quantum = 2*1024*1024;
2161
2162 #ifdef MH_SC_MARK
2163 const int max_snoop_level = 128;
2164 #endif //MH_SC_MARK
2165
2166
2167 #ifdef CARD_BUNDLE
2168 //threshold of heap size to turn on card bundles.
2169 #define SH_TH_CARD_BUNDLE  (40*1024*1024)
2170 #define MH_TH_CARD_BUNDLE  (180*1024*1024)
2171 #endif //CARD_BUNDLE
2172
2173 #define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000
2174
2175 inline
2176 size_t align_on_page (size_t add)
2177 {
2178     return ((add + OS_PAGE_SIZE - 1) & ~((size_t)OS_PAGE_SIZE - 1));
2179 }
2180
2181 inline
2182 uint8_t* align_on_page (uint8_t* add)
2183 {
2184     return (uint8_t*)align_on_page ((size_t) add);
2185 }
2186
2187 inline
2188 size_t align_lower_page (size_t add)
2189 {
2190     return (add & ~((size_t)OS_PAGE_SIZE - 1));
2191 }
2192
2193 inline
2194 uint8_t* align_lower_page (uint8_t* add)
2195 {
2196     return (uint8_t*)align_lower_page ((size_t)add);
2197 }
2198
2199 inline
2200 size_t align_write_watch_lower_page (size_t add)
2201 {
2202     return (add & ~(WRITE_WATCH_UNIT_SIZE - 1));
2203 }
2204
2205 inline
2206 uint8_t* align_write_watch_lower_page (uint8_t* add)
2207 {
2208     return (uint8_t*)align_lower_page ((size_t)add);
2209 }
2210
2211
2212 inline
2213 BOOL power_of_two_p (size_t integer)
2214 {
2215     return !(integer & (integer-1));
2216 }
2217
2218 inline
2219 BOOL oddp (size_t integer)
2220 {
2221     return (integer & 1) != 0;
2222 }
2223
2224 // we only ever use this for WORDs.
2225 size_t logcount (size_t word)
2226 {
2227     //counts the number of high bits in a 16 bit word.
2228     assert (word < 0x10000);
2229     size_t count;
2230     count = (word & 0x5555) + ( (word >> 1 ) & 0x5555);
2231     count = (count & 0x3333) + ( (count >> 2) & 0x3333);
2232     count = (count & 0x0F0F) + ( (count >> 4) & 0x0F0F);
2233     count = (count & 0x00FF) + ( (count >> 8) & 0x00FF);
2234     return count;
2235 }
2236
2237 #ifndef DACCESS_COMPILE
2238
2239 void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_bounds_check)
2240 {
2241     WriteBarrierParameters args = {};
2242     args.operation = WriteBarrierOp::StompResize;
2243     args.is_runtime_suspended = is_runtime_suspended;
2244     args.requires_upper_bounds_check = requires_upper_bounds_check;
2245
2246     args.card_table = g_gc_card_table;
2247 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
2248     args.card_bundle_table = g_gc_card_bundle_table;
2249 #endif
2250
2251     args.lowest_address = g_gc_lowest_address;
2252     args.highest_address = g_gc_highest_address;
2253
2254 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2255     if (SoftwareWriteWatch::IsEnabledForGCHeap())
2256     {
2257         args.write_watch_table = g_gc_sw_ww_table;
2258     }
2259 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2260
2261     GCToEEInterface::StompWriteBarrier(&args);
2262 }
2263
2264 void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2265 {
2266     WriteBarrierParameters args = {};
2267     args.operation = WriteBarrierOp::StompEphemeral;
2268     args.is_runtime_suspended = true;
2269     args.ephemeral_low = ephemeral_low;
2270     args.ephemeral_high = ephemeral_high;
2271     GCToEEInterface::StompWriteBarrier(&args);
2272 }
2273
2274 void stomp_write_barrier_initialize(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2275 {
2276     WriteBarrierParameters args = {};
2277     args.operation = WriteBarrierOp::Initialize;
2278     args.is_runtime_suspended = true;
2279     args.requires_upper_bounds_check = false;
2280     args.card_table = g_gc_card_table;
2281
2282 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
2283     args.card_bundle_table = g_gc_card_bundle_table;
2284 #endif
2285     
2286     args.lowest_address = g_gc_lowest_address;
2287     args.highest_address = g_gc_highest_address;
2288     args.ephemeral_low = ephemeral_low;
2289     args.ephemeral_high = ephemeral_high;
2290     GCToEEInterface::StompWriteBarrier(&args);
2291 }
2292
2293 #endif // DACCESS_COMPILE
2294
2295 //extract the low bits [0,low[ of a uint32_t
2296 #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1))
2297 //extract the high bits [high, 32] of a uint32_t
2298 #define highbits(wrd, bits) ((wrd) & ~((1 << (bits))-1))
2299
2300 // Things we need to manually initialize:
2301 // gen0 min_size - based on cache
2302 // gen0/1 max_size - based on segment size
2303 static static_data static_data_table[latency_level_last - latency_level_first + 1][NUMBERGENERATIONS] = 
2304 {
2305     // latency_level_memory_footprint
2306     {
2307         // gen0
2308         {0, 0, 40000, 0.5f, 9.0f, 20.0f, 1000, 1},
2309         // gen1
2310         {163840, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10},
2311         // gen2
2312         {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100},
2313         // gen3
2314         {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0}
2315     },
2316
2317     // latency_level_balanced
2318     {
2319         // gen0
2320         {0, 0, 40000, 0.5f,
2321 #ifdef MULTIPLE_HEAPS
2322             20.0f, 40.0f,
2323 #else
2324             9.0f, 20.0f,
2325 #endif //MULTIPLE_HEAPS
2326             1000, 1},
2327         // gen1
2328         {9*32*1024, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10},
2329         // gen2
2330         {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100},
2331         // gen3
2332         {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0}
2333     },
2334 };
2335
2336 class mark;
2337 class generation;
2338 class heap_segment;
2339 class CObjectHeader;
2340 class dynamic_data;
2341 class l_heap;
2342 class sorted_table;
2343 class c_synchronize;
2344
2345 #ifdef FEATURE_PREMORTEM_FINALIZATION
2346 #ifndef DACCESS_COMPILE
2347 static
2348 HRESULT AllocateCFinalize(CFinalize **pCFinalize);
2349 #endif //!DACCESS_COMPILE
2350 #endif // FEATURE_PREMORTEM_FINALIZATION
2351
2352 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address);
2353
2354
2355 #ifdef USE_INTROSORT
2356 #define _sort introsort::sort
2357 #else //USE_INTROSORT
2358 #define _sort qsort1
2359 void qsort1(uint8_t** low, uint8_t** high, unsigned int depth);
2360 #endif //USE_INTROSORT
2361
2362 void* virtual_alloc (size_t size);
2363 void virtual_free (void* add, size_t size);
2364
2365 /* per heap static initialization */
2366 #ifdef MARK_ARRAY
2367 #ifndef MULTIPLE_HEAPS
2368 uint32_t*   gc_heap::mark_array;
2369 #endif //MULTIPLE_HEAPS
2370 #endif //MARK_ARRAY
2371
2372 #ifdef MARK_LIST
2373 uint8_t**   gc_heap::g_mark_list;
2374
2375 #ifdef PARALLEL_MARK_LIST_SORT
2376 uint8_t**   gc_heap::g_mark_list_copy;
2377 #endif //PARALLEL_MARK_LIST_SORT
2378
2379 size_t      gc_heap::mark_list_size;
2380 #endif //MARK_LIST
2381
2382 #ifdef SEG_MAPPING_TABLE
2383 seg_mapping* seg_mapping_table;
2384 #endif //SEG_MAPPING_TABLE
2385
2386 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2387 sorted_table* gc_heap::seg_table;
2388 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
2389
2390 #ifdef MULTIPLE_HEAPS
2391 GCEvent     gc_heap::ee_suspend_event;
2392 size_t      gc_heap::min_balance_threshold = 0;
2393 #endif //MULTIPLE_HEAPS
2394
2395 VOLATILE(BOOL) gc_heap::gc_started;
2396
2397 #ifdef MULTIPLE_HEAPS
2398
2399 GCEvent     gc_heap::gc_start_event;
2400
2401 bool        gc_heap::gc_thread_no_affinitize_p = false;
2402
2403 int         gc_heap::n_heaps;
2404
2405 gc_heap**   gc_heap::g_heaps;
2406
2407 size_t*     gc_heap::g_promoted;
2408
2409 #ifdef MH_SC_MARK
2410 int*        gc_heap::g_mark_stack_busy;
2411 #endif //MH_SC_MARK
2412
2413
2414 #ifdef BACKGROUND_GC
2415 size_t*     gc_heap::g_bpromoted;
2416 #endif //BACKGROUND_GC
2417
2418 #else  //MULTIPLE_HEAPS
2419
2420 size_t      gc_heap::g_promoted;
2421
2422 #ifdef BACKGROUND_GC
2423 size_t      gc_heap::g_bpromoted;
2424 #endif //BACKGROUND_GC
2425
2426 #endif //MULTIPLE_HEAPS
2427
2428 size_t      gc_heap::reserved_memory = 0;
2429 size_t      gc_heap::reserved_memory_limit = 0;
2430 BOOL        gc_heap::g_low_memory_status;
2431
2432 #ifndef DACCESS_COMPILE
2433 static gc_reason gc_trigger_reason = reason_empty;
2434 #endif //DACCESS_COMPILE
2435
2436 gc_latency_level gc_heap::latency_level = latency_level_default;
2437
2438 gc_mechanisms  gc_heap::settings;
2439
2440 gc_history_global gc_heap::gc_data_global;
2441
2442 size_t      gc_heap::gc_last_ephemeral_decommit_time = 0;
2443
2444 size_t      gc_heap::gc_gen0_desired_high;
2445
2446 #ifdef SHORT_PLUGS
2447 double       gc_heap::short_plugs_pad_ratio = 0;
2448 #endif //SHORT_PLUGS
2449
2450 #if defined(BIT64)
2451 #define MAX_ALLOWED_MEM_LOAD 85
2452
2453 // consider putting this in dynamic data -
2454 // we may want different values for workstation
2455 // and server GC.
2456 #define MIN_YOUNGEST_GEN_DESIRED (16*1024*1024)
2457
2458 size_t      gc_heap::youngest_gen_desired_th;
2459 #endif //BIT64
2460
2461 uint32_t    gc_heap::last_gc_memory_load = 0;
2462
2463 size_t      gc_heap::last_gc_heap_size = 0;
2464
2465 size_t      gc_heap::last_gc_fragmentation = 0;
2466
2467 uint64_t    gc_heap::mem_one_percent = 0;
2468
2469 uint32_t    gc_heap::high_memory_load_th = 0;
2470
2471 uint64_t    gc_heap::total_physical_mem = 0;
2472
2473 uint64_t    gc_heap::entry_available_physical_mem = 0;
2474
2475 #ifdef BACKGROUND_GC
2476 GCEvent     gc_heap::bgc_start_event;
2477
2478 gc_mechanisms gc_heap::saved_bgc_settings;
2479
2480 GCEvent     gc_heap::background_gc_done_event;
2481
2482 GCEvent     gc_heap::ee_proceed_event;
2483
2484 bool        gc_heap::gc_can_use_concurrent = false;
2485
2486 bool        gc_heap::temp_disable_concurrent_p = false;
2487
2488 uint32_t    gc_heap::cm_in_progress = FALSE;
2489
2490 BOOL        gc_heap::dont_restart_ee_p = FALSE;
2491
2492 BOOL        gc_heap::keep_bgc_threads_p = FALSE;
2493
2494 GCEvent     gc_heap::bgc_threads_sync_event;
2495
2496 BOOL        gc_heap::do_ephemeral_gc_p = FALSE;
2497
2498 BOOL        gc_heap::do_concurrent_p = FALSE;
2499
2500 size_t      gc_heap::ephemeral_fgc_counts[max_generation];
2501
2502 BOOL        gc_heap::alloc_wait_event_p = FALSE;
2503
2504 VOLATILE(c_gc_state) gc_heap::current_c_gc_state = c_gc_state_free;
2505
2506 #endif //BACKGROUND_GC
2507
2508 #ifndef MULTIPLE_HEAPS
2509 #ifdef SPINLOCK_HISTORY
2510 int         gc_heap::spinlock_info_index = 0;
2511 spinlock_info gc_heap::last_spinlock_info[max_saved_spinlock_info + 8];
2512 #endif //SPINLOCK_HISTORY
2513
2514 size_t      gc_heap::fgn_last_alloc = 0;
2515
2516 int         gc_heap::generation_skip_ratio = 100;
2517
2518 uint64_t    gc_heap::loh_alloc_since_cg = 0;
2519
2520 BOOL        gc_heap::elevation_requested = FALSE;
2521
2522 BOOL        gc_heap::last_gc_before_oom = FALSE;
2523
2524 #ifdef BACKGROUND_GC
2525 uint8_t*    gc_heap::background_saved_lowest_address = 0;
2526 uint8_t*    gc_heap::background_saved_highest_address = 0;
2527 uint8_t*    gc_heap::next_sweep_obj = 0;
2528 uint8_t*    gc_heap::current_sweep_pos = 0;
2529 exclusive_sync* gc_heap::bgc_alloc_lock;
2530 #endif //BACKGROUND_GC
2531
2532 oom_history gc_heap::oom_info;
2533
2534 fgm_history gc_heap::fgm_result;
2535
2536 BOOL        gc_heap::ro_segments_in_range;
2537
2538 size_t      gc_heap::gen0_big_free_spaces = 0;
2539
2540 uint8_t*    gc_heap::ephemeral_low;
2541
2542 uint8_t*    gc_heap::ephemeral_high;
2543
2544 uint8_t*    gc_heap::lowest_address;
2545
2546 uint8_t*    gc_heap::highest_address;
2547
2548 BOOL        gc_heap::ephemeral_promotion;
2549
2550 uint8_t*    gc_heap::saved_ephemeral_plan_start[NUMBERGENERATIONS-1];
2551 size_t      gc_heap::saved_ephemeral_plan_start_size[NUMBERGENERATIONS-1];
2552
2553 short*      gc_heap::brick_table;
2554
2555 uint32_t*   gc_heap::card_table;
2556
2557 #ifdef CARD_BUNDLE
2558 uint32_t*   gc_heap::card_bundle_table;
2559 #endif //CARD_BUNDLE
2560
2561 uint8_t*    gc_heap::gc_low;
2562
2563 uint8_t*    gc_heap::gc_high;
2564
2565 uint8_t*    gc_heap::demotion_low;
2566
2567 uint8_t*    gc_heap::demotion_high;
2568
2569 BOOL        gc_heap::demote_gen1_p = TRUE;
2570
2571 uint8_t*    gc_heap::last_gen1_pin_end;
2572
2573 gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons;
2574
2575 size_t      gc_heap::etw_allocation_running_amount[2];
2576
2577 int         gc_heap::gc_policy = 0;
2578
2579 size_t      gc_heap::allocation_running_time;
2580
2581 size_t      gc_heap::allocation_running_amount;
2582
2583 heap_segment* gc_heap::ephemeral_heap_segment = 0;
2584
2585 BOOL        gc_heap::blocking_collection = FALSE;
2586
2587 heap_segment* gc_heap::freeable_large_heap_segment = 0;
2588
2589 size_t      gc_heap::time_bgc_last = 0;
2590
2591 size_t      gc_heap::mark_stack_tos = 0;
2592
2593 size_t      gc_heap::mark_stack_bos = 0;
2594
2595 size_t      gc_heap::mark_stack_array_length = 0;
2596
2597 mark*       gc_heap::mark_stack_array = 0;
2598
2599 BOOL        gc_heap::verify_pinned_queue_p = FALSE;
2600
2601 uint8_t*    gc_heap::oldest_pinned_plug = 0;
2602
2603 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
2604 size_t      gc_heap::num_pinned_objects = 0;
2605 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
2606
2607 #ifdef FEATURE_LOH_COMPACTION
2608 size_t      gc_heap::loh_pinned_queue_tos = 0;
2609
2610 size_t      gc_heap::loh_pinned_queue_bos = 0;
2611
2612 size_t      gc_heap::loh_pinned_queue_length = 0;
2613
2614 mark*       gc_heap::loh_pinned_queue = 0;
2615
2616 BOOL        gc_heap::loh_compacted_p = FALSE;
2617 #endif //FEATURE_LOH_COMPACTION
2618
2619 #ifdef BACKGROUND_GC
2620
2621 EEThreadId  gc_heap::bgc_thread_id;
2622
2623 uint8_t*    gc_heap::background_written_addresses [array_size+2];
2624
2625 heap_segment* gc_heap::freeable_small_heap_segment = 0;
2626
2627 size_t      gc_heap::bgc_overflow_count = 0;
2628
2629 size_t      gc_heap::bgc_begin_loh_size = 0;
2630 size_t      gc_heap::end_loh_size = 0;
2631
2632 uint32_t    gc_heap::bgc_alloc_spin_loh = 0;
2633
2634 size_t      gc_heap::bgc_loh_size_increased = 0;
2635
2636 size_t      gc_heap::bgc_loh_allocated_in_free = 0;
2637
2638 size_t      gc_heap::background_soh_alloc_count = 0;
2639
2640 size_t      gc_heap::background_loh_alloc_count = 0;
2641
2642 uint8_t**   gc_heap::background_mark_stack_tos = 0;
2643
2644 uint8_t**   gc_heap::background_mark_stack_array = 0;
2645
2646 size_t      gc_heap::background_mark_stack_array_length = 0;
2647
2648 uint8_t*    gc_heap::background_min_overflow_address =0;
2649
2650 uint8_t*    gc_heap::background_max_overflow_address =0;
2651
2652 BOOL        gc_heap::processed_soh_overflow_p = FALSE;
2653
2654 uint8_t*    gc_heap::background_min_soh_overflow_address =0;
2655
2656 uint8_t*    gc_heap::background_max_soh_overflow_address =0;
2657
2658 heap_segment* gc_heap::saved_sweep_ephemeral_seg = 0;
2659
2660 uint8_t*    gc_heap::saved_sweep_ephemeral_start = 0;
2661
2662 heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0;
2663
2664 Thread*     gc_heap::bgc_thread = 0;
2665
2666 BOOL        gc_heap::expanded_in_fgc = FALSE;
2667
2668 uint8_t**   gc_heap::c_mark_list = 0;
2669
2670 size_t      gc_heap::c_mark_list_length = 0;
2671
2672 size_t      gc_heap::c_mark_list_index = 0;
2673
2674 gc_history_per_heap gc_heap::bgc_data_per_heap;
2675
2676 BOOL    gc_heap::bgc_thread_running;
2677
2678 CLRCriticalSection gc_heap::bgc_threads_timeout_cs;
2679
2680 GCEvent gc_heap::gc_lh_block_event;
2681
2682 #endif //BACKGROUND_GC
2683
2684 #ifdef MARK_LIST
2685 uint8_t**   gc_heap::mark_list;
2686 uint8_t**   gc_heap::mark_list_index;
2687 uint8_t**   gc_heap::mark_list_end;
2688 #endif //MARK_LIST
2689
2690 #ifdef SNOOP_STATS
2691 snoop_stats_data gc_heap::snoop_stat;
2692 #endif //SNOOP_STATS
2693
2694 uint8_t*    gc_heap::min_overflow_address = MAX_PTR;
2695
2696 uint8_t*    gc_heap::max_overflow_address = 0;
2697
2698 uint8_t*    gc_heap::shigh = 0;
2699
2700 uint8_t*    gc_heap::slow = MAX_PTR;
2701
2702 size_t      gc_heap::ordered_free_space_indices[MAX_NUM_BUCKETS];
2703
2704 size_t      gc_heap::saved_ordered_free_space_indices[MAX_NUM_BUCKETS];
2705
2706 size_t      gc_heap::ordered_plug_indices[MAX_NUM_BUCKETS];
2707
2708 size_t      gc_heap::saved_ordered_plug_indices[MAX_NUM_BUCKETS];
2709
2710 BOOL        gc_heap::ordered_plug_indices_init = FALSE;
2711
2712 BOOL        gc_heap::use_bestfit = FALSE;
2713
2714 uint8_t*    gc_heap::bestfit_first_pin = 0;
2715
2716 BOOL        gc_heap::commit_end_of_seg = FALSE;
2717
2718 size_t      gc_heap::max_free_space_items = 0;
2719
2720 size_t      gc_heap::free_space_buckets = 0;
2721
2722 size_t      gc_heap::free_space_items = 0;
2723
2724 int         gc_heap::trimmed_free_space_index = 0;
2725
2726 size_t      gc_heap::total_ephemeral_plugs = 0;
2727
2728 seg_free_spaces* gc_heap::bestfit_seg = 0;
2729
2730 size_t      gc_heap::total_ephemeral_size = 0;
2731
2732 #ifdef HEAP_ANALYZE
2733
2734 size_t      gc_heap::internal_root_array_length = initial_internal_roots;
2735
2736 uint8_t**   gc_heap::internal_root_array = 0;
2737
2738 size_t      gc_heap::internal_root_array_index = 0;
2739
2740 BOOL        gc_heap::heap_analyze_success = TRUE;
2741
2742 uint8_t*    gc_heap::current_obj = 0;
2743 size_t      gc_heap::current_obj_size = 0;
2744
2745 #endif //HEAP_ANALYZE
2746
2747 #ifdef GC_CONFIG_DRIVEN
2748 size_t gc_heap::interesting_data_per_gc[max_idp_count];
2749 //size_t gc_heap::interesting_data_per_heap[max_idp_count];
2750 //size_t gc_heap::interesting_mechanisms_per_heap[max_im_count];
2751 #endif //GC_CONFIG_DRIVEN
2752 #endif //MULTIPLE_HEAPS
2753
2754 no_gc_region_info gc_heap::current_no_gc_region_info;
2755 BOOL gc_heap::proceed_with_gc_p = FALSE;
2756 GCSpinLock gc_heap::gc_lock;
2757
2758 size_t gc_heap::eph_gen_starts_size = 0;
2759 heap_segment* gc_heap::segment_standby_list;
2760 size_t        gc_heap::last_gc_index = 0;
2761 #ifdef SEG_MAPPING_TABLE
2762 size_t        gc_heap::min_segment_size = 0;
2763 size_t        gc_heap::min_segment_size_shr = 0;
2764 #endif //SEG_MAPPING_TABLE
2765 size_t        gc_heap::soh_segment_size = 0;
2766 size_t        gc_heap::min_loh_segment_size = 0;
2767 size_t        gc_heap::segment_info_size = 0;
2768
2769 #ifdef GC_CONFIG_DRIVEN
2770 size_t gc_heap::time_init = 0;
2771 size_t gc_heap::time_since_init = 0;
2772 size_t gc_heap::compact_or_sweep_gcs[2];
2773 #endif //GC_CONFIG_DRIVEN
2774
2775 #ifdef FEATURE_LOH_COMPACTION
2776 BOOL                   gc_heap::loh_compaction_always_p = FALSE;
2777 gc_loh_compaction_mode gc_heap::loh_compaction_mode = loh_compaction_default;
2778 int                    gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY;
2779
2780 #endif //FEATURE_LOH_COMPACTION
2781
2782 GCEvent gc_heap::full_gc_approach_event;
2783
2784 GCEvent gc_heap::full_gc_end_event;
2785
2786 uint32_t gc_heap::fgn_maxgen_percent = 0;
2787
2788 uint32_t gc_heap::fgn_loh_percent = 0;
2789
2790 #ifdef BACKGROUND_GC
2791 BOOL gc_heap::fgn_last_gc_was_concurrent = FALSE;
2792 #endif //BACKGROUND_GC
2793
2794 VOLATILE(bool) gc_heap::full_gc_approach_event_set;
2795
2796 size_t gc_heap::full_gc_counts[gc_type_max];
2797
2798 BOOL gc_heap::should_expand_in_full_gc = FALSE;
2799
2800 #ifdef HEAP_ANALYZE
2801 BOOL        gc_heap::heap_analyze_enabled = FALSE;
2802 #endif //HEAP_ANALYZE
2803
2804 #ifndef MULTIPLE_HEAPS
2805
2806 alloc_list gc_heap::loh_alloc_list [NUM_LOH_ALIST-1];
2807 alloc_list gc_heap::gen2_alloc_list[NUM_GEN2_ALIST-1];
2808
2809 dynamic_data gc_heap::dynamic_data_table [NUMBERGENERATIONS+1];
2810 gc_history_per_heap gc_heap::gc_data_per_heap;
2811 size_t gc_heap::maxgen_pinned_compact_before_advance = 0;
2812
2813 uint8_t* gc_heap::alloc_allocated = 0;
2814
2815 size_t gc_heap::allocation_quantum = CLR_SIZE;
2816
2817 GCSpinLock gc_heap::more_space_lock;
2818
2819 #ifdef SYNCHRONIZATION_STATS
2820 unsigned int gc_heap::good_suspension = 0;
2821 unsigned int gc_heap::bad_suspension = 0;
2822 uint64_t     gc_heap::total_msl_acquire = 0;
2823 unsigned int gc_heap::num_msl_acquired = 0;
2824 unsigned int gc_heap::num_high_msl_acquire = 0;
2825 unsigned int gc_heap::num_low_msl_acquire = 0;
2826 #endif //SYNCHRONIZATION_STATS
2827
2828 size_t   gc_heap::alloc_contexts_used = 0;
2829 size_t   gc_heap::soh_allocation_no_gc = 0;
2830 size_t   gc_heap::loh_allocation_no_gc = 0;
2831 bool     gc_heap::no_gc_oom_p = false;
2832 heap_segment* gc_heap::saved_loh_segment_no_gc = 0;
2833
2834 #endif //MULTIPLE_HEAPS
2835
2836 #ifndef MULTIPLE_HEAPS
2837
2838 BOOL        gc_heap::gen0_bricks_cleared = FALSE;
2839
2840 #ifdef FFIND_OBJECT
2841 int         gc_heap::gen0_must_clear_bricks = 0;
2842 #endif //FFIND_OBJECT
2843
2844 #ifdef FEATURE_PREMORTEM_FINALIZATION
2845 CFinalize*  gc_heap::finalize_queue = 0;
2846 #endif // FEATURE_PREMORTEM_FINALIZATION
2847
2848 generation gc_heap::generation_table [NUMBERGENERATIONS + 1];
2849
2850 size_t     gc_heap::interesting_data_per_heap[max_idp_count];
2851
2852 size_t     gc_heap::compact_reasons_per_heap[max_compact_reasons_count];
2853
2854 size_t     gc_heap::expand_mechanisms_per_heap[max_expand_mechanisms_count];
2855
2856 size_t     gc_heap::interesting_mechanism_bits_per_heap[max_gc_mechanism_bits_count];
2857
2858 #endif // MULTIPLE_HEAPS
2859
2860 /* end of per heap static initialization */
2861
2862 /* end of static initialization */
2863
2864 #ifndef DACCESS_COMPILE
2865
2866 void gen_to_condemn_tuning::print (int heap_num)
2867 {
2868 #ifdef DT_LOG
2869     dprintf (DT_LOG_0, ("condemned reasons (%d %d)", condemn_reasons_gen, condemn_reasons_condition));
2870     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_gen_header));
2871     gc_condemn_reason_gen r_gen;
2872     for (int i = 0; i < gcrg_max; i++)
2873     {
2874         r_gen = (gc_condemn_reason_gen)(i);
2875         str_reasons_gen[i * 2] = get_gen_char (get_gen (r_gen));
2876     }
2877     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_gen));
2878
2879     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_condition_header));
2880     gc_condemn_reason_condition r_condition;
2881     for (int i = 0; i < gcrc_max; i++)
2882     {
2883         r_condition = (gc_condemn_reason_condition)(i);
2884         str_reasons_condition[i * 2] = get_condition_char (get_condition (r_condition));
2885     }
2886
2887     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_condition));
2888 #else
2889     UNREFERENCED_PARAMETER(heap_num);
2890 #endif //DT_LOG
2891 }
2892
2893 void gc_generation_data::print (int heap_num, int gen_num)
2894 {
2895 #if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2896     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",
2897                 heap_num, gen_num, 
2898                 size_before, 
2899                 free_list_space_before, free_obj_space_before,
2900                 size_after, 
2901                 free_list_space_after, free_obj_space_after, 
2902                 in, pinned_surv, npinned_surv,
2903                 new_allocation));
2904 #else
2905     UNREFERENCED_PARAMETER(heap_num);
2906     UNREFERENCED_PARAMETER(gen_num);
2907 #endif //SIMPLE_DPRINTF && DT_LOG
2908 }
2909
2910 void gc_history_per_heap::set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value)
2911 {
2912     uint32_t* mechanism = &mechanisms[mechanism_per_heap];
2913     *mechanism = 0;
2914     *mechanism |= mechanism_mask;
2915     *mechanism |= (1 << value);
2916
2917 #ifdef DT_LOG
2918     gc_mechanism_descr* descr = &gc_mechanisms_descr[mechanism_per_heap];
2919     dprintf (DT_LOG_0, ("setting %s: %s", 
2920             descr->name,
2921             (descr->descr)[value]));
2922 #endif //DT_LOG
2923 }
2924
2925 void gc_history_per_heap::print()
2926 {
2927 #if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2928     for (int i = 0; i < (sizeof (gen_data)/sizeof (gc_generation_data)); i++)
2929     {
2930         gen_data[i].print (heap_index, i);
2931     }
2932
2933     dprintf (DT_LOG_0, ("fla %Id flr %Id esa %Id ca %Id pa %Id paa %Id, rfle %d, ec %Id", 
2934                     maxgen_size_info.free_list_allocated,
2935                     maxgen_size_info.free_list_rejected,
2936                     maxgen_size_info.end_seg_allocated,
2937                     maxgen_size_info.condemned_allocated,
2938                     maxgen_size_info.pinned_allocated,
2939                     maxgen_size_info.pinned_allocated_advance,
2940                     maxgen_size_info.running_free_list_efficiency,
2941                     extra_gen0_committed));
2942
2943     int mechanism = 0;
2944     gc_mechanism_descr* descr = 0;
2945
2946     for (int i = 0; i < max_mechanism_per_heap; i++)
2947     {
2948         mechanism = get_mechanism ((gc_mechanism_per_heap)i);
2949
2950         if (mechanism >= 0)
2951         {
2952             descr = &gc_mechanisms_descr[(gc_mechanism_per_heap)i];
2953             dprintf (DT_LOG_0, ("[%2d]%s%s", 
2954                         heap_index,
2955                         descr->name, 
2956                         (descr->descr)[mechanism]));
2957         }
2958     }
2959 #endif //SIMPLE_DPRINTF && DT_LOG
2960 }
2961
2962 void gc_history_global::print()
2963 {
2964 #ifdef DT_LOG
2965     char str_settings[64];
2966     memset (str_settings, '|', sizeof (char) * 64);
2967     str_settings[max_global_mechanisms_count*2] = 0;
2968
2969     for (int i = 0; i < max_global_mechanisms_count; i++)
2970     {
2971         str_settings[i * 2] = (get_mechanism_p ((gc_global_mechanism_p)i) ? 'Y' : 'N');
2972     }
2973
2974     dprintf (DT_LOG_0, ("[hp]|c|p|o|d|b|e|"));
2975
2976     dprintf (DT_LOG_0, ("%4d|%s", num_heaps, str_settings));
2977     dprintf (DT_LOG_0, ("Condemned gen%d(reason: %s; mode: %s), youngest budget %Id(%d), memload %d",
2978                         condemned_generation,
2979                         str_gc_reasons[reason],
2980                         str_gc_pause_modes[pause_mode],                        
2981                         final_youngest_desired,
2982                         gen0_reduction_count,
2983                         mem_pressure));
2984 #endif //DT_LOG
2985 }
2986
2987 void gc_heap::fire_per_heap_hist_event (gc_history_per_heap* current_gc_data_per_heap, int heap_num)
2988 {
2989     maxgen_size_increase* maxgen_size_info = &(current_gc_data_per_heap->maxgen_size_info);
2990     FIRE_EVENT(GCPerHeapHistory_V3, 
2991                (void *)(maxgen_size_info->free_list_allocated),
2992                (void *)(maxgen_size_info->free_list_rejected),                              
2993                (void *)(maxgen_size_info->end_seg_allocated),
2994                (void *)(maxgen_size_info->condemned_allocated),
2995                (void *)(maxgen_size_info->pinned_allocated),
2996                (void *)(maxgen_size_info->pinned_allocated_advance),
2997                maxgen_size_info->running_free_list_efficiency,
2998                current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons0(),
2999                current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons1(),
3000                current_gc_data_per_heap->mechanisms[gc_heap_compact],
3001                current_gc_data_per_heap->mechanisms[gc_heap_expand],
3002                current_gc_data_per_heap->heap_index,
3003                (void *)(current_gc_data_per_heap->extra_gen0_committed),
3004                (max_generation + 2),
3005                (uint32_t)(sizeof (gc_generation_data)),
3006                (void *)&(current_gc_data_per_heap->gen_data[0]));
3007
3008     current_gc_data_per_heap->print();
3009     current_gc_data_per_heap->gen_to_condemn_reasons.print (heap_num);
3010 }
3011
3012 void gc_heap::fire_pevents()
3013 {
3014 #ifndef CORECLR
3015     settings.record (&gc_data_global);
3016     gc_data_global.print();
3017
3018     FIRE_EVENT(GCGlobalHeapHistory_V2, gc_data_global.final_youngest_desired, 
3019                                   gc_data_global.num_heaps, 
3020                                   gc_data_global.condemned_generation, 
3021                                   gc_data_global.gen0_reduction_count, 
3022                                   gc_data_global.reason, 
3023                                   gc_data_global.global_mechanims_p, 
3024                                   gc_data_global.pause_mode, 
3025                                   gc_data_global.mem_pressure);
3026
3027 #ifdef MULTIPLE_HEAPS
3028     for (int i = 0; i < gc_heap::n_heaps; i++)
3029     {
3030         gc_heap* hp = gc_heap::g_heaps[i];
3031         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
3032         fire_per_heap_hist_event (current_gc_data_per_heap, hp->heap_number);
3033     }
3034 #else
3035     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
3036     fire_per_heap_hist_event (current_gc_data_per_heap, heap_number);
3037 #endif    
3038 #endif //!CORECLR
3039 }
3040
3041 inline BOOL
3042 gc_heap::dt_low_ephemeral_space_p (gc_tuning_point tp)
3043 {
3044     BOOL ret = FALSE;
3045
3046     switch (tp)
3047     {
3048         case tuning_deciding_condemned_gen:
3049         case tuning_deciding_compaction:
3050         case tuning_deciding_expansion:
3051         case tuning_deciding_full_gc:
3052         {
3053             ret = (!ephemeral_gen_fit_p (tp));
3054             break;
3055         }
3056         case tuning_deciding_promote_ephemeral:
3057         {
3058             size_t new_gen0size = approximate_new_allocation();
3059             ptrdiff_t plan_ephemeral_size = total_ephemeral_size;
3060             
3061             dprintf (GTC_LOG, ("h%d: plan eph size is %Id, new gen0 is %Id", 
3062                 heap_number, plan_ephemeral_size, new_gen0size));
3063
3064             // If we were in no_gc_region we could have allocated a larger than normal segment,
3065             // and the next seg we allocate will be a normal sized seg so if we can't fit the new
3066             // ephemeral generations there, do an ephemeral promotion.
3067             ret = ((soh_segment_size - segment_info_size) < (plan_ephemeral_size + new_gen0size));
3068
3069             break;
3070         }
3071         default:
3072             break;
3073     }
3074
3075     return ret;
3076 }
3077
3078 BOOL 
3079 gc_heap::dt_high_frag_p (gc_tuning_point tp, 
3080                          int gen_number, 
3081                          BOOL elevate_p)
3082 {
3083     BOOL ret = FALSE;
3084
3085     switch (tp)
3086     {
3087         case tuning_deciding_condemned_gen:
3088         {
3089             dynamic_data* dd = dynamic_data_of (gen_number);
3090             float fragmentation_burden = 0;
3091
3092             if (elevate_p)
3093             {
3094                 ret = (dd_fragmentation (dynamic_data_of (max_generation)) >= dd_max_size(dd));
3095                 dprintf (GTC_LOG, ("h%d: frag is %Id, max size is %Id",
3096                     heap_number, dd_fragmentation (dd), dd_max_size(dd)));
3097             }
3098             else
3099             {
3100 #ifndef MULTIPLE_HEAPS
3101                 if (gen_number == max_generation)
3102                 {
3103                     float frag_ratio = (float)(dd_fragmentation (dynamic_data_of (max_generation))) / (float)generation_size (max_generation);
3104                     if (frag_ratio > 0.65)
3105                     {
3106                         dprintf (GTC_LOG, ("g2 FR: %d%%", (int)(frag_ratio*100)));
3107                         return TRUE;
3108                     }
3109                 }
3110 #endif //!MULTIPLE_HEAPS
3111                 size_t fr = generation_unusable_fragmentation (generation_of (gen_number));
3112                 ret = (fr > dd_fragmentation_limit(dd));
3113                 if (ret)
3114                 {
3115                     fragmentation_burden = (float)fr / generation_size (gen_number);
3116                     ret = (fragmentation_burden > dd_v_fragmentation_burden_limit (dd));
3117                 }
3118                 dprintf (GTC_LOG, ("h%d: gen%d, frag is %Id, alloc effi: %d%%, unusable frag is %Id, ratio is %d",
3119                     heap_number, gen_number, dd_fragmentation (dd), 
3120                     (int)(100*generation_allocator_efficiency (generation_of (gen_number))),
3121                     fr, (int)(fragmentation_burden*100)));
3122             }
3123             break;
3124         }
3125         default:
3126             break;
3127     }
3128
3129     return ret;
3130 }
3131
3132 inline BOOL 
3133 gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number)
3134 {
3135     BOOL ret = FALSE;
3136
3137     switch (tp)
3138     {
3139         case tuning_deciding_condemned_gen:
3140         {
3141             if (gen_number == max_generation)
3142             {
3143                 dynamic_data* dd = dynamic_data_of (gen_number);
3144                 size_t maxgen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd));
3145                 size_t maxgen_total_size = maxgen_allocated + dd_current_size (dd);
3146                 size_t est_maxgen_surv = (size_t)((float) (maxgen_total_size) * dd_surv (dd));
3147                 size_t est_maxgen_free = maxgen_total_size - est_maxgen_surv + dd_fragmentation (dd);
3148
3149                 dprintf (GTC_LOG, ("h%d: Total gen2 size: %Id, est gen2 dead space: %Id (s: %d, allocated: %Id), frag: %Id",
3150                             heap_number,
3151                             maxgen_total_size,
3152                             est_maxgen_free, 
3153                             (int)(dd_surv (dd) * 100),
3154                             maxgen_allocated,
3155                             dd_fragmentation (dd)));
3156
3157                 uint32_t num_heaps = 1;
3158
3159 #ifdef MULTIPLE_HEAPS
3160                 num_heaps = gc_heap::n_heaps;
3161 #endif //MULTIPLE_HEAPS
3162
3163                 size_t min_frag_th = min_reclaim_fragmentation_threshold (num_heaps);
3164                 dprintf (GTC_LOG, ("h%d, min frag is %Id", heap_number, min_frag_th));
3165                 ret = (est_maxgen_free >= min_frag_th);
3166             }
3167             else
3168             {
3169                 assert (0);
3170             }
3171             break;
3172         }
3173
3174         default:
3175             break;
3176     }
3177
3178     return ret;
3179 }
3180
3181 // DTREVIEW: Right now we only estimate gen2 fragmentation. 
3182 // on 64-bit though we should consider gen1 or even gen0 fragmentatioin as
3183 // well 
3184 inline BOOL 
3185 gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, uint64_t available_mem)
3186 {
3187     BOOL ret = FALSE;
3188
3189     switch (tp)
3190     {
3191         case tuning_deciding_condemned_gen:
3192         {
3193             if (gen_number == max_generation)
3194             {
3195                 dynamic_data* dd = dynamic_data_of (gen_number);
3196                 float est_frag_ratio = 0;
3197                 if (dd_current_size (dd) == 0)
3198                 {
3199                     est_frag_ratio = 1;
3200                 }
3201                 else if ((dd_fragmentation (dd) == 0) || (dd_fragmentation (dd) + dd_current_size (dd) == 0))
3202                 {
3203                     est_frag_ratio = 0;
3204                 }
3205                 else
3206                 {
3207                     est_frag_ratio = (float)dd_fragmentation (dd) / (float)(dd_fragmentation (dd) + dd_current_size (dd));
3208                 }
3209                 
3210                 size_t est_frag = (dd_fragmentation (dd) + (size_t)((dd_desired_allocation (dd) - dd_new_allocation (dd)) * est_frag_ratio));
3211                 dprintf (GTC_LOG, ("h%d: gen%d: current_size is %Id, frag is %Id, est_frag_ratio is %d%%, estimated frag is %Id", 
3212                     heap_number,
3213                     gen_number,
3214                     dd_current_size (dd),
3215                     dd_fragmentation (dd),
3216                     (int)(est_frag_ratio*100),
3217                     est_frag));
3218
3219                 uint32_t num_heaps = 1;
3220
3221 #ifdef MULTIPLE_HEAPS
3222                 num_heaps = gc_heap::n_heaps;
3223 #endif //MULTIPLE_HEAPS
3224                 uint64_t min_frag_th = min_high_fragmentation_threshold(available_mem, num_heaps);
3225                 //dprintf (GTC_LOG, ("h%d, min frag is %I64d", heap_number, min_frag_th));
3226                 ret = (est_frag >= min_frag_th);
3227             }
3228             else
3229             {
3230                 assert (0);
3231             }
3232             break;
3233         }
3234
3235         default:
3236             break;
3237     }
3238
3239     return ret;
3240 }
3241
3242 inline BOOL 
3243 gc_heap::dt_low_card_table_efficiency_p (gc_tuning_point tp)
3244 {
3245     BOOL ret = FALSE;
3246
3247     switch (tp)
3248     {
3249     case tuning_deciding_condemned_gen:
3250     {
3251         /* promote into max-generation if the card table has too many
3252         * generation faults besides the n -> 0
3253         */
3254         ret = (generation_skip_ratio < 30);
3255         break;
3256     }
3257
3258     default:
3259         break;
3260     }
3261
3262     return ret;
3263 }
3264
3265 inline BOOL
3266 in_range_for_segment(uint8_t* add, heap_segment* seg)
3267 {
3268     return ((add >= heap_segment_mem (seg)) && (add < heap_segment_reserved (seg)));
3269 }
3270
3271 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
3272 // The array we allocate is organized as follows:
3273 // 0th element is the address of the last array we allocated.
3274 // starting from the 1st element are the segment addresses, that's
3275 // what buckets() returns.
3276 struct bk
3277 {
3278     uint8_t* add;
3279     size_t val;
3280 };
3281
3282 class sorted_table
3283 {
3284 private:
3285     ptrdiff_t size;
3286     ptrdiff_t count;
3287     bk* slots;
3288     bk* buckets() { return (slots + 1); }
3289     uint8_t*& last_slot (bk* arr) { return arr[0].add; }
3290     bk* old_slots;
3291 public:
3292     static  sorted_table* make_sorted_table ();
3293     BOOL    insert (uint8_t* add, size_t val);;
3294     size_t  lookup (uint8_t*& add);
3295     void    remove (uint8_t* add);
3296     void    clear ();
3297     void    delete_sorted_table();
3298     void    delete_old_slots();
3299     void    enqueue_old_slot(bk* sl);
3300     BOOL    ensure_space_for_insert();
3301 };
3302
3303 sorted_table*
3304 sorted_table::make_sorted_table ()
3305 {
3306     size_t size = 400;
3307
3308     // allocate one more bk to store the older slot address.
3309     sorted_table* res = (sorted_table*)new char [sizeof (sorted_table) + (size + 1) * sizeof (bk)];
3310     if (!res)
3311         return 0;
3312     res->size = size;
3313     res->slots = (bk*)(res + 1);
3314     res->old_slots = 0;
3315     res->clear();
3316     return res;
3317 }
3318
3319 void
3320 sorted_table::delete_sorted_table()
3321 {
3322     if (slots != (bk*)(this+1))
3323     {
3324         delete slots;
3325     }
3326     delete_old_slots();
3327     delete this;
3328 }
3329 void
3330 sorted_table::delete_old_slots()
3331 {
3332     uint8_t* sl = (uint8_t*)old_slots;
3333     while (sl)
3334     {
3335         uint8_t* dsl = sl;
3336         sl = last_slot ((bk*)sl);
3337         delete dsl;
3338     }
3339     old_slots = 0;
3340 }
3341 void
3342 sorted_table::enqueue_old_slot(bk* sl)
3343 {
3344     last_slot (sl) = (uint8_t*)old_slots;
3345     old_slots = sl;
3346 }
3347
3348 inline
3349 size_t
3350 sorted_table::lookup (uint8_t*& add)
3351 {
3352     ptrdiff_t high = (count-1);
3353     ptrdiff_t low = 0;
3354     ptrdiff_t ti;
3355     ptrdiff_t mid;
3356     bk* buck = buckets();
3357     while (low <= high)
3358     {
3359         mid = ((low + high)/2);
3360         ti = mid;
3361         if (buck[ti].add > add)
3362         {
3363             if ((ti > 0) && (buck[ti-1].add <= add))
3364             {
3365                 add = buck[ti-1].add;
3366                 return buck[ti - 1].val;
3367             }
3368             high = mid - 1;
3369         }
3370         else
3371         {
3372             if (buck[ti+1].add > add)
3373             {
3374                 add = buck[ti].add;
3375                 return buck[ti].val;
3376             }
3377             low = mid + 1;
3378         }
3379     }
3380     add = 0;
3381     return 0;
3382 }
3383
3384 BOOL
3385 sorted_table::ensure_space_for_insert()
3386 {
3387     if (count == size)
3388     {
3389         size = (size * 3)/2;
3390         assert((size * sizeof (bk)) > 0);
3391         bk* res = (bk*)new (nothrow) char [(size + 1) * sizeof (bk)];
3392         assert (res);
3393         if (!res)
3394             return FALSE;
3395
3396         last_slot (res) = 0;
3397         memcpy (((bk*)res + 1), buckets(), count * sizeof (bk));
3398         bk* last_old_slots = slots;
3399         slots = res;
3400         if (last_old_slots != (bk*)(this + 1))
3401             enqueue_old_slot (last_old_slots);
3402     }
3403     return TRUE;
3404 }
3405
3406 BOOL
3407 sorted_table::insert (uint8_t* add, size_t val)
3408 {
3409     //grow if no more room
3410     assert (count < size);
3411
3412     //insert sorted
3413     ptrdiff_t high = (count-1);
3414     ptrdiff_t low = 0;
3415     ptrdiff_t ti;
3416     ptrdiff_t mid;
3417     bk* buck = buckets();
3418     while (low <= high)
3419     {
3420         mid = ((low + high)/2);
3421         ti = mid;
3422         if (buck[ti].add > add)
3423         {
3424             if ((ti == 0) || (buck[ti-1].add <= add))
3425             {
3426                 // found insertion point
3427                 for (ptrdiff_t k = count; k > ti;k--)
3428                 {
3429                     buck [k] = buck [k-1];
3430                 }
3431                 buck[ti].add = add;
3432                 buck[ti].val = val;
3433                 count++;
3434                 return TRUE;
3435             }
3436             high = mid - 1;
3437         }
3438         else
3439         {
3440             if (buck[ti+1].add > add)
3441             {
3442                 //found the insertion point
3443                 for (ptrdiff_t k = count; k > ti+1;k--)
3444                 {
3445                     buck [k] = buck [k-1];
3446                 }
3447                 buck[ti+1].add = add;
3448                 buck[ti+1].val = val;
3449                 count++;
3450                 return TRUE;
3451             }
3452             low = mid + 1;
3453         }
3454     }
3455     assert (0);
3456     return TRUE;
3457 }
3458
3459 void
3460 sorted_table::remove (uint8_t* add)
3461 {
3462     ptrdiff_t high = (count-1);
3463     ptrdiff_t low = 0;
3464     ptrdiff_t ti;
3465     ptrdiff_t mid;
3466     bk* buck = buckets();
3467     while (low <= high)
3468     {
3469         mid = ((low + high)/2);
3470         ti = mid;
3471         if (buck[ti].add > add)
3472         {
3473             if (buck[ti-1].add <= add)
3474             {
3475                 // found the guy to remove
3476                 for (ptrdiff_t k = ti; k < count; k++)
3477                     buck[k-1] = buck[k];
3478                 count--;
3479                 return;
3480             }
3481             high = mid - 1;
3482         }
3483         else
3484         {
3485             if (buck[ti+1].add > add)
3486             {
3487                 // found the guy to remove
3488                 for (ptrdiff_t k = ti+1; k < count; k++)
3489                     buck[k-1] = buck[k];
3490                 count--;
3491                 return;
3492             }
3493             low = mid + 1;
3494         }
3495     }
3496     assert (0);
3497 }
3498
3499 void
3500 sorted_table::clear()
3501 {
3502     count = 1;
3503     buckets()[0].add = MAX_PTR;
3504 }
3505 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
3506
3507 #ifdef SEG_MAPPING_TABLE
3508 #ifdef GROWABLE_SEG_MAPPING_TABLE
3509 inline
3510 uint8_t* align_on_segment (uint8_t* add)
3511 {
3512     return (uint8_t*)((size_t)(add + (gc_heap::min_segment_size - 1)) & ~(gc_heap::min_segment_size - 1));
3513 }
3514
3515 inline
3516 uint8_t* align_lower_segment (uint8_t* add)
3517 {
3518     return (uint8_t*)((size_t)(add) & ~(gc_heap::min_segment_size - 1));
3519 }
3520
3521 size_t size_seg_mapping_table_of (uint8_t* from, uint8_t* end)
3522 {
3523     from = align_lower_segment (from);
3524     end = align_on_segment (end);
3525     dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((size_t)(end - from) >> gc_heap::min_segment_size_shr))));
3526     return sizeof (seg_mapping)*((size_t)(end - from) >> gc_heap::min_segment_size_shr);
3527 }
3528
3529 // for seg_mapping_table we want it to start from a pointer sized address.
3530 inline
3531 size_t align_for_seg_mapping_table (size_t size)
3532 {
3533     return ((size + (sizeof (uint8_t*) - 1)) &~ (sizeof (uint8_t*) - 1));
3534 }
3535
3536 inline
3537 size_t seg_mapping_word_of (uint8_t* add)
3538 {
3539     return (size_t)add >> gc_heap::min_segment_size_shr;
3540 }
3541 #else //GROWABLE_SEG_MAPPING_TABLE
3542 BOOL seg_mapping_table_init()
3543 {
3544 #ifdef BIT64
3545     uint64_t total_address_space = (uint64_t)8*1024*1024*1024*1024;
3546 #else
3547     uint64_t total_address_space = (uint64_t)4*1024*1024*1024;
3548 #endif // BIT64
3549
3550     size_t num_entries = (size_t)(total_address_space >> gc_heap::min_segment_size_shr);
3551     seg_mapping_table = new seg_mapping[num_entries];
3552
3553     if (seg_mapping_table)
3554     {
3555         memset (seg_mapping_table, 0, num_entries * sizeof (seg_mapping));
3556         dprintf (1, ("created %d entries for heap mapping (%Id bytes)", 
3557                      num_entries, (num_entries * sizeof (seg_mapping))));
3558         return TRUE;
3559     }
3560     else
3561     {
3562         dprintf (1, ("failed to create %d entries for heap mapping (%Id bytes)", 
3563                      num_entries, (num_entries * sizeof (seg_mapping))));
3564         return FALSE;
3565     }
3566 }
3567 #endif //GROWABLE_SEG_MAPPING_TABLE
3568
3569 #ifdef FEATURE_BASICFREEZE
3570 inline
3571 size_t ro_seg_begin_index (heap_segment* seg)
3572 {
3573     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3574     begin_index = max (begin_index, (size_t)g_gc_lowest_address >> gc_heap::min_segment_size_shr);
3575     return begin_index;
3576 }
3577
3578 inline
3579 size_t ro_seg_end_index (heap_segment* seg)
3580 {
3581     size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) >> gc_heap::min_segment_size_shr;
3582     end_index = min (end_index, (size_t)g_gc_highest_address >> gc_heap::min_segment_size_shr);
3583     return end_index;
3584 }
3585
3586 void seg_mapping_table_add_ro_segment (heap_segment* seg)
3587 {
3588 #ifdef GROWABLE_SEG_MAPPING_TABLE
3589     if ((heap_segment_reserved (seg) <= g_gc_lowest_address) || (heap_segment_mem (seg) >= g_gc_highest_address))
3590         return;
3591 #endif //GROWABLE_SEG_MAPPING_TABLE
3592
3593     for (size_t entry_index = ro_seg_begin_index (seg); entry_index <= ro_seg_end_index (seg); entry_index++)
3594         seg_mapping_table[entry_index].seg1 = (heap_segment*)((size_t)seg_mapping_table[entry_index].seg1 | ro_in_entry);
3595 }
3596
3597 void seg_mapping_table_remove_ro_segment (heap_segment* seg)
3598 {
3599     UNREFERENCED_PARAMETER(seg);
3600 #if 0
3601 // POSSIBLE PERF TODO: right now we are not doing anything because we can't simply remove the flag. If it proves
3602 // to be a perf problem, we can search in the current ro segs and see if any lands in this range and only
3603 // remove the flag if none lands in this range.
3604 #endif //0
3605 }
3606
3607 heap_segment* ro_segment_lookup (uint8_t* o)
3608 {
3609     uint8_t* ro_seg_start = o;
3610     heap_segment* seg = (heap_segment*)gc_heap::seg_table->lookup (ro_seg_start);
3611
3612     if (ro_seg_start && in_range_for_segment (o, seg))
3613         return seg;
3614     else
3615         return 0;
3616 }
3617
3618 #endif //FEATURE_BASICFREEZE
3619
3620 void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
3621 {
3622     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3623     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3624     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3625     size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
3626     seg_mapping* end_entry = &seg_mapping_table[end_index];
3627
3628     dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)", 
3629         seg, begin_index, heap_segment_reserved (seg), end_index));
3630
3631     dprintf (1, ("before add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3632         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3633         end_index, (seg_mapping_table[end_index].boundary + 1)));
3634
3635 #ifdef MULTIPLE_HEAPS
3636 #ifdef SIMPLE_DPRINTF
3637     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end %d: h0: %Ix(%d), h1: %Ix(%d)",
3638         begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3639         (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3640         end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3641         (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3642 #endif //SIMPLE_DPRINTF
3643     assert (end_entry->boundary == 0);
3644     assert (end_entry->h0 == 0);
3645     end_entry->h0 = hp;
3646     assert (begin_entry->h1 == 0);
3647     begin_entry->h1 = hp;
3648 #else
3649     UNREFERENCED_PARAMETER(hp);
3650 #endif //MULTIPLE_HEAPS
3651
3652     end_entry->boundary = (uint8_t*)seg_end;
3653
3654     dprintf (1, ("set entry %d seg1 and %d seg0 to %Ix", begin_index, end_index, seg));
3655     assert ((begin_entry->seg1 == 0) || ((size_t)(begin_entry->seg1) == ro_in_entry));
3656     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) | (size_t)seg);
3657     end_entry->seg0 = seg;
3658
3659     // for every entry inbetween we need to set its heap too.
3660     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3661     {
3662         assert (seg_mapping_table[entry_index].boundary == 0);
3663 #ifdef MULTIPLE_HEAPS
3664         assert (seg_mapping_table[entry_index].h0 == 0);
3665         seg_mapping_table[entry_index].h1 = hp;
3666 #endif //MULTIPLE_HEAPS
3667         seg_mapping_table[entry_index].seg1 = seg;
3668     }
3669
3670     dprintf (1, ("after add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3671         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3672         end_index, (seg_mapping_table[end_index].boundary + 1)));
3673 #if defined(MULTIPLE_HEAPS) && defined(SIMPLE_DPRINTF)
3674     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end: %d h0: %Ix(%d), h1: %Ix(%d)",
3675         begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3676         (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3677         end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3678         (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3679 #endif //MULTIPLE_HEAPS && SIMPLE_DPRINTF
3680 }
3681
3682 void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
3683 {
3684     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3685     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3686     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3687     size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
3688     seg_mapping* end_entry = &seg_mapping_table[end_index];
3689     dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)", 
3690         seg, begin_index, heap_segment_reserved (seg), end_index));
3691
3692     assert (end_entry->boundary == (uint8_t*)seg_end);
3693     end_entry->boundary = 0;
3694
3695 #ifdef MULTIPLE_HEAPS
3696     gc_heap* hp = heap_segment_heap (seg);
3697     assert (end_entry->h0 == hp);
3698     end_entry->h0 = 0;
3699     assert (begin_entry->h1 == hp);
3700     begin_entry->h1 = 0;
3701 #endif //MULTIPLE_HEAPS
3702
3703     assert (begin_entry->seg1 != 0);
3704     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) & ro_in_entry);
3705     end_entry->seg0 = 0;
3706
3707     // for every entry inbetween we need to reset its heap too.
3708     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3709     {
3710         assert (seg_mapping_table[entry_index].boundary == 0);
3711 #ifdef MULTIPLE_HEAPS
3712         assert (seg_mapping_table[entry_index].h0 == 0);
3713         assert (seg_mapping_table[entry_index].h1 == hp);
3714         seg_mapping_table[entry_index].h1 = 0;
3715 #endif //MULTIPLE_HEAPS
3716         seg_mapping_table[entry_index].seg1 = 0;
3717     }
3718
3719     dprintf (1, ("after remove: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3720         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3721         end_index, (seg_mapping_table[end_index].boundary + 1)));
3722 #ifdef MULTIPLE_HEAPS
3723     dprintf (1, ("begin %d: h0: %Ix, h1: %Ix; end: %d h0: %Ix, h1: %Ix",
3724         begin_index, (uint8_t*)(begin_entry->h0), (uint8_t*)(begin_entry->h1),
3725         end_index, (uint8_t*)(end_entry->h0), (uint8_t*)(end_entry->h1)));
3726 #endif //MULTIPLE_HEAPS
3727 }
3728
3729 #ifdef MULTIPLE_HEAPS
3730 inline
3731 gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o)
3732 {
3733     size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
3734     seg_mapping* entry = &seg_mapping_table[index];
3735
3736     gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
3737
3738     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
3739         o, index, (entry->boundary + 1), 
3740         (uint8_t*)(entry->h0), (uint8_t*)(entry->seg0),
3741         (uint8_t*)(entry->h1), (uint8_t*)(entry->seg1)));
3742
3743 #ifdef _DEBUG
3744     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3745 #ifdef FEATURE_BASICFREEZE
3746     if ((size_t)seg & ro_in_entry)
3747         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3748 #endif //FEATURE_BASICFREEZE
3749
3750     if (seg)
3751     {
3752         if (in_range_for_segment (o, seg))
3753         {
3754             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, seg, (uint8_t*)heap_segment_allocated (seg)));
3755         }
3756         else
3757         {
3758             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg", 
3759                 seg, (uint8_t*)heap_segment_allocated (seg), o));
3760         }
3761     }
3762     else
3763     {
3764         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3765     }
3766 #endif //_DEBUG
3767
3768     return hp;
3769 }
3770
3771 gc_heap* seg_mapping_table_heap_of (uint8_t* o)
3772 {
3773 #ifdef GROWABLE_SEG_MAPPING_TABLE
3774     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3775         return 0;
3776 #endif //GROWABLE_SEG_MAPPING_TABLE
3777
3778     return seg_mapping_table_heap_of_worker (o);
3779 }
3780
3781 gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o)
3782 {
3783 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3784     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3785         return 0;
3786 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3787
3788     return seg_mapping_table_heap_of_worker (o);
3789 }
3790 #endif //MULTIPLE_HEAPS
3791
3792 // Only returns a valid seg if we can actually find o on the seg.
3793 heap_segment* seg_mapping_table_segment_of (uint8_t* o)
3794 {
3795 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3796     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3797 #ifdef FEATURE_BASICFREEZE
3798         return ro_segment_lookup (o);
3799 #else
3800         return 0;
3801 #endif //FEATURE_BASICFREEZE
3802 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3803
3804     size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
3805     seg_mapping* entry = &seg_mapping_table[index];
3806
3807     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
3808         o, index, (entry->boundary + 1), 
3809         (uint8_t*)(entry->seg0), (uint8_t*)(entry->seg1)));
3810
3811     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3812 #ifdef FEATURE_BASICFREEZE
3813     if ((size_t)seg & ro_in_entry)
3814         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3815 #endif //FEATURE_BASICFREEZE
3816
3817     if (seg)
3818     {
3819         if (in_range_for_segment (o, seg))
3820         {
3821             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg)));
3822         }
3823         else
3824         {
3825             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg, setting it to 0", 
3826                 (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg), o));
3827             seg = 0;
3828         }
3829     }
3830     else
3831     {
3832         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3833     }
3834
3835 #ifdef FEATURE_BASICFREEZE
3836     // TODO: This was originally written assuming that the seg_mapping_table would always contain entries for ro 
3837     // segments whenever the ro segment falls into the [g_gc_lowest_address,g_gc_highest_address) range.  I.e., it had an 
3838     // extra "&& (size_t)(entry->seg1) & ro_in_entry" expression.  However, at the moment, grow_brick_card_table does 
3839     // not correctly go through the ro segments and add them back to the seg_mapping_table when the [lowest,highest) 
3840     // range changes.  We should probably go ahead and modify grow_brick_card_table and put back the 
3841     // "&& (size_t)(entry->seg1) & ro_in_entry" here.
3842     if (!seg)
3843     {
3844         seg = ro_segment_lookup (o);
3845         if (seg && !in_range_for_segment (o, seg))
3846             seg = 0;
3847     }
3848 #endif //FEATURE_BASICFREEZE
3849
3850     return seg;
3851 }
3852 #endif //SEG_MAPPING_TABLE
3853
3854 size_t gcard_of ( uint8_t*);
3855
3856 #define memref(i) *(uint8_t**)(i)
3857
3858 //GC Flags
3859 #define GC_MARKED       (size_t)0x1
3860 #define slot(i, j) ((uint8_t**)(i))[j+1]
3861
3862 #define free_object_base_size (plug_skew + sizeof(ArrayBase))
3863
3864 class CObjectHeader : public Object
3865 {
3866 public:
3867
3868 #if defined(FEATURE_REDHAWK) || defined(BUILD_AS_STANDALONE)
3869     // The GC expects the following methods that are provided by the Object class in the CLR but not provided
3870     // by Redhawk's version of Object.
3871     uint32_t GetNumComponents()
3872     {
3873         return ((ArrayBase *)this)->GetNumComponents();
3874     }
3875
3876     void Validate(BOOL bDeep=TRUE, BOOL bVerifyNextHeader = TRUE)
3877     {
3878         UNREFERENCED_PARAMETER(bVerifyNextHeader);
3879
3880         if (this == NULL)
3881             return;
3882
3883         MethodTable * pMT = GetMethodTable();
3884
3885         _ASSERTE(pMT->SanityCheck());
3886
3887         bool noRangeChecks =
3888             (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_RANGE_CHECKS) == GCConfig::HEAPVERIFY_NO_RANGE_CHECKS;
3889
3890         BOOL fSmallObjectHeapPtr = FALSE, fLargeObjectHeapPtr = FALSE;
3891         if (!noRangeChecks)
3892         {
3893             fSmallObjectHeapPtr = g_theGCHeap->IsHeapPointer(this, TRUE);
3894             if (!fSmallObjectHeapPtr)
3895                 fLargeObjectHeapPtr = g_theGCHeap->IsHeapPointer(this);
3896
3897             _ASSERTE(fSmallObjectHeapPtr || fLargeObjectHeapPtr);
3898         }
3899
3900 #ifdef FEATURE_STRUCTALIGN
3901         _ASSERTE(IsStructAligned((uint8_t *)this, GetMethodTable()->GetBaseAlignment()));
3902 #endif // FEATURE_STRUCTALIGN
3903
3904 #ifdef FEATURE_64BIT_ALIGNMENT
3905         if (pMT->RequiresAlign8())
3906         {
3907             _ASSERTE((((size_t)this) & 0x7) == (pMT->IsValueType() ? 4U : 0U));
3908         }
3909 #endif // FEATURE_64BIT_ALIGNMENT
3910
3911 #ifdef VERIFY_HEAP
3912         if (bDeep && (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC))
3913             g_theGCHeap->ValidateObjectMember(this);
3914 #endif
3915         if (fSmallObjectHeapPtr)
3916         {
3917 #ifdef FEATURE_BASICFREEZE
3918             _ASSERTE(!g_theGCHeap->IsLargeObject(pMT) || g_theGCHeap->IsInFrozenSegment(this));
3919 #else
3920             _ASSERTE(!g_theGCHeap->IsLargeObject(pMT));
3921 #endif
3922         }
3923     }
3924
3925     void ValidatePromote(ScanContext *sc, uint32_t flags)
3926     {
3927         UNREFERENCED_PARAMETER(sc);
3928         UNREFERENCED_PARAMETER(flags);
3929
3930         Validate();
3931     }
3932
3933     void ValidateHeap(Object *from, BOOL bDeep)
3934     {
3935         UNREFERENCED_PARAMETER(from);
3936
3937         Validate(bDeep, FALSE);
3938     }
3939
3940     ADIndex GetAppDomainIndex()
3941     {
3942         return (ADIndex)RH_DEFAULT_DOMAIN_ID;
3943     }
3944 #endif //FEATURE_REDHAWK
3945
3946     /////
3947     //
3948     // Header Status Information
3949     //
3950
3951     MethodTable    *GetMethodTable() const
3952     {
3953         return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED))));
3954     }
3955
3956     void SetMarked()
3957     {
3958         RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED));
3959     }
3960
3961     BOOL IsMarked() const
3962     {
3963         return !!(((size_t)RawGetMethodTable()) & GC_MARKED);
3964     }
3965
3966     void SetPinned()
3967     {
3968         assert (!(gc_heap::settings.concurrent));
3969         GetHeader()->SetGCBit();
3970     }
3971
3972     BOOL IsPinned() const
3973     {
3974         return !!((((CObjectHeader*)this)->GetHeader()->GetBits()) & BIT_SBLK_GC_RESERVE);
3975     }
3976
3977     void ClearMarked()
3978     {
3979         RawSetMethodTable( GetMethodTable() );
3980     }
3981
3982     CGCDesc *GetSlotMap ()
3983     {
3984         assert (GetMethodTable()->ContainsPointers());
3985         return CGCDesc::GetCGCDescFromMT(GetMethodTable());
3986     }
3987
3988     void SetFree(size_t size)
3989     {
3990         assert (size >= free_object_base_size);
3991
3992         assert (g_gc_pFreeObjectMethodTable->GetBaseSize() == free_object_base_size);
3993         assert (g_gc_pFreeObjectMethodTable->RawGetComponentSize() == 1);
3994
3995         RawSetMethodTable( g_gc_pFreeObjectMethodTable );
3996
3997         size_t* numComponentsPtr = (size_t*) &((uint8_t*) this)[ArrayBase::GetOffsetOfNumComponents()];
3998         *numComponentsPtr = size - free_object_base_size;
3999 #ifdef VERIFY_HEAP
4000         //This introduces a bug in the free list management. 
4001         //((void**) this)[-1] = 0;    // clear the sync block,
4002         assert (*numComponentsPtr >= 0);
4003         if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
4004             memset (((uint8_t*)this)+sizeof(ArrayBase), 0xcc, *numComponentsPtr);
4005 #endif //VERIFY_HEAP
4006     }
4007
4008     void UnsetFree()
4009     {
4010         size_t size = free_object_base_size - plug_skew;
4011
4012         // since we only need to clear 2 ptr size, we do it manually
4013         PTR_PTR m = (PTR_PTR) this;
4014         for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
4015             *(m++) = 0;
4016     }
4017
4018     BOOL IsFree () const
4019     {
4020         return (GetMethodTable() == g_gc_pFreeObjectMethodTable);
4021     }
4022
4023 #ifdef FEATURE_STRUCTALIGN
4024     int GetRequiredAlignment () const
4025     {
4026         return GetMethodTable()->GetRequiredAlignment();
4027     }
4028 #endif // FEATURE_STRUCTALIGN
4029
4030     BOOL ContainsPointers() const
4031     {
4032         return GetMethodTable()->ContainsPointers();
4033     }
4034
4035 #ifdef COLLECTIBLE_CLASS
4036     BOOL Collectible() const
4037     {
4038         return GetMethodTable()->Collectible();
4039     }
4040
4041     FORCEINLINE BOOL ContainsPointersOrCollectible() const
4042     {
4043         MethodTable *pMethodTable = GetMethodTable();
4044         return (pMethodTable->ContainsPointers() || pMethodTable->Collectible());
4045     }
4046 #endif //COLLECTIBLE_CLASS
4047
4048     Object* GetObjectBase() const
4049     {
4050         return (Object*) this;
4051     }
4052 };
4053
4054 #define header(i) ((CObjectHeader*)(i))
4055
4056 #define free_list_slot(x) ((uint8_t**)(x))[2]
4057 #define free_list_undo(x) ((uint8_t**)(x))[-1]
4058 #define UNDO_EMPTY ((uint8_t*)1)
4059
4060 #ifdef SHORT_PLUGS
4061 inline 
4062 void set_plug_padded (uint8_t* node)
4063 {
4064     header(node)->SetMarked();
4065 }
4066 inline
4067 void clear_plug_padded (uint8_t* node)
4068 {
4069     header(node)->ClearMarked();
4070 }
4071 inline
4072 BOOL is_plug_padded (uint8_t* node)
4073 {
4074     return header(node)->IsMarked();
4075 }
4076 #else //SHORT_PLUGS
4077 inline void set_plug_padded (uint8_t* node){}
4078 inline void clear_plug_padded (uint8_t* node){}
4079 inline
4080 BOOL is_plug_padded (uint8_t* node){return FALSE;}
4081 #endif //SHORT_PLUGS
4082
4083
4084 inline size_t unused_array_size(uint8_t * p)
4085 {
4086     assert(((CObjectHeader*)p)->IsFree());
4087
4088     size_t* numComponentsPtr = (size_t*)(p + ArrayBase::GetOffsetOfNumComponents());
4089     return free_object_base_size + *numComponentsPtr;
4090 }
4091
4092 heap_segment* heap_segment_rw (heap_segment* ns)
4093 {
4094     if ((ns == 0) || !heap_segment_read_only_p (ns))
4095     {
4096         return ns;
4097     }
4098     else
4099     {
4100         do
4101         {
4102             ns = heap_segment_next (ns);
4103         } while ((ns != 0) && heap_segment_read_only_p (ns));
4104         return ns;
4105     }
4106 }
4107
4108 //returns the next non ro segment.
4109 heap_segment* heap_segment_next_rw (heap_segment* seg)
4110 {
4111     heap_segment* ns = heap_segment_next (seg);
4112     return heap_segment_rw (ns);
4113 }
4114
4115 // returns the segment before seg.
4116 heap_segment* heap_segment_prev_rw (heap_segment* begin, heap_segment* seg)
4117 {
4118     assert (begin != 0);
4119     heap_segment* prev = begin;
4120     heap_segment* current = heap_segment_next_rw (begin);
4121
4122     while (current && current != seg)
4123     {
4124         prev = current;
4125         current = heap_segment_next_rw (current);
4126     }
4127
4128     if (current == seg)
4129     {
4130         return prev;
4131     }
4132     else
4133     {
4134         return 0;
4135     }
4136 }
4137
4138 // returns the segment before seg.
4139 heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg)
4140 {
4141     assert (begin != 0);
4142     heap_segment* prev = begin;
4143     heap_segment* current = heap_segment_next (begin);
4144
4145     while (current && current != seg)
4146     {
4147         prev = current;
4148         current = heap_segment_next (current);
4149     }
4150
4151     if (current == seg)
4152     {
4153         return prev;
4154     }
4155     else
4156     {
4157         return 0;
4158     }
4159 }
4160
4161 heap_segment* heap_segment_in_range (heap_segment* ns)
4162 {
4163     if ((ns == 0) || heap_segment_in_range_p (ns))
4164     {
4165         return ns;
4166     }
4167     else
4168     {
4169         do
4170         {
4171             ns = heap_segment_next (ns);
4172         } while ((ns != 0) && !heap_segment_in_range_p (ns));
4173         return ns;
4174     }
4175 }
4176
4177 heap_segment* heap_segment_next_in_range (heap_segment* seg)
4178 {
4179     heap_segment* ns = heap_segment_next (seg);
4180     return heap_segment_in_range (ns);
4181 }
4182
4183 typedef struct
4184 {
4185     uint8_t* memory_base;
4186 } imemory_data;
4187
4188 typedef struct
4189 {
4190     imemory_data *initial_memory;
4191     imemory_data *initial_normal_heap; // points into initial_memory_array
4192     imemory_data *initial_large_heap;  // points into initial_memory_array
4193
4194     size_t block_size_normal;
4195     size_t block_size_large;
4196
4197     size_t block_count;                // # of blocks in each
4198     size_t current_block_normal;
4199     size_t current_block_large;
4200
4201     enum 
4202     { 
4203         ALLATONCE = 1, 
4204         TWO_STAGE, 
4205         EACH_BLOCK 
4206     };
4207
4208     size_t allocation_pattern;
4209 } initial_memory_details;
4210
4211 initial_memory_details memory_details;
4212
4213 BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
4214 {
4215     BOOL reserve_success = FALSE;
4216
4217     // should only be called once
4218     assert (memory_details.initial_memory == 0);
4219
4220     memory_details.initial_memory = new (nothrow) imemory_data[num_heaps*2];
4221     if (memory_details.initial_memory == 0)
4222     {
4223         dprintf (2, ("failed to reserve %Id bytes for imemory_data", num_heaps*2*sizeof(imemory_data)));
4224         return FALSE;
4225     }
4226
4227     memory_details.initial_normal_heap = memory_details.initial_memory;
4228     memory_details.initial_large_heap = memory_details.initial_memory + num_heaps;
4229     memory_details.block_size_normal = normal_size;
4230     memory_details.block_size_large = large_size;
4231     memory_details.block_count = num_heaps;
4232
4233     memory_details.current_block_normal = 0;
4234     memory_details.current_block_large = 0;
4235
4236     g_gc_lowest_address = MAX_PTR;
4237     g_gc_highest_address = 0;
4238
4239     if (((size_t)MAX_PTR - large_size) < normal_size)
4240     {
4241         // we are already overflowing with just one heap.
4242         dprintf (2, ("0x%Ix + 0x%Ix already overflow", normal_size, large_size));
4243         return FALSE;
4244     }
4245
4246     if (((size_t)MAX_PTR / memory_details.block_count) < (normal_size + large_size))
4247     {
4248         dprintf (2, ("(0x%Ix + 0x%Ix)*0x%Ix overflow", normal_size, large_size, memory_details.block_count));
4249         return FALSE;
4250     }
4251
4252     size_t requestedMemory = memory_details.block_count * (normal_size + large_size);
4253
4254     uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory);
4255     if (allatonce_block)
4256     {
4257         g_gc_lowest_address =  allatonce_block;
4258         g_gc_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size));
4259         memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
4260
4261         for(size_t i = 0; i < memory_details.block_count; i++)
4262         {
4263             memory_details.initial_normal_heap[i].memory_base = allatonce_block + (i*normal_size);
4264             memory_details.initial_large_heap[i].memory_base = allatonce_block +
4265                             (memory_details.block_count*normal_size) + (i*large_size);
4266             reserve_success = TRUE;
4267         }
4268     }
4269     else
4270     {
4271         // try to allocate 2 blocks
4272         uint8_t* b1 = 0;
4273         uint8_t* b2 = 0;
4274         b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size);
4275         if (b1)
4276         {
4277             b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size);
4278             if (b2)
4279             {
4280                 memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
4281                 g_gc_lowest_address = min(b1,b2);
4282                 g_gc_highest_address = max(b1 + memory_details.block_count*normal_size,
4283                                         b2 + memory_details.block_count*large_size);
4284                 for(size_t i = 0; i < memory_details.block_count; i++)
4285                 {
4286                     memory_details.initial_normal_heap[i].memory_base = b1 + (i*normal_size);
4287                     memory_details.initial_large_heap[i].memory_base = b2 + (i*large_size);
4288                     reserve_success = TRUE;
4289                 }
4290             }
4291             else
4292             {
4293                 // b2 allocation failed, we'll go on to try allocating each block.
4294                 // We could preserve the b1 alloc, but code complexity increases
4295                 virtual_free (b1, memory_details.block_count * normal_size);
4296             }
4297         }
4298
4299         if ((b2==NULL) && ( memory_details.block_count > 1))
4300         {
4301             memory_details.allocation_pattern = initial_memory_details::EACH_BLOCK;
4302
4303             imemory_data *current_block = memory_details.initial_memory;
4304             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4305             {
4306                 size_t block_size = ((i < memory_details.block_count) ?
4307                                      memory_details.block_size_normal :
4308                                      memory_details.block_size_large);
4309                 current_block->memory_base =
4310                     (uint8_t*)virtual_alloc (block_size);
4311                 if (current_block->memory_base == 0)
4312                 {
4313                     // Free the blocks that we've allocated so far
4314                     current_block = memory_details.initial_memory;
4315                     for(size_t j = 0; j < i; j++, current_block++){
4316                         if (current_block->memory_base != 0){
4317                             block_size = ((j < memory_details.block_count) ?
4318                                      memory_details.block_size_normal :
4319                                      memory_details.block_size_large);
4320                              virtual_free (current_block->memory_base , block_size);
4321                         }
4322                     }
4323                     reserve_success = FALSE;
4324                     break;
4325                 }
4326                 else
4327                 {
4328                     if (current_block->memory_base < g_gc_lowest_address)
4329                         g_gc_lowest_address =  current_block->memory_base;
4330                     if (((uint8_t *) current_block->memory_base + block_size) > g_gc_highest_address)
4331                         g_gc_highest_address = (current_block->memory_base + block_size);
4332                 }
4333                 reserve_success = TRUE;
4334             }
4335         }
4336     }
4337
4338     return reserve_success;
4339 }
4340
4341 void destroy_initial_memory()
4342 {
4343     if (memory_details.initial_memory != NULL)
4344     {
4345         if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE)
4346         {
4347             virtual_free(memory_details.initial_memory[0].memory_base,
4348                 memory_details.block_count*(memory_details.block_size_normal +
4349                 memory_details.block_size_large));
4350         }
4351         else if (memory_details.allocation_pattern == initial_memory_details::TWO_STAGE)
4352         {
4353             virtual_free (memory_details.initial_normal_heap[0].memory_base,
4354                 memory_details.block_count*memory_details.block_size_normal);
4355
4356             virtual_free (memory_details.initial_large_heap[0].memory_base,
4357                 memory_details.block_count*memory_details.block_size_large);
4358         }
4359         else
4360         {
4361             assert (memory_details.allocation_pattern == initial_memory_details::EACH_BLOCK);
4362             imemory_data *current_block = memory_details.initial_memory;
4363             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4364             {
4365                 size_t block_size = (i < memory_details.block_count) ? memory_details.block_size_normal :
4366                                                                        memory_details.block_size_large;
4367                 if (current_block->memory_base != NULL)
4368                 {
4369                     virtual_free (current_block->memory_base, block_size);
4370                 }
4371             }
4372         }
4373
4374         delete [] memory_details.initial_memory;
4375         memory_details.initial_memory = NULL;
4376         memory_details.initial_normal_heap = NULL;
4377         memory_details.initial_large_heap = NULL;
4378     }
4379 }
4380
4381 void* next_initial_memory (size_t size)
4382 {
4383     assert ((size == memory_details.block_size_normal) || (size == memory_details.block_size_large));
4384     void *res = NULL;
4385
4386     if ((size != memory_details.block_size_normal) ||
4387         ((memory_details.current_block_normal == memory_details.block_count) &&
4388          (memory_details.block_size_normal == memory_details.block_size_large)))
4389     {
4390         // If the block sizes are the same, flow block requests from normal to large
4391         assert (memory_details.current_block_large < memory_details.block_count);
4392         assert (memory_details.initial_large_heap != 0);
4393
4394         res = memory_details.initial_large_heap[memory_details.current_block_large].memory_base;
4395         memory_details.current_block_large++;
4396     }
4397     else
4398     {
4399         assert (memory_details.current_block_normal < memory_details.block_count);
4400         assert (memory_details.initial_normal_heap != NULL);
4401
4402         res = memory_details.initial_normal_heap[memory_details.current_block_normal].memory_base;
4403         memory_details.current_block_normal++;
4404     }
4405
4406     return res;
4407 }
4408
4409 heap_segment* get_initial_segment (size_t size, int h_number)
4410 {
4411     void* mem = next_initial_memory (size);
4412     heap_segment* res = gc_heap::make_heap_segment ((uint8_t*)mem, size , h_number);
4413
4414     return res;
4415 }
4416
4417 void* virtual_alloc (size_t size)
4418 {
4419     size_t requested_size = size;
4420
4421     if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4422     {
4423         gc_heap::reserved_memory_limit =
4424             GCScan::AskForMoreReservedMemory (gc_heap::reserved_memory_limit, requested_size);
4425         if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4426         {
4427             return 0;
4428         }
4429     }
4430
4431     uint32_t flags = VirtualReserveFlags::None;
4432 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4433     if (virtual_alloc_hardware_write_watch)
4434     {
4435         flags = VirtualReserveFlags::WriteWatch;
4436     }
4437 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4438     void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags);
4439     void *aligned_mem = prgmem;
4440
4441     // We don't want (prgmem + size) to be right at the end of the address space 
4442     // because we'd have to worry about that everytime we do (address + size).
4443     // We also want to make sure that we leave LARGE_OBJECT_SIZE at the end 
4444     // so we allocate a small object we don't need to worry about overflow there
4445     // when we do alloc_ptr+size.
4446     if (prgmem)
4447     {
4448         uint8_t* end_mem = (uint8_t*)prgmem + requested_size;
4449
4450         if ((end_mem == 0) || ((size_t)(MAX_PTR - end_mem) <= END_SPACE_AFTER_GC))
4451         {
4452             GCToOSInterface::VirtualRelease (prgmem, requested_size);
4453             dprintf (2, ("Virtual Alloc size %Id returned memory right against 4GB [%Ix, %Ix[ - discarding",
4454                         requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4455             prgmem = 0;
4456             aligned_mem = 0;
4457         }
4458     }
4459
4460     if (prgmem)
4461     {
4462         gc_heap::reserved_memory += requested_size;
4463     }
4464
4465     dprintf (2, ("Virtual Alloc size %Id: [%Ix, %Ix[",
4466                  requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4467
4468     return aligned_mem;
4469 }
4470
4471 void virtual_free (void* add, size_t size)
4472 {
4473     GCToOSInterface::VirtualRelease (add, size);
4474     gc_heap::reserved_memory -= size;
4475     dprintf (2, ("Virtual Free size %Id: [%Ix, %Ix[",
4476                  size, (size_t)add, (size_t)((uint8_t*)add+size)));
4477 }
4478
4479 static size_t get_valid_segment_size (BOOL large_seg=FALSE)
4480 {
4481     size_t seg_size, initial_seg_size;
4482
4483     if (!large_seg)
4484     {
4485         initial_seg_size = INITIAL_ALLOC;
4486         seg_size = static_cast<size_t>(GCConfig::GetSegmentSize());
4487     }
4488     else
4489     {
4490         initial_seg_size = LHEAP_ALLOC;
4491         seg_size = static_cast<size_t>(GCConfig::GetSegmentSize()) / 2;
4492     }
4493
4494 #ifdef MULTIPLE_HEAPS
4495 #ifdef BIT64
4496     if (!large_seg)
4497 #endif // BIT64
4498     {
4499         if (g_num_processors > 4)
4500             initial_seg_size /= 2;
4501         if (g_num_processors > 8)
4502             initial_seg_size /= 2;
4503     }
4504 #endif //MULTIPLE_HEAPS
4505
4506     // if seg_size is small but not 0 (0 is default if config not set)
4507     // then set the segment to the minimum size
4508     if (!g_theGCHeap->IsValidSegmentSize(seg_size))
4509     {
4510         // if requested size is between 1 byte and 4MB, use min
4511         if ((seg_size >> 1) && !(seg_size >> 22))
4512             seg_size = 1024*1024*4;
4513         else
4514             seg_size = initial_seg_size;
4515     }
4516
4517 #ifdef SEG_MAPPING_TABLE
4518 #ifdef BIT64
4519     seg_size = round_up_power2 (seg_size);
4520 #else
4521     seg_size = round_down_power2 (seg_size);
4522 #endif // BIT64
4523 #endif //SEG_MAPPING_TABLE
4524
4525     return (seg_size);
4526 }
4527
4528 void
4529 gc_heap::compute_new_ephemeral_size()
4530 {
4531     int eph_gen_max = max_generation - 1 - (settings.promotion ? 1 : 0);
4532     size_t padding_size = 0;
4533
4534     for (int i = 0; i <= eph_gen_max; i++)
4535     {
4536         dynamic_data* dd = dynamic_data_of (i);
4537         total_ephemeral_size += (dd_survived_size (dd) - dd_pinned_survived_size (dd));
4538 #ifdef RESPECT_LARGE_ALIGNMENT
4539         total_ephemeral_size += dd_num_npinned_plugs (dd) * switch_alignment_size (FALSE);
4540 #endif //RESPECT_LARGE_ALIGNMENT
4541 #ifdef FEATURE_STRUCTALIGN
4542         total_ephemeral_size += dd_num_npinned_plugs (dd) * MAX_STRUCTALIGN;
4543 #endif //FEATURE_STRUCTALIGN
4544
4545 #ifdef SHORT_PLUGS
4546         padding_size += dd_padding_size (dd);
4547 #endif //SHORT_PLUGS
4548     }
4549
4550     total_ephemeral_size += eph_gen_starts_size;
4551
4552 #ifdef RESPECT_LARGE_ALIGNMENT
4553     size_t planned_ephemeral_size = heap_segment_plan_allocated (ephemeral_heap_segment) -
4554                                        generation_plan_allocation_start (generation_of (max_generation-1));
4555     total_ephemeral_size = min (total_ephemeral_size, planned_ephemeral_size);
4556 #endif //RESPECT_LARGE_ALIGNMENT
4557
4558 #ifdef SHORT_PLUGS
4559     total_ephemeral_size = Align ((size_t)((double)total_ephemeral_size * short_plugs_pad_ratio) + 1);
4560     total_ephemeral_size += Align (DESIRED_PLUG_LENGTH);
4561 #endif //SHORT_PLUGS
4562
4563     dprintf (3, ("total ephemeral size is %Ix, padding %Ix(%Ix)", 
4564         total_ephemeral_size,
4565         padding_size, (total_ephemeral_size - padding_size)));
4566 }
4567
4568 #ifdef _MSC_VER
4569 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
4570 #endif // _MSC_VER
4571
4572 heap_segment*
4573 gc_heap::soh_get_segment_to_expand()
4574 {
4575     size_t size = soh_segment_size;
4576
4577     ordered_plug_indices_init = FALSE;
4578     use_bestfit = FALSE;
4579
4580     //compute the size of the new ephemeral heap segment.
4581     compute_new_ephemeral_size();
4582
4583     if ((settings.pause_mode != pause_low_latency) &&
4584         (settings.pause_mode != pause_no_gc)
4585 #ifdef BACKGROUND_GC
4586         && (!recursive_gc_sync::background_running_p())
4587 #endif //BACKGROUND_GC
4588         )
4589     {
4590         allocator*  gen_alloc = ((settings.condemned_generation == max_generation) ? 0 :
4591                               generation_allocator (generation_of (max_generation)));
4592         dprintf (2, ("(gen%d)soh_get_segment_to_expand", settings.condemned_generation));
4593
4594         // try to find one in the gen 2 segment list, search backwards because the first segments
4595         // tend to be more compact than the later ones.
4596         heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
4597
4598         PREFIX_ASSUME(fseg != NULL);
4599
4600 #ifdef SEG_REUSE_STATS
4601         int try_reuse = 0;
4602 #endif //SEG_REUSE_STATS
4603
4604         heap_segment* seg = ephemeral_heap_segment;
4605         while ((seg = heap_segment_prev_rw (fseg, seg)) && (seg != fseg))
4606         {
4607 #ifdef SEG_REUSE_STATS
4608         try_reuse++;
4609 #endif //SEG_REUSE_STATS
4610
4611             if (can_expand_into_p (seg, size/3, total_ephemeral_size, gen_alloc))
4612             {
4613                 get_gc_data_per_heap()->set_mechanism (gc_heap_expand, 
4614                     (use_bestfit ? expand_reuse_bestfit : expand_reuse_normal));
4615                 if (settings.condemned_generation == max_generation)
4616                 {
4617                     if (use_bestfit)
4618                     {
4619                         build_ordered_free_spaces (seg);
4620                         dprintf (GTC_LOG, ("can use best fit"));
4621                     }
4622
4623 #ifdef SEG_REUSE_STATS
4624                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse", 
4625                         settings.condemned_generation, try_reuse));
4626 #endif //SEG_REUSE_STATS
4627                     dprintf (GTC_LOG, ("max_gen: Found existing segment to expand into %Ix", (size_t)seg));
4628                     return seg;
4629                 }
4630                 else
4631                 {
4632 #ifdef SEG_REUSE_STATS
4633                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse - returning", 
4634                         settings.condemned_generation, try_reuse));
4635 #endif //SEG_REUSE_STATS
4636                     dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix", (size_t)seg));
4637
4638                     // If we return 0 here, the allocator will think since we are short on end
4639                     // of seg we neeed to trigger a full compacting GC. So if sustained low latency 
4640                     // is set we should acquire a new seg instead, that way we wouldn't be short.
4641                     // The real solution, of course, is to actually implement seg reuse in gen1.
4642                     if (settings.pause_mode != pause_sustained_low_latency)
4643                     {
4644                         dprintf (GTC_LOG, ("max_gen-1: SustainedLowLatency is set, acquire a new seg"));
4645                         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_next_full_gc);
4646                         return 0;
4647                     }
4648                 }
4649             }
4650         }
4651     }
4652
4653     heap_segment* result = get_segment (size, FALSE);
4654
4655     if(result)
4656     {
4657 #ifdef BACKGROUND_GC
4658         if (current_c_gc_state == c_gc_state_planning)
4659         {
4660             // When we expand heap during bgc sweep, we set the seg to be swept so 
4661             // we'll always look at cards for objects on the new segment.
4662             result->flags |= heap_segment_flags_swept;
4663         }
4664 #endif //BACKGROUND_GC
4665
4666         FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(result),
4667                                   (size_t)(heap_segment_reserved (result) - heap_segment_mem(result)),
4668                                   gc_etw_segment_small_object_heap);
4669     }
4670
4671     get_gc_data_per_heap()->set_mechanism (gc_heap_expand, (result ? expand_new_seg : expand_no_memory));
4672
4673     if (result == 0)
4674     {
4675         dprintf (2, ("h%d: failed to allocate a new segment!", heap_number));
4676     }
4677     else
4678     {
4679 #ifdef MULTIPLE_HEAPS
4680         heap_segment_heap (result) = this;
4681 #endif //MULTIPLE_HEAPS
4682     }
4683
4684     dprintf (GTC_LOG, ("(gen%d)creating new segment %Ix", settings.condemned_generation, result));
4685     return result;
4686 }
4687
4688 #ifdef _MSC_VER
4689 #pragma warning(default:4706)
4690 #endif // _MSC_VER
4691
4692 //returns 0 in case of allocation failure
4693 heap_segment*
4694 gc_heap::get_segment (size_t size, BOOL loh_p)
4695 {
4696     heap_segment* result = 0;
4697
4698     if (segment_standby_list != 0)
4699     {
4700         result = segment_standby_list;
4701         heap_segment* last = 0;
4702         while (result)
4703         {
4704             size_t hs = (size_t)(heap_segment_reserved (result) - (uint8_t*)result);
4705             if ((hs >= size) && ((hs / 2) < size))
4706             {
4707                 dprintf (2, ("Hoarded segment %Ix found", (size_t) result));
4708                 if (last)
4709                 {
4710                     heap_segment_next (last) = heap_segment_next (result);
4711                 }
4712                 else
4713                 {
4714                     segment_standby_list = heap_segment_next (result);
4715                 }
4716                 break;
4717             }
4718             else
4719             {
4720                 last = result;
4721                 result = heap_segment_next (result);
4722             }
4723         }
4724     }
4725
4726     if (result)
4727     {
4728         init_heap_segment (result);
4729 #ifdef BACKGROUND_GC
4730         if (should_commit_mark_array())
4731         {
4732             dprintf (GC_TABLE_LOG, ("hoarded seg %Ix, mark_array is %Ix", result, mark_array));
4733             if (!commit_mark_array_new_seg (__this, result))
4734             {
4735                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for hoarded seg"));
4736                 // If we can't use it we need to thread it back.
4737                 if (segment_standby_list != 0)
4738                 {
4739                     heap_segment_next (result) = segment_standby_list;
4740                     segment_standby_list = result;
4741                 }
4742                 else
4743                 {
4744                     segment_standby_list = result;
4745                 }
4746
4747                 result = 0;
4748             }
4749         }
4750 #endif //BACKGROUND_GC
4751
4752 #ifdef SEG_MAPPING_TABLE
4753         if (result)
4754             seg_mapping_table_add_segment (result, __this);
4755 #endif //SEG_MAPPING_TABLE
4756     }
4757
4758     if (!result)
4759     {
4760 #ifndef SEG_MAPPING_TABLE
4761         if (!seg_table->ensure_space_for_insert ())
4762             return 0;
4763 #endif //SEG_MAPPING_TABLE
4764         void* mem = virtual_alloc (size);
4765         if (!mem)
4766         {
4767             fgm_result.set_fgm (fgm_reserve_segment, size, loh_p);
4768             return 0;
4769         }
4770
4771         result = gc_heap::make_heap_segment ((uint8_t*)mem, size, heap_number);
4772
4773         if (result)
4774         {
4775             uint8_t* start;
4776             uint8_t* end;
4777             if (mem < g_gc_lowest_address)
4778             {
4779                 start =  (uint8_t*)mem;
4780             }
4781             else
4782             {
4783                 start = (uint8_t*)g_gc_lowest_address;
4784             }
4785
4786             if (((uint8_t*)mem + size) > g_gc_highest_address)
4787             {
4788                 end = (uint8_t*)mem + size;
4789             }
4790             else
4791             {
4792                 end = (uint8_t*)g_gc_highest_address;
4793             }
4794
4795             if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0)
4796             {
4797                 virtual_free (mem, size);
4798                 return 0;
4799             }
4800         }
4801         else
4802         {
4803             fgm_result.set_fgm (fgm_commit_segment_beg, SEGMENT_INITIAL_COMMIT, loh_p);
4804             virtual_free (mem, size);
4805         }
4806
4807         if (result)
4808         {
4809 #ifdef SEG_MAPPING_TABLE
4810             seg_mapping_table_add_segment (result, __this);
4811 #else //SEG_MAPPING_TABLE
4812             gc_heap::seg_table->insert ((uint8_t*)result, delta);
4813 #endif //SEG_MAPPING_TABLE
4814         }
4815     }
4816
4817 #ifdef BACKGROUND_GC
4818     if (result)
4819     {
4820         ::record_changed_seg ((uint8_t*)result, heap_segment_reserved (result), 
4821                             settings.gc_index, current_bgc_state,
4822                             seg_added);
4823         bgc_verify_mark_array_cleared (result);
4824     }
4825 #endif //BACKGROUND_GC
4826
4827     dprintf (GC_TABLE_LOG, ("h%d: new seg: %Ix-%Ix (%Id)", heap_number, result, ((uint8_t*)result + size), size));
4828     return result;
4829 }
4830
4831 void release_segment (heap_segment* sg)
4832 {
4833     ptrdiff_t delta = 0;
4834     FIRE_EVENT(GCFreeSegment_V1, heap_segment_mem(sg));
4835     virtual_free (sg, (uint8_t*)heap_segment_reserved (sg)-(uint8_t*)sg);
4836 }
4837
4838 heap_segment* gc_heap::get_segment_for_loh (size_t size
4839 #ifdef MULTIPLE_HEAPS
4840                                            , gc_heap* hp
4841 #endif //MULTIPLE_HEAPS
4842                                            )
4843 {
4844 #ifndef MULTIPLE_HEAPS
4845     gc_heap* hp = 0;
4846 #endif //MULTIPLE_HEAPS
4847     heap_segment* res = hp->get_segment (size, TRUE);
4848     if (res != 0)
4849     {
4850 #ifdef MULTIPLE_HEAPS
4851         heap_segment_heap (res) = hp;
4852 #endif //MULTIPLE_HEAPS
4853         res->flags |= heap_segment_flags_loh;
4854
4855         FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(res), (size_t)(heap_segment_reserved (res) - heap_segment_mem(res)), gc_etw_segment_large_object_heap);
4856
4857         GCToEEInterface::DiagUpdateGenerationBounds();
4858
4859 #ifdef MULTIPLE_HEAPS
4860         hp->thread_loh_segment (res);
4861 #else
4862         thread_loh_segment (res);
4863 #endif //MULTIPLE_HEAPS
4864     }
4865
4866     return res;
4867 }
4868
4869 void gc_heap::thread_loh_segment (heap_segment* new_seg)
4870 {
4871     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
4872
4873     while (heap_segment_next_rw (seg))
4874         seg = heap_segment_next_rw (seg);
4875     heap_segment_next (seg) = new_seg;
4876 }
4877
4878 heap_segment*
4879 gc_heap::get_large_segment (size_t size, BOOL* did_full_compact_gc)
4880 {
4881     *did_full_compact_gc = FALSE;
4882     size_t last_full_compact_gc_count = get_full_compact_gc_count();
4883
4884     //access to get_segment needs to be serialized
4885     add_saved_spinlock_info (me_release, mt_get_large_seg);
4886
4887     dprintf (SPINLOCK_LOG, ("[%d]Seg: Lmsl", heap_number));
4888     leave_spin_lock (&more_space_lock);
4889     enter_spin_lock (&gc_heap::gc_lock);
4890     dprintf (SPINLOCK_LOG, ("[%d]Seg: Egc", heap_number));
4891     // if a GC happened between here and before we ask for a segment in 
4892     // get_large_segment, we need to count that GC.
4893     size_t current_full_compact_gc_count = get_full_compact_gc_count();
4894
4895     if (current_full_compact_gc_count > last_full_compact_gc_count)
4896     {
4897         *did_full_compact_gc = TRUE;
4898     }
4899
4900 #ifdef BACKGROUND_GC
4901     while (current_c_gc_state == c_gc_state_planning)
4902     {
4903         dprintf (3, ("lh state planning, waiting to get a large seg"));
4904
4905         dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Lgc", heap_number));
4906         leave_spin_lock (&gc_lock);
4907         background_gc_wait_lh (awr_get_loh_seg);
4908         enter_spin_lock (&gc_lock);
4909         dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Egc", heap_number));
4910     }
4911     assert ((current_c_gc_state == c_gc_state_free) ||
4912             (current_c_gc_state == c_gc_state_marking));
4913 #endif //BACKGROUND_GC
4914
4915     heap_segment* res = get_segment_for_loh (size
4916 #ifdef MULTIPLE_HEAPS
4917                                             , this
4918 #endif //MULTIPLE_HEAPS
4919                                             );
4920
4921     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Lgc", heap_number));
4922     leave_spin_lock (&gc_heap::gc_lock);
4923     enter_spin_lock (&more_space_lock);
4924     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Emsl", heap_number));
4925     add_saved_spinlock_info (me_acquire, mt_get_large_seg);
4926     
4927 #ifdef BACKGROUND_GC
4928     wait_for_background_planning (awr_get_loh_seg);
4929 #endif //BACKGROUND_GC
4930
4931     return res;
4932 }
4933
4934 #if 0
4935 BOOL gc_heap::unprotect_segment (heap_segment* seg)
4936 {
4937     uint8_t* start = align_lower_page (heap_segment_mem (seg));
4938     ptrdiff_t region_size = heap_segment_allocated (seg) - start;
4939
4940     if (region_size != 0 )
4941     {
4942         dprintf (3, ("unprotecting segment %Ix:", (size_t)seg));
4943
4944         BOOL status = GCToOSInterface::VirtualUnprotect (start, region_size);
4945         assert (status);
4946         return status;
4947     }
4948     return FALSE;
4949 }
4950 #endif
4951
4952 #ifdef MULTIPLE_HEAPS
4953 #ifdef _X86_
4954 #ifdef _MSC_VER
4955 #pragma warning(disable:4035)
4956     static ptrdiff_t  get_cycle_count()
4957     {
4958         __asm   rdtsc
4959     }
4960 #pragma warning(default:4035)
4961 #elif defined(__GNUC__)
4962     static ptrdiff_t  get_cycle_count()
4963     {
4964         ptrdiff_t cycles;
4965         ptrdiff_t cyclesHi;
4966         __asm__ __volatile__
4967         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4968         return cycles;
4969     }
4970 #else //_MSC_VER
4971 #error Unknown compiler
4972 #endif //_MSC_VER
4973 #elif defined(_TARGET_AMD64_) 
4974 #ifdef _MSC_VER
4975 extern "C" uint64_t __rdtsc();
4976 #pragma intrinsic(__rdtsc)
4977     static ptrdiff_t get_cycle_count()
4978     {
4979         return (ptrdiff_t)__rdtsc();
4980     }
4981 #elif defined(__clang__)    
4982     static ptrdiff_t get_cycle_count()
4983     {
4984         ptrdiff_t cycles;
4985         ptrdiff_t cyclesHi;
4986         __asm__ __volatile__
4987         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4988         return (cyclesHi << 32) | cycles;
4989     }
4990 #else // _MSC_VER
4991     extern "C" ptrdiff_t get_cycle_count(void);
4992 #endif // _MSC_VER
4993 #elif defined(_TARGET_ARM_)
4994     static ptrdiff_t get_cycle_count()
4995     {
4996         // @ARMTODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
4997         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
4998         // all buffer access times being reported as equal in access_time().
4999         return 0;
5000     }
5001 #elif defined(_TARGET_ARM64_)
5002     static ptrdiff_t get_cycle_count()
5003     {
5004         // @ARM64TODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
5005         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
5006         // all buffer access times being reported as equal in access_time().
5007         return 0;
5008     }
5009 #else
5010 #error NYI platform: get_cycle_count
5011 #endif //_TARGET_X86_
5012
5013 class heap_select
5014 {
5015     heap_select() {}
5016     static uint8_t* sniff_buffer;
5017     static unsigned n_sniff_buffers;
5018     static unsigned cur_sniff_index;
5019
5020     static uint16_t proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5021     static uint16_t heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5022     static uint16_t heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5023     static uint16_t heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5024     static uint16_t heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5025     static uint16_t numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5026
5027     static int access_time(uint8_t *sniff_buffer, int heap_number, unsigned sniff_index, unsigned n_sniff_buffers)
5028     {
5029         ptrdiff_t start_cycles = get_cycle_count();
5030         uint8_t sniff = sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE];
5031         assert (sniff == 0);
5032         ptrdiff_t elapsed_cycles = get_cycle_count() - start_cycles;
5033         // add sniff here just to defeat the optimizer
5034         elapsed_cycles += sniff;
5035         return (int) elapsed_cycles;
5036     }
5037
5038 public:
5039     static BOOL init(int n_heaps)
5040     {
5041         assert (sniff_buffer == NULL && n_sniff_buffers == 0);
5042         if (!GCToOSInterface::CanGetCurrentProcessorNumber())
5043         {
5044             n_sniff_buffers = n_heaps*2+1;
5045             size_t n_cache_lines = 1 + n_heaps * n_sniff_buffers + 1;
5046             size_t sniff_buf_size = n_cache_lines * HS_CACHE_LINE_SIZE;
5047             if (sniff_buf_size / HS_CACHE_LINE_SIZE != n_cache_lines) // check for overlow
5048             {
5049                 return FALSE;
5050             }
5051
5052             sniff_buffer = new (nothrow) uint8_t[sniff_buf_size];
5053             if (sniff_buffer == 0)
5054                 return FALSE;
5055             memset(sniff_buffer, 0, sniff_buf_size*sizeof(uint8_t));
5056         }
5057
5058         //can not enable gc numa aware, force all heaps to be in
5059         //one numa node by filling the array with all 0s
5060         if (!GCToOSInterface::CanEnableGCNumaAware())
5061             memset(heap_no_to_numa_node, 0, sizeof (heap_no_to_numa_node)); 
5062
5063         return TRUE;
5064     }
5065
5066     static void init_cpu_mapping(gc_heap * /*heap*/, int heap_number)
5067     {
5068         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5069         {
5070             uint32_t proc_no = GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps;
5071             // We can safely cast heap_number to a uint16_t 'cause GetCurrentProcessCpuCount
5072             // only returns up to MAX_SUPPORTED_CPUS procs right now. We only ever create at most
5073             // MAX_SUPPORTED_CPUS GC threads.
5074             proc_no_to_heap_no[proc_no] = (uint16_t)heap_number;
5075         }
5076     }
5077
5078     static void mark_heap(int heap_number)
5079     {
5080         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5081             return;
5082
5083         for (unsigned sniff_index = 0; sniff_index < n_sniff_buffers; sniff_index++)
5084             sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5085     }
5086
5087     static int select_heap(alloc_context* acontext, int /*hint*/)
5088     {
5089         UNREFERENCED_PARAMETER(acontext); // only referenced by dprintf
5090
5091         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5092             return proc_no_to_heap_no[GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps];
5093
5094         unsigned sniff_index = Interlocked::Increment(&cur_sniff_index);
5095         sniff_index %= n_sniff_buffers;
5096
5097         int best_heap = 0;
5098         int best_access_time = 1000*1000*1000;
5099         int second_best_access_time = best_access_time;
5100
5101         uint8_t *l_sniff_buffer = sniff_buffer;
5102         unsigned l_n_sniff_buffers = n_sniff_buffers;
5103         for (int heap_number = 0; heap_number < gc_heap::n_heaps; heap_number++)
5104         {
5105             int this_access_time = access_time(l_sniff_buffer, heap_number, sniff_index, l_n_sniff_buffers);
5106             if (this_access_time < best_access_time)
5107             {
5108                 second_best_access_time = best_access_time;
5109                 best_access_time = this_access_time;
5110                 best_heap = heap_number;
5111             }
5112             else if (this_access_time < second_best_access_time)
5113             {
5114                 second_best_access_time = this_access_time;
5115             }
5116         }
5117
5118         if (best_access_time*2 < second_best_access_time)
5119         {
5120             sniff_buffer[(1 + best_heap*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5121
5122             dprintf (3, ("select_heap yields crisp %d for context %p\n", best_heap, (void *)acontext));
5123         }
5124         else
5125         {
5126             dprintf (3, ("select_heap yields vague %d for context %p\n", best_heap, (void *)acontext ));
5127         }
5128
5129         return best_heap;
5130     }
5131
5132     static bool can_find_heap_fast()
5133     {
5134         return GCToOSInterface::CanGetCurrentProcessorNumber();
5135     }
5136
5137     static uint16_t find_proc_no_from_heap_no(int heap_number)
5138     {
5139         return heap_no_to_proc_no[heap_number];
5140     }
5141
5142     static void set_proc_no_for_heap(int heap_number, uint16_t proc_no)
5143     {
5144         heap_no_to_proc_no[heap_number] = proc_no;
5145     }
5146
5147     static uint16_t find_numa_node_from_heap_no(int heap_number)
5148     {
5149         return heap_no_to_numa_node[heap_number];
5150     }
5151
5152     static void set_numa_node_for_heap(int heap_number, uint16_t numa_node)
5153     {
5154         heap_no_to_numa_node[heap_number] = numa_node;
5155     }
5156
5157     static uint16_t find_cpu_group_from_heap_no(int heap_number)
5158     {
5159         return heap_no_to_cpu_group[heap_number];
5160     }
5161
5162     static void set_cpu_group_for_heap(int heap_number, uint16_t group_number)
5163     {
5164         heap_no_to_cpu_group[heap_number] = group_number;
5165     }
5166
5167     static uint16_t find_group_proc_from_heap_no(int heap_number)
5168     {
5169         return heap_no_to_group_proc[heap_number];
5170     }
5171
5172     static void set_group_proc_for_heap(int heap_number, uint16_t group_proc)
5173     {
5174         heap_no_to_group_proc[heap_number] = group_proc;
5175     }
5176
5177     static void init_numa_node_to_heap_map(int nheaps)
5178     {   // called right after GCHeap::Init() for each heap is finished
5179         // when numa is not enabled, heap_no_to_numa_node[] are all filled
5180         // with 0s during initialization, and will be treated as one node
5181         numa_node_to_heap_map[0] = 0;
5182         int node_index = 1;
5183
5184         for (int i=1; i < nheaps; i++)
5185         {
5186             if (heap_no_to_numa_node[i] != heap_no_to_numa_node[i-1])
5187                 numa_node_to_heap_map[node_index++] = (uint16_t)i;
5188         }
5189         numa_node_to_heap_map[node_index] = (uint16_t)nheaps; //mark the end with nheaps
5190     }
5191
5192     static void get_heap_range_for_heap(int hn, int* start, int* end)
5193     {   // 1-tier/no numa case: heap_no_to_numa_node[] all zeros, 
5194         // and treated as in one node. thus: start=0, end=n_heaps
5195         uint16_t numa_node = heap_no_to_numa_node[hn];
5196         *start = (int)numa_node_to_heap_map[numa_node];
5197         *end   = (int)(numa_node_to_heap_map[numa_node+1]);
5198     }
5199 };
5200 uint8_t* heap_select::sniff_buffer;
5201 unsigned heap_select::n_sniff_buffers;
5202 unsigned heap_select::cur_sniff_index;
5203 uint16_t heap_select::proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5204 uint16_t heap_select::heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5205 uint16_t heap_select::heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5206 uint16_t heap_select::heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5207 uint16_t heap_select::heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5208 uint16_t heap_select::numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5209
5210 BOOL gc_heap::create_thread_support (unsigned number_of_heaps)
5211 {
5212     BOOL ret = FALSE;
5213     if (!gc_start_event.CreateOSManualEventNoThrow (FALSE))
5214     {
5215         goto cleanup;
5216     }
5217     if (!ee_suspend_event.CreateOSAutoEventNoThrow (FALSE))
5218     {
5219         goto cleanup;
5220     }
5221     if (!gc_t_join.init (number_of_heaps, join_flavor_server_gc))
5222     {
5223         goto cleanup;
5224     }
5225
5226     ret = TRUE;
5227
5228 cleanup:
5229
5230     if (!ret)
5231     {
5232         destroy_thread_support();
5233     }
5234
5235     return ret;
5236 }
5237
5238 void gc_heap::destroy_thread_support ()
5239 {
5240     if (ee_suspend_event.IsValid())
5241     {
5242         ee_suspend_event.CloseEvent();
5243     }
5244     if (gc_start_event.IsValid())
5245     {
5246         gc_start_event.CloseEvent();
5247     }
5248 }
5249
5250 void set_thread_group_affinity_for_heap(int heap_number, GCThreadAffinity* affinity)
5251 {
5252     affinity->Group = GCThreadAffinity::None;
5253     affinity->Processor = GCThreadAffinity::None;
5254
5255     uint16_t gn, gpn;
5256     GCToOSInterface::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
5257
5258     int bit_number = 0;
5259     for (uintptr_t mask = 1; mask !=0; mask <<=1) 
5260     {
5261         if (bit_number == gpn)
5262         {
5263             dprintf(3, ("using processor group %d, mask %Ix for heap %d\n", gn, mask, heap_number));
5264             affinity->Processor = gpn;
5265             affinity->Group = gn;
5266             heap_select::set_cpu_group_for_heap(heap_number, gn);
5267             heap_select::set_group_proc_for_heap(heap_number, gpn);
5268             if (GCToOSInterface::CanEnableGCNumaAware())
5269             {  
5270                 PROCESSOR_NUMBER proc_no;
5271                 proc_no.Group    = gn;
5272                 proc_no.Number   = (uint8_t)gpn;
5273                 proc_no.Reserved = 0;
5274
5275                 uint16_t node_no = 0;
5276                 if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no))
5277                     heap_select::set_numa_node_for_heap(heap_number, node_no);
5278             }
5279             else
5280             {   // no numa setting, each cpu group is treated as a node
5281                 heap_select::set_numa_node_for_heap(heap_number, gn);
5282             }
5283             return;
5284         }
5285         bit_number++;
5286     }
5287 }
5288
5289 void set_thread_affinity_mask_for_heap(int heap_number, GCThreadAffinity* affinity)
5290 {
5291     affinity->Group = GCThreadAffinity::None;
5292     affinity->Processor = GCThreadAffinity::None;
5293
5294     uintptr_t pmask, smask;
5295     if (GCToOSInterface::GetCurrentProcessAffinityMask(&pmask, &smask))
5296     {
5297         pmask &= smask;
5298         int bit_number = 0; 
5299         uint8_t proc_number = 0;
5300         for (uintptr_t mask = 1; mask != 0; mask <<= 1)
5301         {
5302             if ((mask & pmask) != 0)
5303             {
5304                 if (bit_number == heap_number)
5305                 {
5306                     dprintf (3, ("Using processor %d for heap %d", proc_number, heap_number));
5307                     affinity->Processor = proc_number;
5308                     heap_select::set_proc_no_for_heap(heap_number, proc_number);
5309                     if (GCToOSInterface::CanEnableGCNumaAware())
5310                     {
5311                         uint16_t node_no = 0;
5312                         PROCESSOR_NUMBER proc_no;
5313                         proc_no.Group = 0;
5314                         proc_no.Number = (uint8_t)proc_number;
5315                         proc_no.Reserved = 0;
5316                         if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no))
5317                         {
5318                             heap_select::set_numa_node_for_heap(heap_number, node_no);
5319                         }
5320                     }
5321                     return;
5322                 }
5323                 bit_number++;
5324             }
5325             proc_number++;
5326         }
5327     }
5328 }
5329
5330 bool gc_heap::create_gc_thread ()
5331 {
5332     dprintf (3, ("Creating gc thread\n"));
5333     return GCToEEInterface::CreateThread(gc_thread_stub, this, false, ".NET Server GC");
5334 }
5335
5336 #ifdef _MSC_VER
5337 #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
5338 #endif //_MSC_VER
5339 void gc_heap::gc_thread_function ()
5340 {
5341     assert (gc_done_event.IsValid());
5342     assert (gc_start_event.IsValid());
5343     dprintf (3, ("gc thread started"));
5344
5345     heap_select::init_cpu_mapping(this, heap_number);
5346
5347     while (1)
5348     {
5349         assert (!gc_t_join.joined());
5350
5351         if (heap_number == 0)
5352         {
5353             gc_heap::ee_suspend_event.Wait(INFINITE, FALSE);
5354
5355             BEGIN_TIMING(suspend_ee_during_log);
5356             GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
5357             END_TIMING(suspend_ee_during_log);
5358
5359             proceed_with_gc_p = TRUE;
5360
5361             if (!should_proceed_with_gc())
5362             {
5363                 update_collection_counts_for_no_gc();
5364                 proceed_with_gc_p = FALSE;
5365             }
5366             else
5367             {
5368                 settings.init_mechanisms();
5369                 gc_start_event.Set();
5370             }
5371             dprintf (3, ("%d gc thread waiting...", heap_number));
5372         }
5373         else
5374         {
5375             gc_start_event.Wait(INFINITE, FALSE);
5376             dprintf (3, ("%d gc thread waiting... Done", heap_number));
5377         }
5378
5379         assert ((heap_number == 0) || proceed_with_gc_p);
5380
5381         if (proceed_with_gc_p)
5382             garbage_collect (GCHeap::GcCondemnedGeneration);
5383
5384         if (heap_number == 0)
5385         {
5386             if (proceed_with_gc_p && (!settings.concurrent))
5387             {
5388                 do_post_gc();
5389             }
5390
5391 #ifdef BACKGROUND_GC
5392             recover_bgc_settings();
5393 #endif //BACKGROUND_GC
5394
5395 #ifdef MULTIPLE_HEAPS
5396             for (int i = 0; i < gc_heap::n_heaps; i++)
5397             {
5398                 gc_heap* hp = gc_heap::g_heaps[i];
5399                 hp->add_saved_spinlock_info (me_release, mt_block_gc);
5400                 dprintf (SPINLOCK_LOG, ("[%d]GC Lmsl", i));
5401                 leave_spin_lock(&hp->more_space_lock);
5402             }
5403 #endif //MULTIPLE_HEAPS
5404
5405             gc_heap::gc_started = FALSE;
5406
5407             BEGIN_TIMING(restart_ee_during_log);
5408             GCToEEInterface::RestartEE(TRUE);
5409             END_TIMING(restart_ee_during_log);
5410             process_sync_log_stats();
5411
5412             dprintf (SPINLOCK_LOG, ("GC Lgc"));
5413             leave_spin_lock (&gc_heap::gc_lock);
5414
5415             gc_heap::internal_gc_done = true;
5416
5417             if (proceed_with_gc_p)
5418                 set_gc_done();
5419             else
5420             {
5421                 // If we didn't actually do a GC, it means we didn't wait up the other threads,
5422                 // we still need to set the gc_done_event for those threads.
5423                 for (int i = 0; i < gc_heap::n_heaps; i++)
5424                 {
5425                     gc_heap* hp = gc_heap::g_heaps[i];
5426                     hp->set_gc_done();
5427                 }
5428             }
5429         }
5430         else
5431         {
5432             int spin_count = 32 * (gc_heap::n_heaps - 1);
5433
5434             // wait until RestartEE has progressed to a stage where we can restart user threads
5435             while (!gc_heap::internal_gc_done && !GCHeap::SafeToRestartManagedThreads())
5436             {
5437                 spin_and_switch (spin_count, (gc_heap::internal_gc_done || GCHeap::SafeToRestartManagedThreads()));
5438             }
5439             set_gc_done();
5440         }
5441     }
5442 }
5443 #ifdef _MSC_VER
5444 #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
5445 #endif //_MSC_VER
5446
5447 #endif //MULTIPLE_HEAPS
5448
5449 bool virtual_alloc_commit_for_heap(void* addr, size_t size, int h_number)
5450 {
5451 #if defined(MULTIPLE_HEAPS) && !defined(FEATURE_REDHAWK)
5452     // Currently there is no way for us to specific the numa node to allocate on via hosting interfaces to
5453     // a host. This will need to be added later.
5454 #if !defined(FEATURE_CORECLR)
5455     if (!CLRMemoryHosted())
5456 #endif
5457     {
5458         if (GCToOSInterface::CanEnableGCNumaAware())
5459         {
5460             uint32_t numa_node = heap_select::find_numa_node_from_heap_no(h_number);
5461             if (GCToOSInterface::VirtualCommit(addr, size, numa_node))
5462                 return true;
5463         }
5464     }
5465 #else
5466     UNREFERENCED_PARAMETER(h_number);
5467 #endif
5468
5469     //numa aware not enabled, or call failed --> fallback to VirtualCommit()
5470     return GCToOSInterface::VirtualCommit(addr, size);
5471 }
5472
5473 #ifndef SEG_MAPPING_TABLE
5474 inline
5475 heap_segment* gc_heap::segment_of (uint8_t* add, ptrdiff_t& delta, BOOL verify_p)
5476 {
5477     uint8_t* sadd = add;
5478     heap_segment* hs = 0;
5479     heap_segment* hs1 = 0;
5480     if (!((add >= g_gc_lowest_address) && (add < g_gc_highest_address)))
5481     {
5482         delta = 0;
5483         return 0;
5484     }
5485     //repeat in case there is a concurrent insertion in the table.
5486     do
5487     {
5488         hs = hs1;
5489         sadd = add;
5490         seg_table->lookup (sadd);
5491         hs1 = (heap_segment*)sadd;
5492     } while (hs1 && !in_range_for_segment (add, hs1) && (hs != hs1));
5493
5494     hs = hs1;
5495
5496     if ((hs == 0) ||
5497         (verify_p && (add > heap_segment_reserved ((heap_segment*)(sadd + delta)))))
5498         delta = 0;
5499     return hs;
5500 }
5501 #endif //SEG_MAPPING_TABLE
5502
5503 class mark
5504 {
5505 public:
5506     uint8_t* first;
5507     size_t len;
5508
5509     // If we want to save space we can have a pool of plug_and_gap's instead of 
5510     // always having 2 allocated for each pinned plug.
5511     gap_reloc_pair saved_pre_plug;
5512     // If we decide to not compact, we need to restore the original values.
5513     gap_reloc_pair saved_pre_plug_reloc;
5514
5515     gap_reloc_pair saved_post_plug;
5516
5517     // Supposedly Pinned objects cannot have references but we are seeing some from pinvoke 
5518     // frames. Also if it's an artificially pinned plug created by us, it can certainly 
5519     // have references. 
5520     // We know these cases will be rare so we can optimize this to be only allocated on decommand. 
5521     gap_reloc_pair saved_post_plug_reloc;
5522
5523     // We need to calculate this after we are done with plan phase and before compact
5524     // phase because compact phase will change the bricks so relocate_address will no 
5525     // longer work.
5526     uint8_t* saved_pre_plug_info_reloc_start;
5527
5528     // We need to save this because we will have no way to calculate it, unlike the 
5529     // pre plug info start which is right before this plug.
5530     uint8_t* saved_post_plug_info_start;
5531
5532 #ifdef SHORT_PLUGS
5533     uint8_t* allocation_context_start_region;
5534 #endif //SHORT_PLUGS
5535
5536     // How the bits in these bytes are organized:
5537     // MSB --> LSB
5538     // 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
5539     // 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.
5540     BOOL saved_pre_p;
5541     BOOL saved_post_p;
5542
5543 #ifdef _DEBUG
5544     // We are seeing this is getting corrupted for a PP with a NP after.
5545     // Save it when we first set it and make sure it doesn't change.
5546     gap_reloc_pair saved_post_plug_debug;
5547 #endif //_DEBUG
5548
5549     size_t get_max_short_bits()
5550     {
5551         return (sizeof (gap_reloc_pair) / sizeof (uint8_t*));
5552     }
5553
5554     // pre bits
5555     size_t get_pre_short_start_bit ()
5556     {
5557         return (sizeof (saved_pre_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5558     }
5559
5560     BOOL pre_short_p()
5561     {
5562         return (saved_pre_p & (1 << (sizeof (saved_pre_p) * 8 - 1)));
5563     }
5564
5565     void set_pre_short()
5566     {
5567         saved_pre_p |= (1 << (sizeof (saved_pre_p) * 8 - 1));
5568     }
5569
5570     void set_pre_short_bit (size_t bit)
5571     {
5572         saved_pre_p |= 1 << (get_pre_short_start_bit() + bit);
5573     }
5574
5575     BOOL pre_short_bit_p (size_t bit)
5576     {
5577         return (saved_pre_p & (1 << (get_pre_short_start_bit() + bit)));
5578     }
5579
5580 #ifdef COLLECTIBLE_CLASS
5581     void set_pre_short_collectible()
5582     {
5583         saved_pre_p |= 2;
5584     }
5585
5586     BOOL pre_short_collectible_p()
5587     {
5588         return (saved_pre_p & 2);
5589     }
5590 #endif //COLLECTIBLE_CLASS
5591
5592     // post bits
5593     size_t get_post_short_start_bit ()
5594     {
5595         return (sizeof (saved_post_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5596     }
5597
5598     BOOL post_short_p()
5599     {
5600         return (saved_post_p & (1 << (sizeof (saved_post_p) * 8 - 1)));
5601     }
5602
5603     void set_post_short()
5604     {
5605         saved_post_p |= (1 << (sizeof (saved_post_p) * 8 - 1));
5606     }
5607
5608     void set_post_short_bit (size_t bit)
5609     {
5610         saved_post_p |= 1 << (get_post_short_start_bit() + bit);
5611     }
5612
5613     BOOL post_short_bit_p (size_t bit)
5614     {
5615         return (saved_post_p & (1 << (get_post_short_start_bit() + bit)));
5616     }
5617
5618 #ifdef COLLECTIBLE_CLASS
5619     void set_post_short_collectible()
5620     {
5621         saved_post_p |= 2;
5622     }
5623
5624     BOOL post_short_collectible_p()
5625     {
5626         return (saved_post_p & 2);
5627     }
5628 #endif //COLLECTIBLE_CLASS
5629
5630     uint8_t* get_plug_address() { return first; }
5631
5632     BOOL has_pre_plug_info() { return saved_pre_p; }
5633     BOOL has_post_plug_info() { return saved_post_p; }
5634
5635     gap_reloc_pair* get_pre_plug_reloc_info() { return &saved_pre_plug_reloc; }
5636     gap_reloc_pair* get_post_plug_reloc_info() { return &saved_post_plug_reloc; }
5637     void set_pre_plug_info_reloc_start (uint8_t* reloc) { saved_pre_plug_info_reloc_start = reloc; }
5638     uint8_t* get_post_plug_info_start() { return saved_post_plug_info_start; }
5639
5640     // We need to temporarily recover the shortened plugs for compact phase so we can
5641     // copy over the whole plug and their related info (mark bits/cards). But we will
5642     // need to set the artificial gap back so compact phase can keep reading the plug info.
5643     // We also need to recover the saved info because we'll need to recover it later.
5644     // 
5645     // So we would call swap_p*_plug_and_saved once to recover the object info; then call 
5646     // it again to recover the artificial gap.
5647     void swap_pre_plug_and_saved()
5648     {
5649         gap_reloc_pair temp;
5650         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5651         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5652         saved_pre_plug_reloc = temp;
5653     }
5654
5655     void swap_post_plug_and_saved()
5656     {
5657         gap_reloc_pair temp;
5658         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5659         memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5660         saved_post_plug_reloc = temp;
5661     }
5662
5663     void swap_pre_plug_and_saved_for_profiler()
5664     {
5665         gap_reloc_pair temp;
5666         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5667         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5668         saved_pre_plug = temp;
5669     }
5670
5671     void swap_post_plug_and_saved_for_profiler()
5672     {
5673         gap_reloc_pair temp;
5674         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5675         memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5676         saved_post_plug = temp;
5677     }
5678
5679     // We should think about whether it's really necessary to have to copy back the pre plug
5680     // info since it was already copied during compacting plugs. But if a plug doesn't move
5681     // by >= 3 ptr size (the size of gap_reloc_pair), it means we'd have to recover pre plug info.
5682     void recover_plug_info() 
5683     {
5684         if (saved_pre_p)
5685         {
5686             if (gc_heap::settings.compaction)
5687             {
5688                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
5689                     first,
5690                     &saved_pre_plug_reloc, 
5691                     saved_pre_plug_info_reloc_start));
5692                 memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5693             }
5694             else
5695             {
5696                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
5697                     first,
5698                     &saved_pre_plug, 
5699                     (first - sizeof (plug_and_gap))));
5700                 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5701             }
5702         }
5703
5704         if (saved_post_p)
5705         {
5706             if (gc_heap::settings.compaction)
5707             {
5708                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
5709                     first,
5710                     &saved_post_plug_reloc, 
5711                     saved_post_plug_info_start));
5712                 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5713             }
5714             else
5715             {
5716                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
5717                     first,
5718                     &saved_post_plug, 
5719                     saved_post_plug_info_start));
5720                 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5721             }
5722         }
5723     }
5724 };
5725
5726
5727 void gc_mechanisms::init_mechanisms()
5728 {
5729     condemned_generation = 0;
5730     promotion = FALSE;//TRUE;
5731     compaction = TRUE;
5732 #ifdef FEATURE_LOH_COMPACTION
5733     loh_compaction = gc_heap::should_compact_loh();
5734 #else
5735     loh_compaction = FALSE;
5736 #endif //FEATURE_LOH_COMPACTION
5737     heap_expansion = FALSE;
5738     concurrent = FALSE;
5739     demotion = FALSE;
5740     elevation_reduced = FALSE;
5741     found_finalizers = FALSE;
5742 #ifdef BACKGROUND_GC
5743     background_p = recursive_gc_sync::background_running_p() != FALSE;
5744     allocations_allowed = TRUE;
5745 #endif //BACKGROUND_GC
5746
5747     entry_memory_load = 0;
5748     exit_memory_load = 0;
5749
5750 #ifdef STRESS_HEAP
5751     stress_induced = FALSE;
5752 #endif // STRESS_HEAP
5753 }
5754
5755 void gc_mechanisms::first_init()
5756 {
5757     gc_index = 0;
5758     gen0_reduction_count = 0;
5759     should_lock_elevation = FALSE;
5760     elevation_locked_count = 0;
5761     reason = reason_empty;
5762 #ifdef BACKGROUND_GC
5763     pause_mode = gc_heap::gc_can_use_concurrent ? pause_interactive : pause_batch;
5764 #ifdef _DEBUG
5765     int debug_pause_mode = static_cast<int>(GCConfig::GetLatencyMode());
5766     if (debug_pause_mode >= 0)
5767     {
5768         assert (debug_pause_mode <= pause_sustained_low_latency);
5769         pause_mode = (gc_pause_mode)debug_pause_mode;
5770     }
5771 #endif //_DEBUG
5772 #else //BACKGROUND_GC
5773     pause_mode = pause_batch;
5774 #endif //BACKGROUND_GC
5775
5776     init_mechanisms();
5777 }
5778
5779 void gc_mechanisms::record (gc_history_global* history)
5780 {
5781 #ifdef MULTIPLE_HEAPS
5782     history->num_heaps = gc_heap::n_heaps;
5783 #else
5784     history->num_heaps = 1;
5785 #endif //MULTIPLE_HEAPS
5786
5787     history->condemned_generation = condemned_generation;
5788     history->gen0_reduction_count = gen0_reduction_count;
5789     history->reason = reason;
5790     history->pause_mode = (int)pause_mode;
5791     history->mem_pressure = entry_memory_load;
5792     history->global_mechanims_p = 0;
5793
5794     // start setting the boolean values.
5795     if (concurrent)
5796         history->set_mechanism_p (global_concurrent);
5797     
5798     if (compaction)
5799         history->set_mechanism_p (global_compaction);
5800
5801     if (promotion)
5802         history->set_mechanism_p (global_promotion);
5803     
5804     if (demotion)
5805         history->set_mechanism_p (global_demotion);
5806
5807     if (card_bundles)
5808         history->set_mechanism_p (global_card_bundles);
5809
5810     if (elevation_reduced)
5811         history->set_mechanism_p (global_elevation);
5812 }
5813
5814 /**********************************
5815    called at the beginning of GC to fix the allocated size to
5816    what is really allocated, or to turn the free area into an unused object
5817    It needs to be called after all of the other allocation contexts have been
5818    fixed since it relies on alloc_allocated.
5819  ********************************/
5820
5821 //for_gc_p indicates that the work is being done for GC,
5822 //as opposed to concurrent heap verification
5823 void gc_heap::fix_youngest_allocation_area (BOOL for_gc_p)
5824 {
5825     UNREFERENCED_PARAMETER(for_gc_p);
5826
5827     // The gen 0 alloc context is never used for allocation in the allocator path. It's
5828     // still used in the allocation path during GCs.
5829     assert (generation_allocation_pointer (youngest_generation) == nullptr);
5830     assert (generation_allocation_limit (youngest_generation) == nullptr);
5831     heap_segment_allocated (ephemeral_heap_segment) = alloc_allocated;
5832 }
5833
5834 void gc_heap::fix_large_allocation_area (BOOL for_gc_p)
5835 {
5836     UNREFERENCED_PARAMETER(for_gc_p);
5837
5838 #ifdef _DEBUG
5839     alloc_context* acontext = 
5840 #endif // _DEBUG
5841         generation_alloc_context (large_object_generation);
5842     assert (acontext->alloc_ptr == 0);
5843     assert (acontext->alloc_limit == 0); 
5844 #if 0
5845     dprintf (3, ("Large object alloc context: ptr: %Ix, limit %Ix",
5846                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5847     fix_allocation_context (acontext, FALSE, get_alignment_constant (FALSE));
5848     if (for_gc_p)
5849     {
5850         acontext->alloc_ptr = 0;
5851         acontext->alloc_limit = acontext->alloc_ptr;
5852     }
5853 #endif //0
5854 }
5855
5856 //for_gc_p indicates that the work is being done for GC,
5857 //as opposed to concurrent heap verification
5858 void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
5859                                       int align_const)
5860 {
5861     dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
5862                  (size_t)acontext,
5863                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5864
5865     if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) ||
5866         !for_gc_p)
5867     {
5868         uint8_t*  point = acontext->alloc_ptr;
5869         if (point != 0)
5870         {
5871             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
5872             // the allocation area was from the free list
5873             // it was shortened by Align (min_obj_size) to make room for
5874             // at least the shortest unused object
5875             size += Align (min_obj_size, align_const);
5876             assert ((size >= Align (min_obj_size)));
5877
5878             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point,
5879                        (size_t)point + size ));
5880             make_unused_array (point, size);
5881
5882             if (for_gc_p)
5883             {
5884                 generation_free_obj_space (generation_of (0)) += size;
5885                 alloc_contexts_used ++;
5886             }
5887         }
5888     }
5889     else if (for_gc_p)
5890     {
5891         alloc_allocated = acontext->alloc_ptr;
5892         assert (heap_segment_allocated (ephemeral_heap_segment) <=
5893                 heap_segment_committed (ephemeral_heap_segment));
5894         alloc_contexts_used ++;
5895     }
5896
5897     if (for_gc_p)
5898     {
5899         // We need to update the alloc_bytes to reflect the portion that we have not used  
5900         acontext->alloc_bytes -= (acontext->alloc_limit - acontext->alloc_ptr);  
5901         acontext->alloc_ptr = 0;
5902         acontext->alloc_limit = acontext->alloc_ptr;
5903     }
5904 }
5905
5906 //used by the heap verification for concurrent gc.
5907 //it nulls out the words set by fix_allocation_context for heap_verification
5908 void repair_allocation (gc_alloc_context* acontext, void*)
5909 {
5910     uint8_t*  point = acontext->alloc_ptr;
5911
5912     if (point != 0)
5913     {
5914         dprintf (3, ("Clearing [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5915                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
5916         memclr (acontext->alloc_ptr - plug_skew,
5917                 (acontext->alloc_limit - acontext->alloc_ptr)+Align (min_obj_size));
5918     }
5919 }
5920
5921 void void_allocation (gc_alloc_context* acontext, void*)
5922 {
5923     uint8_t*  point = acontext->alloc_ptr;
5924
5925     if (point != 0)
5926     {
5927         dprintf (3, ("Void [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5928                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
5929         acontext->alloc_ptr = 0;
5930         acontext->alloc_limit = acontext->alloc_ptr;
5931     }
5932 }
5933
5934 void gc_heap::repair_allocation_contexts (BOOL repair_p)
5935 {
5936     GCToEEInterface::GcEnumAllocContexts (repair_p ? repair_allocation : void_allocation, NULL);
5937 }
5938
5939 struct fix_alloc_context_args
5940 {
5941     BOOL for_gc_p;
5942     void* heap;
5943 };
5944
5945 void fix_alloc_context(gc_alloc_context* acontext, void* param)
5946 {
5947     fix_alloc_context_args* args = (fix_alloc_context_args*)param;
5948     g_theGCHeap->FixAllocContext(acontext, false, (void*)(size_t)(args->for_gc_p), args->heap);
5949 }
5950
5951 void gc_heap::fix_allocation_contexts(BOOL for_gc_p)
5952 {
5953     fix_alloc_context_args args;
5954     args.for_gc_p = for_gc_p;
5955     args.heap = __this;
5956
5957     GCToEEInterface::GcEnumAllocContexts(fix_alloc_context, &args);
5958     fix_youngest_allocation_area(for_gc_p);
5959     fix_large_allocation_area(for_gc_p);
5960 }
5961
5962 void gc_heap::fix_older_allocation_area (generation* older_gen)
5963 {
5964     heap_segment* older_gen_seg = generation_allocation_segment (older_gen);
5965     if (generation_allocation_limit (older_gen) !=
5966         heap_segment_plan_allocated (older_gen_seg))
5967     {
5968         uint8_t*  point = generation_allocation_pointer (older_gen);
5969
5970         size_t  size = (generation_allocation_limit (older_gen) -
5971                                generation_allocation_pointer (older_gen));
5972         if (size != 0)
5973         {
5974             assert ((size >= Align (min_obj_size)));
5975             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, (size_t)point+size));
5976             make_unused_array (point, size);
5977         }
5978     }
5979     else
5980     {
5981         assert (older_gen_seg != ephemeral_heap_segment);
5982         heap_segment_plan_allocated (older_gen_seg) =
5983             generation_allocation_pointer (older_gen);
5984         generation_allocation_limit (older_gen) =
5985             generation_allocation_pointer (older_gen);
5986     }
5987 }
5988
5989 void gc_heap::set_allocation_heap_segment (generation* gen)
5990 {
5991     uint8_t* p = generation_allocation_start (gen);
5992     assert (p);
5993     heap_segment* seg = generation_allocation_segment (gen);
5994     if (in_range_for_segment (p, seg))
5995         return;
5996
5997     // try ephemeral heap segment in case of heap expansion
5998     seg = ephemeral_heap_segment;
5999     if (!in_range_for_segment (p, seg))
6000     {
6001         seg = heap_segment_rw (generation_start_segment (gen));
6002
6003         PREFIX_ASSUME(seg != NULL);
6004
6005         while (!in_range_for_segment (p, seg))
6006         {
6007             seg = heap_segment_next_rw (seg);
6008             PREFIX_ASSUME(seg != NULL);
6009         }
6010     }
6011
6012     generation_allocation_segment (gen) = seg;
6013 }
6014
6015 void gc_heap::reset_allocation_pointers (generation* gen, uint8_t* start)
6016 {
6017     assert (start);
6018     assert (Align ((size_t)start) == (size_t)start);
6019     generation_allocation_start (gen) = start;
6020     generation_allocation_pointer (gen) =  0;//start + Align (min_obj_size);
6021     generation_allocation_limit (gen) = 0;//generation_allocation_pointer (gen);
6022     set_allocation_heap_segment (gen);
6023 }
6024
6025 #ifdef BACKGROUND_GC
6026 //TODO BACKGROUND_GC this is for test only
6027 void
6028 gc_heap::disallow_new_allocation (int gen_number)
6029 {
6030     UNREFERENCED_PARAMETER(gen_number);
6031     settings.allocations_allowed = FALSE;
6032 }
6033 void
6034 gc_heap::allow_new_allocation (int gen_number)
6035 {
6036     UNREFERENCED_PARAMETER(gen_number);
6037     settings.allocations_allowed = TRUE;
6038 }
6039
6040 #endif //BACKGROUND_GC
6041
6042 bool gc_heap::new_allocation_allowed (int gen_number)
6043 {
6044 #ifdef BACKGROUND_GC
6045     //TODO BACKGROUND_GC this is for test only
6046     if (!settings.allocations_allowed)
6047     {
6048         dprintf (2, ("new allocation not allowed"));
6049         return FALSE;
6050     }
6051 #endif //BACKGROUND_GC
6052
6053     if (dd_new_allocation (dynamic_data_of (gen_number)) < 0)
6054     {
6055         if (gen_number != 0)
6056         {
6057             // For LOH we will give it more budget before we try a GC.
6058             if (settings.concurrent)
6059             {
6060                 dynamic_data* dd2 = dynamic_data_of (max_generation + 1 );
6061
6062                 if (dd_new_allocation (dd2) <= (ptrdiff_t)(-2 * dd_desired_allocation (dd2)))
6063                 {
6064                     return TRUE;
6065                 }
6066             }
6067         }
6068         return FALSE;
6069     }
6070 #ifndef MULTIPLE_HEAPS
6071     else if ((settings.pause_mode != pause_no_gc) && (gen_number == 0))
6072     {
6073         dprintf (3, ("evaluating allocation rate"));
6074         dynamic_data* dd0 = dynamic_data_of (0);
6075         if ((allocation_running_amount - dd_new_allocation (dd0)) >
6076             dd_min_size (dd0))
6077         {
6078             uint32_t ctime = GCToOSInterface::GetLowPrecisionTimeStamp();
6079             if ((ctime - allocation_running_time) > 1000)
6080             {
6081                 dprintf (2, (">1s since last gen0 gc"));
6082                 return FALSE;
6083             }
6084             else
6085             {
6086                 allocation_running_amount = dd_new_allocation (dd0);
6087             }
6088         }
6089     }
6090 #endif //MULTIPLE_HEAPS
6091     return TRUE;
6092 }
6093
6094 inline
6095 ptrdiff_t gc_heap::get_desired_allocation (int gen_number)
6096 {
6097     return dd_desired_allocation (dynamic_data_of (gen_number));
6098 }
6099
6100 inline
6101 ptrdiff_t  gc_heap::get_new_allocation (int gen_number)
6102 {
6103     return dd_new_allocation (dynamic_data_of (gen_number));
6104 }
6105
6106 //return the amount allocated so far in gen_number
6107 inline
6108 ptrdiff_t  gc_heap::get_allocation (int gen_number)
6109 {
6110     dynamic_data* dd = dynamic_data_of (gen_number);
6111
6112     return dd_desired_allocation (dd) - dd_new_allocation (dd);
6113 }
6114
6115 inline
6116 BOOL grow_mark_stack (mark*& m, size_t& len, size_t init_len)
6117 {
6118     size_t new_size = max (init_len, 2*len);
6119     mark* tmp = new (nothrow) mark [new_size];
6120     if (tmp)
6121     {
6122         memcpy (tmp, m, len * sizeof (mark));
6123         delete m;
6124         m = tmp;
6125         len = new_size;
6126         return TRUE;
6127     }
6128     else
6129     {
6130         dprintf (1, ("Failed to allocate %Id bytes for mark stack", (len * sizeof (mark))));
6131         return FALSE;
6132     }
6133 }
6134
6135 inline
6136 uint8_t* pinned_plug (mark* m)
6137 {
6138    return m->first;
6139 }
6140
6141 inline
6142 size_t& pinned_len (mark* m)
6143 {
6144     return m->len;
6145 }
6146
6147 inline
6148 void set_new_pin_info (mark* m, uint8_t* pin_free_space_start)
6149 {
6150     m->len = pinned_plug (m) - pin_free_space_start;
6151 #ifdef SHORT_PLUGS
6152     m->allocation_context_start_region = pin_free_space_start;
6153 #endif //SHORT_PLUGS
6154 }
6155
6156 #ifdef SHORT_PLUGS
6157 inline
6158 uint8_t*& pin_allocation_context_start_region (mark* m)
6159 {
6160     return m->allocation_context_start_region;
6161 }
6162
6163 uint8_t* get_plug_start_in_saved (uint8_t* old_loc, mark* pinned_plug_entry)
6164 {
6165     uint8_t* saved_pre_plug_info = (uint8_t*)(pinned_plug_entry->get_pre_plug_reloc_info());
6166     uint8_t* plug_start_in_saved = saved_pre_plug_info + (old_loc - (pinned_plug (pinned_plug_entry) - sizeof (plug_and_gap)));
6167     //dprintf (1, ("detected a very short plug: %Ix before PP %Ix, pad %Ix", 
6168     //    old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6169     dprintf (1, ("EP: %Ix(%Ix), %Ix", old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6170     return plug_start_in_saved;
6171 }
6172
6173 inline
6174 void set_padding_in_expand (uint8_t* old_loc,
6175                             BOOL set_padding_on_saved_p,
6176                             mark* pinned_plug_entry)
6177 {
6178     if (set_padding_on_saved_p)
6179     {
6180         set_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6181     }
6182     else
6183     {
6184         set_plug_padded (old_loc);
6185     }
6186 }
6187
6188 inline
6189 void clear_padding_in_expand (uint8_t* old_loc,
6190                               BOOL set_padding_on_saved_p,
6191                               mark* pinned_plug_entry)
6192 {
6193     if (set_padding_on_saved_p)
6194     {
6195         clear_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6196     }
6197     else
6198     {
6199         clear_plug_padded (old_loc);
6200     }
6201 }
6202 #endif //SHORT_PLUGS
6203
6204 void gc_heap::reset_pinned_queue()
6205 {
6206     mark_stack_tos = 0;
6207     mark_stack_bos = 0;
6208 }
6209
6210 void gc_heap::reset_pinned_queue_bos()
6211 {
6212     mark_stack_bos = 0;
6213 }
6214
6215 // last_pinned_plug is only for asserting purpose.
6216 void gc_heap::merge_with_last_pinned_plug (uint8_t* last_pinned_plug, size_t plug_size)
6217 {
6218     if (last_pinned_plug)
6219     {
6220         mark& last_m = mark_stack_array[mark_stack_tos - 1];
6221         assert (last_pinned_plug == last_m.first);
6222         if (last_m.saved_post_p)
6223         {
6224             last_m.saved_post_p = FALSE;
6225             dprintf (3, ("setting last plug %Ix post to false", last_m.first));
6226             // We need to recover what the gap has overwritten.
6227             memcpy ((last_m.first + last_m.len - sizeof (plug_and_gap)), &(last_m.saved_post_plug), sizeof (gap_reloc_pair));
6228         }
6229         last_m.len += plug_size;
6230         dprintf (3, ("recovered the last part of plug %Ix, setting its plug size to %Ix", last_m.first, last_m.len));
6231     }
6232 }
6233
6234 void gc_heap::set_allocator_next_pin (uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6235 {
6236     dprintf (3, ("sanp: ptr: %Ix, limit: %Ix", alloc_pointer, alloc_limit));
6237     dprintf (3, ("oldest %Id: %Ix", mark_stack_bos, pinned_plug (oldest_pin())));
6238     if (!(pinned_plug_que_empty_p()))
6239     {
6240         mark*  oldest_entry = oldest_pin();
6241         uint8_t* plug = pinned_plug (oldest_entry);
6242         if ((plug >= alloc_pointer) && (plug < alloc_limit))
6243         {
6244             alloc_limit = pinned_plug (oldest_entry);
6245             dprintf (3, ("now setting alloc context: %Ix->%Ix(%Id)",
6246                 alloc_pointer, alloc_limit, (alloc_limit - alloc_pointer)));
6247         }
6248     }
6249 }
6250
6251 void gc_heap::set_allocator_next_pin (generation* gen)
6252 {
6253     dprintf (3, ("SANP: gen%d, ptr; %Ix, limit: %Ix", gen->gen_num, generation_allocation_pointer (gen), generation_allocation_limit (gen)));
6254     if (!(pinned_plug_que_empty_p()))
6255     {
6256         mark*  oldest_entry = oldest_pin();
6257         uint8_t* plug = pinned_plug (oldest_entry);
6258         if ((plug >= generation_allocation_pointer (gen)) &&
6259             (plug <  generation_allocation_limit (gen)))
6260         {
6261             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
6262             dprintf (3, ("SANP: get next pin free space in gen%d for alloc: %Ix->%Ix(%Id)", 
6263                 gen->gen_num,
6264                 generation_allocation_pointer (gen), generation_allocation_limit (gen),
6265                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
6266         }
6267         else
6268             assert (!((plug < generation_allocation_pointer (gen)) &&
6269                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
6270     }
6271 }
6272
6273 // After we set the info, we increase tos.
6274 void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6275 {
6276     UNREFERENCED_PARAMETER(last_pinned_plug);
6277
6278     mark& m = mark_stack_array[mark_stack_tos];
6279     assert (m.first == last_pinned_plug);
6280
6281     m.len = plug_len;
6282     mark_stack_tos++;
6283     set_allocator_next_pin (alloc_pointer, alloc_limit);
6284 }
6285
6286 // After we set the info, we increase tos.
6287 void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, generation* gen)
6288 {
6289     UNREFERENCED_PARAMETER(last_pinned_plug);
6290
6291     mark& m = mark_stack_array[mark_stack_tos];
6292     assert (m.first == last_pinned_plug);
6293
6294     m.len = plug_len;
6295     mark_stack_tos++;
6296     assert (gen != 0);
6297     // Why are we checking here? gen is never 0.
6298     if (gen != 0)
6299     {
6300         set_allocator_next_pin (gen);
6301     }
6302 }
6303
6304 size_t gc_heap::deque_pinned_plug ()
6305 {
6306     dprintf (3, ("dequed: %Id", mark_stack_bos));
6307     size_t m = mark_stack_bos;
6308     mark_stack_bos++;
6309     return m;
6310 }
6311
6312 inline
6313 mark* gc_heap::pinned_plug_of (size_t bos)
6314 {
6315     return &mark_stack_array [ bos ];
6316 }
6317
6318 inline
6319 mark* gc_heap::oldest_pin ()
6320 {
6321     return pinned_plug_of (mark_stack_bos);
6322 }
6323
6324 inline
6325 BOOL gc_heap::pinned_plug_que_empty_p ()
6326 {
6327     return (mark_stack_bos == mark_stack_tos);
6328 }
6329
6330 inline
6331 mark* gc_heap::before_oldest_pin()
6332 {
6333     if (mark_stack_bos >= 1)
6334         return pinned_plug_of (mark_stack_bos-1);
6335     else
6336         return 0;
6337 }
6338
6339 inline
6340 BOOL gc_heap::ephemeral_pointer_p (uint8_t* o)
6341 {
6342     return ((o >= ephemeral_low) && (o < ephemeral_high));
6343 }
6344
6345 #ifdef MH_SC_MARK
6346 inline
6347 int& gc_heap::mark_stack_busy()
6348 {
6349     return  g_mark_stack_busy [(heap_number+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
6350 }
6351 #endif //MH_SC_MARK
6352
6353 void gc_heap::make_mark_stack (mark* arr)
6354 {
6355     reset_pinned_queue();
6356     mark_stack_array = arr;
6357     mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6358 #ifdef MH_SC_MARK
6359     mark_stack_busy() = 0;
6360 #endif //MH_SC_MARK
6361 }
6362
6363 #ifdef BACKGROUND_GC
6364 inline
6365 size_t& gc_heap::bpromoted_bytes(int thread)
6366 {
6367 #ifdef MULTIPLE_HEAPS
6368     return g_bpromoted [thread*16];
6369 #else //MULTIPLE_HEAPS
6370     UNREFERENCED_PARAMETER(thread);
6371     return g_bpromoted;
6372 #endif //MULTIPLE_HEAPS
6373 }
6374
6375 void gc_heap::make_background_mark_stack (uint8_t** arr)
6376 {
6377     background_mark_stack_array = arr;
6378     background_mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6379     background_mark_stack_tos = arr;
6380 }
6381
6382 void gc_heap::make_c_mark_list (uint8_t** arr)
6383 {
6384     c_mark_list = arr;
6385     c_mark_list_index = 0;
6386     c_mark_list_length = 1 + (OS_PAGE_SIZE / MIN_OBJECT_SIZE);
6387 }
6388 #endif //BACKGROUND_GC
6389
6390
6391 #ifdef CARD_BUNDLE
6392
6393 // The card bundle keeps track of groups of card words.
6394 static const size_t card_bundle_word_width = 32;
6395
6396 // How do we express the fact that 32 bits (card_word_width) is one uint32_t?
6397 static const size_t card_bundle_size = (size_t)(GC_PAGE_SIZE / (sizeof(uint32_t)*card_bundle_word_width));
6398
6399 inline
6400 size_t card_bundle_word (size_t cardb)
6401 {
6402     return cardb / card_bundle_word_width;
6403 }
6404
6405 inline
6406 uint32_t card_bundle_bit (size_t cardb)
6407 {
6408     return (uint32_t)(cardb % card_bundle_word_width);
6409 }
6410
6411 size_t align_cardw_on_bundle (size_t cardw)
6412 {
6413     return ((size_t)(cardw + card_bundle_size - 1) & ~(card_bundle_size - 1 ));
6414 }
6415
6416 // Get the card bundle representing a card word
6417 size_t cardw_card_bundle (size_t cardw)
6418 {
6419     return cardw / card_bundle_size;
6420 }
6421
6422 // Get the first card word in a card bundle
6423 size_t card_bundle_cardw (size_t cardb)
6424 {
6425     return cardb * card_bundle_size;
6426 }
6427
6428 // Clear the specified card bundle
6429 void gc_heap::card_bundle_clear (size_t cardb)
6430 {
6431     card_bundle_table [card_bundle_word (cardb)] &= ~(1 << card_bundle_bit (cardb));
6432     dprintf (1,("Cleared card bundle %Ix [%Ix, %Ix[", cardb, (size_t)card_bundle_cardw (cardb),
6433               (size_t)card_bundle_cardw (cardb+1)));
6434 }
6435
6436 void gc_heap::card_bundle_set (size_t cardb)
6437 {
6438     if (!card_bundle_set_p (cardb))
6439     {
6440         card_bundle_table [card_bundle_word (cardb)] |= (1 << card_bundle_bit (cardb));
6441     }
6442 }
6443
6444 // Set the card bundle bits between start_cardb and end_cardb
6445 void gc_heap::card_bundles_set (size_t start_cardb, size_t end_cardb)
6446 {
6447     if (start_cardb == end_cardb)
6448     {
6449         card_bundle_set(start_cardb);
6450         return;
6451     }
6452
6453     size_t start_word = card_bundle_word (start_cardb);
6454     size_t end_word = card_bundle_word (end_cardb);
6455
6456     if (start_word < end_word)
6457     {
6458         // Set the partial words
6459         card_bundle_table [start_word] |= highbits (~0u, card_bundle_bit (start_cardb));
6460
6461         if (card_bundle_bit (end_cardb))
6462             card_bundle_table [end_word] |= lowbits (~0u, card_bundle_bit (end_cardb));
6463
6464         // Set the full words
6465         for (size_t i = start_word + 1; i < end_word; i++)
6466             card_bundle_table [i] = ~0u;
6467     }
6468     else
6469     {
6470         card_bundle_table [start_word] |= (highbits (~0u, card_bundle_bit (start_cardb)) &
6471                                             lowbits (~0u, card_bundle_bit (end_cardb)));
6472     }
6473 }
6474
6475 // Indicates whether the specified bundle is set.
6476 BOOL gc_heap::card_bundle_set_p (size_t cardb)
6477 {
6478     return (card_bundle_table[card_bundle_word(cardb)] & (1 << card_bundle_bit (cardb)));
6479 }
6480
6481 // Returns the size (in bytes) of a card bundle representing the region from 'from' to 'end'
6482 size_t size_card_bundle_of (uint8_t* from, uint8_t* end)
6483 {
6484     // Number of heap bytes represented by a card bundle word
6485     size_t cbw_span = card_size * card_word_width * card_bundle_size * card_bundle_word_width;
6486
6487     // Align the start of the region down
6488     from = (uint8_t*)((size_t)from & ~(cbw_span - 1));
6489
6490     // Align the end of the region up
6491     end = (uint8_t*)((size_t)(end + (cbw_span - 1)) & ~(cbw_span - 1));
6492
6493     // Make sure they're really aligned
6494     assert (((size_t)from & (cbw_span - 1)) == 0);
6495     assert (((size_t)end  & (cbw_span - 1)) == 0);
6496
6497     return ((end - from) / cbw_span) * sizeof (uint32_t);
6498 }
6499
6500 // Takes a pointer to a card bundle table and an address, and returns a pointer that represents
6501 // where a theoretical card bundle table that represents every address (starting from 0) would
6502 // start if the bundle word representing the address were to be located at the pointer passed in.
6503 // The returned 'translated' pointer makes it convenient/fast to calculate where the card bundle
6504 // for a given address is using a simple shift operation on the address.
6505 uint32_t* translate_card_bundle_table (uint32_t* cb, uint8_t* lowest_address)
6506 {
6507     // The number of bytes of heap memory represented by a card bundle word
6508     const size_t heap_bytes_for_bundle_word = card_size * card_word_width * card_bundle_size * card_bundle_word_width;
6509
6510     // Each card bundle word is 32 bits
6511     return (uint32_t*)((uint8_t*)cb - (((size_t)lowest_address / heap_bytes_for_bundle_word) * sizeof (uint32_t)));
6512 }
6513
6514 void gc_heap::enable_card_bundles ()
6515 {
6516     if (can_use_write_watch_for_card_table() && (!card_bundles_enabled()))
6517     {
6518         dprintf (1, ("Enabling card bundles"));
6519
6520         // We initially set all of the card bundles
6521         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
6522                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
6523         settings.card_bundles = TRUE;
6524     }
6525 }
6526
6527 BOOL gc_heap::card_bundles_enabled ()
6528 {
6529     return settings.card_bundles;
6530 }
6531
6532 #endif // CARD_BUNDLE
6533
6534 #if defined (_TARGET_AMD64_)
6535 #define brick_size ((size_t)4096)
6536 #else
6537 #define brick_size ((size_t)2048)
6538 #endif //_TARGET_AMD64_
6539
6540 inline
6541 size_t gc_heap::brick_of (uint8_t* add)
6542 {
6543     return (size_t)(add - lowest_address) / brick_size;
6544 }
6545
6546 inline
6547 uint8_t* gc_heap::brick_address (size_t brick)
6548 {
6549     return lowest_address + (brick_size * brick);
6550 }
6551
6552
6553 void gc_heap::clear_brick_table (uint8_t* from, uint8_t* end)
6554 {
6555     for (size_t i = brick_of (from);i < brick_of (end); i++)
6556         brick_table[i] = 0;
6557 }
6558
6559 //codes for the brick entries:
6560 //entry == 0 -> not assigned
6561 //entry >0 offset is entry-1
6562 //entry <0 jump back entry bricks
6563
6564
6565 inline
6566 void gc_heap::set_brick (size_t index, ptrdiff_t val)
6567 {
6568     if (val < -32767)
6569     {
6570         val = -32767;
6571     }
6572     assert (val < 32767);
6573     if (val >= 0)
6574         brick_table [index] = (short)val+1;
6575     else
6576         brick_table [index] = (short)val;
6577 }
6578
6579 inline
6580 int gc_heap::get_brick_entry (size_t index)
6581 {
6582 #ifdef MULTIPLE_HEAPS
6583     return VolatileLoadWithoutBarrier(&brick_table [index]);
6584 #else
6585     return brick_table[index];
6586 #endif
6587 }
6588
6589
6590 inline
6591 uint8_t* align_on_brick (uint8_t* add)
6592 {
6593     return (uint8_t*)((size_t)(add + brick_size - 1) & ~(brick_size - 1));
6594 }
6595
6596 inline
6597 uint8_t* align_lower_brick (uint8_t* add)
6598 {
6599     return (uint8_t*)(((size_t)add) & ~(brick_size - 1));
6600 }
6601
6602 size_t size_brick_of (uint8_t* from, uint8_t* end)
6603 {
6604     assert (((size_t)from & (brick_size-1)) == 0);
6605     assert (((size_t)end  & (brick_size-1)) == 0);
6606
6607     return ((end - from) / brick_size) * sizeof (short);
6608 }
6609
6610 inline
6611 uint8_t* gc_heap::card_address (size_t card)
6612 {
6613     return  (uint8_t*) (card_size * card);
6614 }
6615
6616 inline
6617 size_t gc_heap::card_of ( uint8_t* object)
6618 {
6619     return (size_t)(object) / card_size;
6620 }
6621
6622 inline
6623 size_t gc_heap::card_to_brick (size_t card)
6624 {
6625     return brick_of (card_address (card));
6626 }
6627
6628 inline
6629 uint8_t* align_on_card (uint8_t* add)
6630 {
6631     return (uint8_t*)((size_t)(add + card_size - 1) & ~(card_size - 1 ));
6632 }
6633 inline
6634 uint8_t* align_on_card_word (uint8_t* add)
6635 {
6636     return (uint8_t*) ((size_t)(add + (card_size*card_word_width)-1) & ~(card_size*card_word_width - 1));
6637 }
6638
6639 inline
6640 uint8_t* align_lower_card (uint8_t* add)
6641 {
6642     return (uint8_t*)((size_t)add & ~(card_size-1));
6643 }
6644
6645 inline
6646 void gc_heap::clear_card (size_t card)
6647 {
6648     card_table [card_word (card)] =
6649         (card_table [card_word (card)] & ~(1 << card_bit (card)));
6650     dprintf (3,("Cleared card %Ix [%Ix, %Ix[", card, (size_t)card_address (card),
6651               (size_t)card_address (card+1)));
6652 }
6653
6654 inline
6655 void gc_heap::set_card (size_t card)
6656 {
6657     size_t word = card_word (card);
6658     card_table[word] = (card_table [word] | (1 << card_bit (card)));
6659
6660 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
6661     // Also set the card bundle that corresponds to the card
6662     size_t bundle_to_set = cardw_card_bundle(word);
6663
6664     card_bundle_set(bundle_to_set);
6665
6666     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));
6667     assert(card_bundle_set_p(bundle_to_set) != 0);
6668 #endif
6669 }
6670
6671 inline
6672 BOOL  gc_heap::card_set_p (size_t card)
6673 {
6674     return ( card_table [ card_word (card) ] & (1 << card_bit (card)));
6675 }
6676
6677 // Returns the number of DWORDs in the card table that cover the
6678 // range of addresses [from, end[.
6679 size_t count_card_of (uint8_t* from, uint8_t* end)
6680 {
6681     return card_word (gcard_of (end - 1)) - card_word (gcard_of (from)) + 1;
6682 }
6683
6684 // Returns the number of bytes to allocate for a card table
6685 // that covers the range of addresses [from, end[.
6686 size_t size_card_of (uint8_t* from, uint8_t* end)
6687 {
6688     return count_card_of (from, end) * sizeof(uint32_t);
6689 }
6690
6691 // We don't store seg_mapping_table in card_table_info because there's only always one view.
6692 class card_table_info
6693 {
6694 public:
6695     unsigned    recount;
6696     uint8_t*    lowest_address;
6697     uint8_t*    highest_address;
6698     short*      brick_table;
6699
6700 #ifdef CARD_BUNDLE
6701     uint32_t*   card_bundle_table;
6702 #endif //CARD_BUNDLE
6703
6704     // mark_array is always at the end of the data structure because we
6705     // want to be able to make one commit call for everything before it.
6706 #ifdef MARK_ARRAY
6707     uint32_t*   mark_array;
6708 #endif //MARK_ARRAY
6709
6710     size_t      size;
6711     uint32_t*   next_card_table;
6712 };
6713
6714 //These are accessors on untranslated cardtable
6715 inline
6716 unsigned& card_table_refcount (uint32_t* c_table)
6717 {
6718     return *(unsigned*)((char*)c_table - sizeof (card_table_info));
6719 }
6720
6721 inline
6722 uint8_t*& card_table_lowest_address (uint32_t* c_table)
6723 {
6724     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->lowest_address;
6725 }
6726
6727 uint32_t* translate_card_table (uint32_t* ct)
6728 {
6729     return (uint32_t*)((uint8_t*)ct - card_word (gcard_of (card_table_lowest_address (ct))) * sizeof(uint32_t));
6730 }
6731
6732 inline
6733 uint8_t*& card_table_highest_address (uint32_t* c_table)
6734 {
6735     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->highest_address;
6736 }
6737
6738 inline
6739 short*& card_table_brick_table (uint32_t* c_table)
6740 {
6741     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->brick_table;
6742 }
6743
6744 #ifdef CARD_BUNDLE
6745 // Get the card bundle table for the specified card table.
6746 inline
6747 uint32_t*& card_table_card_bundle_table (uint32_t* c_table)
6748 {
6749     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->card_bundle_table;
6750 }
6751 #endif //CARD_BUNDLE
6752
6753 #ifdef MARK_ARRAY
6754 /* Support for mark_array */
6755
6756 inline
6757 uint32_t*& card_table_mark_array (uint32_t* c_table)
6758 {
6759     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->mark_array;
6760 }
6761
6762 #ifdef BIT64
6763 #define mark_bit_pitch ((size_t)16)
6764 #else
6765 #define mark_bit_pitch ((size_t)8)
6766 #endif // BIT64
6767 #define mark_word_width ((size_t)32)
6768 #define mark_word_size (mark_word_width * mark_bit_pitch)
6769
6770 inline
6771 uint8_t* align_on_mark_bit (uint8_t* add)
6772 {
6773     return (uint8_t*)((size_t)(add + (mark_bit_pitch - 1)) & ~(mark_bit_pitch - 1));
6774 }
6775
6776 inline
6777 uint8_t* align_lower_mark_bit (uint8_t* add)
6778 {
6779     return (uint8_t*)((size_t)(add) & ~(mark_bit_pitch - 1));
6780 }
6781
6782 inline
6783 BOOL is_aligned_on_mark_word (uint8_t* add)
6784 {
6785     return ((size_t)add == ((size_t)(add) & ~(mark_word_size - 1)));
6786 }
6787
6788 inline
6789 uint8_t* align_on_mark_word (uint8_t* add)
6790 {
6791     return (uint8_t*)((size_t)(add + mark_word_size - 1) & ~(mark_word_size - 1));
6792 }
6793
6794 inline
6795 uint8_t* align_lower_mark_word (uint8_t* add)
6796 {
6797     return (uint8_t*)((size_t)(add) & ~(mark_word_size - 1));
6798 }
6799
6800 inline
6801 size_t mark_bit_of (uint8_t* add)
6802 {
6803     return ((size_t)add / mark_bit_pitch);
6804 }
6805
6806 inline
6807 unsigned int mark_bit_bit (size_t mark_bit)
6808 {
6809     return (unsigned int)(mark_bit % mark_word_width);
6810 }
6811
6812 inline
6813 size_t mark_bit_word (size_t mark_bit)
6814 {
6815     return (mark_bit / mark_word_width);
6816 }
6817
6818 inline
6819 size_t mark_word_of (uint8_t* add)
6820 {
6821     return ((size_t)add) / mark_word_size;
6822 }
6823
6824 uint8_t* mark_word_address (size_t wd)
6825 {
6826     return (uint8_t*)(wd*mark_word_size);
6827 }
6828
6829 uint8_t* mark_bit_address (size_t mark_bit)
6830 {
6831     return (uint8_t*)(mark_bit*mark_bit_pitch);
6832 }
6833
6834 inline
6835 size_t mark_bit_bit_of (uint8_t* add)
6836 {
6837     return  (((size_t)add / mark_bit_pitch) % mark_word_width);
6838 }
6839
6840 inline
6841 unsigned int gc_heap::mark_array_marked(uint8_t* add)
6842 {
6843     return mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add));
6844 }
6845
6846 inline
6847 BOOL gc_heap::is_mark_bit_set (uint8_t* add)
6848 {
6849     return (mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add)));
6850 }
6851
6852 inline
6853 void gc_heap::mark_array_set_marked (uint8_t* add)
6854 {
6855     size_t index = mark_word_of (add);
6856     uint32_t val = (1 << mark_bit_bit_of (add));
6857 #ifdef MULTIPLE_HEAPS
6858     Interlocked::Or (&(mark_array [index]), val);
6859 #else
6860     mark_array [index] |= val;
6861 #endif 
6862 }
6863
6864 inline
6865 void gc_heap::mark_array_clear_marked (uint8_t* add)
6866 {
6867     mark_array [mark_word_of (add)] &= ~(1 << mark_bit_bit_of (add));
6868 }
6869
6870 size_t size_mark_array_of (uint8_t* from, uint8_t* end)
6871 {
6872     assert (((size_t)from & ((mark_word_size)-1)) == 0);
6873     assert (((size_t)end  & ((mark_word_size)-1)) == 0);
6874     return sizeof (uint32_t)*(((end - from) / mark_word_size));
6875 }
6876
6877 //In order to eliminate the lowest_address in the mark array
6878 //computations (mark_word_of, etc) mark_array is offset
6879 // according to the lowest_address.
6880 uint32_t* translate_mark_array (uint32_t* ma)
6881 {
6882     return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_gc_lowest_address));
6883 }
6884
6885 // from and end must be page aligned addresses. 
6886 void gc_heap::clear_mark_array (uint8_t* from, uint8_t* end, BOOL check_only/*=TRUE*/
6887 #ifdef FEATURE_BASICFREEZE
6888                                 , BOOL read_only/*=FALSE*/
6889 #endif // FEATURE_BASICFREEZE
6890                                 )
6891 {
6892     if(!gc_can_use_concurrent)
6893         return;
6894
6895 #ifdef FEATURE_BASICFREEZE
6896     if (!read_only)
6897 #endif // FEATURE_BASICFREEZE
6898     {
6899         assert (from == align_on_mark_word (from));
6900     }
6901     assert (end == align_on_mark_word (end));
6902
6903 #ifdef BACKGROUND_GC
6904     uint8_t* current_lowest_address = background_saved_lowest_address;
6905     uint8_t* current_highest_address = background_saved_highest_address;
6906 #else
6907     uint8_t* current_lowest_address = lowest_address;
6908     uint8_t* current_highest_address = highest_address;
6909 #endif //BACKGROUND_GC
6910
6911     //there is a possibility of the addresses to be
6912     //outside of the covered range because of a newly allocated
6913     //large object segment
6914     if ((end <= current_highest_address) && (from >= current_lowest_address))
6915     {
6916         size_t beg_word = mark_word_of (align_on_mark_word (from));
6917         MAYBE_UNUSED_VAR(beg_word);
6918         //align end word to make sure to cover the address
6919         size_t end_word = mark_word_of (align_on_mark_word (end));
6920         MAYBE_UNUSED_VAR(end_word);
6921         dprintf (3, ("Calling clearing mark array [%Ix, %Ix[ for addresses [%Ix, %Ix[(%s)",
6922                      (size_t)mark_word_address (beg_word),
6923                      (size_t)mark_word_address (end_word),
6924                      (size_t)from, (size_t)end,
6925                      (check_only ? "check_only" : "clear")));
6926         if (!check_only)
6927         {
6928             uint8_t* op = from;
6929             while (op < mark_word_address (beg_word))
6930             {
6931                 mark_array_clear_marked (op);
6932                 op += mark_bit_pitch;
6933             }
6934
6935             memset (&mark_array[beg_word], 0, (end_word - beg_word)*sizeof (uint32_t));
6936         }
6937 #ifdef _DEBUG
6938         else
6939         {
6940             //Beware, it is assumed that the mark array word straddling
6941             //start has been cleared before
6942             //verify that the array is empty.
6943             size_t  markw = mark_word_of (align_on_mark_word (from));
6944             size_t  markw_end = mark_word_of (align_on_mark_word (end));
6945             while (markw < markw_end)
6946             {
6947                 assert (!(mark_array [markw]));
6948                 markw++;
6949             }
6950             uint8_t* p = mark_word_address (markw_end);
6951             while (p < end)
6952             {
6953                 assert (!(mark_array_marked (p)));
6954                 p++;
6955             }
6956         }
6957 #endif //_DEBUG
6958     }
6959 }
6960 #endif //MARK_ARRAY
6961
6962 //These work on untranslated card tables
6963 inline
6964 uint32_t*& card_table_next (uint32_t* c_table)
6965 {
6966     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->next_card_table;
6967 }
6968
6969 inline
6970 size_t& card_table_size (uint32_t* c_table)
6971 {
6972     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->size;
6973 }
6974
6975 void own_card_table (uint32_t* c_table)
6976 {
6977     card_table_refcount (c_table) += 1;
6978 }
6979
6980 void destroy_card_table (uint32_t* c_table);
6981
6982 void delete_next_card_table (uint32_t* c_table)
6983 {
6984     uint32_t* n_table = card_table_next (c_table);
6985     if (n_table)
6986     {
6987         if (card_table_next (n_table))
6988         {
6989             delete_next_card_table (n_table);
6990         }
6991         if (card_table_refcount (n_table) == 0)
6992         {
6993             destroy_card_table (n_table);
6994             card_table_next (c_table) = 0;
6995         }
6996     }
6997 }
6998
6999 void release_card_table (uint32_t* c_table)
7000 {
7001     assert (card_table_refcount (c_table) >0);
7002     card_table_refcount (c_table) -= 1;
7003     if (card_table_refcount (c_table) == 0)
7004     {
7005         delete_next_card_table (c_table);
7006         if (card_table_next (c_table) == 0)
7007         {
7008             destroy_card_table (c_table);
7009             // sever the link from the parent
7010             if (&g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))] == c_table)
7011             {
7012                 g_gc_card_table = 0;
7013
7014 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7015                 g_gc_card_bundle_table = 0;
7016 #endif
7017 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7018                 SoftwareWriteWatch::StaticClose();
7019 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7020             }
7021             else
7022             {
7023                 uint32_t* p_table = &g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))];
7024                 if (p_table)
7025                 {
7026                     while (p_table && (card_table_next (p_table) != c_table))
7027                         p_table = card_table_next (p_table);
7028                     card_table_next (p_table) = 0;
7029                 }
7030             }
7031         }
7032     }
7033 }
7034
7035 void destroy_card_table (uint32_t* c_table)
7036 {
7037 //  delete (uint32_t*)&card_table_refcount(c_table);
7038
7039     GCToOSInterface::VirtualRelease (&card_table_refcount(c_table), card_table_size(c_table));
7040     dprintf (2, ("Table Virtual Free : %Ix", (size_t)&card_table_refcount(c_table)));
7041 }
7042
7043 uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end)
7044 {
7045     assert (g_gc_lowest_address == start);
7046     assert (g_gc_highest_address == end);
7047
7048     uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7049
7050     size_t bs = size_brick_of (start, end);
7051     size_t cs = size_card_of (start, end);
7052 #ifdef MARK_ARRAY
7053     size_t ms = (gc_can_use_concurrent ? 
7054                  size_mark_array_of (start, end) :
7055                  0);
7056 #else
7057     size_t ms = 0;
7058 #endif //MARK_ARRAY
7059
7060     size_t cb = 0;
7061
7062 #ifdef CARD_BUNDLE
7063     if (can_use_write_watch_for_card_table())
7064     {
7065         cb = size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address);
7066 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7067         // If we're not manually managing the card bundles, we will need to use OS write
7068         // watch APIs over this region to track changes.
7069         virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
7070 #endif
7071     }
7072 #endif //CARD_BUNDLE
7073
7074     size_t wws = 0;
7075 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7076     size_t sw_ww_table_offset = 0;
7077     if (gc_can_use_concurrent)
7078     {
7079         size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7080         sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7081         wws = sw_ww_table_offset - sw_ww_size_before_table + SoftwareWriteWatch::GetTableByteSize(start, end);
7082     }
7083 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7084
7085 #ifdef GROWABLE_SEG_MAPPING_TABLE
7086     size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7087     size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7088     size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7089
7090     st += (st_table_offset_aligned - st_table_offset);
7091 #else //GROWABLE_SEG_MAPPING_TABLE
7092     size_t st = 0;
7093 #endif //GROWABLE_SEG_MAPPING_TABLE
7094
7095     // it is impossible for alloc_size to overflow due bounds on each of 
7096     // its components.
7097     size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7098     uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags);
7099
7100     if (!mem)
7101         return 0;
7102
7103     dprintf (2, ("Init - Card table alloc for %Id bytes: [%Ix, %Ix[",
7104                  alloc_size, (size_t)mem, (size_t)(mem+alloc_size)));
7105
7106     // mark array will be committed separately (per segment).
7107     size_t commit_size = alloc_size - ms;
7108
7109     if (!GCToOSInterface::VirtualCommit (mem, commit_size))
7110     {
7111         dprintf (2, ("Card table commit failed"));
7112         GCToOSInterface::VirtualRelease (mem, alloc_size);
7113         return 0;
7114     }
7115
7116     // initialize the ref count
7117     uint32_t* ct = (uint32_t*)(mem+sizeof (card_table_info));
7118     card_table_refcount (ct) = 0;
7119     card_table_lowest_address (ct) = start;
7120     card_table_highest_address (ct) = end;
7121     card_table_brick_table (ct) = (short*)((uint8_t*)ct + cs);
7122     card_table_size (ct) = alloc_size;
7123     card_table_next (ct) = 0;
7124
7125 #ifdef CARD_BUNDLE
7126     card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7127
7128 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7129     g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), g_gc_lowest_address);
7130 #endif
7131
7132 #endif //CARD_BUNDLE
7133
7134 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7135     if (gc_can_use_concurrent)
7136     {
7137         SoftwareWriteWatch::InitializeUntranslatedTable(mem + sw_ww_table_offset, start);
7138     }
7139 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7140
7141 #ifdef GROWABLE_SEG_MAPPING_TABLE
7142     seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7143     seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table - 
7144                                         size_seg_mapping_table_of (0, (align_lower_segment (g_gc_lowest_address))));
7145 #endif //GROWABLE_SEG_MAPPING_TABLE
7146
7147 #ifdef MARK_ARRAY
7148     if (gc_can_use_concurrent)
7149         card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7150     else
7151         card_table_mark_array (ct) = NULL;
7152 #endif //MARK_ARRAY
7153
7154     return translate_card_table(ct);
7155 }
7156
7157 void gc_heap::set_fgm_result (failure_get_memory f, size_t s, BOOL loh_p)
7158 {
7159 #ifdef MULTIPLE_HEAPS
7160     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
7161     {
7162         gc_heap* hp = gc_heap::g_heaps [hn];
7163         hp->fgm_result.set_fgm (f, s, loh_p);
7164     }
7165 #else //MULTIPLE_HEAPS
7166     fgm_result.set_fgm (f, s, loh_p);
7167 #endif //MULTIPLE_HEAPS
7168 }
7169
7170 //returns 0 for success, -1 otherwise
7171 // We are doing all the decommitting here because we want to make sure we have
7172 // enough memory to do so - if we do this during copy_brick_card_table and 
7173 // and fail to decommit it would make the failure case very complicated to 
7174 // handle. This way we can waste some decommit if we call this multiple 
7175 // times before the next FGC but it's easier to handle the failure case.
7176 int gc_heap::grow_brick_card_tables (uint8_t* start,
7177                                      uint8_t* end,
7178                                      size_t size,
7179                                      heap_segment* new_seg, 
7180                                      gc_heap* hp, 
7181                                      BOOL loh_p)
7182 {
7183     uint8_t* la = g_gc_lowest_address;
7184     uint8_t* ha = g_gc_highest_address;
7185     uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address);
7186     uint8_t* saved_g_highest_address = max (end, g_gc_highest_address);
7187     seg_mapping* new_seg_mapping_table = nullptr;
7188 #ifdef BACKGROUND_GC
7189     // This value is only for logging purpose - it's not necessarily exactly what we 
7190     // would commit for mark array but close enough for diagnostics purpose.
7191     size_t logging_ma_commit_size = size_mark_array_of (0, (uint8_t*)size);
7192 #endif //BACKGROUND_GC
7193
7194     // See if the address is already covered
7195     if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address))
7196     {
7197         {
7198             //modify the higest address so the span covered
7199             //is twice the previous one.
7200             uint8_t* top = (uint8_t*)0 + Align (GCToOSInterface::GetVirtualMemoryLimit());
7201             // On non-Windows systems, we get only an approximate value that can possibly be
7202             // slightly lower than the saved_g_highest_address.
7203             // In such case, we set the top to the saved_g_highest_address so that the
7204             // card and brick tables always cover the whole new range.
7205             if (top < saved_g_highest_address)
7206             {
7207                 top = saved_g_highest_address;
7208             }
7209             size_t ps = ha-la;
7210 #ifdef BIT64
7211             if (ps > (uint64_t)200*1024*1024*1024)
7212                 ps += (uint64_t)100*1024*1024*1024;
7213             else
7214 #endif // BIT64
7215                 ps *= 2;
7216
7217             if (saved_g_lowest_address < g_gc_lowest_address)
7218             {
7219                 if (ps > (size_t)g_gc_lowest_address)
7220                     saved_g_lowest_address = (uint8_t*)(size_t)OS_PAGE_SIZE;
7221                 else
7222                 {
7223                     assert (((size_t)g_gc_lowest_address - ps) >= OS_PAGE_SIZE);
7224                     saved_g_lowest_address = min (saved_g_lowest_address, (g_gc_lowest_address - ps));
7225                 }
7226             }
7227
7228             if (saved_g_highest_address > g_gc_highest_address)
7229             {
7230                 saved_g_highest_address = max ((saved_g_lowest_address + ps), saved_g_highest_address);
7231                 if (saved_g_highest_address > top)
7232                     saved_g_highest_address = top;
7233             }
7234         }
7235         dprintf (GC_TABLE_LOG, ("Growing card table [%Ix, %Ix[",
7236                                 (size_t)saved_g_lowest_address,
7237                                 (size_t)saved_g_highest_address));
7238
7239         bool write_barrier_updated = false;
7240         uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7241         uint32_t* saved_g_card_table = g_gc_card_table;
7242
7243 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7244         uint32_t* saved_g_card_bundle_table = g_gc_card_bundle_table;
7245 #endif
7246
7247         uint32_t* ct = 0;
7248         uint32_t* translated_ct = 0;
7249         short* bt = 0;
7250
7251         size_t cs = size_card_of (saved_g_lowest_address, saved_g_highest_address);
7252         size_t bs = size_brick_of (saved_g_lowest_address, saved_g_highest_address);
7253
7254 #ifdef MARK_ARRAY
7255         size_t ms = (gc_heap::gc_can_use_concurrent ? 
7256                     size_mark_array_of (saved_g_lowest_address, saved_g_highest_address) :
7257                     0);
7258 #else
7259         size_t ms = 0;
7260 #endif //MARK_ARRAY
7261
7262         size_t cb = 0;
7263
7264 #ifdef CARD_BUNDLE
7265         if (can_use_write_watch_for_card_table())
7266         {
7267             cb = size_card_bundle_of (saved_g_lowest_address, saved_g_highest_address);
7268
7269 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7270             // If we're not manually managing the card bundles, we will need to use OS write
7271             // watch APIs over this region to track changes.
7272             virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
7273 #endif
7274         }
7275 #endif //CARD_BUNDLE
7276
7277         size_t wws = 0;
7278 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7279         size_t sw_ww_table_offset = 0;
7280         if (gc_can_use_concurrent)
7281         {
7282             size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7283             sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7284             wws =
7285                 sw_ww_table_offset -
7286                 sw_ww_size_before_table +
7287                 SoftwareWriteWatch::GetTableByteSize(saved_g_lowest_address, saved_g_highest_address);
7288         }
7289 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7290
7291 #ifdef GROWABLE_SEG_MAPPING_TABLE
7292         size_t st = size_seg_mapping_table_of (saved_g_lowest_address, saved_g_highest_address);
7293         size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7294         size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7295         st += (st_table_offset_aligned - st_table_offset);
7296 #else //GROWABLE_SEG_MAPPING_TABLE
7297         size_t st = 0;
7298 #endif //GROWABLE_SEG_MAPPING_TABLE
7299
7300         // it is impossible for alloc_size to overflow due bounds on each of 
7301         // its components.
7302         size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7303         dprintf (GC_TABLE_LOG, ("card table: %Id; brick table: %Id; card bundle: %Id; sw ww table: %Id; seg table: %Id; mark array: %Id",
7304                                   cs, bs, cb, wws, st, ms));
7305
7306         uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags);
7307
7308         if (!mem)
7309         {
7310             set_fgm_result (fgm_grow_table, alloc_size, loh_p);
7311             goto fail;
7312         }
7313
7314         dprintf (GC_TABLE_LOG, ("Table alloc for %Id bytes: [%Ix, %Ix[",
7315                                  alloc_size, (size_t)mem, (size_t)((uint8_t*)mem+alloc_size)));
7316
7317         {   
7318             // mark array will be committed separately (per segment).
7319             size_t commit_size = alloc_size - ms;
7320
7321             if (!GCToOSInterface::VirtualCommit (mem, commit_size))
7322             {
7323                 dprintf (GC_TABLE_LOG, ("Table commit failed"));
7324                 set_fgm_result (fgm_commit_table, commit_size, loh_p);
7325                 goto fail;
7326             }
7327         }
7328
7329         ct = (uint32_t*)(mem + sizeof (card_table_info));
7330         card_table_refcount (ct) = 0;
7331         card_table_lowest_address (ct) = saved_g_lowest_address;
7332         card_table_highest_address (ct) = saved_g_highest_address;
7333         card_table_next (ct) = &g_gc_card_table[card_word (gcard_of (la))];
7334
7335         //clear the card table
7336 /*
7337         memclr ((uint8_t*)ct,
7338                 (((saved_g_highest_address - saved_g_lowest_address)*sizeof (uint32_t) /
7339                   (card_size * card_word_width))
7340                  + sizeof (uint32_t)));
7341 */
7342
7343         bt = (short*)((uint8_t*)ct + cs);
7344
7345         // No initialization needed, will be done in copy_brick_card
7346
7347         card_table_brick_table (ct) = bt;
7348
7349 #ifdef CARD_BUNDLE
7350         card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7351         //set all bundle to look at all of the cards
7352         memset(card_table_card_bundle_table (ct), 0xFF, cb);
7353 #endif //CARD_BUNDLE
7354
7355 #ifdef GROWABLE_SEG_MAPPING_TABLE
7356         {
7357             new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7358             new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table -
7359                                               size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address))));
7360             memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7361                 &seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7362                 size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address));
7363
7364             // new_seg_mapping_table gets assigned to seg_mapping_table at the bottom of this function,
7365             // not here. The reason for this is that, if we fail at mark array committing (OOM) and we've
7366             // already switched seg_mapping_table to point to the new mapping table, we'll decommit it and
7367             // run into trouble. By not assigning here, we're making sure that we will not change seg_mapping_table
7368             // if an OOM occurs.
7369         }
7370 #endif //GROWABLE_SEG_MAPPING_TABLE
7371
7372 #ifdef MARK_ARRAY
7373         if(gc_can_use_concurrent)
7374             card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7375         else
7376             card_table_mark_array (ct) = NULL;
7377 #endif //MARK_ARRAY
7378
7379         translated_ct = translate_card_table (ct);
7380
7381         dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix", 
7382             (size_t)ct, (size_t)translated_ct, (size_t)new_seg_mapping_table, (size_t)card_table_mark_array (ct)));
7383
7384 #ifdef BACKGROUND_GC
7385         if (hp->should_commit_mark_array())
7386         {
7387             dprintf (GC_TABLE_LOG, ("new low: %Ix, new high: %Ix, latest mark array is %Ix(translate: %Ix)", 
7388                                     saved_g_lowest_address, saved_g_highest_address,
7389                                     card_table_mark_array (ct),
7390                                     translate_mark_array (card_table_mark_array (ct))));
7391             uint32_t* new_mark_array = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, saved_g_lowest_address));
7392             if (!commit_new_mark_array_global (new_mark_array))
7393             {
7394                 dprintf (GC_TABLE_LOG, ("failed to commit portions in the mark array for existing segments"));
7395                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7396                 goto fail;
7397             }
7398
7399             if (!commit_mark_array_new_seg (hp, new_seg, translated_ct, saved_g_lowest_address))
7400             {
7401                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg"));
7402                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7403                 goto fail;
7404             }
7405         }
7406         else
7407         {
7408             clear_commit_flag_global();
7409         }
7410 #endif //BACKGROUND_GC
7411
7412 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7413         if (gc_can_use_concurrent)
7414         {
7415             // The current design of software write watch requires that the runtime is suspended during resize. Suspending
7416             // on resize is preferred because it is a far less frequent operation than GetWriteWatch() / ResetWriteWatch().
7417             // Suspending here allows copying dirty state from the old table into the new table, and not have to merge old
7418             // table info lazily as done for card tables.
7419
7420             // Either this thread was the thread that did the suspension which means we are suspended; or this is called
7421             // from a GC thread which means we are in a blocking GC and also suspended.
7422             bool is_runtime_suspended = GCToEEInterface::IsGCThread();
7423             if (!is_runtime_suspended)
7424             {
7425                 // Note on points where the runtime is suspended anywhere in this function. Upon an attempt to suspend the
7426                 // runtime, a different thread may suspend first, causing this thread to block at the point of the suspend call.
7427                 // So, at any suspend point, externally visible state needs to be consistent, as code that depends on that state
7428                 // may run while this thread is blocked. This includes updates to g_gc_card_table, g_gc_lowest_address, and
7429                 // g_gc_highest_address.
7430                 suspend_EE();
7431             }
7432
7433             g_gc_card_table = translated_ct;
7434
7435 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7436             g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address);
7437 #endif
7438
7439             SoftwareWriteWatch::SetResizedUntranslatedTable(
7440                 mem + sw_ww_table_offset,
7441                 saved_g_lowest_address,
7442                 saved_g_highest_address);
7443
7444             // Since the runtime is already suspended, update the write barrier here as well.
7445             // This passes a bool telling whether we need to switch to the post
7446             // grow version of the write barrier.  This test tells us if the new
7447             // segment was allocated at a lower address than the old, requiring
7448             // that we start doing an upper bounds check in the write barrier.
7449             g_gc_lowest_address = saved_g_lowest_address;
7450             g_gc_highest_address = saved_g_highest_address;
7451             stomp_write_barrier_resize(true, la != saved_g_lowest_address);
7452             write_barrier_updated = true;
7453
7454             if (!is_runtime_suspended)
7455             {
7456                 restart_EE();
7457             }
7458         }
7459         else
7460 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7461         {
7462             g_gc_card_table = translated_ct;
7463
7464 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7465             g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address);
7466 #endif
7467         }
7468
7469         seg_mapping_table = new_seg_mapping_table;
7470
7471         GCToOSInterface::FlushProcessWriteBuffers();
7472         g_gc_lowest_address = saved_g_lowest_address;
7473         g_gc_highest_address = saved_g_highest_address;
7474
7475         if (!write_barrier_updated)
7476         {
7477             // This passes a bool telling whether we need to switch to the post
7478             // grow version of the write barrier.  This test tells us if the new
7479             // segment was allocated at a lower address than the old, requiring
7480             // that we start doing an upper bounds check in the write barrier.
7481             // This will also suspend the runtime if the write barrier type needs
7482             // to be changed, so we are doing this after all global state has
7483             // been updated. See the comment above suspend_EE() above for more
7484             // info.
7485             stomp_write_barrier_resize(GCToEEInterface::IsGCThread(), la != saved_g_lowest_address);
7486         }
7487
7488
7489         return 0;
7490         
7491 fail:
7492         //cleanup mess and return -1;
7493
7494         if (mem)
7495         {
7496             assert(g_gc_card_table == saved_g_card_table);
7497
7498 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7499             assert(g_gc_card_bundle_table  == saved_g_card_bundle_table);
7500 #endif
7501
7502             //delete (uint32_t*)((uint8_t*)ct - sizeof(card_table_info));
7503             if (!GCToOSInterface::VirtualRelease (mem, alloc_size))
7504             {
7505                 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualRelease failed"));
7506                 assert (!"release failed");
7507             }
7508         }
7509
7510         return -1;
7511     }
7512     else
7513     {
7514 #ifdef BACKGROUND_GC
7515         if (hp->should_commit_mark_array())
7516         {
7517             dprintf (GC_TABLE_LOG, ("in range new seg %Ix, mark_array is %Ix", new_seg, hp->mark_array));
7518             if (!commit_mark_array_new_seg (hp, new_seg))
7519             {
7520                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg in range"));
7521                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7522                 return -1;
7523             }
7524         }
7525 #endif //BACKGROUND_GC
7526     }
7527
7528     return 0;
7529 }
7530
7531 //copy all of the arrays managed by the card table for a page aligned range
7532 void gc_heap::copy_brick_card_range (uint8_t* la, uint32_t* old_card_table,
7533                                      short* old_brick_table,
7534                                      heap_segment* seg,
7535                                      uint8_t* start, uint8_t* end)
7536 {
7537     ptrdiff_t brick_offset = brick_of (start) - brick_of (la);
7538
7539
7540     dprintf (2, ("copying tables for range [%Ix %Ix[", (size_t)start, (size_t)end));
7541
7542     // copy brick table
7543     short* brick_start = &brick_table [brick_of (start)];
7544     if (old_brick_table)
7545     {
7546         // segments are always on page boundaries
7547         memcpy (brick_start, &old_brick_table[brick_offset],
7548                 size_brick_of (start, end));
7549
7550     }
7551     else
7552     {
7553         // This is a large heap, just clear the brick table
7554     }
7555
7556     uint32_t* old_ct = &old_card_table[card_word (card_of (la))];
7557 #ifdef MARK_ARRAY
7558 #ifdef BACKGROUND_GC
7559     UNREFERENCED_PARAMETER(seg);
7560     if (recursive_gc_sync::background_running_p())
7561     {
7562         uint32_t* old_mark_array = card_table_mark_array (old_ct);
7563
7564         // We don't need to go through all the card tables here because 
7565         // we only need to copy from the GC version of the mark array - when we
7566         // mark (even in allocate_large_object) we always use that mark array.
7567         if ((card_table_highest_address (old_ct) >= start) &&
7568             (card_table_lowest_address (old_ct) <= end))
7569         {
7570             if ((background_saved_highest_address >= start) &&
7571                 (background_saved_lowest_address <= end))
7572             {
7573                 //copy the mark bits
7574                 // segments are always on page boundaries
7575                 uint8_t* m_start = max (background_saved_lowest_address, start);
7576                 uint8_t* m_end = min (background_saved_highest_address, end);
7577                 memcpy (&mark_array[mark_word_of (m_start)],
7578                         &old_mark_array[mark_word_of (m_start) - mark_word_of (la)],
7579                         size_mark_array_of (m_start, m_end));
7580             }
7581         }
7582         else
7583         {
7584             //only large segments can be out of range
7585             assert (old_brick_table == 0);
7586         }
7587     }
7588 #else //BACKGROUND_GC
7589     assert (seg != 0);
7590     clear_mark_array (start, heap_segment_committed(seg));
7591 #endif //BACKGROUND_GC
7592 #endif //MARK_ARRAY
7593
7594     // n way merge with all of the card table ever used in between
7595     uint32_t* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]);
7596
7597     assert (ct);
7598     while (card_table_next (old_ct) != ct)
7599     {
7600         //copy if old card table contained [start, end[
7601         if ((card_table_highest_address (ct) >= end) &&
7602             (card_table_lowest_address (ct) <= start))
7603         {
7604             // or the card_tables
7605
7606             size_t start_word = card_word (card_of (start));
7607
7608             uint32_t* dest = &card_table[start_word];
7609             uint32_t* src = &((translate_card_table (ct))[start_word]);
7610             ptrdiff_t count = count_card_of (start, end);
7611             for (int x = 0; x < count; x++)
7612             {
7613                 *dest |= *src;
7614
7615 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7616                 if (*src != 0)
7617                 {
7618                     card_bundle_set(cardw_card_bundle(start_word+x));
7619                 }
7620 #endif
7621
7622                 dest++;
7623                 src++;
7624             }
7625         }
7626         ct = card_table_next (ct);
7627     }
7628 }
7629
7630 //initialize all of the arrays managed by the card table for a page aligned range when an existing ro segment becomes in range
7631 void gc_heap::init_brick_card_range (heap_segment* seg)
7632 {
7633     dprintf (2, ("initialising tables for range [%Ix %Ix[",
7634                  (size_t)heap_segment_mem (seg),
7635                  (size_t)heap_segment_allocated (seg)));
7636
7637     // initialize the brick table
7638     for (size_t b = brick_of (heap_segment_mem (seg));
7639          b < brick_of (align_on_brick (heap_segment_allocated (seg)));
7640          b++)
7641     {
7642         set_brick (b, -1);
7643     }
7644
7645 #ifdef MARK_ARRAY
7646     if (recursive_gc_sync::background_running_p() && (seg->flags & heap_segment_flags_ma_committed))
7647     {
7648         assert (seg != 0);
7649         clear_mark_array (heap_segment_mem (seg), heap_segment_committed(seg));
7650     }
7651 #endif //MARK_ARRAY
7652
7653     clear_card_for_addresses (heap_segment_mem (seg),
7654                               heap_segment_allocated (seg));
7655 }
7656
7657 void gc_heap::copy_brick_card_table()
7658 {
7659     uint8_t* la = lowest_address;
7660     uint8_t* ha = highest_address;
7661     MAYBE_UNUSED_VAR(ha);
7662     uint32_t* old_card_table = card_table;
7663     short* old_brick_table = brick_table;
7664
7665     assert (la == card_table_lowest_address (&old_card_table[card_word (card_of (la))]));
7666     assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))]));
7667
7668     /* todo: Need a global lock for this */
7669     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
7670     own_card_table (ct);
7671     card_table = translate_card_table (ct);
7672     /* End of global lock */
7673     highest_address = card_table_highest_address (ct);
7674     lowest_address = card_table_lowest_address (ct);
7675
7676     brick_table = card_table_brick_table (ct);
7677
7678 #ifdef MARK_ARRAY
7679     if (gc_can_use_concurrent)
7680     {
7681         mark_array = translate_mark_array (card_table_mark_array (ct));
7682         assert (mark_word_of (g_gc_highest_address) ==
7683             mark_word_of (align_on_mark_word (g_gc_highest_address)));
7684     }
7685     else
7686         mark_array = NULL;
7687 #endif //MARK_ARRAY
7688
7689 #ifdef CARD_BUNDLE
7690 #if defined(MARK_ARRAY) && defined(_DEBUG)
7691 #ifdef GROWABLE_SEG_MAPPING_TABLE
7692     size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7693 #else  //GROWABLE_SEG_MAPPING_TABLE
7694     size_t st = 0;
7695 #endif //GROWABLE_SEG_MAPPING_TABLE
7696 #endif //MARK_ARRAY && _DEBUG
7697     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
7698
7699     // Ensure that the word that represents g_gc_lowest_address in the translated table is located at the
7700     // start of the untranslated table.
7701     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
7702             card_table_card_bundle_table (ct));
7703
7704     //set the card table if we are in a heap growth scenario
7705     if (card_bundles_enabled())
7706     {
7707         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
7708                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
7709     }
7710     //check if we need to turn on card_bundles.
7711 #ifdef MULTIPLE_HEAPS
7712     // use INT64 arithmetic here because of possible overflow on 32p
7713     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*gc_heap::n_heaps;
7714 #else
7715     // use INT64 arithmetic here because of possible overflow on 32p
7716     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
7717 #endif //MULTIPLE_HEAPS
7718     if (reserved_memory >= th)
7719     {
7720         enable_card_bundles();
7721     }
7722
7723 #endif //CARD_BUNDLE
7724
7725     // for each of the segments and heaps, copy the brick table and
7726     // or the card table
7727     heap_segment* seg = generation_start_segment (generation_of (max_generation));
7728     while (seg)
7729     {
7730         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7731         {
7732             //check if it became in range
7733             if ((heap_segment_reserved (seg) > lowest_address) &&
7734                 (heap_segment_mem (seg) < highest_address))
7735             {
7736                 set_ro_segment_in_range (seg);
7737             }
7738         }
7739         else
7740         {
7741
7742             uint8_t* end = align_on_page (heap_segment_allocated (seg));
7743             copy_brick_card_range (la, old_card_table,
7744                                    old_brick_table,
7745                                    seg,
7746                                    align_lower_page (heap_segment_mem (seg)),
7747                                    end);
7748         }
7749         seg = heap_segment_next (seg);
7750     }
7751
7752     seg = generation_start_segment (large_object_generation);
7753     while (seg)
7754     {
7755         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7756         {
7757             //check if it became in range
7758             if ((heap_segment_reserved (seg) > lowest_address) &&
7759                 (heap_segment_mem (seg) < highest_address))
7760             {
7761                 set_ro_segment_in_range (seg);
7762             }
7763         }
7764         else
7765         {
7766             uint8_t* end = align_on_page (heap_segment_allocated (seg));
7767             copy_brick_card_range (la, old_card_table,
7768                                    0,
7769                                    seg,
7770                                    align_lower_page (heap_segment_mem (seg)),
7771                                    end);
7772         }
7773         seg = heap_segment_next (seg);
7774     }
7775
7776     release_card_table (&old_card_table[card_word (card_of(la))]);
7777 }
7778
7779 #ifdef FEATURE_BASICFREEZE
7780 BOOL gc_heap::insert_ro_segment (heap_segment* seg)
7781 {
7782     enter_spin_lock (&gc_heap::gc_lock);
7783
7784     if (!gc_heap::seg_table->ensure_space_for_insert ()
7785         || (should_commit_mark_array() && !commit_mark_array_new_seg(__this, seg)))
7786     {
7787         leave_spin_lock(&gc_heap::gc_lock);
7788         return FALSE;
7789     }
7790
7791     //insert at the head of the segment list
7792     generation* gen2 = generation_of (max_generation);
7793     heap_segment* oldhead = generation_start_segment (gen2);
7794     heap_segment_next (seg) = oldhead;
7795     generation_start_segment (gen2) = seg;
7796
7797     seg_table->insert (heap_segment_mem(seg), (size_t)seg);
7798
7799 #ifdef SEG_MAPPING_TABLE
7800     seg_mapping_table_add_ro_segment (seg);
7801 #endif //SEG_MAPPING_TABLE
7802
7803     //test if in range
7804     if ((heap_segment_reserved (seg) > lowest_address) &&
7805         (heap_segment_mem (seg) < highest_address))
7806     {
7807         set_ro_segment_in_range (seg);
7808     }
7809
7810     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg), (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), gc_etw_segment_read_only_heap);
7811
7812     leave_spin_lock (&gc_heap::gc_lock);
7813     return TRUE;
7814 }
7815
7816 // No one is calling this function right now. If this is getting called we need
7817 // to take care of decommitting the mark array for it - we will need to remember
7818 // which portion of the mark array was committed and only decommit that.
7819 void gc_heap::remove_ro_segment (heap_segment* seg)
7820 {
7821 //clear the mark bits so a new segment allocated in its place will have a clear mark bits
7822 #ifdef MARK_ARRAY
7823     if (gc_can_use_concurrent)
7824     {
7825         clear_mark_array (align_lower_mark_word (max (heap_segment_mem (seg), lowest_address)),
7826                       align_on_card_word (min (heap_segment_allocated (seg), highest_address)),
7827                       false); // read_only segments need the mark clear
7828     }
7829 #endif //MARK_ARRAY
7830
7831     enter_spin_lock (&gc_heap::gc_lock);
7832
7833     seg_table->remove ((uint8_t*)seg);
7834
7835 #ifdef SEG_MAPPING_TABLE
7836     seg_mapping_table_remove_ro_segment (seg);
7837 #endif //SEG_MAPPING_TABLE
7838
7839     // Locate segment (and previous segment) in the list.
7840     generation* gen2 = generation_of (max_generation);
7841     heap_segment* curr_seg = generation_start_segment (gen2);
7842     heap_segment* prev_seg = NULL;
7843
7844     while (curr_seg && curr_seg != seg)
7845     {
7846         prev_seg = curr_seg;
7847         curr_seg = heap_segment_next (curr_seg);
7848     }
7849     assert (curr_seg == seg);
7850
7851     // Patch previous segment (or list head if there is none) to skip the removed segment.
7852     if (prev_seg)
7853         heap_segment_next (prev_seg) = heap_segment_next (curr_seg);
7854     else
7855         generation_start_segment (gen2) = heap_segment_next (curr_seg);
7856
7857     leave_spin_lock (&gc_heap::gc_lock);
7858 }
7859 #endif //FEATURE_BASICFREEZE
7860
7861 BOOL gc_heap::set_ro_segment_in_range (heap_segment* seg)
7862 {
7863     //set it in range
7864     seg->flags |= heap_segment_flags_inrange;
7865 //    init_brick_card_range (seg);
7866     ro_segments_in_range = TRUE;
7867     //right now, segments aren't protected
7868     //unprotect_segment (seg);
7869     return TRUE;
7870 }
7871
7872 #ifdef MARK_LIST
7873
7874 uint8_t** make_mark_list (size_t size)
7875 {
7876     uint8_t** mark_list = new (nothrow) uint8_t* [size];
7877     return mark_list;
7878 }
7879
7880 #define swap(a,b){uint8_t* t; t = a; a = b; b = t;}
7881
7882 void verify_qsort_array (uint8_t* *low, uint8_t* *high)
7883 {
7884     uint8_t **i = 0;
7885
7886     for (i = low+1; i <= high; i++)
7887     {
7888         if (*i < *(i-1))
7889         {
7890             FATAL_GC_ERROR();
7891         }
7892     }
7893 }
7894
7895 #ifndef USE_INTROSORT
7896 void qsort1( uint8_t* *low, uint8_t* *high, unsigned int depth)
7897 {
7898     if (((low + 16) >= high) || (depth > 100))
7899     {
7900         //insertion sort
7901         uint8_t **i, **j;
7902         for (i = low+1; i <= high; i++)
7903         {
7904             uint8_t* val = *i;
7905             for (j=i;j >low && val<*(j-1);j--)
7906             {
7907                 *j=*(j-1);
7908             }
7909             *j=val;
7910         }
7911     }
7912     else
7913     {
7914         uint8_t *pivot, **left, **right;
7915
7916         //sort low middle and high
7917         if (*(low+((high-low)/2)) < *low)
7918             swap (*(low+((high-low)/2)), *low);
7919         if (*high < *low)
7920             swap (*low, *high);
7921         if (*high < *(low+((high-low)/2)))
7922             swap (*(low+((high-low)/2)), *high);
7923
7924         swap (*(low+((high-low)/2)), *(high-1));
7925         pivot =  *(high-1);
7926         left = low; right = high-1;
7927         while (1) {
7928             while (*(--right) > pivot);
7929             while (*(++left)  < pivot);
7930             if (left < right)
7931             {
7932                 swap(*left, *right);
7933             }
7934             else
7935                 break;
7936         }
7937         swap (*left, *(high-1));
7938         qsort1(low, left-1, depth+1);
7939         qsort1(left+1, high, depth+1);
7940     }
7941 }
7942 #endif //USE_INTROSORT
7943 void rqsort1( uint8_t* *low, uint8_t* *high)
7944 {
7945     if ((low + 16) >= high)
7946     {
7947         //insertion sort
7948         uint8_t **i, **j;
7949         for (i = low+1; i <= high; i++)
7950         {
7951             uint8_t* val = *i;
7952             for (j=i;j >low && val>*(j-1);j--)
7953             {
7954                 *j=*(j-1);
7955             }
7956             *j=val;
7957         }
7958     }
7959     else
7960     {
7961         uint8_t *pivot, **left, **right;
7962
7963         //sort low middle and high
7964         if (*(low+((high-low)/2)) > *low)
7965             swap (*(low+((high-low)/2)), *low);
7966         if (*high > *low)
7967             swap (*low, *high);
7968         if (*high > *(low+((high-low)/2)))
7969             swap (*(low+((high-low)/2)), *high);
7970
7971         swap (*(low+((high-low)/2)), *(high-1));
7972         pivot =  *(high-1);
7973         left = low; right = high-1;
7974         while (1) {
7975             while (*(--right) < pivot);
7976             while (*(++left)  > pivot);
7977             if (left < right)
7978             {
7979                 swap(*left, *right);
7980             }
7981             else
7982                 break;
7983         }
7984         swap (*left, *(high-1));
7985         rqsort1(low, left-1);
7986         rqsort1(left+1, high);
7987     }
7988 }
7989
7990 #ifdef USE_INTROSORT
7991 class introsort 
7992 {
7993
7994 private: 
7995     static const int size_threshold = 64;
7996     static const int max_depth = 100;
7997
7998
7999 inline static void swap_elements(uint8_t** i,uint8_t** j)
8000     {
8001         uint8_t* t=*i;
8002         *i=*j; 
8003         *j=t;
8004     }
8005
8006 public:
8007     static void sort (uint8_t** begin, uint8_t** end, int ignored)
8008     {
8009         ignored = 0;
8010         introsort_loop (begin, end, max_depth);
8011         insertionsort (begin, end);
8012     }
8013
8014 private: 
8015
8016     static void introsort_loop (uint8_t** lo, uint8_t** hi, int depth_limit)
8017     {
8018         while (hi-lo >= size_threshold)
8019         {
8020             if (depth_limit == 0)
8021             {
8022                 heapsort (lo, hi);
8023                 return;
8024             }
8025             uint8_t** p=median_partition (lo, hi);
8026             depth_limit=depth_limit-1;
8027             introsort_loop (p, hi, depth_limit);
8028             hi=p-1;
8029         }        
8030     }
8031
8032     static uint8_t** median_partition (uint8_t** low, uint8_t** high)
8033     {
8034         uint8_t *pivot, **left, **right;
8035
8036         //sort low middle and high
8037         if (*(low+((high-low)/2)) < *low)
8038             swap_elements ((low+((high-low)/2)), low);
8039         if (*high < *low)
8040             swap_elements (low, high);
8041         if (*high < *(low+((high-low)/2)))
8042             swap_elements ((low+((high-low)/2)), high);
8043
8044         swap_elements ((low+((high-low)/2)), (high-1));
8045         pivot =  *(high-1);
8046         left = low; right = high-1;
8047         while (1) {
8048             while (*(--right) > pivot);
8049             while (*(++left)  < pivot);
8050             if (left < right)
8051             {
8052                 swap_elements(left, right);
8053             }
8054             else
8055                 break;
8056         }
8057         swap_elements (left, (high-1));
8058         return left;
8059     }
8060
8061
8062     static void insertionsort (uint8_t** lo, uint8_t** hi)
8063     {
8064         for (uint8_t** i=lo+1; i <= hi; i++)
8065         {
8066             uint8_t** j = i;
8067             uint8_t* t = *i;
8068             while((j > lo) && (t <*(j-1)))
8069             {
8070                 *j = *(j-1);
8071                 j--;
8072             }
8073             *j = t;
8074         }
8075     }
8076
8077     static void heapsort (uint8_t** lo, uint8_t** hi)
8078     { 
8079         size_t n = hi - lo + 1;
8080         for (size_t i=n / 2; i >= 1; i--)
8081         {
8082             downheap (i,n,lo);
8083         }
8084         for (size_t i = n; i > 1; i--)
8085         {
8086             swap_elements (lo, lo + i - 1);
8087             downheap(1, i - 1,  lo);
8088         }
8089     }
8090
8091     static void downheap (size_t i, size_t n, uint8_t** lo)
8092     {
8093         uint8_t* d = *(lo + i - 1);
8094         size_t child;
8095         while (i <= n / 2)
8096         {
8097             child = 2*i;
8098             if (child < n && *(lo + child - 1)<(*(lo + child)))
8099             {
8100                 child++;
8101             }
8102             if (!(d<*(lo + child - 1))) 
8103             {
8104                 break;
8105             }
8106             *(lo + i - 1) = *(lo + child - 1);
8107             i = child;
8108         }
8109         *(lo + i - 1) = d;
8110     }
8111
8112 };
8113
8114 #endif //USE_INTROSORT    
8115
8116 #ifdef MULTIPLE_HEAPS
8117 #ifdef PARALLEL_MARK_LIST_SORT
8118 void gc_heap::sort_mark_list()
8119 {
8120     // if this heap had a mark list overflow, we don't do anything
8121     if (mark_list_index > mark_list_end)
8122     {
8123 //        printf("sort_mark_list: overflow on heap %d\n", heap_number);
8124         return;
8125     }
8126
8127     // if any other heap had a mark list overflow, we fake one too,
8128     // so we don't use an incomplete mark list by mistake
8129     for (int i = 0; i < n_heaps; i++)
8130     {
8131         if (g_heaps[i]->mark_list_index > g_heaps[i]->mark_list_end)
8132         {
8133             mark_list_index = mark_list_end + 1;
8134 //            printf("sort_mark_list: overflow on heap %d\n", i);
8135             return;
8136         }
8137     }
8138
8139 //    unsigned long start = GetCycleCount32();
8140
8141     dprintf (3, ("Sorting mark lists"));
8142     if (mark_list_index > mark_list)
8143         _sort (mark_list, mark_list_index - 1, 0);
8144
8145 //    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);
8146 //    start = GetCycleCount32();
8147
8148     // first set the pieces for all heaps to empty
8149     int heap_num;
8150     for (heap_num = 0; heap_num < n_heaps; heap_num++)
8151     {
8152         mark_list_piece_start[heap_num] = NULL;
8153         mark_list_piece_end[heap_num] = NULL;
8154     }
8155
8156     uint8_t** x = mark_list;
8157
8158 // predicate means: x is still within the mark list, and within the bounds of this heap
8159 #define predicate(x) (((x) < mark_list_index) && (*(x) < heap->ephemeral_high))
8160
8161     heap_num = -1;
8162     while (x < mark_list_index)
8163     {
8164         gc_heap* heap;
8165         // find the heap x points into - searching cyclically from the last heap,
8166         // because in many cases the right heap is the next one or comes soon after
8167         int last_heap_num = heap_num;
8168         MAYBE_UNUSED_VAR(last_heap_num);
8169         do
8170         {
8171             heap_num++;
8172             if (heap_num >= n_heaps)
8173                 heap_num = 0;
8174             assert(heap_num != last_heap_num); // we should always find the heap - infinite loop if not!
8175             heap = g_heaps[heap_num];
8176         }
8177         while (!(*x >= heap->ephemeral_low && *x < heap->ephemeral_high));
8178
8179         // x is the start of the mark list piece for this heap
8180         mark_list_piece_start[heap_num] = x;
8181
8182         // to find the end of the mark list piece for this heap, find the first x
8183         // that has !predicate(x), i.e. that is either not in this heap, or beyond the end of the list
8184         if (predicate(x))
8185         {
8186             // let's see if we get lucky and the whole rest belongs to this piece
8187             if (predicate(mark_list_index-1))
8188             {
8189                 x = mark_list_index;
8190                 mark_list_piece_end[heap_num] = x;
8191                 break;
8192             }
8193
8194             // we play a variant of binary search to find the point sooner.
8195             // the first loop advances by increasing steps until the predicate turns false.
8196             // then we retreat the last step, and the second loop advances by decreasing steps, keeping the predicate true.
8197             unsigned inc = 1;
8198             do
8199             {
8200                 inc *= 2;
8201                 uint8_t** temp_x = x;
8202                 x += inc;
8203                 if (temp_x > x)
8204                 {
8205                     break;
8206                 }
8207             }
8208             while (predicate(x));
8209             // we know that only the last step was wrong, so we undo it
8210             x -= inc;
8211             do
8212             {
8213                 // loop invariant - predicate holds at x, but not x + inc
8214                 assert (predicate(x) && !(((x + inc) > x) && predicate(x + inc)));
8215                 inc /= 2;
8216                 if (((x + inc) > x) && predicate(x + inc))
8217                 {
8218                     x += inc;
8219                 }
8220             }
8221             while (inc > 1);
8222             // the termination condition and the loop invariant together imply this:
8223             assert(predicate(x) && !predicate(x + inc) && (inc == 1));
8224             // so the spot we're looking for is one further
8225             x += 1;
8226         }
8227         mark_list_piece_end[heap_num] = x;
8228     }
8229
8230 #undef predicate
8231
8232 //    printf("second phase of sort_mark_list for heap %d took %u cycles\n", this->heap_number, GetCycleCount32() - start);
8233 }
8234
8235 void gc_heap::append_to_mark_list(uint8_t **start, uint8_t **end)
8236 {
8237     size_t slots_needed = end - start;
8238     size_t slots_available = mark_list_end + 1 - mark_list_index;
8239     size_t slots_to_copy = min(slots_needed, slots_available);
8240     memcpy(mark_list_index, start, slots_to_copy*sizeof(*start));
8241     mark_list_index += slots_to_copy;
8242 //    printf("heap %d: appended %Id slots to mark_list\n", heap_number, slots_to_copy);
8243 }
8244
8245 void gc_heap::merge_mark_lists()
8246 {
8247     uint8_t** source[MAX_SUPPORTED_CPUS];
8248     uint8_t** source_end[MAX_SUPPORTED_CPUS];
8249     int source_heap[MAX_SUPPORTED_CPUS];
8250     int source_count = 0;
8251
8252     // in case of mark list overflow, don't bother
8253     if (mark_list_index >  mark_list_end)
8254     {
8255 //        printf("merge_mark_lists: overflow\n");
8256         return;
8257     }
8258
8259     dprintf(3, ("merge_mark_lists: heap_number = %d  starts out with %Id entries", heap_number, mark_list_index - mark_list));
8260 //    unsigned long start = GetCycleCount32();
8261     for (int i = 0; i < n_heaps; i++)
8262     {
8263         gc_heap* heap = g_heaps[i];
8264         if (heap->mark_list_piece_start[heap_number] < heap->mark_list_piece_end[heap_number])
8265         {
8266             source[source_count] = heap->mark_list_piece_start[heap_number];
8267             source_end[source_count] = heap->mark_list_piece_end[heap_number];
8268             source_heap[source_count] = i;
8269             if (source_count < MAX_SUPPORTED_CPUS)
8270                 source_count++;
8271         }
8272     }
8273 //    printf("first phase of merge_mark_lists for heap %d took %u cycles\n", heap_number, GetCycleCount32() - start);
8274
8275     dprintf(3, ("heap_number = %d  has %d sources\n", heap_number, source_count));
8276 #if defined(_DEBUG) || defined(TRACE_GC)
8277     for (int j = 0; j < source_count; j++)
8278     {
8279         dprintf(3, ("heap_number = %d  ", heap_number));
8280         dprintf(3, (" source from heap %d = %Ix .. %Ix (%Id entries)",
8281             (size_t)(source_heap[j]), (size_t)(source[j][0]), (size_t)(source_end[j][-1]), (size_t)(source_end[j] - source[j])));
8282        // the sources should all be sorted
8283         for (uint8_t **x = source[j]; x < source_end[j] - 1; x++)
8284         {
8285             if (x[0] > x[1])
8286             {
8287                 dprintf(3, ("oops, mark_list from source %d for heap %d isn't sorted\n", j, heap_number));
8288                 assert (0);
8289             }
8290         }
8291     }
8292 #endif //_DEBUG || TRACE_GC
8293
8294 //    start = GetCycleCount32();
8295
8296     mark_list = &g_mark_list_copy [heap_number*mark_list_size];
8297     mark_list_index = mark_list;
8298     mark_list_end = &mark_list [mark_list_size-1];
8299     int piece_count = 0;
8300     if (source_count == 0)
8301     {
8302         ; // nothing to do
8303     }
8304     else if (source_count == 1)
8305     {
8306         mark_list = source[0];
8307         mark_list_index = source_end[0];
8308         mark_list_end = mark_list_index;
8309         piece_count++;
8310     }
8311     else
8312     {
8313         while (source_count > 1)
8314         {
8315             // find the lowest and second lowest value in the sources we're merging from
8316             int lowest_source = 0;
8317             uint8_t *lowest = *source[0];
8318             uint8_t *second_lowest = *source[1];
8319             for (int i = 1; i < source_count; i++)
8320             {
8321                 if (lowest > *source[i])
8322                 {
8323                     second_lowest = lowest;
8324                     lowest = *source[i];
8325                     lowest_source = i;
8326                 }
8327                 else if (second_lowest > *source[i])
8328                 {
8329                     second_lowest = *source[i];
8330                 }
8331             }
8332
8333             // find the point in the lowest source where it either runs out or is not <= second_lowest anymore
8334
8335             // let's first try to get lucky and see if the whole source is <= second_lowest -- this is actually quite common
8336             uint8_t **x;
8337             if (source_end[lowest_source][-1] <= second_lowest)
8338                 x = source_end[lowest_source];
8339             else
8340             {
8341                 // use linear search to find the end -- could also use binary search as in sort_mark_list,
8342                 // but saw no improvement doing that
8343                 for (x = source[lowest_source]; x < source_end[lowest_source] && *x <= second_lowest; x++)
8344                     ;
8345             }
8346
8347             // blast this piece to the mark list
8348             append_to_mark_list(source[lowest_source], x);
8349             piece_count++;
8350
8351             source[lowest_source] = x;
8352
8353             // check whether this source is now exhausted
8354             if (x >= source_end[lowest_source])
8355             {
8356                 // if it's not the source with the highest index, copy the source with the highest index
8357                 // over it so the non-empty sources are always at the beginning
8358                 if (lowest_source < source_count-1)
8359                 {
8360                     source[lowest_source] = source[source_count-1];
8361                     source_end[lowest_source] = source_end[source_count-1];
8362                 }
8363                 source_count--;
8364             }
8365         }
8366         // we're left with just one source that we copy
8367         append_to_mark_list(source[0], source_end[0]);
8368         piece_count++;
8369     }
8370
8371 //    printf("second phase of merge_mark_lists for heap %d took %u cycles to merge %d pieces\n", heap_number, GetCycleCount32() - start, piece_count);
8372
8373 #if defined(_DEBUG) || defined(TRACE_GC)
8374     // the final mark list must be sorted
8375     for (uint8_t **x = mark_list; x < mark_list_index - 1; x++)
8376     {
8377         if (x[0] > x[1])
8378         {
8379             dprintf(3, ("oops, mark_list for heap %d isn't sorted at the end of merge_mark_lists", heap_number));
8380             assert (0);
8381         }
8382     }
8383 #endif //defined(_DEBUG) || defined(TRACE_GC)
8384 }
8385 #else //PARALLEL_MARK_LIST_SORT
8386 void gc_heap::combine_mark_lists()
8387 {
8388     dprintf (3, ("Combining mark lists"));
8389     //verify if a heap has overflowed its mark list
8390     BOOL use_mark_list = TRUE;
8391     for (int i = 0; i < n_heaps; i++)
8392     {
8393         if (g_heaps [i]->mark_list_index >  g_heaps [i]->mark_list_end)
8394         {
8395             use_mark_list = FALSE;
8396             break;
8397         }
8398     }
8399
8400     if (use_mark_list)
8401     {
8402         dprintf (3, ("Using mark list"));
8403         //compact the gaps out of the mark list
8404         int gn = 0;
8405         uint8_t** current_gap = g_heaps [gn]->mark_list_index;
8406         uint8_t** current_gap_end = g_heaps[gn]->mark_list_end + 1;
8407         uint8_t** dst_last = current_gap-1;
8408
8409         int srcn = n_heaps-1;
8410         gc_heap* srch = g_heaps [srcn];
8411         uint8_t** src = srch->mark_list_index - 1;
8412         uint8_t** src_beg = srch->mark_list;
8413
8414         while (current_gap <= src)
8415         {
8416             while ((gn < n_heaps-1) && (current_gap >= current_gap_end))
8417             {
8418                 //go to the next gap
8419                 gn++;
8420                 dprintf (3, ("Going to the next gap %d", gn));
8421                 assert (gn < n_heaps);
8422                 current_gap = g_heaps [gn]->mark_list_index;
8423                 current_gap_end = g_heaps[gn]->mark_list_end + 1;
8424                 assert ((gn == (n_heaps-1)) || (current_gap_end == g_heaps[gn+1]->mark_list));
8425             }
8426             while ((srcn > 0) && (src < src_beg))
8427             {
8428                 //go to the previous source
8429                 srcn--;
8430                 dprintf (3, ("going to the previous source %d", srcn));
8431                 assert (srcn>=0);
8432                 gc_heap* srch = g_heaps [srcn];
8433                 src = srch->mark_list_index - 1;
8434                 src_beg = srch->mark_list;
8435             }
8436             if (current_gap < src)
8437             {
8438                 dst_last = current_gap;
8439                 *current_gap++ = *src--;
8440             }
8441         }
8442         dprintf (3, ("src: %Ix dst_last: %Ix", (size_t)src, (size_t)dst_last));
8443
8444         uint8_t** end_of_list = max (src, dst_last);
8445
8446         //sort the resulting compacted list
8447         assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]);
8448         if (end_of_list > &g_mark_list[0])
8449             _sort (&g_mark_list[0], end_of_list, 0);
8450         //adjust the mark_list to the begining of the resulting mark list.
8451         for (int i = 0; i < n_heaps; i++)
8452         {
8453             g_heaps [i]->mark_list = g_mark_list;
8454             g_heaps [i]->mark_list_index = end_of_list + 1;
8455             g_heaps [i]->mark_list_end = end_of_list + 1;
8456         }
8457     }
8458     else
8459     {
8460         uint8_t** end_of_list = g_mark_list;
8461         //adjust the mark_list to the begining of the resulting mark list.
8462         //put the index beyond the end to turn off mark list processing
8463         for (int i = 0; i < n_heaps; i++)
8464         {
8465             g_heaps [i]->mark_list = g_mark_list;
8466             g_heaps [i]->mark_list_index = end_of_list + 1;
8467             g_heaps [i]->mark_list_end = end_of_list;
8468         }
8469     }
8470 }
8471 #endif // PARALLEL_MARK_LIST_SORT
8472 #endif //MULTIPLE_HEAPS
8473 #endif //MARK_LIST
8474
8475 class seg_free_spaces
8476 {
8477     struct seg_free_space
8478     {
8479         BOOL is_plug;
8480         void* start;
8481     };
8482
8483     struct free_space_bucket
8484     {
8485         seg_free_space* free_space;
8486         ptrdiff_t count_add; // Assigned when we first contruct the array.
8487         ptrdiff_t count_fit; // How many items left when we are fitting plugs.
8488     };
8489
8490     void move_bucket (int old_power2, int new_power2)
8491     {
8492         // PREFAST warning 22015: old_power2 could be negative
8493         assert (old_power2 >= 0);
8494         assert (old_power2 >= new_power2);
8495
8496         if (old_power2 == new_power2)
8497         {
8498             return;
8499         }
8500
8501         seg_free_space* src_index = free_space_buckets[old_power2].free_space;
8502         for (int i = old_power2; i > new_power2; i--)
8503         {
8504             seg_free_space** dest = &(free_space_buckets[i].free_space);
8505             (*dest)++;
8506
8507             seg_free_space* dest_index = free_space_buckets[i - 1].free_space;
8508             if (i > (new_power2 + 1))
8509             {
8510                 seg_free_space temp = *src_index;
8511                 *src_index = *dest_index;
8512                 *dest_index = temp;
8513             }
8514             src_index = dest_index;
8515         }
8516
8517         free_space_buckets[old_power2].count_fit--;
8518         free_space_buckets[new_power2].count_fit++;
8519     }
8520
8521 #ifdef _DEBUG
8522
8523     void dump_free_space (seg_free_space* item)
8524     {
8525         uint8_t* addr = 0;
8526         size_t len = 0;
8527
8528         if (item->is_plug)
8529         {
8530             mark* m = (mark*)(item->start);
8531             len = pinned_len (m);
8532             addr = pinned_plug (m) - len;
8533         }
8534         else
8535         {
8536             heap_segment* seg = (heap_segment*)(item->start);
8537             addr = heap_segment_plan_allocated (seg);
8538             len = heap_segment_committed (seg) - addr;
8539         }
8540
8541         dprintf (SEG_REUSE_LOG_1, ("[%d]0x%Ix %Id", heap_num, addr, len));
8542     }
8543
8544     void dump()
8545     {
8546         seg_free_space* item = NULL;
8547         int i = 0;
8548
8549         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------\nnow the free spaces look like:", heap_num));
8550         for (i = 0; i < (free_space_bucket_count - 1); i++)
8551         {
8552             dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8553             dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8554             item = free_space_buckets[i].free_space;
8555             while (item < free_space_buckets[i + 1].free_space)
8556             {
8557                 dump_free_space (item);
8558                 item++;
8559             }
8560             dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8561         }
8562
8563         dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8564         dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8565         item = free_space_buckets[i].free_space;
8566
8567         while (item <= &seg_free_space_array[free_space_item_count - 1])
8568         {
8569             dump_free_space (item);
8570             item++;
8571         }
8572         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8573     }
8574
8575 #endif //_DEBUG
8576
8577     free_space_bucket* free_space_buckets;
8578     seg_free_space* seg_free_space_array;
8579     ptrdiff_t free_space_bucket_count;
8580     ptrdiff_t free_space_item_count;
8581     int base_power2;
8582     int heap_num;
8583 #ifdef _DEBUG
8584     BOOL has_end_of_seg;
8585 #endif //_DEBUG
8586
8587 public:
8588
8589     seg_free_spaces (int h_number)
8590     {
8591         heap_num = h_number;
8592     }
8593
8594     BOOL alloc ()
8595     {
8596         size_t total_prealloc_size = 
8597             MAX_NUM_BUCKETS * sizeof (free_space_bucket) +
8598             MAX_NUM_FREE_SPACES * sizeof (seg_free_space);
8599
8600         free_space_buckets = (free_space_bucket*) new (nothrow) uint8_t[total_prealloc_size];
8601
8602         return (!!free_space_buckets);
8603     }
8604
8605     // We take the ordered free space array we got from the 1st pass,
8606     // and feed the portion that we decided to use to this method, ie,
8607     // the largest item_count free spaces.
8608     void add_buckets (int base, size_t* ordered_free_spaces, int bucket_count, size_t item_count)
8609     {
8610         assert (free_space_buckets);
8611         assert (item_count <= (size_t)MAX_PTR);
8612
8613         free_space_bucket_count = bucket_count;
8614         free_space_item_count = item_count;
8615         base_power2 = base;
8616 #ifdef _DEBUG
8617         has_end_of_seg = FALSE;
8618 #endif //_DEBUG
8619
8620         ptrdiff_t total_item_count = 0;
8621         ptrdiff_t i = 0;
8622
8623         seg_free_space_array = (seg_free_space*)(free_space_buckets + free_space_bucket_count);
8624
8625         for (i = 0; i < (ptrdiff_t)item_count; i++)
8626         {
8627             seg_free_space_array[i].start = 0;
8628             seg_free_space_array[i].is_plug = FALSE;
8629         }
8630
8631         for (i = 0; i < bucket_count; i++)
8632         {
8633             free_space_buckets[i].count_add = ordered_free_spaces[i];
8634             free_space_buckets[i].count_fit = ordered_free_spaces[i];
8635             free_space_buckets[i].free_space = &seg_free_space_array[total_item_count];
8636             total_item_count += free_space_buckets[i].count_add;
8637         }
8638
8639         assert (total_item_count == (ptrdiff_t)item_count);
8640     }
8641
8642     // If we are adding a free space before a plug we pass the
8643     // mark stack position so we can update the length; we could
8644     // also be adding the free space after the last plug in which
8645     // case start is the segment which we'll need to update the 
8646     // heap_segment_plan_allocated.
8647     void add (void* start, BOOL plug_p, BOOL first_p)
8648     {
8649         size_t size = (plug_p ? 
8650                        pinned_len ((mark*)start) : 
8651                        (heap_segment_committed ((heap_segment*)start) - 
8652                            heap_segment_plan_allocated ((heap_segment*)start)));
8653         
8654         if (plug_p)
8655         {
8656             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space before plug: %Id", heap_num, size));
8657         }
8658         else
8659         {
8660             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space at end of seg: %Id", heap_num, size));
8661 #ifdef _DEBUG
8662             has_end_of_seg = TRUE;
8663 #endif //_DEBUG
8664         }
8665                   
8666         if (first_p)
8667         {
8668             size_t eph_gen_starts = gc_heap::eph_gen_starts_size;
8669             size -= eph_gen_starts;
8670             if (plug_p)
8671             {
8672                 mark* m = (mark*)(start);
8673                 pinned_len (m) -= eph_gen_starts;
8674             }
8675             else
8676             {
8677                 heap_segment* seg = (heap_segment*)start;
8678                 heap_segment_plan_allocated (seg) += eph_gen_starts;
8679             }
8680         }
8681
8682         int bucket_power2 = index_of_highest_set_bit (size);
8683         if (bucket_power2 < base_power2)
8684         {
8685             return;
8686         }
8687
8688         free_space_bucket* bucket = &free_space_buckets[bucket_power2 - base_power2];
8689
8690         seg_free_space* bucket_free_space = bucket->free_space;
8691         assert (plug_p || (!plug_p && bucket->count_add));
8692
8693         if (bucket->count_add == 0)
8694         {
8695             dprintf (SEG_REUSE_LOG_1, ("[%d]Already have enough of 2^%d", heap_num, bucket_power2));
8696             return;
8697         }
8698
8699         ptrdiff_t index = bucket->count_add - 1;
8700
8701         dprintf (SEG_REUSE_LOG_1, ("[%d]Building free spaces: adding %Ix; len: %Id (2^%d)", 
8702                     heap_num, 
8703                     (plug_p ? 
8704                         (pinned_plug ((mark*)start) - pinned_len ((mark*)start)) : 
8705                         heap_segment_plan_allocated ((heap_segment*)start)),
8706                     size,
8707                     bucket_power2));
8708
8709         if (plug_p)
8710         {
8711             bucket_free_space[index].is_plug = TRUE;
8712         }
8713
8714         bucket_free_space[index].start = start;
8715         bucket->count_add--;
8716     }
8717
8718 #ifdef _DEBUG
8719
8720     // Do a consistency check after all free spaces are added.
8721     void check()
8722     {
8723         ptrdiff_t i = 0;
8724         int end_of_seg_count = 0;
8725
8726         for (i = 0; i < free_space_item_count; i++)
8727         {
8728             assert (seg_free_space_array[i].start);
8729             if (!(seg_free_space_array[i].is_plug))
8730             {
8731                 end_of_seg_count++;
8732             }
8733         }
8734         
8735         if (has_end_of_seg)
8736         {
8737             assert (end_of_seg_count == 1);
8738         }
8739         else
8740         {
8741             assert (end_of_seg_count == 0);
8742         }
8743
8744         for (i = 0; i < free_space_bucket_count; i++)
8745         {
8746             assert (free_space_buckets[i].count_add == 0);
8747         }
8748     }
8749
8750 #endif //_DEBUG
8751
8752     uint8_t* fit (uint8_t* old_loc,
8753 #ifdef SHORT_PLUGS
8754                BOOL set_padding_on_saved_p,
8755                mark* pinned_plug_entry,
8756 #endif //SHORT_PLUGS
8757                size_t plug_size
8758                REQD_ALIGN_AND_OFFSET_DCL)
8759     {
8760         if (old_loc)
8761         {
8762 #ifdef SHORT_PLUGS
8763             assert (!is_plug_padded (old_loc));
8764 #endif //SHORT_PLUGS
8765             assert (!node_realigned (old_loc));
8766         }
8767
8768         size_t saved_plug_size = plug_size;
8769
8770 #ifdef FEATURE_STRUCTALIGN
8771         // BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account
8772         _ASSERTE(requiredAlignment == DATA_ALIGNMENT && false);
8773 #endif // FEATURE_STRUCTALIGN
8774         // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the 
8775         // the bucket.
8776
8777         size_t plug_size_to_fit = plug_size;
8778
8779         int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
8780
8781 #ifdef SHORT_PLUGS
8782         plug_size_to_fit += (pad_in_front ? Align(min_obj_size) : 0);
8783 #endif //SHORT_PLUGS
8784
8785         int plug_power2 = index_of_highest_set_bit (round_up_power2 (plug_size_to_fit + Align(min_obj_size)));
8786         ptrdiff_t i;
8787         uint8_t* new_address = 0;
8788
8789         if (plug_power2 < base_power2)
8790         {
8791             plug_power2 = base_power2;
8792         }
8793
8794         int chosen_power2 = plug_power2 - base_power2;
8795 retry:
8796         for (i = chosen_power2; i < free_space_bucket_count; i++)
8797         {
8798             if (free_space_buckets[i].count_fit != 0)
8799             {
8800                 break;
8801             }
8802             chosen_power2++;
8803         }
8804
8805         dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting plug len %Id (2^%d) using 2^%d free space", 
8806             heap_num, 
8807             plug_size, 
8808             plug_power2, 
8809             (chosen_power2 + base_power2)));
8810
8811         assert (i < free_space_bucket_count);
8812         
8813         seg_free_space* bucket_free_space = free_space_buckets[chosen_power2].free_space;
8814         ptrdiff_t free_space_count = free_space_buckets[chosen_power2].count_fit;
8815         size_t new_free_space_size = 0;
8816         BOOL can_fit = FALSE;
8817         size_t pad = 0;
8818
8819         for (i = 0; i < free_space_count; i++)
8820         {
8821             size_t free_space_size = 0;
8822             pad = 0;
8823 #ifdef SHORT_PLUGS
8824             BOOL short_plugs_padding_p = FALSE;
8825 #endif //SHORT_PLUGS
8826             BOOL realign_padding_p = FALSE;
8827
8828             if (bucket_free_space[i].is_plug)
8829             {
8830                 mark* m = (mark*)(bucket_free_space[i].start);
8831                 uint8_t* plug_free_space_start = pinned_plug (m) - pinned_len (m);
8832                 
8833 #ifdef SHORT_PLUGS
8834                 if ((pad_in_front & USE_PADDING_FRONT) &&
8835                     (((plug_free_space_start - pin_allocation_context_start_region (m))==0) ||
8836                     ((plug_free_space_start - pin_allocation_context_start_region (m))>=DESIRED_PLUG_LENGTH)))
8837                 {
8838                     pad = Align (min_obj_size);
8839                     short_plugs_padding_p = TRUE;
8840                 }
8841 #endif //SHORT_PLUGS
8842
8843                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, plug_free_space_start+pad)))
8844                 {
8845                     pad += switch_alignment_size (pad != 0);
8846                     realign_padding_p = TRUE;
8847                 }
8848
8849                 plug_size = saved_plug_size + pad;
8850
8851                 free_space_size = pinned_len (m);
8852                 new_address = pinned_plug (m) - pinned_len (m);
8853
8854                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8855                     free_space_size == plug_size)
8856                 {
8857                     new_free_space_size = free_space_size - plug_size;
8858                     pinned_len (m) = new_free_space_size;
8859 #ifdef SIMPLE_DPRINTF
8860                     dprintf (SEG_REUSE_LOG_0, ("[%d]FP: 0x%Ix->0x%Ix(%Ix)(%Ix), [0x%Ix (2^%d) -> [0x%Ix (2^%d)",
8861                                 heap_num, 
8862                                 old_loc,
8863                                 new_address, 
8864                                 (plug_size - pad),
8865                                 pad,
8866                                 pinned_plug (m), 
8867                                 index_of_highest_set_bit (free_space_size),
8868                                 (pinned_plug (m) - pinned_len (m)), 
8869                                 index_of_highest_set_bit (new_free_space_size)));
8870 #endif //SIMPLE_DPRINTF
8871
8872 #ifdef SHORT_PLUGS
8873                     if (short_plugs_padding_p)
8874                     {
8875                         pin_allocation_context_start_region (m) = plug_free_space_start;
8876                         set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
8877                     }
8878 #endif //SHORT_PLUGS
8879
8880                     if (realign_padding_p)
8881                     {
8882                         set_node_realigned (old_loc);
8883                     }
8884
8885                     can_fit = TRUE;
8886                 }
8887             }
8888             else
8889             {
8890                 heap_segment* seg = (heap_segment*)(bucket_free_space[i].start);
8891                 free_space_size = heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
8892
8893                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, heap_segment_plan_allocated (seg))))
8894                 {
8895                     pad = switch_alignment_size (FALSE);
8896                     realign_padding_p = TRUE;
8897                 }
8898
8899                 plug_size = saved_plug_size + pad;
8900
8901                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8902                     free_space_size == plug_size)
8903                 {
8904                     new_address = heap_segment_plan_allocated (seg);
8905                     new_free_space_size = free_space_size - plug_size;
8906                     heap_segment_plan_allocated (seg) = new_address + plug_size;
8907 #ifdef SIMPLE_DPRINTF
8908                     dprintf (SEG_REUSE_LOG_0, ("[%d]FS: 0x%Ix-> 0x%Ix(%Ix) (2^%d) -> 0x%Ix (2^%d)",
8909                                 heap_num, 
8910                                 old_loc,
8911                                 new_address, 
8912                                 (plug_size - pad),
8913                                 index_of_highest_set_bit (free_space_size),
8914                                 heap_segment_plan_allocated (seg), 
8915                                 index_of_highest_set_bit (new_free_space_size)));
8916 #endif //SIMPLE_DPRINTF
8917
8918                     if (realign_padding_p)
8919                         set_node_realigned (old_loc);
8920
8921                     can_fit = TRUE;
8922                 }
8923             }
8924
8925             if (can_fit)
8926             {
8927                 break;
8928             }
8929         }
8930
8931         if (!can_fit)
8932         {
8933             assert (chosen_power2 == 0);
8934             chosen_power2 = 1;
8935             goto retry;
8936         }
8937         else
8938         {
8939             if (pad)
8940             {
8941                 new_address += pad;
8942             }
8943             assert ((chosen_power2 && (i == 0)) ||
8944                     (!chosen_power2) && (i < free_space_count));
8945         }
8946
8947         int new_bucket_power2 = index_of_highest_set_bit (new_free_space_size);
8948
8949         if (new_bucket_power2 < base_power2)
8950         {
8951             new_bucket_power2 = base_power2;
8952         }
8953
8954         move_bucket (chosen_power2, new_bucket_power2 - base_power2);
8955
8956         //dump();
8957
8958         return new_address;
8959     }
8960
8961     void cleanup ()
8962     {
8963         if (free_space_buckets)
8964         {
8965             delete [] free_space_buckets;
8966         }
8967         if (seg_free_space_array)
8968         {
8969             delete [] seg_free_space_array;
8970         }
8971     }
8972 };
8973
8974
8975 #define marked(i) header(i)->IsMarked()
8976 #define set_marked(i) header(i)->SetMarked()
8977 #define clear_marked(i) header(i)->ClearMarked()
8978 #define pinned(i) header(i)->IsPinned()
8979 #define set_pinned(i) header(i)->SetPinned()
8980 #define clear_pinned(i) header(i)->GetHeader()->ClrGCBit();
8981
8982 inline size_t my_get_size (Object* ob)
8983 {
8984     MethodTable* mT = header(ob)->GetMethodTable();
8985     return (mT->GetBaseSize() +
8986             (mT->HasComponentSize() ?
8987              ((size_t)((CObjectHeader*)ob)->GetNumComponents() * mT->RawGetComponentSize()) : 0));
8988 }
8989
8990 //#define size(i) header(i)->GetSize()
8991 #define size(i) my_get_size (header(i))
8992
8993 #define contain_pointers(i) header(i)->ContainsPointers()
8994 #ifdef COLLECTIBLE_CLASS
8995 #define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible()
8996
8997 #define get_class_object(i) GCToEEInterface::GetLoaderAllocatorObjectForGC((Object *)i)
8998 #define is_collectible(i) method_table(i)->Collectible()
8999 #else //COLLECTIBLE_CLASS
9000 #define contain_pointers_or_collectible(i) header(i)->ContainsPointers()
9001 #endif //COLLECTIBLE_CLASS
9002
9003 #if defined (MARK_ARRAY) && defined (BACKGROUND_GC)
9004 inline
9005 void gc_heap::seg_clear_mark_array_bits_soh (heap_segment* seg)
9006 {
9007     uint8_t* range_beg = 0;
9008     uint8_t* range_end = 0;
9009     if (bgc_mark_array_range (seg, FALSE, &range_beg, &range_end))
9010     {
9011         clear_mark_array (range_beg, align_on_mark_word (range_end), FALSE
9012 #ifdef FEATURE_BASICFREEZE
9013             , TRUE
9014 #endif // FEATURE_BASICFREEZE
9015             );
9016     }
9017 }
9018
9019 void gc_heap::clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
9020 {
9021     if ((start < background_saved_highest_address) &&
9022         (end > background_saved_lowest_address))
9023     {
9024         start = max (start, background_saved_lowest_address);
9025         end = min (end, background_saved_highest_address);
9026
9027         size_t start_mark_bit = mark_bit_of (start);
9028         size_t end_mark_bit = mark_bit_of (end);
9029         unsigned int startbit = mark_bit_bit (start_mark_bit);
9030         unsigned int endbit = mark_bit_bit (end_mark_bit);
9031         size_t startwrd = mark_bit_word (start_mark_bit);
9032         size_t endwrd = mark_bit_word (end_mark_bit);
9033
9034         dprintf (3, ("Clearing all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
9035             (size_t)start, (size_t)start_mark_bit, 
9036             (size_t)end, (size_t)end_mark_bit));
9037
9038         unsigned int firstwrd = lowbits (~0, startbit);
9039         unsigned int lastwrd = highbits (~0, endbit);
9040
9041         if (startwrd == endwrd)
9042         {
9043             unsigned int wrd = firstwrd | lastwrd;
9044             mark_array[startwrd] &= wrd;
9045             return;
9046         }
9047
9048         // clear the first mark word.
9049         if (startbit)
9050         {
9051             mark_array[startwrd] &= firstwrd;
9052             startwrd++;
9053         }
9054
9055         for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
9056         {
9057             mark_array[wrdtmp] = 0;
9058         }
9059
9060         // clear the last mark word.
9061         if (endbit)
9062         {
9063             mark_array[endwrd] &= lastwrd;
9064         }
9065     }
9066 }
9067
9068 void gc_heap::bgc_clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
9069 {
9070     if ((start < background_saved_highest_address) &&
9071         (end > background_saved_lowest_address))
9072     {
9073         start = max (start, background_saved_lowest_address);
9074         end = min (end, background_saved_highest_address);
9075
9076         clear_batch_mark_array_bits (start, end);
9077     }
9078 }
9079
9080 void gc_heap::clear_mark_array_by_objects (uint8_t* from, uint8_t* end, BOOL loh_p)
9081 {
9082     dprintf (3, ("clearing mark array bits by objects for addr [%Ix,[%Ix", 
9083                   from, end));
9084     int align_const = get_alignment_constant (!loh_p);
9085
9086     uint8_t* o = from;
9087
9088     while (o < end)
9089     {
9090         uint8_t*  next_o = o + Align (size (o), align_const);
9091
9092         if (background_object_marked (o, TRUE))
9093         {
9094             dprintf (3, ("%Ix was marked by bgc, is now cleared", o));
9095         }
9096
9097         o = next_o;
9098     }
9099 }
9100 #endif //MARK_ARRAY && BACKGROUND_GC
9101
9102 inline
9103 BOOL gc_heap::is_mark_set (uint8_t* o)
9104 {
9105     return marked (o);
9106 }
9107
9108 #if defined (_MSC_VER) && defined (_TARGET_X86_)
9109 #pragma optimize("y", on)        // Small critical routines, don't put in EBP frame 
9110 #endif //_MSC_VER && _TARGET_X86_
9111
9112 // return the generation number of an object.
9113 // It is assumed that the object is valid.
9114 //Note that this will return max_generation for a LOH object
9115 int gc_heap::object_gennum (uint8_t* o)
9116 {
9117     if (in_range_for_segment (o, ephemeral_heap_segment) &&
9118         (o >= generation_allocation_start (generation_of (max_generation-1))))
9119     {
9120         // in an ephemeral generation.
9121         for ( int i = 0; i < max_generation-1; i++)
9122         {
9123             if ((o >= generation_allocation_start (generation_of (i))))
9124                 return i;
9125         }
9126         return max_generation-1;
9127     }
9128     else
9129     {
9130         return max_generation;
9131     }
9132 }
9133
9134 int gc_heap::object_gennum_plan (uint8_t* o)
9135 {
9136     if (in_range_for_segment (o, ephemeral_heap_segment))
9137     {
9138         for (int i = 0; i <= max_generation-1; i++)
9139         {
9140             uint8_t* plan_start = generation_plan_allocation_start (generation_of (i));
9141             if (plan_start && (o >= plan_start))
9142             {
9143                 return i;
9144             }
9145         }
9146     }
9147     return max_generation;
9148 }
9149
9150 #if defined(_MSC_VER) && defined(_TARGET_X86_)
9151 #pragma optimize("", on)        // Go back to command line default optimizations
9152 #endif //_MSC_VER && _TARGET_X86_
9153
9154 heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h_number)
9155 {
9156     size_t initial_commit = SEGMENT_INITIAL_COMMIT;
9157
9158     //Commit the first page
9159     if (!virtual_alloc_commit_for_heap (new_pages, initial_commit, h_number))
9160     {
9161         return 0;
9162     }
9163
9164     //overlay the heap_segment
9165     heap_segment* new_segment = (heap_segment*)new_pages;
9166
9167     uint8_t* start = new_pages + segment_info_size;
9168     heap_segment_mem (new_segment) = start;
9169     heap_segment_used (new_segment) = start;
9170     heap_segment_reserved (new_segment) = new_pages + size;
9171     heap_segment_committed (new_segment) = new_pages + initial_commit;
9172     init_heap_segment (new_segment);
9173     dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
9174     return new_segment;
9175 }
9176
9177 void gc_heap::init_heap_segment (heap_segment* seg)
9178 {
9179     seg->flags = 0;
9180     heap_segment_next (seg) = 0;
9181     heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
9182     heap_segment_allocated (seg) = heap_segment_mem (seg);
9183 #ifdef BACKGROUND_GC
9184     heap_segment_background_allocated (seg) = 0;
9185     heap_segment_saved_bg_allocated (seg) = 0;
9186 #endif //BACKGROUND_GC
9187 }
9188
9189 //Releases the segment to the OS.
9190 // this is always called on one thread only so calling seg_table->remove is fine.
9191 void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding)
9192 {
9193     if (!heap_segment_loh_p (seg))
9194     {
9195         //cleanup the brick table back to the empty value
9196         clear_brick_table (heap_segment_mem (seg), heap_segment_reserved (seg));
9197     }
9198
9199     if (consider_hoarding)
9200     {
9201         assert ((heap_segment_mem (seg) - (uint8_t*)seg) <= ptrdiff_t(2*OS_PAGE_SIZE));
9202         size_t ss = (size_t) (heap_segment_reserved (seg) - (uint8_t*)seg);
9203         //Don't keep the big ones.
9204         if (ss <= INITIAL_ALLOC)
9205         {
9206             dprintf (2, ("Hoarding segment %Ix", (size_t)seg));
9207 #ifdef BACKGROUND_GC
9208             // We don't need to clear the decommitted flag because when this segment is used
9209             // for a new segment the flags will be cleared.
9210             if (!heap_segment_decommitted_p (seg))
9211 #endif //BACKGROUND_GC
9212             {
9213                 decommit_heap_segment (seg);
9214             }
9215
9216 #ifdef SEG_MAPPING_TABLE
9217             seg_mapping_table_remove_segment (seg);
9218 #endif //SEG_MAPPING_TABLE
9219
9220             heap_segment_next (seg) = segment_standby_list;
9221             segment_standby_list = seg;
9222             seg = 0;
9223         }
9224     }
9225
9226     if (seg != 0)
9227     {
9228         dprintf (2, ("h%d: del seg: [%Ix, %Ix[", 
9229                      heap_number, (size_t)seg,
9230                      (size_t)(heap_segment_reserved (seg))));
9231
9232 #ifdef BACKGROUND_GC
9233         ::record_changed_seg ((uint8_t*)seg, heap_segment_reserved (seg), 
9234                             settings.gc_index, current_bgc_state,
9235                             seg_deleted);
9236         decommit_mark_array_by_seg (seg);
9237 #endif //BACKGROUND_GC
9238
9239 #ifdef SEG_MAPPING_TABLE
9240         seg_mapping_table_remove_segment (seg);
9241 #else //SEG_MAPPING_TABLE
9242         seg_table->remove ((uint8_t*)seg);
9243 #endif //SEG_MAPPING_TABLE
9244
9245         release_segment (seg);
9246     }
9247 }
9248
9249 //resets the pages beyond allocates size so they won't be swapped out and back in
9250
9251 void gc_heap::reset_heap_segment_pages (heap_segment* seg)
9252 {
9253     size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg));
9254     size_t size = (size_t)heap_segment_committed (seg) - page_start;
9255     if (size != 0)
9256         GCToOSInterface::VirtualReset((void*)page_start, size, false /* unlock */);
9257 }
9258
9259 void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
9260                                            size_t extra_space)
9261 {
9262     uint8_t*  page_start = align_on_page (heap_segment_allocated(seg));
9263     size_t size = heap_segment_committed (seg) - page_start;
9264     extra_space = align_on_page (extra_space);
9265     if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE))
9266     {
9267         page_start += max(extra_space, 32*OS_PAGE_SIZE);
9268         size -= max (extra_space, 32*OS_PAGE_SIZE);
9269
9270         GCToOSInterface::VirtualDecommit (page_start, size);
9271         dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)", 
9272             (size_t)page_start, 
9273             (size_t)(page_start + size),
9274             size));
9275         heap_segment_committed (seg) = page_start;
9276         if (heap_segment_used (seg) > heap_segment_committed (seg))
9277         {
9278             heap_segment_used (seg) = heap_segment_committed (seg);
9279         }
9280     }
9281 }
9282
9283 //decommit all pages except one or 2
9284 void gc_heap::decommit_heap_segment (heap_segment* seg)
9285 {
9286     uint8_t*  page_start = align_on_page (heap_segment_mem (seg));
9287
9288     dprintf (3, ("Decommitting heap segment %Ix", (size_t)seg));
9289
9290 #ifdef BACKGROUND_GC
9291     page_start += OS_PAGE_SIZE;
9292 #endif //BACKGROUND_GC
9293
9294     size_t size = heap_segment_committed (seg) - page_start;
9295     GCToOSInterface::VirtualDecommit (page_start, size);
9296
9297     //re-init the segment object
9298     heap_segment_committed (seg) = page_start;
9299     if (heap_segment_used (seg) > heap_segment_committed (seg))
9300     {
9301         heap_segment_used (seg) = heap_segment_committed (seg);
9302     }
9303 }
9304
9305 void gc_heap::clear_gen0_bricks()
9306 {
9307     if (!gen0_bricks_cleared)
9308     {
9309         gen0_bricks_cleared = TRUE;
9310         //initialize brick table for gen 0
9311         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
9312                 b < brick_of (align_on_brick
9313                             (heap_segment_allocated (ephemeral_heap_segment)));
9314                 b++)
9315         {
9316             set_brick (b, -1);
9317         }
9318     }
9319 }
9320
9321 #ifdef BACKGROUND_GC
9322 void gc_heap::rearrange_small_heap_segments()
9323 {
9324     heap_segment* seg = freeable_small_heap_segment;
9325     while (seg)
9326     {
9327         heap_segment* next_seg = heap_segment_next (seg);
9328         // TODO: we need to consider hoarding here.
9329         delete_heap_segment (seg, FALSE);
9330         seg = next_seg;
9331     }
9332     freeable_small_heap_segment = 0;
9333 }
9334 #endif //BACKGROUND_GC
9335
9336 void gc_heap::rearrange_large_heap_segments()
9337 {
9338     dprintf (2, ("deleting empty large segments"));
9339     heap_segment* seg = freeable_large_heap_segment;
9340     while (seg)
9341     {
9342         heap_segment* next_seg = heap_segment_next (seg);
9343         delete_heap_segment (seg, GCConfig::GetRetainVM());
9344         seg = next_seg;
9345     }
9346     freeable_large_heap_segment = 0;
9347 }
9348
9349 void gc_heap::rearrange_heap_segments(BOOL compacting)
9350 {
9351     heap_segment* seg =
9352         generation_start_segment (generation_of (max_generation));
9353
9354     heap_segment* prev_seg = 0;
9355     heap_segment* next_seg = 0;
9356     while (seg)
9357     {
9358         next_seg = heap_segment_next (seg);
9359
9360         //link ephemeral segment when expanding
9361         if ((next_seg == 0) && (seg != ephemeral_heap_segment))
9362         {
9363             seg->next = ephemeral_heap_segment;
9364             next_seg = heap_segment_next (seg);
9365         }
9366
9367         //re-used expanded heap segment
9368         if ((seg == ephemeral_heap_segment) && next_seg)
9369         {
9370             heap_segment_next (prev_seg) = next_seg;
9371             heap_segment_next (seg) = 0;
9372         }
9373         else
9374         {
9375             uint8_t* end_segment = (compacting ?
9376                                  heap_segment_plan_allocated (seg) : 
9377                                  heap_segment_allocated (seg));
9378             // check if the segment was reached by allocation
9379             if ((end_segment == heap_segment_mem (seg))&&
9380                 !heap_segment_read_only_p (seg))
9381             {
9382                 //if not, unthread and delete
9383                 assert (prev_seg);
9384                 assert (seg != ephemeral_heap_segment);
9385                 heap_segment_next (prev_seg) = next_seg;
9386                 delete_heap_segment (seg, GCConfig::GetRetainVM());
9387
9388                 dprintf (2, ("Deleting heap segment %Ix", (size_t)seg));
9389             }
9390             else
9391             {
9392                 if (!heap_segment_read_only_p (seg))
9393                 {
9394                     if (compacting)
9395                     {
9396                         heap_segment_allocated (seg) =
9397                             heap_segment_plan_allocated (seg);
9398                     }
9399
9400                     // reset the pages between allocated and committed.
9401                     if (seg != ephemeral_heap_segment)
9402                     {
9403                         decommit_heap_segment_pages (seg, 0);
9404                     }
9405                 }
9406                 prev_seg = seg;
9407             }
9408         }
9409
9410         seg = next_seg;
9411     }
9412 }
9413
9414
9415 #ifdef WRITE_WATCH
9416
9417 uint8_t* g_addresses [array_size+2]; // to get around the bug in GetWriteWatch
9418
9419 #ifdef TIME_WRITE_WATCH
9420 static unsigned int tot_cycles = 0;
9421 #endif //TIME_WRITE_WATCH
9422
9423 #ifdef CARD_BUNDLE
9424
9425 inline void gc_heap::verify_card_bundle_bits_set(size_t first_card_word, size_t last_card_word)
9426 {
9427 #ifdef _DEBUG
9428     for (size_t x = cardw_card_bundle (first_card_word); x < cardw_card_bundle (last_card_word); x++)
9429     {
9430         if (!card_bundle_set_p (x))
9431         {
9432             assert (!"Card bundle not set");
9433             dprintf (3, ("Card bundle %Ix not set", x));
9434         }
9435     }
9436 #endif
9437 }
9438
9439 // Verifies that any bundles that are not set represent only cards that are not set.
9440 inline void gc_heap::verify_card_bundles()
9441 {
9442 #ifdef _DEBUG
9443     size_t lowest_card = card_word (card_of (lowest_address));
9444     size_t highest_card = card_word (card_of (highest_address));
9445     size_t cardb = cardw_card_bundle (lowest_card);
9446     size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (highest_card));
9447
9448     while (cardb < end_cardb)
9449     {
9450         uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb), lowest_card)];
9451         uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1), highest_card)];
9452
9453         if (card_bundle_set_p (cardb) == 0)
9454         {
9455             // Verify that no card is set
9456             while (card_word < card_word_end)
9457             {
9458                 if (*card_word != 0)
9459                 {
9460                     dprintf  (3, ("gc: %d, Card word %Ix for address %Ix set, card_bundle %Ix clear",
9461                             dd_collection_count (dynamic_data_of (0)), 
9462                             (size_t)(card_word-&card_table[0]),
9463                             (size_t)(card_address ((size_t)(card_word-&card_table[0]) * card_word_width)), cardb));
9464                 }
9465
9466                 assert((*card_word)==0);
9467                 card_word++;
9468             }
9469         }
9470
9471         cardb++;
9472     }
9473 #endif
9474 }
9475
9476 // If card bundles are enabled, use write watch to find pages in the card table that have 
9477 // been dirtied, and set the corresponding card bundle bits.
9478 void gc_heap::update_card_table_bundle()
9479 {
9480     if (card_bundles_enabled())
9481     {
9482         // The address of the card word containing the card representing the lowest heap address
9483         uint8_t* base_address = (uint8_t*)(&card_table[card_word (card_of (lowest_address))]);
9484
9485         // The address of the card word containing the card representing the highest heap address
9486         uint8_t* high_address = (uint8_t*)(&card_table[card_word (card_of (highest_address))]);
9487         
9488         uint8_t* saved_base_address = base_address;
9489         uintptr_t bcount = array_size;
9490         size_t saved_region_size = align_on_page (high_address) - saved_base_address;
9491
9492         do
9493         {
9494             size_t region_size = align_on_page (high_address) - base_address;
9495
9496             dprintf (3,("Probing card table pages [%Ix, %Ix[", (size_t)base_address, (size_t)base_address+region_size));
9497             bool success = GCToOSInterface::GetWriteWatch(false /* resetState */,
9498                                                           base_address,
9499                                                           region_size,
9500                                                           (void**)g_addresses,
9501                                                           &bcount);
9502             assert (success && "GetWriteWatch failed!");
9503
9504             dprintf (3,("Found %d pages written", bcount));
9505             for (unsigned i = 0; i < bcount; i++)
9506             {
9507                 // Offset of the dirty page from the start of the card table (clamped to base_address)
9508                 size_t bcardw = (uint32_t*)(max(g_addresses[i],base_address)) - &card_table[0];
9509
9510                 // Offset of the end of the page from the start of the card table (clamped to high addr)
9511                 size_t ecardw = (uint32_t*)(min(g_addresses[i]+OS_PAGE_SIZE, high_address)) - &card_table[0];
9512                 assert (bcardw >= card_word (card_of (g_gc_lowest_address)));
9513
9514                 // Set the card bundle bits representing the dirty card table page
9515                 card_bundles_set (cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw)));
9516                 dprintf (3,("Set Card bundle [%Ix, %Ix[", cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))));
9517
9518                 verify_card_bundle_bits_set(bcardw, ecardw);
9519             }
9520
9521             if (bcount >= array_size)
9522             {
9523                 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
9524                 bcount = array_size;
9525             }
9526
9527         } while ((bcount >= array_size) && (base_address < high_address));
9528
9529         // Now that we've updated the card bundle bits, reset the write-tracking state. 
9530         GCToOSInterface::ResetWriteWatch (saved_base_address, saved_region_size);
9531     }
9532 }
9533 #endif //CARD_BUNDLE
9534
9535 // static
9536 void gc_heap::reset_write_watch_for_gc_heap(void* base_address, size_t region_size)
9537 {
9538 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9539     SoftwareWriteWatch::ClearDirty(base_address, region_size);
9540 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9541     GCToOSInterface::ResetWriteWatch(base_address, region_size);
9542 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9543 }
9544
9545 // static
9546 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)
9547 {
9548 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9549     SoftwareWriteWatch::GetDirty(base_address, region_size, dirty_pages, dirty_page_count_ref, reset, is_runtime_suspended);
9550 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9551     UNREFERENCED_PARAMETER(is_runtime_suspended);
9552     bool success = GCToOSInterface::GetWriteWatch(reset, base_address, region_size, dirty_pages, dirty_page_count_ref);
9553     assert(success);
9554 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9555 }
9556
9557 const size_t ww_reset_quantum = 128*1024*1024;
9558
9559 inline
9560 void gc_heap::switch_one_quantum()
9561 {
9562     enable_preemptive ();
9563     GCToOSInterface::Sleep (1);
9564     disable_preemptive (true);
9565 }
9566
9567 void gc_heap::reset_ww_by_chunk (uint8_t* start_address, size_t total_reset_size)
9568 {
9569     size_t reset_size = 0;
9570     size_t remaining_reset_size = 0;
9571     size_t next_reset_size = 0;
9572
9573     while (reset_size != total_reset_size)
9574     {
9575         remaining_reset_size = total_reset_size - reset_size;
9576         next_reset_size = ((remaining_reset_size >= ww_reset_quantum) ? ww_reset_quantum : remaining_reset_size);
9577         if (next_reset_size)
9578         {
9579             reset_write_watch_for_gc_heap(start_address, next_reset_size);
9580             reset_size += next_reset_size;
9581
9582             switch_one_quantum();
9583         }
9584     }
9585
9586     assert (reset_size == total_reset_size);
9587 }
9588
9589 // This does a Sleep(1) for every reset ww_reset_quantum bytes of reset 
9590 // we do concurrently.
9591 void gc_heap::switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size)
9592 {
9593     if (concurrent_p)
9594     {
9595         *current_total_reset_size += last_reset_size;
9596
9597         dprintf (2, ("reset %Id bytes so far", *current_total_reset_size));
9598
9599         if (*current_total_reset_size > ww_reset_quantum)
9600         {
9601             switch_one_quantum();
9602
9603             *current_total_reset_size = 0;
9604         }
9605     }
9606 }
9607
9608 void gc_heap::reset_write_watch (BOOL concurrent_p)
9609 {
9610 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9611     // Software write watch currently requires the runtime to be suspended during reset. See SoftwareWriteWatch::ClearDirty().
9612     assert(!concurrent_p);
9613 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9614
9615     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
9616
9617     PREFIX_ASSUME(seg != NULL);
9618
9619     size_t reset_size = 0;
9620     size_t region_size = 0;
9621
9622     dprintf (2, ("bgc lowest: %Ix, bgc highest: %Ix", background_saved_lowest_address, background_saved_highest_address));
9623
9624     while (seg)
9625     {
9626         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9627         base_address = max (base_address, background_saved_lowest_address);
9628
9629         uint8_t* high_address = 0;
9630         high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
9631         high_address = min (high_address, background_saved_highest_address);
9632         
9633         if (base_address < high_address)
9634         {
9635             region_size = high_address - base_address;
9636
9637 #ifdef TIME_WRITE_WATCH
9638             unsigned int time_start = GetCycleCount32();
9639 #endif //TIME_WRITE_WATCH
9640             dprintf (3, ("h%d: soh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9641             //reset_ww_by_chunk (base_address, region_size);
9642             reset_write_watch_for_gc_heap(base_address, region_size);
9643
9644 #ifdef TIME_WRITE_WATCH
9645             unsigned int time_stop = GetCycleCount32();
9646             tot_cycles += time_stop - time_start;
9647             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9648                     time_stop - time_start, tot_cycles);
9649 #endif //TIME_WRITE_WATCH
9650
9651             switch_on_reset (concurrent_p, &reset_size, region_size);
9652         }
9653
9654         seg = heap_segment_next_rw (seg);
9655
9656         concurrent_print_time_delta ("CRWW soh");
9657     }
9658
9659     //concurrent_print_time_delta ("CRW soh");
9660
9661     seg = heap_segment_rw (generation_start_segment (large_object_generation));
9662
9663     PREFIX_ASSUME(seg != NULL);
9664
9665     while (seg)
9666     {
9667         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9668         uint8_t* high_address =  heap_segment_allocated (seg);
9669
9670         base_address = max (base_address, background_saved_lowest_address);
9671         high_address = min (high_address, background_saved_highest_address);
9672
9673         if (base_address < high_address)
9674         {
9675             region_size = high_address - base_address;
9676             
9677 #ifdef TIME_WRITE_WATCH
9678             unsigned int time_start = GetCycleCount32();
9679 #endif //TIME_WRITE_WATCH
9680             dprintf (3, ("h%d: loh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9681             //reset_ww_by_chunk (base_address, region_size);
9682             reset_write_watch_for_gc_heap(base_address, region_size);
9683
9684 #ifdef TIME_WRITE_WATCH
9685             unsigned int time_stop = GetCycleCount32();
9686             tot_cycles += time_stop - time_start;
9687             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9688                     time_stop - time_start, tot_cycles);
9689 #endif //TIME_WRITE_WATCH
9690     
9691             switch_on_reset (concurrent_p, &reset_size, region_size);
9692         }
9693
9694         seg = heap_segment_next_rw (seg);
9695
9696         concurrent_print_time_delta ("CRWW loh");
9697     }
9698
9699 #ifdef DEBUG_WRITE_WATCH
9700     debug_write_watch = (uint8_t**)~0;
9701 #endif //DEBUG_WRITE_WATCH
9702 }
9703
9704 #endif //WRITE_WATCH
9705
9706 #ifdef BACKGROUND_GC
9707 void gc_heap::restart_vm()
9708 {
9709     //assert (generation_allocation_pointer (youngest_generation) == 0);
9710     dprintf (3, ("Restarting EE"));
9711     STRESS_LOG0(LF_GC, LL_INFO10000, "Concurrent GC: Retarting EE\n");
9712     ee_proceed_event.Set();
9713 }
9714
9715 inline
9716 void fire_alloc_wait_event (alloc_wait_reason awr, BOOL begin_p)
9717 {
9718     if (awr != awr_ignored)
9719     {
9720         if (begin_p)
9721         {
9722             FIRE_EVENT(BGCAllocWaitBegin, awr);
9723         }
9724         else
9725         {
9726             FIRE_EVENT(BGCAllocWaitEnd, awr);
9727         }
9728     }
9729 }
9730
9731
9732 void gc_heap::fire_alloc_wait_event_begin (alloc_wait_reason awr)
9733 {
9734     fire_alloc_wait_event (awr, TRUE);
9735 }
9736
9737
9738 void gc_heap::fire_alloc_wait_event_end (alloc_wait_reason awr)
9739 {
9740     fire_alloc_wait_event (awr, FALSE);
9741 }
9742 #endif //BACKGROUND_GC
9743 void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* start, uint8_t* pointer)
9744 {
9745     gen.allocation_start = start;
9746     gen.allocation_context.alloc_ptr = pointer;
9747     gen.allocation_context.alloc_limit = pointer;
9748     gen.allocation_context.alloc_bytes = 0;
9749     gen.allocation_context.alloc_bytes_loh = 0;
9750     gen.allocation_context_start_region = pointer;
9751     gen.start_segment = seg;
9752     gen.allocation_segment = seg;
9753     gen.plan_allocation_start = 0;
9754     gen.free_list_space = 0;
9755     gen.pinned_allocated = 0; 
9756     gen.free_list_allocated = 0; 
9757     gen.end_seg_allocated = 0;
9758     gen.condemned_allocated = 0; 
9759     gen.free_obj_space = 0;
9760     gen.allocation_size = 0;
9761     gen.pinned_allocation_sweep_size = 0;
9762     gen.pinned_allocation_compact_size = 0;
9763     gen.allocate_end_seg_p = FALSE;
9764     gen.free_list_allocator.clear();
9765
9766 #ifdef FREE_USAGE_STATS
9767     memset (gen.gen_free_spaces, 0, sizeof (gen.gen_free_spaces));
9768     memset (gen.gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces));
9769     memset (gen.gen_plugs, 0, sizeof (gen.gen_plugs));
9770 #endif //FREE_USAGE_STATS
9771 }
9772
9773 void gc_heap::adjust_ephemeral_limits ()
9774 {
9775     ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
9776     ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);
9777
9778     dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
9779                  (size_t)ephemeral_low, (size_t)ephemeral_high))
9780
9781 #ifndef MULTIPLE_HEAPS
9782     // This updates the write barrier helpers with the new info.
9783     stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high);
9784 #endif // MULTIPLE_HEAPS
9785 }
9786
9787 #if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN)
9788 FILE* CreateLogFile(const GCConfigStringHolder& temp_logfile_name, bool is_config)
9789 {
9790     FILE* logFile;
9791
9792     if (!temp_logfile_name.Get())
9793     {
9794         return nullptr;
9795     }
9796
9797     char logfile_name[MAX_LONGPATH+1];
9798     uint32_t pid = GCToOSInterface::GetCurrentProcessId();
9799     const char* suffix = is_config ? ".config.log" : ".log";
9800     _snprintf_s(logfile_name, MAX_LONGPATH+1, _TRUNCATE, "%s.%d%s", temp_logfile_name.Get(), pid, suffix);
9801     logFile = fopen(logfile_name, "wb");
9802     return logFile;
9803 }
9804 #endif //TRACE_GC || GC_CONFIG_DRIVEN
9805
9806 HRESULT gc_heap::initialize_gc (size_t segment_size,
9807                                 size_t heap_size
9808 #ifdef MULTIPLE_HEAPS
9809                                 ,unsigned number_of_heaps
9810 #endif //MULTIPLE_HEAPS
9811 )
9812 {
9813 #ifdef TRACE_GC
9814     if (GCConfig::GetLogEnabled())
9815     {
9816         gc_log = CreateLogFile(GCConfig::GetLogFile(), false);
9817
9818         if (gc_log == NULL)
9819             return E_FAIL;
9820
9821         // GCLogFileSize in MBs.
9822         gc_log_file_size = static_cast<size_t>(GCConfig::GetLogFileSize());
9823
9824         if (gc_log_file_size <= 0 || gc_log_file_size > 500)
9825         {
9826             fclose (gc_log);
9827             return E_FAIL;
9828         }
9829
9830         gc_log_lock.Initialize();
9831         gc_log_buffer = new (nothrow) uint8_t [gc_log_buffer_size];
9832         if (!gc_log_buffer)
9833         {
9834             fclose(gc_log);
9835             return E_FAIL;
9836         }
9837
9838         memset (gc_log_buffer, '*', gc_log_buffer_size);
9839
9840         max_gc_buffers = gc_log_file_size * 1024 * 1024 / gc_log_buffer_size;
9841     }
9842 #endif // TRACE_GC
9843
9844 #ifdef GC_CONFIG_DRIVEN
9845     if (GCConfig::GetConfigLogEnabled())
9846     {
9847         gc_config_log = CreateLogFile(GCConfig::GetConfigLogFile(), true);
9848
9849         if (gc_config_log == NULL)
9850             return E_FAIL;
9851
9852         gc_config_log_buffer = new (nothrow) uint8_t [gc_config_log_buffer_size];
9853         if (!gc_config_log_buffer)
9854         {
9855             fclose(gc_config_log);
9856             return E_FAIL;
9857         }
9858
9859         compact_ratio = static_cast<int>(GCConfig::GetCompactRatio());
9860
9861         //         h#  | GC  | gen | C   | EX   | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP | 
9862         cprintf (("%2s | %6s | %1s | %1s | %2s | %2s | %2s | %2s | %2s || %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s |",
9863                 "h#", // heap index
9864                 "GC", // GC index
9865                 "g", // generation
9866                 "C",  // compaction (empty means sweeping), 'M' means it was mandatory, 'W' means it was not
9867                 "EX", // heap expansion
9868                 "NF", // normal fit
9869                 "BF", // best fit (if it indicates neither NF nor BF it means it had to acquire a new seg.
9870                 "ML", // mark list
9871                 "DM", // demotion
9872                 "PreS", // short object before pinned plug
9873                 "PostS", // short object after pinned plug
9874                 "Merge", // merged pinned plugs
9875                 "Conv", // converted to pinned plug
9876                 "Pre", // plug before pinned plug but not after
9877                 "Post", // plug after pinned plug but not before
9878                 "PrPo", // plug both before and after pinned plug
9879                 "PreP", // pre short object padded
9880                 "PostP" // post short object padded
9881                 ));
9882     }
9883 #endif //GC_CONFIG_DRIVEN
9884
9885 #ifdef GC_STATS
9886     GCConfigStringHolder logFileName = GCConfig::GetMixLogFile();
9887     if (logFileName.Get() != nullptr)
9888     {
9889         GCStatistics::logFileName = _strdup(logFileName.Get());
9890         GCStatistics::logFile = fopen(GCStatistics::logFileName, "a");
9891         if (!GCStatistics::logFile)
9892         {
9893             return E_FAIL;
9894         }
9895     }
9896 #endif // GC_STATS
9897
9898     HRESULT hres = S_OK;
9899
9900 #ifdef WRITE_WATCH
9901     hardware_write_watch_api_supported();
9902 #ifdef BACKGROUND_GC
9903     if (can_use_write_watch_for_gc_heap() && GCConfig::GetConcurrentGC())
9904     {
9905         gc_can_use_concurrent = true;
9906 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9907         virtual_alloc_hardware_write_watch = true;
9908 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9909     }
9910     else
9911     {
9912         gc_can_use_concurrent = false;
9913     }
9914 #endif //BACKGROUND_GC
9915 #endif //WRITE_WATCH
9916
9917 #ifdef BACKGROUND_GC
9918     // leave the first page to contain only segment info
9919     // because otherwise we could need to revisit the first page frequently in 
9920     // background GC.
9921     segment_info_size = OS_PAGE_SIZE;
9922 #else
9923     segment_info_size = Align (sizeof (heap_segment), get_alignment_constant (FALSE));
9924 #endif //BACKGROUND_GC
9925
9926     reserved_memory = 0;
9927     unsigned block_count;
9928 #ifdef MULTIPLE_HEAPS
9929     reserved_memory_limit = (segment_size + heap_size) * number_of_heaps;
9930     block_count = number_of_heaps;
9931 #else //MULTIPLE_HEAPS
9932     reserved_memory_limit = segment_size + heap_size;
9933     block_count = 1;
9934 #endif //MULTIPLE_HEAPS
9935
9936     if (!reserve_initial_memory(segment_size,heap_size,block_count))
9937         return E_OUTOFMEMORY;
9938
9939 #ifdef CARD_BUNDLE
9940     //check if we need to turn on card_bundles.
9941 #ifdef MULTIPLE_HEAPS
9942     // use INT64 arithmetic here because of possible overflow on 32p
9943     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*number_of_heaps;
9944 #else
9945     // use INT64 arithmetic here because of possible overflow on 32p
9946     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
9947 #endif //MULTIPLE_HEAPS
9948
9949     if (can_use_write_watch_for_card_table() && reserved_memory >= th)
9950     {
9951         settings.card_bundles = TRUE;
9952     }
9953     else
9954     {
9955         settings.card_bundles = FALSE;
9956     }
9957 #endif //CARD_BUNDLE
9958
9959     settings.first_init();
9960
9961     int latency_level_from_config = static_cast<int>(GCConfig::GetLatencyLevel());
9962     if (latency_level_from_config >= latency_level_first && latency_level_from_config <= latency_level_last)
9963     {
9964         gc_heap::latency_level = static_cast<gc_latency_level>(latency_level_from_config);
9965     }
9966
9967     init_static_data();
9968
9969     g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address);
9970
9971     if (!g_gc_card_table)
9972         return E_OUTOFMEMORY;
9973
9974     gc_started = FALSE;
9975
9976 #ifdef MULTIPLE_HEAPS
9977     n_heaps = number_of_heaps;
9978
9979     g_heaps = new (nothrow) gc_heap* [number_of_heaps];
9980     if (!g_heaps)
9981         return E_OUTOFMEMORY;
9982
9983 #ifdef _PREFAST_ 
9984 #pragma warning(push)
9985 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
9986 #endif // _PREFAST_
9987     g_promoted = new (nothrow) size_t [number_of_heaps*16];
9988     g_bpromoted = new (nothrow) size_t [number_of_heaps*16];
9989 #ifdef MH_SC_MARK
9990     g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
9991 #endif //MH_SC_MARK
9992 #ifdef _PREFAST_ 
9993 #pragma warning(pop)
9994 #endif // _PREFAST_
9995     if (!g_promoted || !g_bpromoted)
9996         return E_OUTOFMEMORY;
9997
9998 #ifdef MH_SC_MARK
9999     if (!g_mark_stack_busy)
10000         return E_OUTOFMEMORY;
10001 #endif //MH_SC_MARK
10002
10003     if (!create_thread_support (number_of_heaps))
10004         return E_OUTOFMEMORY;
10005
10006     if (!heap_select::init (number_of_heaps))
10007         return E_OUTOFMEMORY;
10008
10009 #endif //MULTIPLE_HEAPS
10010
10011     if (!init_semi_shared())
10012     {
10013         hres = E_FAIL;
10014     }
10015
10016     return hres;
10017 }
10018
10019 //Initializes PER_HEAP_ISOLATED data members.
10020 int
10021 gc_heap::init_semi_shared()
10022 {
10023     int ret = 0;
10024
10025     // This is used for heap expansion - it's to fix exactly the start for gen 0
10026     // through (max_generation-1). When we expand the heap we allocate all these
10027     // gen starts at the beginning of the new ephemeral seg. 
10028     eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
10029
10030 #ifdef MARK_LIST
10031 #ifdef MULTIPLE_HEAPS
10032     mark_list_size = min (150*1024, max (8192, soh_segment_size/(2*10*32)));
10033     g_mark_list = make_mark_list (mark_list_size*n_heaps);
10034
10035     min_balance_threshold = alloc_quantum_balance_units * CLR_SIZE * 2;
10036 #ifdef PARALLEL_MARK_LIST_SORT
10037     g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
10038     if (!g_mark_list_copy)
10039     {
10040         goto cleanup;
10041     }
10042 #endif //PARALLEL_MARK_LIST_SORT
10043
10044 #else //MULTIPLE_HEAPS
10045
10046     mark_list_size = max (8192, soh_segment_size/(64*32));
10047     g_mark_list = make_mark_list (mark_list_size);
10048
10049 #endif //MULTIPLE_HEAPS
10050
10051     dprintf (3, ("mark_list_size: %d", mark_list_size));
10052
10053     if (!g_mark_list)
10054     {
10055         goto cleanup;
10056     }
10057 #endif //MARK_LIST
10058
10059 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10060     if (!seg_mapping_table_init())
10061         goto cleanup;
10062 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10063
10064 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10065     seg_table = sorted_table::make_sorted_table();
10066
10067     if (!seg_table)
10068         goto cleanup;
10069 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10070
10071     segment_standby_list = 0;
10072
10073     if (!full_gc_approach_event.CreateManualEventNoThrow(FALSE))
10074     {
10075         goto cleanup;
10076     }
10077     if (!full_gc_end_event.CreateManualEventNoThrow(FALSE))
10078     {
10079         goto cleanup;
10080     }
10081
10082     fgn_maxgen_percent = 0;
10083     fgn_loh_percent = 0;
10084     full_gc_approach_event_set = false;
10085
10086     memset (full_gc_counts, 0, sizeof (full_gc_counts));
10087
10088     last_gc_index = 0;
10089     should_expand_in_full_gc = FALSE;
10090
10091 #ifdef FEATURE_LOH_COMPACTION
10092     loh_compaction_always_p = GCConfig::GetLOHCompactionMode() != 0;
10093     loh_compaction_mode = loh_compaction_default;
10094 #endif //FEATURE_LOH_COMPACTION
10095
10096 #ifdef BACKGROUND_GC
10097     memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
10098     bgc_alloc_spin_count = static_cast<uint32_t>(GCConfig::GetBGCSpinCount());
10099     bgc_alloc_spin = static_cast<uint32_t>(GCConfig::GetBGCSpin());
10100
10101     {   
10102         int number_bgc_threads = 1;
10103 #ifdef MULTIPLE_HEAPS
10104         number_bgc_threads = n_heaps;
10105 #endif //MULTIPLE_HEAPS
10106         if (!create_bgc_threads_support (number_bgc_threads))
10107         {
10108             goto cleanup;
10109         }
10110     }
10111 #endif //BACKGROUND_GC
10112
10113     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
10114
10115 #ifdef GC_CONFIG_DRIVEN
10116     compact_or_sweep_gcs[0] = 0;
10117     compact_or_sweep_gcs[1] = 0;
10118 #endif //GC_CONFIG_DRIVEN
10119
10120 #ifdef SHORT_PLUGS
10121     short_plugs_pad_ratio = (double)DESIRED_PLUG_LENGTH / (double)(DESIRED_PLUG_LENGTH - Align (min_obj_size));
10122 #endif //SHORT_PLUGS
10123
10124     ret = 1;
10125
10126 cleanup:
10127
10128     if (!ret)
10129     {
10130         if (full_gc_approach_event.IsValid())
10131         {
10132             full_gc_approach_event.CloseEvent();
10133         }
10134         if (full_gc_end_event.IsValid())
10135         {
10136             full_gc_end_event.CloseEvent();
10137         }
10138     }
10139
10140     return ret;
10141 }
10142
10143 gc_heap* gc_heap::make_gc_heap (
10144 #ifdef MULTIPLE_HEAPS
10145                                 GCHeap* vm_hp,
10146                                 int heap_number
10147 #endif //MULTIPLE_HEAPS
10148                                 )
10149 {
10150     gc_heap* res = 0;
10151
10152 #ifdef MULTIPLE_HEAPS
10153     res = new (nothrow) gc_heap;
10154     if (!res)
10155         return 0;
10156
10157     res->vm_heap = vm_hp;
10158     res->alloc_context_count = 0;
10159
10160 #ifdef MARK_LIST
10161 #ifdef PARALLEL_MARK_LIST_SORT
10162     res->mark_list_piece_start = new (nothrow) uint8_t**[n_heaps];
10163     if (!res->mark_list_piece_start)
10164         return 0;
10165
10166 #ifdef _PREFAST_ 
10167 #pragma warning(push)
10168 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10169 #endif // _PREFAST_
10170     res->mark_list_piece_end = new (nothrow) uint8_t**[n_heaps + 32]; // +32 is padding to reduce false sharing
10171 #ifdef _PREFAST_ 
10172 #pragma warning(pop)
10173 #endif // _PREFAST_
10174
10175     if (!res->mark_list_piece_end)
10176         return 0;
10177 #endif //PARALLEL_MARK_LIST_SORT
10178 #endif //MARK_LIST
10179
10180
10181 #endif //MULTIPLE_HEAPS
10182
10183     if (res->init_gc_heap (
10184 #ifdef MULTIPLE_HEAPS
10185         heap_number
10186 #else  //MULTIPLE_HEAPS
10187         0
10188 #endif //MULTIPLE_HEAPS
10189         )==0)
10190     {
10191         return 0;
10192     }
10193
10194 #ifdef MULTIPLE_HEAPS
10195     return res;
10196 #else
10197     return (gc_heap*)1;
10198 #endif //MULTIPLE_HEAPS
10199 }
10200
10201 uint32_t
10202 gc_heap::wait_for_gc_done(int32_t timeOut)
10203 {
10204     bool cooperative_mode = enable_preemptive ();
10205
10206     uint32_t dwWaitResult = NOERROR;
10207
10208     gc_heap* wait_heap = NULL;
10209     while (gc_heap::gc_started)
10210     {       
10211 #ifdef MULTIPLE_HEAPS
10212         wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
10213         dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
10214 #endif // MULTIPLE_HEAPS
10215
10216 #ifdef _PREFAST_
10217         PREFIX_ASSUME(wait_heap != NULL);
10218 #endif // _PREFAST_
10219
10220         dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE); 
10221     }
10222     disable_preemptive (cooperative_mode);
10223
10224     return dwWaitResult;
10225 }
10226
10227 void 
10228 gc_heap::set_gc_done()
10229 {
10230     enter_gc_done_event_lock();
10231     if (!gc_done_event_set)
10232     {
10233         gc_done_event_set = true;
10234         dprintf (2, ("heap %d: setting gc_done_event", heap_number));
10235         gc_done_event.Set();
10236     }
10237     exit_gc_done_event_lock();
10238 }
10239
10240 void 
10241 gc_heap::reset_gc_done()
10242 {
10243     enter_gc_done_event_lock();
10244     if (gc_done_event_set)
10245     {
10246         gc_done_event_set = false;
10247         dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
10248         gc_done_event.Reset();
10249     }
10250     exit_gc_done_event_lock();
10251 }
10252
10253 void 
10254 gc_heap::enter_gc_done_event_lock()
10255 {
10256     uint32_t dwSwitchCount = 0;
10257 retry:
10258
10259     if (Interlocked::CompareExchange(&gc_done_event_lock, 0, -1) >= 0)
10260     {
10261         while (gc_done_event_lock >= 0)
10262         {
10263             if  (g_num_processors > 1)
10264             {
10265                 int spin_count = 32 * g_num_processors;
10266                 for (int j = 0; j < spin_count; j++)
10267                 {
10268                     if  (gc_done_event_lock < 0)
10269                         break;
10270                     YieldProcessor();           // indicate to the processor that we are spining
10271                 }
10272                 if  (gc_done_event_lock >= 0)
10273                     GCToOSInterface::YieldThread(++dwSwitchCount);
10274             }
10275             else
10276                 GCToOSInterface::YieldThread(++dwSwitchCount);
10277         }
10278         goto retry;
10279     }
10280 }
10281
10282 void 
10283 gc_heap::exit_gc_done_event_lock()
10284 {
10285     gc_done_event_lock = -1;
10286 }
10287
10288 #ifndef MULTIPLE_HEAPS
10289
10290 #ifdef RECORD_LOH_STATE
10291 int gc_heap::loh_state_index = 0;
10292 gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
10293 #endif //RECORD_LOH_STATE
10294
10295 VOLATILE(int32_t) gc_heap::gc_done_event_lock;
10296 VOLATILE(bool) gc_heap::gc_done_event_set;
10297 GCEvent gc_heap::gc_done_event;
10298 #endif //!MULTIPLE_HEAPS
10299 VOLATILE(bool) gc_heap::internal_gc_done;
10300
10301 void gc_heap::add_saved_spinlock_info (
10302             msl_enter_state enter_state, 
10303             msl_take_state take_state)
10304
10305 {
10306 #ifdef SPINLOCK_HISTORY
10307     spinlock_info* current = &last_spinlock_info[spinlock_info_index];
10308
10309     current->enter_state = enter_state;
10310     current->take_state = take_state;
10311     current->thread_id.SetToCurrentThread();
10312
10313     spinlock_info_index++;
10314
10315     assert (spinlock_info_index <= max_saved_spinlock_info);
10316
10317     if (spinlock_info_index >= max_saved_spinlock_info)
10318     {
10319         spinlock_info_index = 0;
10320     }
10321 #else
10322     MAYBE_UNUSED_VAR(enter_state);
10323     MAYBE_UNUSED_VAR(take_state);
10324 #endif //SPINLOCK_HISTORY
10325 }
10326
10327 int
10328 gc_heap::init_gc_heap (int  h_number)
10329 {
10330 #ifdef MULTIPLE_HEAPS
10331
10332     time_bgc_last = 0;
10333
10334 #ifdef SPINLOCK_HISTORY
10335     spinlock_info_index = 0;
10336     memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
10337 #endif //SPINLOCK_HISTORY
10338
10339     // initialize per heap members.
10340     ephemeral_low = (uint8_t*)1;
10341
10342     ephemeral_high = MAX_PTR;
10343
10344     ephemeral_heap_segment = 0;
10345
10346     freeable_large_heap_segment = 0;
10347
10348     condemned_generation_num = 0;
10349
10350     blocking_collection = FALSE;
10351
10352     generation_skip_ratio = 100;
10353
10354     mark_stack_tos = 0;
10355
10356     mark_stack_bos = 0;
10357
10358     mark_stack_array_length = 0;
10359
10360     mark_stack_array = 0;
10361
10362     verify_pinned_queue_p = FALSE;
10363
10364     loh_pinned_queue_tos = 0;
10365
10366     loh_pinned_queue_bos = 0;
10367
10368     loh_pinned_queue_length = 0;
10369
10370     loh_pinned_queue_decay = LOH_PIN_DECAY;
10371
10372     loh_pinned_queue = 0;
10373
10374     min_overflow_address = MAX_PTR;
10375
10376     max_overflow_address = 0;
10377
10378     gen0_bricks_cleared = FALSE;
10379
10380     gen0_must_clear_bricks = 0;
10381
10382     allocation_quantum = CLR_SIZE;
10383
10384     more_space_lock = gc_lock;
10385
10386     ro_segments_in_range = FALSE;
10387
10388     loh_alloc_since_cg = 0;
10389
10390     new_heap_segment = NULL;
10391
10392 #ifdef RECORD_LOH_STATE
10393     loh_state_index = 0;
10394 #endif //RECORD_LOH_STATE
10395 #endif //MULTIPLE_HEAPS
10396
10397 #ifdef MULTIPLE_HEAPS
10398     if (h_number > n_heaps)
10399     {
10400         assert (!"Number of heaps exceeded");
10401         return 0;
10402     }
10403
10404     heap_number = h_number;
10405 #endif //MULTIPLE_HEAPS
10406
10407     memset (&oom_info, 0, sizeof (oom_info));
10408     memset (&fgm_result, 0, sizeof (fgm_result));
10409     if (!gc_done_event.CreateManualEventNoThrow(FALSE))
10410     {
10411         return 0;
10412     }
10413     gc_done_event_lock = -1;
10414     gc_done_event_set = false;
10415
10416 #ifndef SEG_MAPPING_TABLE
10417     if (!gc_heap::seg_table->ensure_space_for_insert ())
10418     {
10419         return 0;
10420     }
10421 #endif //!SEG_MAPPING_TABLE
10422
10423     heap_segment* seg = get_initial_segment (soh_segment_size, h_number);
10424     if (!seg)
10425         return 0;
10426
10427     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg),
10428                               (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)),
10429                               gc_etw_segment_small_object_heap);
10430     
10431 #ifdef SEG_MAPPING_TABLE
10432     seg_mapping_table_add_segment (seg, __this);
10433 #else //SEG_MAPPING_TABLE
10434     seg_table->insert ((uint8_t*)seg, sdelta);
10435 #endif //SEG_MAPPING_TABLE
10436
10437 #ifdef MULTIPLE_HEAPS
10438     heap_segment_heap (seg) = this;
10439 #endif //MULTIPLE_HEAPS
10440
10441     /* todo: Need a global lock for this */
10442     uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))];
10443     own_card_table (ct);
10444     card_table = translate_card_table (ct);
10445     /* End of global lock */
10446
10447     brick_table = card_table_brick_table (ct);
10448     highest_address = card_table_highest_address (ct);
10449     lowest_address = card_table_lowest_address (ct);
10450
10451 #ifdef CARD_BUNDLE
10452     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
10453     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
10454             card_table_card_bundle_table (ct));
10455 #endif //CARD_BUNDLE
10456
10457 #ifdef MARK_ARRAY
10458     if (gc_can_use_concurrent)
10459         mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))]));
10460     else
10461         mark_array = NULL;
10462 #endif //MARK_ARRAY
10463
10464     uint8_t*  start = heap_segment_mem (seg);
10465
10466     for (int i = 0; i < 1 + max_generation; i++)
10467     {
10468         make_generation (generation_table [ (max_generation - i) ],
10469                          seg, start, 0);
10470         generation_table [(max_generation - i)].gen_num = max_generation - i;
10471         start += Align (min_obj_size);
10472     }
10473
10474     heap_segment_allocated (seg) = start;
10475     alloc_allocated = start;
10476     heap_segment_used (seg) = start - plug_skew;
10477
10478     ephemeral_heap_segment = seg;
10479
10480 #ifndef SEG_MAPPING_TABLE
10481     if (!gc_heap::seg_table->ensure_space_for_insert ())
10482     {
10483         return 0;
10484     }
10485 #endif //!SEG_MAPPING_TABLE
10486     //Create the large segment generation
10487     heap_segment* lseg = get_initial_segment(min_loh_segment_size, h_number);
10488     if (!lseg)
10489         return 0;
10490     lseg->flags |= heap_segment_flags_loh;
10491
10492     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(lseg),
10493                               (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)),
10494                               gc_etw_segment_large_object_heap);
10495
10496 #ifdef SEG_MAPPING_TABLE
10497     seg_mapping_table_add_segment (lseg, __this);
10498 #else //SEG_MAPPING_TABLE
10499     seg_table->insert ((uint8_t*)lseg, sdelta);
10500 #endif //SEG_MAPPING_TABLE
10501
10502     generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
10503     //assign the alloc_list for the large generation 
10504     generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
10505     generation_table [max_generation+1].gen_num = max_generation+1;
10506     make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
10507     heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
10508     heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
10509
10510     for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
10511     {
10512         generation*  gen = generation_of (gen_num);
10513         make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
10514     }
10515
10516 #ifdef MULTIPLE_HEAPS
10517     heap_segment_heap (lseg) = this;
10518
10519     //initialize the alloc context heap
10520     generation_alloc_context (generation_of (0))->set_alloc_heap(vm_heap);
10521
10522     //initialize the alloc context heap
10523     generation_alloc_context (generation_of (max_generation+1))->set_alloc_heap(vm_heap);
10524
10525 #endif //MULTIPLE_HEAPS
10526
10527     //Do this only once
10528 #ifdef MULTIPLE_HEAPS
10529     if (h_number == 0)
10530 #endif //MULTIPLE_HEAPS
10531     {
10532 #ifndef INTERIOR_POINTERS
10533         //set the brick_table for large objects
10534         //but default value is clearded
10535         //clear_brick_table ((uint8_t*)heap_segment_mem (lseg),
10536         //                   (uint8_t*)heap_segment_reserved (lseg));
10537
10538 #else //INTERIOR_POINTERS
10539
10540         //Because of the interior pointer business, we have to clear
10541         //the whole brick table
10542         //but the default value is cleared
10543         // clear_brick_table (lowest_address, highest_address);
10544 #endif //INTERIOR_POINTERS
10545     }
10546
10547     if (!init_dynamic_data())
10548     {
10549         return 0;
10550     }
10551
10552     etw_allocation_running_amount[0] = 0;
10553     etw_allocation_running_amount[1] = 0;
10554
10555     //needs to be done after the dynamic data has been initialized
10556 #ifndef MULTIPLE_HEAPS
10557     allocation_running_amount = dd_min_size (dynamic_data_of (0));
10558 #endif //!MULTIPLE_HEAPS
10559
10560     fgn_last_alloc = dd_min_size (dynamic_data_of (0));
10561
10562     mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
10563     if (!arr)
10564         return 0;
10565
10566     make_mark_stack(arr);
10567
10568 #ifdef BACKGROUND_GC
10569     freeable_small_heap_segment = 0;
10570     gchist_index_per_heap = 0;
10571     uint8_t** b_arr = new (nothrow) (uint8_t* [MARK_STACK_INITIAL_LENGTH]);
10572     if (!b_arr)
10573         return 0;
10574
10575     make_background_mark_stack (b_arr);
10576 #endif //BACKGROUND_GC
10577
10578     ephemeral_low = generation_allocation_start(generation_of(max_generation - 1));
10579     ephemeral_high = heap_segment_reserved(ephemeral_heap_segment);
10580     if (heap_number == 0)
10581     {
10582         stomp_write_barrier_initialize(
10583 #ifdef MULTIPLE_HEAPS
10584             reinterpret_cast<uint8_t*>(1), reinterpret_cast<uint8_t*>(~0)
10585 #else
10586             ephemeral_low, ephemeral_high
10587 #endif //!MULTIPLE_HEAPS
10588         );
10589     }
10590
10591 #ifdef MARK_ARRAY
10592     // why would we clear the mark array for this page? it should be cleared..
10593     // clear the first committed page
10594     //if(gc_can_use_concurrent)
10595     //{
10596     //    clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10597     //}
10598 #endif //MARK_ARRAY
10599
10600 #ifdef MULTIPLE_HEAPS
10601     //register the heap in the heaps array
10602
10603     if (!create_gc_thread ())
10604         return 0;
10605
10606     g_heaps [heap_number] = this;
10607
10608 #endif //MULTIPLE_HEAPS
10609
10610 #ifdef FEATURE_PREMORTEM_FINALIZATION
10611     HRESULT hr = AllocateCFinalize(&finalize_queue);
10612     if (FAILED(hr))
10613         return 0;
10614 #endif // FEATURE_PREMORTEM_FINALIZATION
10615
10616     max_free_space_items = MAX_NUM_FREE_SPACES;
10617
10618     bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10619
10620     if (!bestfit_seg)
10621     {
10622         return 0;
10623     }
10624
10625     if (!bestfit_seg->alloc())
10626     {
10627         return 0;
10628     }
10629
10630     last_gc_before_oom = FALSE;
10631
10632 #ifdef MULTIPLE_HEAPS
10633
10634 #ifdef HEAP_ANALYZE
10635
10636     heap_analyze_success = TRUE;
10637
10638     internal_root_array  = 0;
10639
10640     internal_root_array_index = 0;
10641
10642     internal_root_array_length = initial_internal_roots;
10643
10644     current_obj          = 0;
10645
10646     current_obj_size     = 0;
10647
10648 #endif //HEAP_ANALYZE
10649
10650 #endif // MULTIPLE_HEAPS
10651
10652 #ifdef BACKGROUND_GC
10653     bgc_thread_id.Clear();
10654
10655     if (!create_bgc_thread_support())
10656     {
10657         return 0;
10658     }
10659
10660     bgc_alloc_lock = new (nothrow) exclusive_sync;
10661     if (!bgc_alloc_lock)
10662     {
10663         return 0;
10664     }
10665
10666     bgc_alloc_lock->init();
10667
10668     if (h_number == 0)
10669     {
10670         if (!recursive_gc_sync::init())
10671             return 0;
10672     }
10673
10674     bgc_thread_running = 0;
10675     bgc_thread = 0;
10676     bgc_threads_timeout_cs.Initialize();
10677     expanded_in_fgc = 0;
10678     current_bgc_state = bgc_not_in_process;
10679     background_soh_alloc_count = 0;
10680     background_loh_alloc_count = 0;
10681     bgc_overflow_count = 0;
10682     end_loh_size = dd_min_size (dynamic_data_of (max_generation + 1));
10683 #endif //BACKGROUND_GC
10684
10685 #ifdef GC_CONFIG_DRIVEN
10686     memset (interesting_data_per_heap, 0, sizeof (interesting_data_per_heap));
10687     memset(compact_reasons_per_heap, 0, sizeof (compact_reasons_per_heap));
10688     memset(expand_mechanisms_per_heap, 0, sizeof (expand_mechanisms_per_heap));
10689     memset(interesting_mechanism_bits_per_heap, 0, sizeof (interesting_mechanism_bits_per_heap));
10690 #endif //GC_CONFIG_DRIVEN
10691
10692     return 1;
10693 }
10694
10695 void
10696 gc_heap::destroy_semi_shared()
10697 {
10698 //TODO: will need to move this to per heap
10699 //#ifdef BACKGROUND_GC
10700 //    if (c_mark_list)
10701 //        delete c_mark_list;
10702 //#endif //BACKGROUND_GC
10703
10704 #ifdef MARK_LIST
10705     if (g_mark_list)
10706         delete g_mark_list;
10707 #endif //MARK_LIST
10708
10709 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10710     if (seg_mapping_table)
10711         delete seg_mapping_table;
10712 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10713
10714 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10715     //destroy the segment map
10716     seg_table->delete_sorted_table();
10717 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10718 }
10719
10720 void
10721 gc_heap::self_destroy()
10722 {
10723 #ifdef BACKGROUND_GC
10724     kill_gc_thread();
10725 #endif //BACKGROUND_GC
10726
10727     if (gc_done_event.IsValid())
10728     {
10729         gc_done_event.CloseEvent();
10730     }
10731
10732     // destroy every segment.
10733     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10734
10735     PREFIX_ASSUME(seg != NULL);
10736
10737     heap_segment* next_seg;
10738     while (seg)
10739     {
10740         next_seg = heap_segment_next_rw (seg);
10741         delete_heap_segment (seg);
10742         seg = next_seg;
10743     }
10744
10745     seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10746
10747     PREFIX_ASSUME(seg != NULL);
10748
10749     while (seg)
10750     {
10751         next_seg = heap_segment_next_rw (seg);
10752         delete_heap_segment (seg);
10753         seg = next_seg;
10754     }
10755
10756     // get rid of the card table
10757     release_card_table (card_table);
10758
10759     // destroy the mark stack
10760     delete mark_stack_array;
10761
10762 #ifdef FEATURE_PREMORTEM_FINALIZATION
10763     if (finalize_queue)
10764         delete finalize_queue;
10765 #endif // FEATURE_PREMORTEM_FINALIZATION
10766 }
10767
10768 void
10769 gc_heap::destroy_gc_heap(gc_heap* heap)
10770 {
10771     heap->self_destroy();
10772     delete heap;
10773 }
10774
10775 // Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10776 // the finalizer queue has been drained.
10777 void gc_heap::shutdown_gc()
10778 {
10779     destroy_semi_shared();
10780
10781 #ifdef MULTIPLE_HEAPS
10782     //delete the heaps array
10783     delete g_heaps;
10784     destroy_thread_support();
10785     n_heaps = 0;
10786 #endif //MULTIPLE_HEAPS
10787     //destroy seg_manager
10788
10789     destroy_initial_memory();
10790
10791     GCToOSInterface::Shutdown();
10792 }
10793
10794 inline
10795 BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10796                           uint8_t* old_loc, int use_padding)
10797 {
10798     BOOL already_padded = FALSE;
10799 #ifdef SHORT_PLUGS
10800     if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
10801     {
10802         alloc_pointer = alloc_pointer + Align (min_obj_size);
10803         already_padded = TRUE;
10804     }
10805 #endif //SHORT_PLUGS
10806
10807     if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
10808         size = size + switch_alignment_size (already_padded);
10809
10810 #ifdef FEATURE_STRUCTALIGN
10811     alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
10812 #endif // FEATURE_STRUCTALIGN
10813
10814     // in allocate_in_condemned_generation we can have this when we
10815     // set the alloc_limit to plan_allocated which could be less than 
10816     // alloc_ptr
10817     if (alloc_limit < alloc_pointer)
10818     {
10819         return FALSE;
10820     }
10821
10822     if (old_loc != 0)
10823     {
10824         return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0))) 
10825 #ifdef SHORT_PLUGS
10826                 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
10827 #else //SHORT_PLUGS
10828                 ||((alloc_pointer + size) == alloc_limit)
10829 #endif //SHORT_PLUGS
10830             );
10831     }
10832     else
10833     {
10834         assert (size == Align (min_obj_size));
10835         return ((size_t)(alloc_limit - alloc_pointer) >= size);
10836     }
10837 }
10838
10839 inline
10840 BOOL gc_heap::a_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10841                             int align_const)
10842 {
10843     // We could have run into cases where this is true when alloc_allocated is the 
10844     // the same as the seg committed.
10845     if (alloc_limit < alloc_pointer)
10846     {
10847         return FALSE;
10848     }
10849
10850     return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
10851 }
10852
10853 // Grow by committing more pages
10854 BOOL gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* high_address)
10855 {
10856     assert (high_address <= heap_segment_reserved (seg));
10857
10858     //return 0 if we are at the end of the segment.
10859     if (align_on_page (high_address) > heap_segment_reserved (seg))
10860         return FALSE;
10861
10862     if (high_address <= heap_segment_committed (seg))
10863         return TRUE;
10864
10865     size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
10866     c_size = max (c_size, 16*OS_PAGE_SIZE);
10867     c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
10868
10869     if (c_size == 0)
10870         return FALSE;
10871
10872     STRESS_LOG2(LF_GC, LL_INFO10000,
10873                 "Growing heap_segment: %Ix high address: %Ix\n",
10874                 (size_t)seg, (size_t)high_address);
10875
10876     dprintf(3, ("Growing segment allocation %Ix %Ix", (size_t)heap_segment_committed(seg),c_size));
10877     
10878     if (!virtual_alloc_commit_for_heap(heap_segment_committed (seg), c_size, heap_number))
10879     {
10880         dprintf(3, ("Cannot grow heap segment"));
10881         return FALSE;
10882     }
10883 #ifdef MARK_ARRAY
10884 #ifndef BACKGROUND_GC
10885     clear_mark_array (heap_segment_committed (seg),
10886                       heap_segment_committed (seg)+c_size, TRUE);
10887 #endif //BACKGROUND_GC
10888 #endif //MARK_ARRAY
10889     heap_segment_committed (seg) += c_size;
10890     STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
10891                 (size_t)heap_segment_committed (seg));
10892
10893     assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
10894
10895     assert (high_address <= heap_segment_committed (seg));
10896
10897     return TRUE;
10898 }
10899
10900 inline
10901 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)
10902 {
10903 #ifdef SHORT_PLUGS
10904     if ((old_loc != 0) && pad_front_p)
10905     {
10906         allocated = allocated + Align (min_obj_size);
10907     }
10908 #endif //SHORT_PLUGS
10909
10910     if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
10911         size = size + switch_alignment_size (FALSE);
10912 #ifdef FEATURE_STRUCTALIGN
10913     size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
10914     return grow_heap_segment (seg, allocated + pad + size);
10915 #else // FEATURE_STRUCTALIGN
10916     return grow_heap_segment (seg, allocated + size);
10917 #endif // FEATURE_STRUCTALIGN
10918 }
10919
10920 //used only in older generation allocation (i.e during gc).
10921 void gc_heap::adjust_limit (uint8_t* start, size_t limit_size, generation* gen,
10922                             int gennum)
10923 {
10924     UNREFERENCED_PARAMETER(gennum);
10925     dprintf (3, ("gc Expanding segment allocation"));
10926     heap_segment* seg = generation_allocation_segment (gen);
10927     if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
10928     {
10929         if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
10930         {
10931             assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
10932             assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
10933             heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
10934         }
10935         else
10936         {
10937             uint8_t*  hole = generation_allocation_pointer (gen);
10938             size_t  size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
10939
10940             if (size != 0)
10941             {
10942                 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
10943                 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
10944                 if (size >= Align (min_free_list))
10945                 {
10946                     if (allocated_size < min_free_list)
10947                     {
10948                         if (size >= (Align (min_free_list) + Align (min_obj_size)))
10949                         {
10950                             //split hole into min obj + threadable free item
10951                             make_unused_array (hole, min_obj_size);
10952                             generation_free_obj_space (gen) += Align (min_obj_size);
10953                             make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
10954                             generation_free_list_space (gen) += size - Align (min_obj_size);
10955                             generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size), 
10956                                                                           size - Align (min_obj_size));
10957                             add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
10958                         }
10959                         else
10960                         {
10961                             dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
10962                             make_unused_array (hole, size);
10963                             generation_free_obj_space (gen) += size;
10964                         }
10965                     }
10966                     else 
10967                     {
10968                         dprintf (3, ("threading hole in front of free list"));
10969                         make_unused_array (hole, size);
10970                         generation_free_list_space (gen) += size;
10971                         generation_allocator(gen)->thread_item_front (hole, size);
10972                         add_gen_free (gen->gen_num, size);
10973                     }
10974                 }
10975                 else
10976                 {
10977                     make_unused_array (hole, size);
10978                     generation_free_obj_space (gen) += size;
10979                 }
10980             }
10981         }
10982         generation_allocation_pointer (gen) = start;
10983         generation_allocation_context_start_region (gen) = start;
10984     }
10985     generation_allocation_limit (gen) = (start + limit_size);
10986 }
10987
10988 void verify_mem_cleared (uint8_t* start, size_t size)
10989 {
10990     if (!Aligned (size))
10991     {
10992         FATAL_GC_ERROR();
10993     }
10994
10995     PTR_PTR curr_ptr = (PTR_PTR) start;
10996     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
10997     {
10998         if (*(curr_ptr++) != 0)
10999         {
11000             FATAL_GC_ERROR();
11001         }
11002     }
11003 }
11004
11005 #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
11006 void gc_heap::set_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11007 {
11008     size_t start_mark_bit = mark_bit_of (start);
11009     size_t end_mark_bit = mark_bit_of (end);
11010     unsigned int startbit = mark_bit_bit (start_mark_bit);
11011     unsigned int endbit = mark_bit_bit (end_mark_bit);
11012     size_t startwrd = mark_bit_word (start_mark_bit);
11013     size_t endwrd = mark_bit_word (end_mark_bit);
11014
11015     dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11016         (size_t)start, (size_t)start_mark_bit, 
11017         (size_t)end, (size_t)end_mark_bit));
11018
11019     unsigned int firstwrd = ~(lowbits (~0, startbit));
11020     unsigned int lastwrd = ~(highbits (~0, endbit));
11021
11022     if (startwrd == endwrd)
11023     {
11024         unsigned int wrd = firstwrd & lastwrd;
11025         mark_array[startwrd] |= wrd;
11026         return;
11027     }
11028
11029     // set the first mark word.
11030     if (startbit)
11031     {
11032         mark_array[startwrd] |= firstwrd;
11033         startwrd++;
11034     }
11035
11036     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11037     {
11038         mark_array[wrdtmp] = ~(unsigned int)0;
11039     }
11040
11041     // set the last mark word.
11042     if (endbit)
11043     {
11044         mark_array[endwrd] |= lastwrd;
11045     }
11046 }
11047
11048 // makes sure that the mark array bits between start and end are 0.
11049 void gc_heap::check_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11050 {
11051     size_t start_mark_bit = mark_bit_of (start);
11052     size_t end_mark_bit = mark_bit_of (end);
11053     unsigned int startbit = mark_bit_bit (start_mark_bit);
11054     unsigned int endbit = mark_bit_bit (end_mark_bit);
11055     size_t startwrd = mark_bit_word (start_mark_bit);
11056     size_t endwrd = mark_bit_word (end_mark_bit);
11057
11058     //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11059     //    (size_t)start, (size_t)start_mark_bit, 
11060     //    (size_t)end, (size_t)end_mark_bit));
11061
11062     unsigned int firstwrd = ~(lowbits (~0, startbit));
11063     unsigned int lastwrd = ~(highbits (~0, endbit));
11064
11065     if (startwrd == endwrd)
11066     {
11067         unsigned int wrd = firstwrd & lastwrd;
11068         if (mark_array[startwrd] & wrd)
11069         {
11070             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11071                             wrd, startwrd, 
11072                             mark_array [startwrd], mark_word_address (startwrd)));
11073             FATAL_GC_ERROR();
11074         }
11075         return;
11076     }
11077
11078     // set the first mark word.
11079     if (startbit)
11080     {
11081         if (mark_array[startwrd] & firstwrd)
11082         {
11083             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11084                             firstwrd, startwrd, 
11085                             mark_array [startwrd], mark_word_address (startwrd)));
11086             FATAL_GC_ERROR();
11087         }
11088
11089         startwrd++;
11090     }
11091
11092     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11093     {
11094         if (mark_array[wrdtmp])
11095         {
11096             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11097                             wrdtmp, 
11098                             mark_array [wrdtmp], mark_word_address (wrdtmp)));
11099             FATAL_GC_ERROR();
11100         }
11101     }
11102
11103     // set the last mark word.
11104     if (endbit)
11105     {
11106         if (mark_array[endwrd] & lastwrd)
11107         {
11108             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11109                             lastwrd, lastwrd, 
11110                             mark_array [lastwrd], mark_word_address (lastwrd)));
11111             FATAL_GC_ERROR();
11112         }
11113     }
11114 }
11115 #endif //VERIFY_HEAP && BACKGROUND_GC
11116
11117 allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
11118 {
11119     assert (num_b < MAX_BUCKET_COUNT);
11120     num_buckets = num_b;
11121     frst_bucket_size = fbs;
11122     buckets = b;
11123 }
11124
11125 alloc_list& allocator::alloc_list_of (unsigned int bn)
11126 {
11127     assert (bn < num_buckets);
11128     if (bn == 0)
11129         return first_bucket;
11130     else
11131         return buckets [bn-1];
11132 }
11133
11134 size_t& allocator::alloc_list_damage_count_of (unsigned int bn)
11135 {
11136     assert (bn < num_buckets);
11137     if (bn == 0)
11138         return first_bucket.alloc_list_damage_count();
11139     else
11140         return buckets [bn-1].alloc_list_damage_count();
11141 }
11142
11143 void allocator::unlink_item (unsigned int bn, uint8_t* item, uint8_t* prev_item, BOOL use_undo_p)
11144 {
11145     //unlink the free_item
11146     alloc_list* al = &alloc_list_of (bn);
11147     if (prev_item)
11148     {
11149         if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
11150         {
11151             assert (item == free_list_slot (prev_item));
11152             free_list_undo (prev_item) = item;
11153             alloc_list_damage_count_of (bn)++;
11154         }
11155         free_list_slot (prev_item) = free_list_slot(item);
11156     }
11157     else
11158     {
11159         al->alloc_list_head() = (uint8_t*)free_list_slot(item);
11160     }
11161     if (al->alloc_list_tail() == item)
11162     {
11163         al->alloc_list_tail() = prev_item;
11164     }
11165 }
11166
11167 void allocator::clear()
11168 {
11169     for (unsigned int i = 0; i < num_buckets; i++)
11170     {
11171         alloc_list_head_of (i) = 0;
11172         alloc_list_tail_of (i) = 0;
11173     }
11174 }
11175
11176 //always thread to the end.
11177 void allocator::thread_free_item (uint8_t* item, uint8_t*& head, uint8_t*& tail)
11178 {
11179     free_list_slot (item) = 0;
11180     free_list_undo (item) = UNDO_EMPTY;
11181     assert (item != head);
11182
11183     if (head == 0)
11184     {
11185        head = item;
11186     }
11187     //TODO: This shouldn't happen anymore - verify that's the case.
11188     //the following is necessary because the last free element
11189     //may have been truncated, and tail isn't updated.
11190     else if (free_list_slot (head) == 0)
11191     {
11192         free_list_slot (head) = item;
11193     }
11194     else
11195     {
11196         assert (item != tail);
11197         assert (free_list_slot(tail) == 0);
11198         free_list_slot (tail) = item;
11199     }
11200     tail = item;
11201 }
11202
11203 void allocator::thread_item (uint8_t* item, size_t size)
11204 {
11205     size_t sz = frst_bucket_size;
11206     unsigned int a_l_number = 0; 
11207
11208     for (; a_l_number < (num_buckets-1); a_l_number++)
11209     {
11210         if (size < sz)
11211         {
11212             break;
11213         }
11214         sz = sz * 2;
11215     }
11216     alloc_list* al = &alloc_list_of (a_l_number);
11217     thread_free_item (item, 
11218                       al->alloc_list_head(),
11219                       al->alloc_list_tail());
11220 }
11221
11222 void allocator::thread_item_front (uint8_t* item, size_t size)
11223 {
11224     //find right free list
11225     size_t sz = frst_bucket_size;
11226     unsigned int a_l_number = 0; 
11227     for (; a_l_number < (num_buckets-1); a_l_number++)
11228     {
11229         if (size < sz)
11230         {
11231             break;
11232         }
11233         sz = sz * 2;
11234     }
11235     alloc_list* al = &alloc_list_of (a_l_number);
11236     free_list_slot (item) = al->alloc_list_head();
11237     free_list_undo (item) = UNDO_EMPTY;
11238
11239     if (al->alloc_list_tail() == 0)
11240     {
11241         al->alloc_list_tail() = al->alloc_list_head();
11242     }
11243     al->alloc_list_head() = item;
11244     if (al->alloc_list_tail() == 0)
11245     {
11246         al->alloc_list_tail() = item;
11247     }
11248 }
11249
11250 void allocator::copy_to_alloc_list (alloc_list* toalist)
11251 {
11252     for (unsigned int i = 0; i < num_buckets; i++)
11253     {
11254         toalist [i] = alloc_list_of (i);
11255 #ifdef FL_VERIFICATION
11256         uint8_t* free_item = alloc_list_head_of (i);
11257         size_t count = 0;
11258         while (free_item)
11259         {
11260             count++;
11261             free_item = free_list_slot (free_item);
11262         }
11263
11264         toalist[i].item_count = count;
11265 #endif //FL_VERIFICATION
11266     }
11267 }
11268
11269 void allocator::copy_from_alloc_list (alloc_list* fromalist)
11270 {
11271     BOOL repair_list = !discard_if_no_fit_p ();
11272     for (unsigned int i = 0; i < num_buckets; i++)
11273     {
11274         size_t count = alloc_list_damage_count_of (i);
11275         alloc_list_of (i) = fromalist [i];
11276         assert (alloc_list_damage_count_of (i) == 0);
11277
11278         if (repair_list)
11279         {
11280             //repair the the list
11281             //new items may have been added during the plan phase 
11282             //items may have been unlinked. 
11283             uint8_t* free_item = alloc_list_head_of (i);
11284             while (free_item && count)
11285             {
11286                 assert (((CObjectHeader*)free_item)->IsFree());
11287                 if ((free_list_undo (free_item) != UNDO_EMPTY))
11288                 {
11289                     count--;
11290                     free_list_slot (free_item) = free_list_undo (free_item);
11291                     free_list_undo (free_item) = UNDO_EMPTY;
11292                 }
11293
11294                 free_item = free_list_slot (free_item);
11295             }
11296
11297 #ifdef FL_VERIFICATION
11298             free_item = alloc_list_head_of (i);
11299             size_t item_count = 0;
11300             while (free_item)
11301             {
11302                 item_count++;
11303                 free_item = free_list_slot (free_item);
11304             }
11305
11306             assert (item_count == alloc_list_of (i).item_count);
11307 #endif //FL_VERIFICATION
11308         }
11309 #ifdef DEBUG
11310         uint8_t* tail_item = alloc_list_tail_of (i);
11311         assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
11312 #endif
11313     }
11314 }
11315
11316 void allocator::commit_alloc_list_changes()
11317 {
11318     BOOL repair_list = !discard_if_no_fit_p ();
11319     if (repair_list)
11320     {
11321         for (unsigned int i = 0; i < num_buckets; i++)
11322         {
11323             //remove the undo info from list. 
11324             uint8_t* free_item = alloc_list_head_of (i);
11325             size_t count = alloc_list_damage_count_of (i);
11326             while (free_item && count)
11327             {
11328                 assert (((CObjectHeader*)free_item)->IsFree());
11329
11330                 if (free_list_undo (free_item) != UNDO_EMPTY)
11331                 {
11332                     free_list_undo (free_item) = UNDO_EMPTY;
11333                     count--;
11334                 }
11335
11336                 free_item = free_list_slot (free_item);
11337             }
11338
11339             alloc_list_damage_count_of (i) = 0; 
11340         }
11341     }
11342 }
11343
11344 void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size,
11345                                 alloc_context* acontext, heap_segment* seg,
11346                                 int align_const, int gen_number)
11347 {
11348     size_t aligned_min_obj_size = Align(min_obj_size, align_const);
11349
11350     //probably should pass seg==0 for free lists.
11351     if (seg)
11352     {
11353         assert (heap_segment_used (seg) <= heap_segment_committed (seg));
11354     }
11355
11356     dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
11357                (size_t)start + limit_size - aligned_min_obj_size));
11358
11359     if ((acontext->alloc_limit != start) &&
11360         (acontext->alloc_limit + aligned_min_obj_size)!= start)
11361     {
11362         uint8_t*  hole = acontext->alloc_ptr;
11363         if (hole != 0)
11364         {
11365             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
11366             dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
11367             // when we are finishing an allocation from a free list
11368             // we know that the free area was Align(min_obj_size) larger
11369             acontext->alloc_bytes -= size;
11370             size_t free_obj_size = size + aligned_min_obj_size;
11371             make_unused_array (hole, free_obj_size);
11372             generation_free_obj_space (generation_of (gen_number)) += free_obj_size;
11373         }
11374         acontext->alloc_ptr = start;
11375     }
11376     else  
11377     {  
11378         // If the next alloc context is right up against the current one it means we are absorbing the min  
11379         // object, so need to account for that.  
11380         acontext->alloc_bytes += (start - acontext->alloc_limit);  
11381     }  
11382
11383     acontext->alloc_limit = (start + limit_size - aligned_min_obj_size);
11384     acontext->alloc_bytes += limit_size - ((gen_number < max_generation + 1) ? aligned_min_obj_size : 0);
11385
11386 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11387     if (g_fEnableARM)
11388     {
11389         AppDomain* alloc_appdomain = GetAppDomain();
11390         alloc_appdomain->RecordAllocBytes (limit_size, heap_number);
11391     }
11392 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11393
11394     uint8_t* saved_used = 0;
11395
11396     if (seg)
11397     {
11398         saved_used = heap_segment_used (seg);
11399     }
11400
11401     if (seg == ephemeral_heap_segment)
11402     {
11403         //Sometimes the allocated size is advanced without clearing the
11404         //memory. Let's catch up here
11405         if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
11406         {
11407 #ifdef MARK_ARRAY
11408 #ifndef BACKGROUND_GC
11409             clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
11410 #endif //BACKGROUND_GC
11411 #endif //MARK_ARRAY
11412             heap_segment_used (seg) = alloc_allocated - plug_skew;
11413         }
11414     }
11415 #ifdef BACKGROUND_GC
11416     else if (seg)
11417     {
11418         uint8_t* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
11419 #ifdef FEATURE_LOH_COMPACTION
11420         old_allocated -= Align (loh_padding_obj_size, align_const);
11421 #endif //FEATURE_LOH_COMPACTION
11422
11423         assert (heap_segment_used (seg) >= old_allocated);
11424     }
11425 #endif //BACKGROUND_GC
11426     if ((seg == 0) ||
11427         (start - plug_skew + limit_size) <= heap_segment_used (seg))
11428     {
11429         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory(1)", heap_number));
11430         add_saved_spinlock_info (me_release, mt_clr_mem);
11431         leave_spin_lock (&more_space_lock);
11432         dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
11433         memclr (start - plug_skew, limit_size);
11434     }
11435     else
11436     {
11437         uint8_t* used = heap_segment_used (seg);
11438         heap_segment_used (seg) = start + limit_size - plug_skew;
11439
11440         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory", heap_number));
11441         add_saved_spinlock_info (me_release, mt_clr_mem);
11442         leave_spin_lock (&more_space_lock);
11443         if ((start - plug_skew) < used)
11444         {
11445             if (used != saved_used)
11446             {
11447                 FATAL_GC_ERROR ();
11448             }
11449
11450             dprintf (2, ("clearing memory before used at %Ix for %Id bytes", 
11451                 (start - plug_skew), (plug_skew + used - start)));
11452             memclr (start - plug_skew, used - (start - plug_skew));
11453         }
11454     }
11455
11456     //this portion can be done after we release the lock
11457     if (seg == ephemeral_heap_segment)
11458     {
11459 #ifdef FFIND_OBJECT
11460         if (gen0_must_clear_bricks > 0)
11461         {
11462             //set the brick table to speed up find_object
11463             size_t b = brick_of (acontext->alloc_ptr);
11464             set_brick (b, acontext->alloc_ptr - brick_address (b));
11465             b++;
11466             dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
11467                          b, brick_of (align_on_brick (start + limit_size))));
11468             volatile short* x = &brick_table [b];
11469             short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
11470
11471             for (;x < end_x;x++)
11472                 *x = -1;
11473         }
11474         else
11475 #endif //FFIND_OBJECT
11476         {
11477             gen0_bricks_cleared = FALSE;
11478         }
11479     }
11480
11481     // verifying the memory is completely cleared.
11482     //verify_mem_cleared (start - plug_skew, limit_size);
11483 }
11484
11485 /* in order to make the allocator faster, allocate returns a
11486  * 0 filled object. Care must be taken to set the allocation limit to the
11487  * allocation pointer after gc
11488  */
11489
11490 size_t gc_heap::limit_from_size (size_t size, size_t room, int gen_number,
11491                                  int align_const)
11492 {
11493     size_t new_limit = new_allocation_limit ((size + Align (min_obj_size, align_const)),
11494                                              min (room,max (size + Align (min_obj_size, align_const),
11495                                                             ((gen_number < max_generation+1) ?
11496                                                              allocation_quantum :
11497                                                              0))),
11498                                              gen_number);
11499     assert (new_limit >= (size + Align (min_obj_size, align_const)));
11500     dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
11501     return new_limit;
11502 }
11503
11504 void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size, 
11505                           uint8_t* allocated, uint8_t* reserved)
11506 {
11507     dprintf (1, ("total committed on the heap is %Id", get_total_committed_size()));
11508
11509     UNREFERENCED_PARAMETER(heap_num);
11510
11511     if (reason == oom_budget)
11512     {
11513         alloc_size = dd_min_size (dynamic_data_of (0)) / 2;
11514     }
11515
11516     if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
11517     {
11518         // This means during the last GC we needed to reserve and/or commit more memory
11519         // but we couldn't. We proceeded with the GC and ended up not having enough
11520         // memory at the end. This is a legitimate OOM situtation. Otherwise we 
11521         // probably made a mistake and didn't expand the heap when we should have.
11522         reason = oom_low_mem;
11523     }
11524
11525     oom_info.reason = reason;
11526     oom_info.allocated = allocated;
11527     oom_info.reserved = reserved;
11528     oom_info.alloc_size = alloc_size;
11529     oom_info.gc_index = settings.gc_index;
11530     oom_info.fgm = fgm_result.fgm;
11531     oom_info.size = fgm_result.size;
11532     oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
11533     oom_info.loh_p = fgm_result.loh_p;
11534
11535     fgm_result.fgm = fgm_no_failure;
11536
11537     // Break early - before the more_space_lock is release so no other threads
11538     // could have allocated on the same heap when OOM happened.
11539     if (GCConfig::GetBreakOnOOM())
11540     {
11541         GCToOSInterface::DebugBreak();
11542     }
11543 }
11544
11545 #ifdef BACKGROUND_GC
11546 BOOL gc_heap::background_allowed_p()
11547 {
11548     return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
11549 }
11550 #endif //BACKGROUND_GC
11551
11552 void gc_heap::check_for_full_gc (int gen_num, size_t size)
11553 {
11554     BOOL should_notify = FALSE;
11555     // if we detect full gc because of the allocation budget specified this is TRUE;
11556     // it's FALSE if it's due to other factors.
11557     BOOL alloc_factor = TRUE; 
11558     int i = 0;
11559     int n = 0;
11560     int n_initial = gen_num;
11561     BOOL local_blocking_collection = FALSE;
11562     BOOL local_elevation_requested = FALSE;
11563     int new_alloc_remain_percent = 0;
11564
11565     if (full_gc_approach_event_set)
11566     {
11567         return;
11568     }
11569     
11570     if (gen_num != (max_generation + 1))
11571     {
11572         gen_num = max_generation;
11573     }
11574
11575     dynamic_data* dd_full = dynamic_data_of (gen_num);
11576     ptrdiff_t new_alloc_remain = 0;
11577     uint32_t pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
11578
11579     for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
11580     {
11581         dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)", 
11582                      heap_number, gen_index,
11583                      dd_new_allocation (dynamic_data_of (gen_index)),
11584                      dd_desired_allocation (dynamic_data_of (gen_index))));
11585     }
11586
11587     // For small object allocations we only check every fgn_check_quantum bytes.
11588     if (n_initial == 0)
11589     {
11590         dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
11591         dynamic_data* dd_0 = dynamic_data_of (n_initial);
11592         if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
11593             (dd_new_allocation (dd_0) >= 0))
11594         {
11595             return;
11596         }
11597         else
11598         {
11599             fgn_last_alloc = dd_new_allocation (dd_0);
11600             dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
11601         }
11602
11603         // We don't consider the size that came from soh 'cause it doesn't contribute to the
11604         // gen2 budget.
11605         size = 0;
11606     }
11607
11608     for (i = n+1; i <= max_generation; i++)
11609     {
11610         if (get_new_allocation (i) <= 0)
11611         {
11612             n = min (i, max_generation);
11613         }
11614         else
11615             break;
11616     }
11617
11618     dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
11619     if (gen_num == max_generation)
11620     {
11621         // If it's small object heap we should first see if we will even be looking at gen2 budget
11622         // in the next GC or not. If not we should go directly to checking other factors.
11623         if (n < (max_generation - 1))
11624         {
11625             goto check_other_factors;
11626         }
11627     }
11628
11629     new_alloc_remain = dd_new_allocation (dd_full) - size;
11630
11631     new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
11632
11633     dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%", 
11634                  gen_num, pct, new_alloc_remain_percent));
11635
11636     if (new_alloc_remain_percent <= (int)pct)
11637     {
11638 #ifdef BACKGROUND_GC
11639         // If background GC is enabled, we still want to check whether this will
11640         // be a blocking GC or not because we only want to notify when it's a 
11641         // blocking full GC.
11642         if (background_allowed_p())
11643         {
11644             goto check_other_factors;
11645         }
11646 #endif //BACKGROUND_GC
11647
11648         should_notify = TRUE;
11649         goto done;
11650     }
11651
11652 check_other_factors:
11653
11654     dprintf (2, ("FGC: checking other factors"));
11655     n = generation_to_condemn (n, 
11656                                &local_blocking_collection, 
11657                                &local_elevation_requested, 
11658                                TRUE);
11659
11660     if (local_elevation_requested && (n == max_generation))
11661     {
11662         if (settings.should_lock_elevation)
11663         {
11664             int local_elevation_locked_count = settings.elevation_locked_count + 1;
11665             if (local_elevation_locked_count != 6)
11666             {
11667                 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1", 
11668                     local_elevation_locked_count));
11669                 n = max_generation - 1;
11670             }
11671         }
11672     }
11673
11674     dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11675
11676 #ifdef BACKGROUND_GC
11677     // When background GC is enabled it decreases the accuracy of our predictability -
11678     // by the time the GC happens, we may not be under BGC anymore. If we try to 
11679     // predict often enough it should be ok.
11680     if ((n == max_generation) &&
11681         (recursive_gc_sync::background_running_p()))
11682     {
11683         n = max_generation - 1;
11684         dprintf (2, ("FGN: bgc - 1 instead of 2"));
11685     }
11686
11687     if ((n == max_generation) && !local_blocking_collection)
11688     {
11689         if (!background_allowed_p())
11690         {
11691             local_blocking_collection = TRUE;
11692         }
11693     }
11694 #endif //BACKGROUND_GC
11695
11696     dprintf (2, ("FGN: we estimate gen%d will be collected: %s", 
11697                        n, 
11698                        (local_blocking_collection ? "blocking" : "background")));
11699
11700     if ((n == max_generation) && local_blocking_collection)
11701     {
11702         alloc_factor = FALSE;
11703         should_notify = TRUE;
11704         goto done;
11705     }
11706
11707 done:
11708
11709     if (should_notify)
11710     {
11711         dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)", 
11712                      n_initial,
11713                      (alloc_factor ? "alloc" : "other"),
11714                      dd_collection_count (dynamic_data_of (0)),
11715                      new_alloc_remain_percent, 
11716                      gen_num));
11717
11718         send_full_gc_notification (n_initial, alloc_factor);
11719     }
11720 }
11721
11722 void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11723 {
11724     if (!full_gc_approach_event_set)
11725     {
11726         assert (full_gc_approach_event.IsValid());
11727         FIRE_EVENT(GCFullNotify_V1, gen_num, due_to_alloc_p);
11728
11729         full_gc_end_event.Reset();
11730         full_gc_approach_event.Set();
11731         full_gc_approach_event_set = true;
11732     }
11733 }
11734
11735 wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms)
11736 {
11737     if (fgn_maxgen_percent == 0)
11738     {
11739         return wait_full_gc_na;
11740     }
11741
11742     uint32_t wait_result = user_thread_wait(event, FALSE, time_out_ms);
11743
11744     if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11745     {
11746         if (fgn_maxgen_percent == 0)
11747         {
11748             return wait_full_gc_cancelled;
11749         }
11750         
11751         if (wait_result == WAIT_OBJECT_0)
11752         {
11753 #ifdef BACKGROUND_GC
11754             if (fgn_last_gc_was_concurrent)
11755             {
11756                 fgn_last_gc_was_concurrent = FALSE;
11757                 return wait_full_gc_na;
11758             }
11759             else
11760 #endif //BACKGROUND_GC
11761             {
11762                 return wait_full_gc_success;
11763             }
11764         }
11765         else
11766         {
11767             return wait_full_gc_timeout;
11768         }
11769     }
11770     else
11771     {
11772         return wait_full_gc_failed;
11773     }
11774 }
11775
11776 size_t gc_heap::get_full_compact_gc_count()
11777 {
11778     return full_gc_counts[gc_type_compacting];
11779 }
11780
11781 // DTREVIEW - we should check this in dt_low_ephemeral_space_p
11782 // as well.
11783 inline
11784 BOOL gc_heap::short_on_end_of_seg (int gen_number,
11785                                    heap_segment* seg,
11786                                    int align_const)
11787 {
11788     UNREFERENCED_PARAMETER(gen_number);
11789     uint8_t* allocated = heap_segment_allocated(seg);
11790
11791     return (!a_size_fit_p (end_space_after_gc(),
11792                           allocated,
11793                           heap_segment_reserved (seg), 
11794                           align_const));
11795 }
11796
11797 #ifdef _MSC_VER
11798 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
11799 #endif // _MSC_VER
11800
11801 inline
11802 BOOL gc_heap::a_fit_free_list_p (int gen_number, 
11803                                  size_t size, 
11804                                  alloc_context* acontext,
11805                                  int align_const)
11806 {
11807     BOOL can_fit = FALSE;
11808     generation* gen = generation_of (gen_number);
11809     allocator* gen_allocator = generation_allocator (gen);
11810     size_t sz_list = gen_allocator->first_bucket_size();
11811     for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
11812     {
11813         if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
11814         {
11815             uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
11816             uint8_t* prev_free_item = 0;
11817
11818             while (free_list != 0)
11819             {
11820                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11821                 size_t free_list_size = unused_array_size (free_list);
11822                 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
11823                 {
11824                     dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
11825                                  (size_t)free_list, free_list_size));
11826
11827                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11828                     // We ask for more Align (min_obj_size)
11829                     // to make sure that we can insert a free object
11830                     // in adjust_limit will set the limit lower
11831                     size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
11832
11833                     uint8_t*  remain = (free_list + limit);
11834                     size_t remain_size = (free_list_size - limit);
11835                     if (remain_size >= Align(min_free_list, align_const))
11836                     {
11837                         make_unused_array (remain, remain_size);
11838                         gen_allocator->thread_item_front (remain, remain_size);
11839                         assert (remain_size >= Align (min_obj_size, align_const));
11840                     }
11841                     else
11842                     {
11843                         //absorb the entire free list
11844                         limit += remain_size;
11845                     }
11846                     generation_free_list_space (gen) -= limit;
11847
11848                     adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
11849
11850                     can_fit = TRUE;
11851                     goto end;
11852                 }
11853                 else if (gen_allocator->discard_if_no_fit_p())
11854                 {
11855                     assert (prev_free_item == 0);
11856                     dprintf (3, ("couldn't use this free area, discarding"));
11857                     generation_free_obj_space (gen) += free_list_size;
11858
11859                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11860                     generation_free_list_space (gen) -= free_list_size;
11861                 }
11862                 else
11863                 {
11864                     prev_free_item = free_list;
11865                 }
11866                 free_list = free_list_slot (free_list); 
11867             }
11868         }
11869         sz_list = sz_list * 2;
11870     }
11871 end:
11872     return can_fit;
11873 }
11874
11875
11876 #ifdef BACKGROUND_GC
11877 void gc_heap::bgc_loh_alloc_clr (uint8_t* alloc_start,
11878                                  size_t size, 
11879                                  alloc_context* acontext,
11880                                  int align_const, 
11881                                  int lock_index,
11882                                  BOOL check_used_p,
11883                                  heap_segment* seg)
11884 {
11885     make_unused_array (alloc_start, size);
11886
11887 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11888     if (g_fEnableARM)
11889     {
11890         AppDomain* alloc_appdomain = GetAppDomain();
11891         alloc_appdomain->RecordAllocBytes (size, heap_number);
11892     }
11893 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11894
11895     size_t size_of_array_base = sizeof(ArrayBase);
11896
11897     bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
11898
11899     // clear memory while not holding the lock. 
11900     size_t size_to_skip = size_of_array_base;
11901     size_t size_to_clear = size - size_to_skip - plug_skew;
11902     size_t saved_size_to_clear = size_to_clear;
11903     if (check_used_p)
11904     {
11905         uint8_t* end = alloc_start + size - plug_skew;
11906         uint8_t* used = heap_segment_used (seg);
11907         if (used < end)
11908         {
11909             if ((alloc_start + size_to_skip) < used)
11910             {
11911                 size_to_clear = used - (alloc_start + size_to_skip);
11912             }
11913             else
11914             {
11915                 size_to_clear = 0;
11916             }
11917             dprintf (2, ("bgc loh: setting used to %Ix", end));
11918             heap_segment_used (seg) = end;
11919         }
11920
11921         dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
11922                      used, alloc_start, end, size_to_clear));
11923     }
11924     else
11925     {
11926         dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
11927     }
11928
11929 #ifdef VERIFY_HEAP
11930     // since we filled in 0xcc for free object when we verify heap,
11931     // we need to make sure we clear those bytes.
11932     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
11933     {
11934         if (size_to_clear < saved_size_to_clear)
11935         {
11936             size_to_clear = saved_size_to_clear;
11937         }
11938     }
11939 #endif //VERIFY_HEAP
11940     
11941     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
11942     add_saved_spinlock_info (me_release, mt_clr_large_mem);
11943     leave_spin_lock (&more_space_lock);
11944     memclr (alloc_start + size_to_skip, size_to_clear);
11945
11946     bgc_alloc_lock->loh_alloc_set (alloc_start);
11947
11948     acontext->alloc_ptr = alloc_start;
11949     acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
11950
11951     // need to clear the rest of the object before we hand it out.
11952     clear_unused_array(alloc_start, size);
11953 }
11954 #endif //BACKGROUND_GC
11955
11956 BOOL gc_heap::a_fit_free_list_large_p (size_t size, 
11957                                        alloc_context* acontext,
11958                                        int align_const)
11959 {
11960 #ifdef BACKGROUND_GC
11961     wait_for_background_planning (awr_loh_alloc_during_plan);
11962 #endif //BACKGROUND_GC
11963
11964     BOOL can_fit = FALSE;
11965     int gen_number = max_generation + 1;
11966     generation* gen = generation_of (gen_number);
11967     allocator* loh_allocator = generation_allocator (gen); 
11968
11969 #ifdef FEATURE_LOH_COMPACTION
11970     size_t loh_pad = Align (loh_padding_obj_size, align_const);
11971 #endif //FEATURE_LOH_COMPACTION
11972
11973 #ifdef BACKGROUND_GC
11974     int cookie = -1;
11975 #endif //BACKGROUND_GC
11976     size_t sz_list = loh_allocator->first_bucket_size();
11977     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
11978     {
11979         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
11980         {
11981             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
11982             uint8_t* prev_free_item = 0;
11983             while (free_list != 0)
11984             {
11985                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11986
11987                 size_t free_list_size = unused_array_size(free_list);
11988
11989 #ifdef FEATURE_LOH_COMPACTION
11990                 if ((size + loh_pad) <= free_list_size)
11991 #else
11992                 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
11993                     (size == free_list_size))
11994 #endif //FEATURE_LOH_COMPACTION
11995                 {
11996 #ifdef BACKGROUND_GC
11997                     cookie = bgc_alloc_lock->loh_alloc_set (free_list);
11998 #endif //BACKGROUND_GC
11999
12000                     //unlink the free_item
12001                     loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12002
12003                     // Substract min obj size because limit_from_size adds it. Not needed for LOH
12004                     size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size, 
12005                                                     gen_number, align_const);
12006
12007 #ifdef FEATURE_LOH_COMPACTION
12008                     make_unused_array (free_list, loh_pad);
12009                     limit -= loh_pad;
12010                     free_list += loh_pad;
12011                     free_list_size -= loh_pad;
12012 #endif //FEATURE_LOH_COMPACTION
12013
12014                     uint8_t*  remain = (free_list + limit);
12015                     size_t remain_size = (free_list_size - limit);
12016                     if (remain_size != 0)
12017                     {
12018                         assert (remain_size >= Align (min_obj_size, align_const));
12019                         make_unused_array (remain, remain_size);
12020                     }
12021                     if (remain_size >= Align(min_free_list, align_const))
12022                     {
12023                         loh_thread_gap_front (remain, remain_size, gen);
12024                         assert (remain_size >= Align (min_obj_size, align_const));
12025                     }
12026                     else
12027                     {
12028                         generation_free_obj_space (gen) += remain_size;
12029                     }
12030                     generation_free_list_space (gen) -= free_list_size;
12031                     dprintf (3, ("found fit on loh at %Ix", free_list));
12032 #ifdef BACKGROUND_GC
12033                     if (cookie != -1)
12034                     {
12035                         bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
12036                     }
12037                     else
12038 #endif //BACKGROUND_GC
12039                     {
12040                         adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
12041                     }
12042
12043                     //fix the limit to compensate for adjust_limit_clr making it too short 
12044                     acontext->alloc_limit += Align (min_obj_size, align_const);
12045                     can_fit = TRUE;
12046                     goto exit;
12047                 }
12048                 prev_free_item = free_list;
12049                 free_list = free_list_slot (free_list); 
12050             }
12051         }
12052         sz_list = sz_list * 2;
12053     }
12054 exit:
12055     return can_fit;
12056 }
12057
12058 #ifdef _MSC_VER
12059 #pragma warning(default:4706)
12060 #endif // _MSC_VER
12061
12062 BOOL gc_heap::a_fit_segment_end_p (int gen_number,
12063                                    heap_segment* seg,
12064                                    size_t size, 
12065                                    alloc_context* acontext,
12066                                    int align_const,
12067                                    BOOL* commit_failed_p)
12068 {
12069     *commit_failed_p = FALSE;
12070     size_t limit = 0;
12071 #ifdef BACKGROUND_GC
12072     int cookie = -1;
12073 #endif //BACKGROUND_GC
12074
12075     uint8_t*& allocated = ((gen_number == 0) ?
12076                         alloc_allocated : 
12077                         heap_segment_allocated(seg));
12078
12079     size_t pad = Align (min_obj_size, align_const);
12080
12081 #ifdef FEATURE_LOH_COMPACTION
12082     if (gen_number == (max_generation + 1))
12083     {
12084         pad += Align (loh_padding_obj_size, align_const);
12085     }
12086 #endif //FEATURE_LOH_COMPACTION
12087
12088     uint8_t* end = heap_segment_committed (seg) - pad;
12089
12090     if (a_size_fit_p (size, allocated, end, align_const))
12091     {
12092         limit = limit_from_size (size, 
12093                                  (end - allocated), 
12094                                  gen_number, align_const);
12095         goto found_fit;
12096     }
12097
12098     end = heap_segment_reserved (seg) - pad;
12099
12100     if (a_size_fit_p (size, allocated, end, align_const))
12101     {
12102         limit = limit_from_size (size, 
12103                                  (end - allocated), 
12104                                  gen_number, align_const);
12105         if (grow_heap_segment (seg, allocated + limit))
12106         {
12107             goto found_fit;
12108         }
12109         else
12110         {
12111             dprintf (2, ("can't grow segment, doing a full gc"));
12112             *commit_failed_p = TRUE;
12113         }
12114     }
12115     goto found_no_fit;
12116
12117 found_fit:
12118
12119 #ifdef BACKGROUND_GC
12120     if (gen_number != 0)
12121     {
12122         cookie = bgc_alloc_lock->loh_alloc_set (allocated);
12123     }
12124 #endif //BACKGROUND_GC
12125
12126     uint8_t* old_alloc;
12127     old_alloc = allocated;
12128 #ifdef FEATURE_LOH_COMPACTION
12129     if (gen_number == (max_generation + 1))
12130     {
12131         size_t loh_pad = Align (loh_padding_obj_size, align_const);
12132         make_unused_array (old_alloc, loh_pad);
12133         old_alloc += loh_pad;
12134         allocated += loh_pad;
12135         limit -= loh_pad;
12136     }
12137 #endif //FEATURE_LOH_COMPACTION
12138
12139 #if defined (VERIFY_HEAP) && defined (_DEBUG)
12140         ((void**) allocated)[-1] = 0;     //clear the sync block
12141 #endif //VERIFY_HEAP && _DEBUG
12142     allocated += limit;
12143
12144     dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
12145
12146 #ifdef BACKGROUND_GC
12147     if (cookie != -1)
12148     {
12149         bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
12150     }
12151     else
12152 #endif //BACKGROUND_GC
12153     {
12154         adjust_limit_clr (old_alloc, limit, acontext, seg, align_const, gen_number);
12155     }
12156
12157     return TRUE;
12158
12159 found_no_fit:
12160
12161     return FALSE;
12162 }
12163
12164 BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
12165                                        size_t size, 
12166                                        alloc_context* acontext,
12167                                        int align_const,
12168                                        BOOL* commit_failed_p,
12169                                        oom_reason* oom_r)
12170 {
12171     *commit_failed_p = FALSE;
12172     heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
12173     BOOL can_allocate_p = FALSE;
12174
12175     while (seg)
12176     {
12177         if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)), 
12178                                  acontext, align_const, commit_failed_p))
12179         {
12180             acontext->alloc_limit += Align (min_obj_size, align_const);
12181             can_allocate_p = TRUE;
12182             break;
12183         }
12184         else
12185         {
12186             if (*commit_failed_p)
12187             {
12188                 *oom_r = oom_cant_commit;
12189                 break;
12190             }
12191             else
12192             {
12193                 seg = heap_segment_next_rw (seg);
12194             }
12195         }
12196     }
12197
12198     return can_allocate_p;
12199 }
12200
12201 #ifdef BACKGROUND_GC
12202 inline
12203 void gc_heap::wait_for_background (alloc_wait_reason awr)
12204 {
12205     dprintf (2, ("BGC is already in progress, waiting for it to finish"));
12206     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc done", heap_number));
12207     add_saved_spinlock_info (me_release, mt_wait_bgc);
12208     leave_spin_lock (&more_space_lock);
12209     background_gc_wait (awr);
12210     enter_spin_lock (&more_space_lock);
12211     add_saved_spinlock_info (me_acquire, mt_wait_bgc);
12212     dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc done", heap_number));
12213 }
12214
12215 void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr)
12216 {
12217     if (recursive_gc_sync::background_running_p())
12218     {
12219         uint32_t memory_load;
12220         get_memory_info (&memory_load);
12221         if (memory_load >= 95)
12222         {
12223             dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
12224             wait_for_background (awr);
12225         }
12226     }
12227 }
12228
12229 #endif //BACKGROUND_GC
12230
12231 // We request to trigger an ephemeral GC but we may get a full compacting GC.
12232 // return TRUE if that's the case.
12233 BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
12234 {
12235 #ifdef BACKGROUND_GC
12236     wait_for_bgc_high_memory (awr_loh_oos_bgc);
12237 #endif //BACKGROUND_GC
12238
12239     BOOL did_full_compact_gc = FALSE;
12240
12241     dprintf (2, ("triggering a gen1 GC"));
12242     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12243     vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
12244
12245 #ifdef MULTIPLE_HEAPS
12246     enter_spin_lock (&more_space_lock);
12247     add_saved_spinlock_info (me_acquire, mt_t_eph_gc);
12248     dprintf (SPINLOCK_LOG, ("[%d]Emsl after a GC", heap_number));
12249 #endif //MULTIPLE_HEAPS
12250
12251     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12252
12253     if (current_full_compact_gc_count > last_full_compact_gc_count)
12254     {
12255         dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
12256         did_full_compact_gc = TRUE;
12257     }
12258
12259     return did_full_compact_gc;
12260 }
12261
12262 BOOL gc_heap::soh_try_fit (int gen_number,
12263                            size_t size, 
12264                            alloc_context* acontext,
12265                            int align_const,
12266                            BOOL* commit_failed_p,
12267                            BOOL* short_seg_end_p)
12268 {
12269     BOOL can_allocate = TRUE;
12270     if (short_seg_end_p)
12271     {
12272         *short_seg_end_p = FALSE;
12273     }
12274
12275     can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
12276     if (!can_allocate)
12277     {
12278         if (short_seg_end_p)
12279         {
12280             *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
12281         }
12282         // If the caller doesn't care, we always try to fit at the end of seg;
12283         // otherwise we would only try if we are actually not short at end of seg.
12284         if (!short_seg_end_p || !(*short_seg_end_p))
12285         {
12286             can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size, 
12287                                                 acontext, align_const, commit_failed_p);
12288         }
12289     }
12290
12291     return can_allocate;
12292 }
12293
12294 BOOL gc_heap::allocate_small (int gen_number,
12295                               size_t size, 
12296                               alloc_context* acontext,
12297                               int align_const)
12298 {
12299 #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
12300     if (recursive_gc_sync::background_running_p())
12301     {
12302         background_soh_alloc_count++;
12303         if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
12304         {
12305             add_saved_spinlock_info (me_release, mt_alloc_small);
12306             dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl", heap_number));
12307             leave_spin_lock (&more_space_lock);
12308             bool cooperative_mode = enable_preemptive ();
12309             GCToOSInterface::Sleep (bgc_alloc_spin);
12310             disable_preemptive (cooperative_mode);
12311             enter_spin_lock (&more_space_lock);
12312             add_saved_spinlock_info (me_acquire, mt_alloc_small);
12313             dprintf (SPINLOCK_LOG, ("[%d]spin Emsl", heap_number));
12314         }
12315         else
12316         {
12317             //GCToOSInterface::YieldThread (0);
12318         }
12319     }
12320 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
12321
12322     gc_reason gr = reason_oos_soh;
12323     oom_reason oom_r = oom_no_failure;
12324
12325     // No variable values should be "carried over" from one state to the other. 
12326     // That's why there are local variable for each state
12327
12328     allocation_state soh_alloc_state = a_state_start;
12329
12330     // If we can get a new seg it means allocation will succeed.
12331     while (1)
12332     {
12333         dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
12334         switch (soh_alloc_state)
12335         {
12336             case a_state_can_allocate:
12337             case a_state_cant_allocate:
12338             {
12339                 goto exit;
12340             }
12341             case a_state_start:
12342             {
12343                 soh_alloc_state = a_state_try_fit;
12344                 break;
12345             }
12346             case a_state_try_fit:
12347             {
12348                 BOOL commit_failed_p = FALSE;
12349                 BOOL can_use_existing_p = FALSE;
12350
12351                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12352                                                   align_const, &commit_failed_p,
12353                                                   NULL);
12354                 soh_alloc_state = (can_use_existing_p ?
12355                                         a_state_can_allocate : 
12356                                         (commit_failed_p ? 
12357                                             a_state_trigger_full_compact_gc :
12358                                             a_state_trigger_ephemeral_gc));
12359                 break;
12360             }
12361             case a_state_try_fit_after_bgc:
12362             {
12363                 BOOL commit_failed_p = FALSE;
12364                 BOOL can_use_existing_p = FALSE;
12365                 BOOL short_seg_end_p = FALSE;
12366
12367                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12368                                                   align_const, &commit_failed_p,
12369                                                   &short_seg_end_p);
12370                 soh_alloc_state = (can_use_existing_p ? 
12371                                         a_state_can_allocate : 
12372                                         (short_seg_end_p ? 
12373                                             a_state_trigger_2nd_ephemeral_gc : 
12374                                             a_state_trigger_full_compact_gc));
12375                 break;
12376             }
12377             case a_state_try_fit_after_cg:
12378             {
12379                 BOOL commit_failed_p = FALSE;
12380                 BOOL can_use_existing_p = FALSE;
12381                 BOOL short_seg_end_p = FALSE;
12382
12383                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12384                                                   align_const, &commit_failed_p,
12385                                                   &short_seg_end_p);
12386                 if (short_seg_end_p)
12387                 {
12388                     soh_alloc_state = a_state_cant_allocate;
12389                     oom_r = oom_budget;
12390                 }
12391                 else
12392                 {
12393                     if (can_use_existing_p)
12394                     {
12395                         soh_alloc_state = a_state_can_allocate;
12396                     }
12397                     else
12398                     {
12399 #ifdef MULTIPLE_HEAPS
12400                         if (!commit_failed_p)
12401                         {
12402                             // some other threads already grabbed the more space lock and allocated
12403                             // so we should attempt an ephemeral GC again.
12404                             assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
12405                             soh_alloc_state = a_state_trigger_ephemeral_gc; 
12406                         }
12407                         else
12408 #endif //MULTIPLE_HEAPS
12409                         {
12410                             assert (commit_failed_p);
12411                             soh_alloc_state = a_state_cant_allocate;
12412                             oom_r = oom_cant_commit;
12413                         }
12414                     }
12415                 }
12416                 break;
12417             }
12418             case a_state_check_and_wait_for_bgc:
12419             {
12420                 BOOL bgc_in_progress_p = FALSE;
12421                 BOOL did_full_compacting_gc = FALSE;
12422
12423                 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc);
12424                 soh_alloc_state = (did_full_compacting_gc ? 
12425                                         a_state_try_fit_after_cg : 
12426                                         a_state_try_fit_after_bgc);
12427                 break;
12428             }
12429             case a_state_trigger_ephemeral_gc:
12430             {
12431                 BOOL commit_failed_p = FALSE;
12432                 BOOL can_use_existing_p = FALSE;
12433                 BOOL short_seg_end_p = FALSE;
12434                 BOOL bgc_in_progress_p = FALSE;
12435                 BOOL did_full_compacting_gc = FALSE;
12436
12437                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12438                 if (did_full_compacting_gc)
12439                 {
12440                     soh_alloc_state = a_state_try_fit_after_cg;
12441                 }
12442                 else
12443                 {
12444                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12445                                                       align_const, &commit_failed_p,
12446                                                       &short_seg_end_p);
12447 #ifdef BACKGROUND_GC
12448                     bgc_in_progress_p = recursive_gc_sync::background_running_p();
12449 #endif //BACKGROUND_GC
12450
12451                     if (short_seg_end_p)
12452                     {
12453                         soh_alloc_state = (bgc_in_progress_p ? 
12454                                                 a_state_check_and_wait_for_bgc : 
12455                                                 a_state_trigger_full_compact_gc);
12456
12457                         if (fgn_maxgen_percent)
12458                         {
12459                             dprintf (2, ("FGN: doing last GC before we throw OOM"));
12460                             send_full_gc_notification (max_generation, FALSE);
12461                         }
12462                     }
12463                     else
12464                     {
12465                         if (can_use_existing_p)
12466                         {
12467                             soh_alloc_state = a_state_can_allocate;
12468                         }
12469                         else
12470                         {
12471 #ifdef MULTIPLE_HEAPS
12472                             if (!commit_failed_p)
12473                             {
12474                                 // some other threads already grabbed the more space lock and allocated
12475                                 // so we should attempt an ephemeral GC again.
12476                                 assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
12477                                 soh_alloc_state = a_state_trigger_ephemeral_gc;
12478                             }
12479                             else
12480 #endif //MULTIPLE_HEAPS
12481                             {
12482                                 soh_alloc_state = a_state_trigger_full_compact_gc;
12483                                 if (fgn_maxgen_percent)
12484                                 {
12485                                     dprintf (2, ("FGN: failed to commit, doing full compacting GC"));
12486                                     send_full_gc_notification (max_generation, FALSE);
12487                                 }
12488                             }
12489                         }
12490                     }
12491                 }
12492                 break;
12493             }
12494             case a_state_trigger_2nd_ephemeral_gc:
12495             {
12496                 BOOL commit_failed_p = FALSE;
12497                 BOOL can_use_existing_p = FALSE;
12498                 BOOL short_seg_end_p = FALSE;
12499                 BOOL did_full_compacting_gc = FALSE;
12500
12501
12502                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12503                 
12504                 if (did_full_compacting_gc)
12505                 {
12506                     soh_alloc_state = a_state_try_fit_after_cg;
12507                 }
12508                 else
12509                 {
12510                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12511                                                       align_const, &commit_failed_p,
12512                                                       &short_seg_end_p);
12513                     if (short_seg_end_p || commit_failed_p)
12514                     {
12515                         soh_alloc_state = a_state_trigger_full_compact_gc;
12516                     }
12517                     else
12518                     {
12519                         assert (can_use_existing_p);
12520                         soh_alloc_state = a_state_can_allocate;
12521                     }
12522                 }
12523                 break;
12524             }
12525             case a_state_trigger_full_compact_gc:
12526             {
12527                 BOOL got_full_compacting_gc = FALSE;
12528
12529                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
12530                 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12531                 break;
12532             }
12533             default:
12534             {
12535                 assert (!"Invalid state!");
12536                 break;
12537             }
12538         }
12539     }
12540
12541 exit:
12542     if (soh_alloc_state == a_state_cant_allocate)
12543     {
12544         assert (oom_r != oom_no_failure);
12545         handle_oom (heap_number, 
12546                     oom_r, 
12547                     size,
12548                     heap_segment_allocated (ephemeral_heap_segment),
12549                     heap_segment_reserved (ephemeral_heap_segment));
12550
12551         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for oom", heap_number));
12552         add_saved_spinlock_info (me_release, mt_alloc_small_cant);
12553         leave_spin_lock (&more_space_lock);
12554     }
12555
12556     return (soh_alloc_state == a_state_can_allocate);
12557 }
12558
12559 #ifdef BACKGROUND_GC
12560 inline
12561 void gc_heap::wait_for_background_planning (alloc_wait_reason awr)
12562 {
12563     while (current_c_gc_state == c_gc_state_planning)
12564     {
12565         dprintf (3, ("lh state planning, cannot allocate"));
12566
12567         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc plan", heap_number));
12568         add_saved_spinlock_info (me_release, mt_wait_bgc_plan);
12569         leave_spin_lock (&more_space_lock);
12570         background_gc_wait_lh (awr);
12571         enter_spin_lock (&more_space_lock);
12572         add_saved_spinlock_info (me_acquire, mt_wait_bgc_plan);
12573         dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc plan", heap_number));
12574     }
12575     assert ((current_c_gc_state == c_gc_state_free) ||
12576             (current_c_gc_state == c_gc_state_marking));
12577 }
12578
12579 BOOL gc_heap::bgc_loh_should_allocate()
12580 {
12581     size_t min_gc_size = dd_min_size(dynamic_data_of (max_generation + 1));
12582
12583     if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
12584     {
12585         return TRUE;
12586     }
12587
12588     if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
12589     {
12590         if ((bgc_begin_loh_size / end_loh_size) > 2)
12591         {
12592             dprintf (3, ("alloc-ed too much before bgc started"));
12593         }
12594         else
12595         {
12596             dprintf (3, ("alloc-ed too much after bgc started"));
12597         }
12598         return FALSE;
12599     }
12600     else
12601     {
12602         bgc_alloc_spin_loh = (uint32_t)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
12603         return TRUE;
12604     }
12605 }
12606 #endif //BACKGROUND_GC
12607
12608 size_t gc_heap::get_large_seg_size (size_t size)
12609 {
12610     size_t default_seg_size = min_loh_segment_size;
12611 #ifdef SEG_MAPPING_TABLE
12612     size_t align_size =  default_seg_size;
12613 #else //SEG_MAPPING_TABLE
12614     size_t align_size =  default_seg_size / 2;
12615 #endif //SEG_MAPPING_TABLE
12616     int align_const = get_alignment_constant (FALSE);
12617     size_t large_seg_size = align_on_page (
12618         max (default_seg_size,
12619             ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE +
12620             align_size) / align_size * align_size)));
12621     return large_seg_size;
12622 }
12623
12624 BOOL gc_heap::loh_get_new_seg (generation* gen,
12625                                size_t size,
12626                                int align_const,
12627                                BOOL* did_full_compact_gc,
12628                                oom_reason* oom_r)
12629 {
12630     UNREFERENCED_PARAMETER(gen);
12631     UNREFERENCED_PARAMETER(align_const);
12632
12633     *did_full_compact_gc = FALSE;
12634
12635     size_t seg_size = get_large_seg_size (size);
12636
12637     heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
12638
12639     if (new_seg)
12640     {
12641         loh_alloc_since_cg += seg_size;
12642     }
12643     else
12644     {
12645         *oom_r = oom_loh;
12646     }
12647
12648     return (new_seg != 0);
12649 }
12650
12651 BOOL gc_heap::retry_full_compact_gc (size_t size)
12652 {
12653     size_t seg_size = get_large_seg_size (size);
12654
12655     if (loh_alloc_since_cg >= (2 * (uint64_t)seg_size))
12656     {
12657         return TRUE;
12658     }
12659
12660 #ifdef MULTIPLE_HEAPS
12661     uint64_t total_alloc_size = 0;
12662     for (int i = 0; i < n_heaps; i++)
12663     {
12664         total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12665     }
12666
12667     if (total_alloc_size >= (2 * (uint64_t)seg_size))
12668     {
12669         return TRUE;
12670     }
12671 #endif //MULTIPLE_HEAPS
12672
12673     return FALSE;
12674 }
12675
12676 BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12677                                       BOOL* did_full_compact_gc)
12678 {
12679     BOOL bgc_in_progress = FALSE;
12680     *did_full_compact_gc = FALSE;
12681 #ifdef BACKGROUND_GC
12682     if (recursive_gc_sync::background_running_p())
12683     {
12684         bgc_in_progress = TRUE;
12685         size_t last_full_compact_gc_count = get_full_compact_gc_count();
12686         wait_for_background (awr);
12687         size_t current_full_compact_gc_count = get_full_compact_gc_count();
12688         if (current_full_compact_gc_count > last_full_compact_gc_count)
12689         {
12690             *did_full_compact_gc = TRUE;
12691         }
12692     }
12693 #endif //BACKGROUND_GC
12694
12695     return bgc_in_progress;
12696 }
12697
12698 BOOL gc_heap::loh_try_fit (int gen_number,
12699                            size_t size, 
12700                            alloc_context* acontext,
12701                            int align_const,
12702                            BOOL* commit_failed_p,
12703                            oom_reason* oom_r)
12704 {
12705     BOOL can_allocate = TRUE;
12706
12707     if (!a_fit_free_list_large_p (size, acontext, align_const))
12708     {
12709         can_allocate = loh_a_fit_segment_end_p (gen_number, size, 
12710                                                 acontext, align_const, 
12711                                                 commit_failed_p, oom_r);
12712
12713 #ifdef BACKGROUND_GC
12714         if (can_allocate && recursive_gc_sync::background_running_p())
12715         {
12716             bgc_loh_size_increased += size;
12717         }
12718 #endif //BACKGROUND_GC
12719     }
12720 #ifdef BACKGROUND_GC
12721     else
12722     {
12723         if (recursive_gc_sync::background_running_p())
12724         {
12725             bgc_loh_allocated_in_free += size;
12726         }
12727     }
12728 #endif //BACKGROUND_GC
12729
12730     return can_allocate;
12731 }
12732
12733 BOOL gc_heap::trigger_full_compact_gc (gc_reason gr, 
12734                                        oom_reason* oom_r)
12735 {
12736     BOOL did_full_compact_gc = FALSE;
12737
12738     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12739
12740     // Set this so the next GC will be a full compacting GC.
12741     if (!last_gc_before_oom)
12742     {
12743         last_gc_before_oom = TRUE;
12744     }
12745
12746 #ifdef BACKGROUND_GC
12747     if (recursive_gc_sync::background_running_p())
12748     {
12749         wait_for_background ((gr == reason_oos_soh) ? awr_gen0_oos_bgc : awr_loh_oos_bgc);
12750         dprintf (2, ("waited for BGC - done"));
12751     }
12752 #endif //BACKGROUND_GC
12753
12754     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12755     if (current_full_compact_gc_count > last_full_compact_gc_count)
12756     {
12757         dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
12758         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12759         did_full_compact_gc = TRUE;
12760         goto exit;
12761     }
12762
12763     dprintf (3, ("h%d full GC", heap_number));
12764     vm_heap->GarbageCollectGeneration(max_generation, gr);
12765
12766 #ifdef MULTIPLE_HEAPS
12767     enter_spin_lock (&more_space_lock);
12768     dprintf (SPINLOCK_LOG, ("[%d]Emsl after full gc", heap_number));
12769     add_saved_spinlock_info (me_acquire, mt_t_full_gc);
12770 #endif //MULTIPLE_HEAPS
12771
12772     current_full_compact_gc_count = get_full_compact_gc_count();
12773
12774     if (current_full_compact_gc_count == last_full_compact_gc_count)
12775     {
12776         dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
12777         // We requested a full GC but didn't get because of the elevation logic
12778         // which means we should fail.
12779         *oom_r = oom_unproductive_full_gc;
12780     }
12781     else
12782     {
12783         dprintf (3, ("h%d: T full compacting GC (%d->%d)", 
12784             heap_number, 
12785             last_full_compact_gc_count, 
12786             current_full_compact_gc_count));
12787
12788         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12789         did_full_compact_gc = TRUE;
12790     }
12791
12792 exit:
12793     return did_full_compact_gc;
12794 }
12795
12796 #ifdef RECORD_LOH_STATE
12797 void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, EEThreadId thread_id)
12798 {
12799     // When the state is can_allocate we already have released the more
12800     // space lock. So we are not logging states here since this code
12801     // is not thread safe.
12802     if (loh_state_to_save != a_state_can_allocate)
12803     {
12804         last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
12805         last_loh_states[loh_state_index].thread_id = thread_id;
12806         loh_state_index++;
12807
12808         if (loh_state_index == max_saved_loh_states)
12809         {
12810             loh_state_index = 0;
12811         }
12812
12813         assert (loh_state_index < max_saved_loh_states);
12814     }
12815 }
12816 #endif //RECORD_LOH_STATE
12817
12818 BOOL gc_heap::allocate_large (int gen_number,
12819                               size_t size, 
12820                               alloc_context* acontext,
12821                               int align_const)
12822 {
12823 #ifdef BACKGROUND_GC
12824     if (recursive_gc_sync::background_running_p() && (current_c_gc_state != c_gc_state_planning))
12825     {
12826         background_loh_alloc_count++;
12827         //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
12828         {
12829             if (bgc_loh_should_allocate())
12830             {
12831                 if (!bgc_alloc_spin_loh)
12832                 {
12833                     add_saved_spinlock_info (me_release, mt_alloc_large);
12834                     dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl loh", heap_number));
12835                     leave_spin_lock (&more_space_lock);
12836                     bool cooperative_mode = enable_preemptive ();
12837                     GCToOSInterface::YieldThread (bgc_alloc_spin_loh);
12838                     disable_preemptive (cooperative_mode);
12839                     enter_spin_lock (&more_space_lock);
12840                     add_saved_spinlock_info (me_acquire, mt_alloc_large);
12841                     dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
12842                 }
12843             }
12844             else
12845             {
12846                 wait_for_background (awr_loh_alloc_during_bgc);
12847             }
12848         }
12849     }
12850 #endif //BACKGROUND_GC
12851
12852     gc_reason gr = reason_oos_loh;
12853     generation* gen = generation_of (gen_number);
12854     oom_reason oom_r = oom_no_failure;
12855     size_t current_full_compact_gc_count = 0;
12856
12857     // No variable values should be "carried over" from one state to the other. 
12858     // That's why there are local variable for each state
12859     allocation_state loh_alloc_state = a_state_start;
12860 #ifdef RECORD_LOH_STATE
12861     EEThreadId current_thread_id;
12862     current_thread_id.SetToCurrentThread();
12863 #endif //RECORD_LOH_STATE
12864
12865     // If we can get a new seg it means allocation will succeed.
12866     while (1)
12867     {
12868         dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
12869
12870 #ifdef RECORD_LOH_STATE
12871         add_saved_loh_state (loh_alloc_state, current_thread_id);
12872 #endif //RECORD_LOH_STATE
12873         switch (loh_alloc_state)
12874         {
12875             case a_state_can_allocate:
12876             case a_state_cant_allocate:
12877             {
12878                 goto exit;
12879             }
12880             case a_state_start:
12881             {
12882                 loh_alloc_state = a_state_try_fit;
12883                 break;
12884             }
12885             case a_state_try_fit:
12886             {
12887                 BOOL commit_failed_p = FALSE;
12888                 BOOL can_use_existing_p = FALSE;
12889
12890                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12891                                                   align_const, &commit_failed_p, &oom_r);
12892                 loh_alloc_state = (can_use_existing_p ?
12893                                         a_state_can_allocate : 
12894                                         (commit_failed_p ? 
12895                                             a_state_trigger_full_compact_gc :
12896                                             a_state_acquire_seg));
12897                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12898                 break;
12899             }
12900             case a_state_try_fit_new_seg:
12901             {
12902                 BOOL commit_failed_p = FALSE;
12903                 BOOL can_use_existing_p = FALSE;
12904
12905                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12906                                                   align_const, &commit_failed_p, &oom_r);
12907                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12908                 // another LOH allocating thread could have beat us to acquire the msl so 
12909                 // we need to try again.
12910                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
12911                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12912                 break;
12913             }
12914             case a_state_try_fit_new_seg_after_cg:
12915             {
12916                 BOOL commit_failed_p = FALSE;
12917                 BOOL can_use_existing_p = FALSE;
12918
12919                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12920                                                   align_const, &commit_failed_p, &oom_r);
12921                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12922                 // another LOH allocating thread could have beat us to acquire the msl so 
12923                 // we need to try again. However, if we failed to commit, which means we 
12924                 // did have space on the seg, we bail right away 'cause we already did a 
12925                 // full compacting GC.
12926                 loh_alloc_state = (can_use_existing_p ? 
12927                                         a_state_can_allocate : 
12928                                         (commit_failed_p ? 
12929                                             a_state_cant_allocate :
12930                                             a_state_acquire_seg_after_cg));
12931                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12932                 break;
12933             }
12934             case a_state_try_fit_no_seg:
12935             {
12936                 BOOL commit_failed_p = FALSE;
12937                 BOOL can_use_existing_p = FALSE;
12938
12939                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12940                                                   align_const, &commit_failed_p, &oom_r);
12941                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_cant_allocate);
12942                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12943                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12944                 break;
12945             }
12946             case a_state_try_fit_after_cg:
12947             {
12948                 BOOL commit_failed_p = FALSE;
12949                 BOOL can_use_existing_p = FALSE;
12950
12951                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12952                                                   align_const, &commit_failed_p, &oom_r);
12953                 loh_alloc_state = (can_use_existing_p ?
12954                                         a_state_can_allocate : 
12955                                         (commit_failed_p ? 
12956                                             a_state_cant_allocate :
12957                                             a_state_acquire_seg_after_cg));
12958                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12959                 break;
12960             }
12961             case a_state_try_fit_after_bgc:
12962             {
12963                 BOOL commit_failed_p = FALSE;
12964                 BOOL can_use_existing_p = FALSE;
12965
12966                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12967                                                   align_const, &commit_failed_p, &oom_r);
12968                 loh_alloc_state = (can_use_existing_p ?
12969                                         a_state_can_allocate : 
12970                                         (commit_failed_p ? 
12971                                             a_state_trigger_full_compact_gc :
12972                                             a_state_acquire_seg_after_bgc));
12973                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12974                 break;
12975             }
12976             case a_state_acquire_seg:
12977             {
12978                 BOOL can_get_new_seg_p = FALSE;
12979                 BOOL did_full_compacting_gc = FALSE;
12980
12981                 current_full_compact_gc_count = get_full_compact_gc_count();
12982
12983                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12984                 loh_alloc_state = (can_get_new_seg_p ? 
12985                                         a_state_try_fit_new_seg : 
12986                                         (did_full_compacting_gc ? 
12987                                             a_state_check_retry_seg :
12988                                             a_state_check_and_wait_for_bgc));
12989                 break;
12990             }
12991             case a_state_acquire_seg_after_cg:
12992             {
12993                 BOOL can_get_new_seg_p = FALSE;
12994                 BOOL did_full_compacting_gc = FALSE;
12995
12996                 current_full_compact_gc_count = get_full_compact_gc_count();
12997
12998                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12999                 // Since we release the msl before we try to allocate a seg, other
13000                 // threads could have allocated a bunch of segments before us so
13001                 // we might need to retry.
13002                 loh_alloc_state = (can_get_new_seg_p ? 
13003                                         a_state_try_fit_new_seg_after_cg : 
13004                                         a_state_check_retry_seg);
13005                 break;
13006             }
13007             case a_state_acquire_seg_after_bgc:
13008             {
13009                 BOOL can_get_new_seg_p = FALSE;
13010                 BOOL did_full_compacting_gc = FALSE;
13011              
13012                 current_full_compact_gc_count = get_full_compact_gc_count();
13013
13014                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); 
13015                 loh_alloc_state = (can_get_new_seg_p ? 
13016                                         a_state_try_fit_new_seg : 
13017                                         (did_full_compacting_gc ? 
13018                                             a_state_check_retry_seg :
13019                                             a_state_trigger_full_compact_gc));
13020                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13021                 break;
13022             }
13023             case a_state_check_and_wait_for_bgc:
13024             {
13025                 BOOL bgc_in_progress_p = FALSE;
13026                 BOOL did_full_compacting_gc = FALSE;
13027
13028                 if (fgn_maxgen_percent)
13029                 {
13030                     dprintf (2, ("FGN: failed to acquire seg, may need to do a full blocking GC"));
13031                     send_full_gc_notification (max_generation, FALSE);
13032                 }
13033
13034                 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc);
13035                 loh_alloc_state = (!bgc_in_progress_p ?
13036                                         a_state_trigger_full_compact_gc : 
13037                                         (did_full_compacting_gc ? 
13038                                             a_state_try_fit_after_cg :
13039                                             a_state_try_fit_after_bgc));
13040                 break;
13041             }
13042             case a_state_trigger_full_compact_gc:
13043             {
13044                 BOOL got_full_compacting_gc = FALSE;
13045
13046                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
13047                 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
13048                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13049                 break;
13050             }
13051             case a_state_check_retry_seg:
13052             {
13053                 BOOL should_retry_gc = retry_full_compact_gc (size);
13054                 BOOL should_retry_get_seg = FALSE;
13055                 if (!should_retry_gc)
13056                 {
13057                     size_t last_full_compact_gc_count = current_full_compact_gc_count;
13058                     current_full_compact_gc_count = get_full_compact_gc_count();
13059
13060                     if (current_full_compact_gc_count > (last_full_compact_gc_count + 1))
13061                     {
13062                         should_retry_get_seg = TRUE;
13063                     }
13064                 }
13065     
13066                 loh_alloc_state = (should_retry_gc ? 
13067                                         a_state_trigger_full_compact_gc : 
13068                                         (should_retry_get_seg ?
13069                                             a_state_acquire_seg_after_cg :
13070                                             a_state_cant_allocate));
13071                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13072                 break;
13073             }
13074             default:
13075             {
13076                 assert (!"Invalid state!");
13077                 break;
13078             }
13079         }
13080     }
13081
13082 exit:
13083     if (loh_alloc_state == a_state_cant_allocate)
13084     {
13085         assert (oom_r != oom_no_failure);
13086         handle_oom (heap_number, 
13087                     oom_r, 
13088                     size,
13089                     0,
13090                     0);
13091
13092         add_saved_spinlock_info (me_release, mt_alloc_large_cant);
13093         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for loh oom", heap_number));
13094         leave_spin_lock (&more_space_lock);
13095     }
13096
13097     return (loh_alloc_state == a_state_can_allocate);
13098 }
13099
13100 int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
13101                                    int gen_number)
13102 {
13103     if (gc_heap::gc_started)
13104     {
13105         wait_for_gc_done();
13106         return -1;
13107     }
13108
13109 #ifdef SYNCHRONIZATION_STATS
13110     int64_t msl_acquire_start = GCToOSInterface::QueryPerformanceCounter();
13111 #endif //SYNCHRONIZATION_STATS
13112     enter_spin_lock (&more_space_lock);
13113     add_saved_spinlock_info (me_acquire, mt_try_alloc);
13114     dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
13115 #ifdef SYNCHRONIZATION_STATS
13116     int64_t msl_acquire = GCToOSInterface::QueryPerformanceCounter() - msl_acquire_start;
13117     total_msl_acquire += msl_acquire;
13118     num_msl_acquired++;
13119     if (msl_acquire > 200)
13120     {
13121         num_high_msl_acquire++;
13122     }
13123     else
13124     {
13125         num_low_msl_acquire++;
13126     }
13127 #endif //SYNCHRONIZATION_STATS
13128
13129     /*
13130     // We are commenting this out 'cause we don't see the point - we already
13131     // have checked gc_started when we were acquiring the msl - no need to check
13132     // again. This complicates the logic in bgc_suspend_EE 'cause that one would
13133     // need to release msl which causes all sorts of trouble.
13134     if (gc_heap::gc_started)
13135     {
13136 #ifdef SYNCHRONIZATION_STATS
13137         good_suspension++;
13138 #endif //SYNCHRONIZATION_STATS
13139         BOOL fStress = (g_pConfig->GetGCStressLevel() & GCConfig::GCSTRESS_TRANSITION) != 0;
13140         if (!fStress)
13141         {
13142             //Rendez vous early (MP scaling issue)
13143             //dprintf (1, ("[%d]waiting for gc", heap_number));
13144             wait_for_gc_done();
13145 #ifdef MULTIPLE_HEAPS
13146             return -1;
13147 #endif //MULTIPLE_HEAPS
13148         }
13149     }
13150     */
13151
13152     dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
13153
13154     int align_const = get_alignment_constant (gen_number != (max_generation+1));
13155
13156     if (fgn_maxgen_percent)
13157     {
13158         check_for_full_gc (gen_number, size);
13159     }
13160
13161     if (!(new_allocation_allowed (gen_number)))
13162     {
13163         if (fgn_maxgen_percent && (gen_number == 0))
13164         {
13165             // We only check gen0 every so often, so take this opportunity to check again.
13166             check_for_full_gc (gen_number, size);
13167         }
13168
13169 #ifdef BACKGROUND_GC
13170         wait_for_bgc_high_memory (awr_gen0_alloc);
13171 #endif //BACKGROUND_GC
13172
13173 #ifdef SYNCHRONIZATION_STATS
13174         bad_suspension++;
13175 #endif //SYNCHRONIZATION_STATS
13176         dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
13177
13178         if (!settings.concurrent || (gen_number == 0))
13179         {
13180             vm_heap->GarbageCollectGeneration (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh));
13181 #ifdef MULTIPLE_HEAPS
13182             enter_spin_lock (&more_space_lock);
13183             add_saved_spinlock_info (me_acquire, mt_try_budget);
13184             dprintf (SPINLOCK_LOG, ("[%d]Emsl out budget", heap_number));
13185 #endif //MULTIPLE_HEAPS
13186         }
13187     }
13188
13189     BOOL can_allocate = ((gen_number == 0) ?
13190         allocate_small (gen_number, size, acontext, align_const) :
13191         allocate_large (gen_number, size, acontext, align_const));
13192    
13193     if (can_allocate)
13194     {
13195         size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
13196         int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
13197
13198         etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
13199
13200
13201         if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
13202         {
13203 #ifdef FEATURE_REDHAWK
13204             FIRE_EVENT(GCAllocationTick_V1, (uint32_t)etw_allocation_running_amount[etw_allocation_index],
13205                                             (gen_number == 0) ? gc_etw_alloc_soh : gc_etw_alloc_loh);
13206 #else
13207             // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
13208             // The ones that do are much less efficient.
13209 #if defined(FEATURE_EVENT_TRACE)
13210             if (EVENT_ENABLED(GCAllocationTick_V3))
13211             {
13212                 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
13213             }
13214 #endif //FEATURE_EVENT_TRACE
13215 #endif //FEATURE_REDHAWK
13216             etw_allocation_running_amount[etw_allocation_index] = 0;
13217         }
13218     }
13219
13220     return (int)can_allocate;
13221 }
13222
13223 #ifdef MULTIPLE_HEAPS
13224 void gc_heap::balance_heaps (alloc_context* acontext)
13225 {
13226
13227     if (acontext->alloc_count < 4)
13228     {
13229         if (acontext->alloc_count == 0)
13230         {
13231             acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, 0) ));
13232             gc_heap* hp = acontext->get_home_heap()->pGenGCHeap;
13233             dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
13234             acontext->set_alloc_heap(acontext->get_home_heap());
13235             hp->alloc_context_count++;
13236         }
13237     }
13238     else
13239     {
13240         BOOL set_home_heap = FALSE;
13241         int hint = 0;
13242
13243         if (heap_select::can_find_heap_fast())
13244         {
13245             if (acontext->get_home_heap() != NULL)
13246                 hint = acontext->get_home_heap()->pGenGCHeap->heap_number;
13247             if (acontext->get_home_heap() != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
13248             {
13249                 set_home_heap = TRUE;
13250             }
13251         }
13252         else
13253         {
13254             // can't use gdt
13255             if ((acontext->alloc_count & 3) == 0)
13256                 set_home_heap = TRUE;
13257         }
13258
13259         if (set_home_heap)
13260         {
13261 /*
13262             // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
13263             if (n_heaps > MAX_SUPPORTED_CPUS)
13264             {
13265                 // on machines with many processors cache affinity is really king, so don't even try
13266                 // to balance on these.
13267                 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
13268                 acontext->alloc_heap = acontext->home_heap;
13269             }
13270             else
13271 */
13272             {
13273                 gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13274
13275                 dynamic_data* dd = org_hp->dynamic_data_of (0);
13276                 ptrdiff_t org_size = dd_new_allocation (dd);
13277                 int org_alloc_context_count;
13278                 int max_alloc_context_count;
13279                 gc_heap* max_hp;
13280                 ptrdiff_t max_size;
13281                 size_t delta = dd_min_size (dd)/4;
13282
13283                 int start, end, finish;
13284                 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13285                 finish = start + n_heaps;
13286
13287 try_again:
13288                 do
13289                 {
13290                     max_hp = org_hp;
13291                     max_size = org_size + delta;
13292                     acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, hint) ));
13293
13294                     if (org_hp == acontext->get_home_heap()->pGenGCHeap)
13295                         max_size = max_size + delta;
13296
13297                     org_alloc_context_count = org_hp->alloc_context_count;
13298                     max_alloc_context_count = org_alloc_context_count;
13299                     if (max_alloc_context_count > 1)
13300                         max_size /= max_alloc_context_count;
13301
13302                     for (int i = start; i < end; i++)
13303                     {
13304                         gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13305                         dd = hp->dynamic_data_of (0);
13306                         ptrdiff_t size = dd_new_allocation (dd);
13307                         if (hp == acontext->get_home_heap()->pGenGCHeap)
13308                             size = size + delta;
13309                         int hp_alloc_context_count = hp->alloc_context_count;
13310                         if (hp_alloc_context_count > 0)
13311                             size /= (hp_alloc_context_count + 1);
13312                         if (size > max_size)
13313                         {
13314                             max_hp = hp;
13315                             max_size = size;
13316                             max_alloc_context_count = hp_alloc_context_count;
13317                         }
13318                     }
13319                 }
13320                 while (org_alloc_context_count != org_hp->alloc_context_count ||
13321                        max_alloc_context_count != max_hp->alloc_context_count);
13322
13323                 if ((max_hp == org_hp) && (end < finish))
13324                 {   
13325                     start = end; end = finish; 
13326                     delta = dd_min_size(dd)/2; // Make it twice as hard to balance to remote nodes on NUMA.
13327                     goto try_again;
13328                 }
13329
13330                 if (max_hp != org_hp)
13331                 {
13332                     org_hp->alloc_context_count--;
13333                     max_hp->alloc_context_count++;
13334                     acontext->set_alloc_heap(GCHeap::GetHeap(max_hp->heap_number));
13335                     if (GCToOSInterface::CanEnableGCCPUGroups())
13336                     {   //only set ideal processor when max_hp and org_hp are in the same cpu
13337                         //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
13338                         uint16_t org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
13339                         uint16_t max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
13340                         if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
13341                         {   
13342                             uint16_t group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
13343
13344                             GCThreadAffinity affinity;
13345                             affinity.Processor = group_proc_no;
13346                             affinity.Group = org_gn;
13347                             if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13348                             {
13349                                 dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
13350                                             org_hp->heap_number));
13351                             }
13352                         }
13353                     }
13354                     else 
13355                     {
13356                         uint16_t proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
13357
13358                         GCThreadAffinity affinity;
13359                         affinity.Processor = proc_no;
13360                         affinity.Group = GCThreadAffinity::None;
13361
13362                         if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13363                         {
13364                             dprintf (3, ("Failed to set the ideal processor for heap %d.",
13365                                         org_hp->heap_number));
13366                         }
13367                     }
13368                     dprintf (3, ("Switching context %p (home heap %d) ", 
13369                                  acontext,
13370                         acontext->get_home_heap()->pGenGCHeap->heap_number));
13371                     dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ", 
13372                                  org_hp->heap_number,
13373                                  org_size,
13374                                  org_alloc_context_count));
13375                     dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n", 
13376                                  max_hp->heap_number,
13377                                  dd_new_allocation(max_hp->dynamic_data_of(0)),
13378                                                    max_alloc_context_count));
13379                 }
13380             }
13381         }
13382     }
13383     acontext->alloc_count++;
13384 }
13385
13386 gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t /*size*/)
13387 {
13388     gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13389     //dprintf (1, ("LA: %Id", size));
13390
13391     //if (size > 128*1024)
13392     if (1)
13393     {
13394         dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
13395
13396         ptrdiff_t org_size = dd_new_allocation (dd);
13397         gc_heap* max_hp;
13398         ptrdiff_t max_size;
13399         size_t delta = dd_min_size (dd) * 4;
13400
13401         int start, end, finish;
13402         heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13403         finish = start + n_heaps;
13404
13405 try_again:
13406         {
13407             max_hp = org_hp;
13408             max_size = org_size + delta;
13409             dprintf (3, ("orig hp: %d, max size: %d",
13410                 org_hp->heap_number,
13411                 max_size));
13412
13413             for (int i = start; i < end; i++)
13414             {
13415                 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13416                 dd = hp->dynamic_data_of (max_generation + 1);
13417                 ptrdiff_t size = dd_new_allocation (dd);
13418                 dprintf (3, ("hp: %d, size: %d",
13419                     hp->heap_number,
13420                     size));
13421                 if (size > max_size)
13422                 {
13423                     max_hp = hp;
13424                     max_size = size;
13425                     dprintf (3, ("max hp: %d, max size: %d",
13426                         max_hp->heap_number,
13427                         max_size));
13428                 }
13429             }
13430         }
13431
13432         if ((max_hp == org_hp) && (end < finish))
13433         {
13434             start = end; end = finish;
13435             delta = dd_min_size(dd) * 4;   // Need to tuning delta
13436             goto try_again;
13437         }
13438
13439         if (max_hp != org_hp)
13440         {
13441             dprintf (3, ("loh: %d(%Id)->%d(%Id)", 
13442                 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
13443                 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
13444         }
13445
13446         return max_hp;
13447     }
13448     else
13449     {
13450         return org_hp;
13451     }
13452 }
13453 #endif //MULTIPLE_HEAPS
13454
13455 BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
13456                                   int alloc_generation_number)
13457 {
13458     int status;
13459     do
13460     { 
13461 #ifdef MULTIPLE_HEAPS
13462         if (alloc_generation_number == 0)
13463         {
13464             balance_heaps (acontext);
13465             status = acontext->get_alloc_heap()->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
13466         }
13467         else
13468         {
13469             gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
13470             status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
13471         }
13472 #else
13473         status = try_allocate_more_space (acontext, size, alloc_generation_number);
13474 #endif //MULTIPLE_HEAPS
13475     }
13476     while (status == -1);
13477     
13478     return (status != 0);
13479 }
13480
13481 inline
13482 CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
13483 {
13484     size_t size = Align (jsize);
13485     assert (size >= Align (min_obj_size));
13486     {
13487     retry:
13488         uint8_t*  result = acontext->alloc_ptr;
13489         acontext->alloc_ptr+=size;
13490         if (acontext->alloc_ptr <= acontext->alloc_limit)
13491         {
13492             CObjectHeader* obj = (CObjectHeader*)result;
13493             assert (obj != 0);
13494             return obj;
13495         }
13496         else
13497         {
13498             acontext->alloc_ptr -= size;
13499
13500 #ifdef _MSC_VER
13501 #pragma inline_depth(0)
13502 #endif //_MSC_VER
13503
13504             if (! allocate_more_space (acontext, size, 0))
13505                 return 0;
13506
13507 #ifdef _MSC_VER
13508 #pragma inline_depth(20)
13509 #endif //_MSC_VER
13510
13511             goto retry;
13512         }
13513     }
13514 }
13515
13516 inline
13517 CObjectHeader* gc_heap::try_fast_alloc (size_t jsize)
13518 {
13519     size_t size = Align (jsize);
13520     assert (size >= Align (min_obj_size));
13521     generation* gen = generation_of (0);
13522     uint8_t*  result = generation_allocation_pointer (gen);
13523     generation_allocation_pointer (gen) += size;
13524     if (generation_allocation_pointer (gen) <=
13525         generation_allocation_limit (gen))
13526     {
13527         return (CObjectHeader*)result;
13528     }
13529     else
13530     {
13531         generation_allocation_pointer (gen) -= size;
13532         return 0;
13533     }
13534 }
13535 void  gc_heap::leave_allocation_segment (generation* gen)
13536 {
13537     adjust_limit (0, 0, gen, max_generation);
13538 }
13539
13540 void gc_heap::init_free_and_plug()
13541 {
13542 #ifdef FREE_USAGE_STATS
13543     for (int i = 0; i <= settings.condemned_generation; i++)
13544     {
13545         generation* gen = generation_of (i);
13546         memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
13547         memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13548         memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
13549     }
13550
13551     if (settings.condemned_generation != max_generation)
13552     {
13553         for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
13554         {
13555             generation* gen = generation_of (i);
13556             memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13557         }
13558     }
13559 #endif //FREE_USAGE_STATS
13560 }
13561
13562 void gc_heap::print_free_and_plug (const char* msg)
13563 {
13564 #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
13565     int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
13566     for (int i = 0; i <= older_gen; i++)
13567     {
13568         generation* gen = generation_of (i);
13569         for (int j = 0; j < NUM_GEN_POWER2; j++)
13570         {
13571             if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
13572             {
13573                 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id", 
13574                     msg, 
13575                     heap_number, 
13576                     (settings.concurrent ? "BGC" : "GC"),
13577                     settings.gc_index,
13578                     i,
13579                     (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
13580             }
13581         }
13582     }
13583 #else
13584     UNREFERENCED_PARAMETER(msg);
13585 #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
13586 }
13587
13588 void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
13589 {
13590 #ifdef FREE_USAGE_STATS
13591     dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
13592     generation* gen = generation_of (gen_number);
13593     size_t sz = BASE_GEN_SIZE;
13594     int i = 0;
13595
13596     for (; i < NUM_GEN_POWER2; i++)
13597     {
13598         if (plug_size < sz)
13599         {
13600             break;
13601         }
13602         sz = sz * 2;
13603     }
13604     
13605     (gen->gen_plugs[i])++;
13606 #else
13607     UNREFERENCED_PARAMETER(gen_number);
13608     UNREFERENCED_PARAMETER(plug_size);
13609 #endif //FREE_USAGE_STATS
13610 }
13611
13612 void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
13613 {
13614 #ifdef FREE_USAGE_STATS
13615     generation* gen = generation_of (gen_number);
13616     size_t sz = BASE_GEN_SIZE;
13617     int i = 0;
13618
13619     for (; i < NUM_GEN_POWER2; i++)
13620     {
13621         if (free_size < sz)
13622         {
13623             break;
13624         }
13625         sz = sz * 2;
13626     }
13627     
13628     (gen->gen_current_pinned_free_spaces[i])++;
13629     generation_pinned_free_obj_space (gen) += free_size;
13630     dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)", 
13631         free_size, (i + 10), gen_number, 
13632         generation_pinned_free_obj_space (gen),
13633         gen->gen_current_pinned_free_spaces[i]));
13634 #else
13635     UNREFERENCED_PARAMETER(gen_number);
13636     UNREFERENCED_PARAMETER(free_size);
13637 #endif //FREE_USAGE_STATS
13638 }
13639
13640 void gc_heap::add_gen_free (int gen_number, size_t free_size)
13641 {
13642 #ifdef FREE_USAGE_STATS
13643     dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13644     generation* gen = generation_of (gen_number);
13645     size_t sz = BASE_GEN_SIZE;
13646     int i = 0;
13647
13648     for (; i < NUM_GEN_POWER2; i++)
13649     {
13650         if (free_size < sz)
13651         {
13652             break;
13653         }
13654         sz = sz * 2;
13655     }
13656     
13657     (gen->gen_free_spaces[i])++;
13658 #else
13659     UNREFERENCED_PARAMETER(gen_number);
13660     UNREFERENCED_PARAMETER(free_size);
13661 #endif //FREE_USAGE_STATS
13662 }
13663
13664 void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13665 {
13666 #ifdef FREE_USAGE_STATS
13667     dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13668     generation* gen = generation_of (gen_number);
13669     size_t sz = BASE_GEN_SIZE;
13670     int i = 0;
13671
13672     for (; i < NUM_GEN_POWER2; i++)
13673     {
13674         if (free_size < sz)
13675         {
13676             break;
13677         }
13678         sz = sz * 2;
13679     }
13680     
13681     (gen->gen_free_spaces[i])--;
13682 #else
13683     UNREFERENCED_PARAMETER(gen_number);
13684     UNREFERENCED_PARAMETER(free_size);
13685 #endif //FREE_USAGE_STATS
13686 }
13687
13688 uint8_t* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13689                                              int from_gen_number,
13690                                              uint8_t* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13691 {
13692     size = Align (size);
13693     assert (size >= Align (min_obj_size));
13694     assert (from_gen_number < max_generation);
13695     assert (from_gen_number >= 0);
13696     assert (generation_of (from_gen_number + 1) == gen);
13697
13698     allocator* gen_allocator = generation_allocator (gen);
13699     BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13700     int pad_in_front = (old_loc != 0)? USE_PADDING_FRONT : 0;
13701
13702     size_t real_size = size + Align (min_obj_size);
13703     if (pad_in_front)
13704         real_size += Align (min_obj_size);
13705
13706     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13707                        generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13708     {
13709         size_t sz_list = gen_allocator->first_bucket_size();
13710         for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13711         {
13712             if ((real_size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13713             {
13714                 uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13715                 uint8_t* prev_free_item = 0;
13716                 while (free_list != 0)
13717                 {
13718                     dprintf (3, ("considering free list %Ix", (size_t)free_list));
13719
13720                     size_t free_list_size = unused_array_size (free_list);
13721
13722                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
13723                                     old_loc, USE_PADDING_TAIL | pad_in_front))
13724                     {
13725                         dprintf (4, ("F:%Ix-%Id",
13726                                      (size_t)free_list, free_list_size));
13727
13728                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
13729                         generation_free_list_space (gen) -= free_list_size;
13730                         remove_gen_free (gen->gen_num, free_list_size);
13731
13732                         adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
13733                         goto finished;
13734                     }
13735                     // We do first fit on bucket 0 because we are not guaranteed to find a fit there.
13736                     else if (discard_p || (a_l_idx == 0))
13737                     {
13738                         dprintf (3, ("couldn't use this free area, discarding"));
13739                         generation_free_obj_space (gen) += free_list_size;
13740
13741                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
13742                         generation_free_list_space (gen) -= free_list_size;
13743                         remove_gen_free (gen->gen_num, free_list_size);
13744                     }
13745                     else
13746                     {
13747                         prev_free_item = free_list;
13748                     }
13749                     free_list = free_list_slot (free_list); 
13750                 }
13751             }
13752             sz_list = sz_list * 2;
13753         }
13754         //go back to the beginning of the segment list 
13755         generation_allocate_end_seg_p (gen) = TRUE;
13756         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
13757         if (seg != generation_allocation_segment (gen))
13758         {
13759             leave_allocation_segment (gen);
13760             generation_allocation_segment (gen) = seg;
13761         }
13762         while (seg != ephemeral_heap_segment)
13763         {
13764             if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13765                            heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
13766             {
13767                 dprintf (3, ("using what's left in committed"));
13768                 adjust_limit (heap_segment_plan_allocated (seg),
13769                               heap_segment_committed (seg) -
13770                               heap_segment_plan_allocated (seg),
13771                               gen, from_gen_number+1);
13772                 // dformat (t, 3, "Expanding segment allocation");
13773                 heap_segment_plan_allocated (seg) =
13774                     heap_segment_committed (seg);
13775                 goto finished;
13776             }
13777             else
13778             {
13779                 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13780                                 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13781                     grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
13782                 {
13783                     dprintf (3, ("using what's left in reserved"));
13784                     adjust_limit (heap_segment_plan_allocated (seg),
13785                                   heap_segment_committed (seg) -
13786                                   heap_segment_plan_allocated (seg),
13787                                   gen, from_gen_number+1);
13788                     heap_segment_plan_allocated (seg) =
13789                         heap_segment_committed (seg);
13790
13791                     goto finished;
13792                 }
13793                 else
13794                 {
13795                     leave_allocation_segment (gen);
13796                     heap_segment*   next_seg = heap_segment_next_rw (seg);
13797                     if (next_seg)
13798                     {
13799                         dprintf (3, ("getting next segment"));
13800                         generation_allocation_segment (gen) = next_seg;
13801                         generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13802                         generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13803                     }
13804                     else
13805                     {
13806                         size = 0;
13807                         goto finished;
13808                     }
13809                 }
13810             }
13811             seg = generation_allocation_segment (gen);
13812         }
13813         //No need to fix the last region. Will be done later
13814         size = 0;
13815         goto finished;
13816     }
13817     finished:
13818     if (0 == size)
13819     {
13820         return 0;
13821     }
13822     else
13823     {
13824         uint8_t*  result = generation_allocation_pointer (gen);
13825         size_t pad = 0;
13826
13827 #ifdef SHORT_PLUGS
13828         if ((pad_in_front & USE_PADDING_FRONT) &&
13829             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13830              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13831         {
13832             pad = Align (min_obj_size);
13833             set_plug_padded (old_loc);
13834         }
13835 #endif //SHORT_PLUGS
13836
13837 #ifdef FEATURE_STRUCTALIGN
13838         _ASSERTE(!old_loc || alignmentOffset != 0);
13839         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13840         if (old_loc != 0)
13841         {
13842             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13843             set_node_aligninfo (old_loc, requiredAlignment, pad1);
13844             pad += pad1;
13845         }
13846 #else // FEATURE_STRUCTALIGN
13847         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13848         {
13849             pad += switch_alignment_size (is_plug_padded (old_loc));
13850             set_node_realigned (old_loc);
13851             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13852                          (size_t)old_loc, (size_t)(result+pad)));
13853             assert (same_large_alignment_p (result + pad, old_loc));
13854         }
13855 #endif // FEATURE_STRUCTALIGN
13856         dprintf (3, ("Allocate %Id bytes", size));
13857
13858         if ((old_loc == 0) || (pad != 0))
13859         {
13860             //allocating a non plug or a gap, so reset the start region
13861             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13862         }
13863
13864         generation_allocation_pointer (gen) += size + pad;
13865         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13866         if (generation_allocate_end_seg_p (gen))
13867         {
13868             generation_end_seg_allocated (gen) += size;
13869         }
13870         else
13871         {
13872             generation_free_list_allocated (gen) += size;
13873         }
13874         generation_allocation_size (gen) += size;
13875
13876         dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix", 
13877             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13878             generation_allocation_context_start_region (gen)));
13879
13880         return result + pad;;
13881     }
13882 }
13883
13884 void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
13885 {
13886     //make sure that every generation has a planned allocation start
13887     int  gen_number = max_generation - 1;
13888     while (gen_number>= 0)
13889     {
13890         generation* gen = generation_of (gen_number);
13891         if (0 == generation_plan_allocation_start (gen))
13892         {
13893             realloc_plan_generation_start (gen, consing_gen);
13894
13895             assert (generation_plan_allocation_start (gen));
13896         }
13897         gen_number--;
13898     }
13899
13900     // now we know the planned allocation size
13901     size_t  size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
13902     heap_segment* seg = generation_allocation_segment (consing_gen);
13903     if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
13904     {
13905         if (size != 0)
13906         {
13907             heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
13908         }
13909     }
13910     else
13911     {
13912         assert (settings.condemned_generation == max_generation);
13913         uint8_t* first_address = generation_allocation_limit (consing_gen);
13914         //look through the pinned plugs for relevant ones.
13915         //Look for the right pinned plug to start from.
13916         size_t mi = 0;
13917         mark* m = 0;
13918         while (mi != mark_stack_tos)
13919         {
13920             m = pinned_plug_of (mi);
13921             if ((pinned_plug (m) == first_address))
13922                 break;
13923             else
13924                 mi++;
13925         }
13926         assert (mi != mark_stack_tos);
13927         pinned_len (m) = size;
13928     }
13929 }
13930
13931 //tododefrag optimize for new segment (plan_allocated == mem)
13932 uint8_t* gc_heap::allocate_in_expanded_heap (generation* gen,
13933                                           size_t size,
13934                                           BOOL& adjacentp,
13935                                           uint8_t* old_loc,
13936 #ifdef SHORT_PLUGS
13937                                           BOOL set_padding_on_saved_p,
13938                                           mark* pinned_plug_entry,
13939 #endif //SHORT_PLUGS
13940                                           BOOL consider_bestfit,
13941                                           int active_new_gen_number
13942                                           REQD_ALIGN_AND_OFFSET_DCL)
13943 {
13944     UNREFERENCED_PARAMETER(active_new_gen_number);
13945     dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
13946
13947     size = Align (size);
13948     assert (size >= Align (min_obj_size));
13949     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
13950
13951     if (consider_bestfit && use_bestfit)
13952     {
13953         assert (bestfit_seg);
13954         dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id", 
13955                     old_loc, size));
13956         return bestfit_seg->fit (old_loc, 
13957 #ifdef SHORT_PLUGS
13958                                  set_padding_on_saved_p,
13959                                  pinned_plug_entry,
13960 #endif //SHORT_PLUGS
13961                                  size REQD_ALIGN_AND_OFFSET_ARG);
13962     }
13963
13964     heap_segment* seg = generation_allocation_segment (gen);
13965
13966     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13967                        generation_allocation_limit (gen), old_loc,
13968                        ((generation_allocation_limit (gen) !=
13969                           heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
13970     {
13971         dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
13972             generation_allocation_limit (gen)));
13973
13974         adjacentp = FALSE;
13975         uint8_t* first_address = (generation_allocation_limit (gen) ?
13976                                generation_allocation_limit (gen) :
13977                                heap_segment_mem (seg));
13978         assert (in_range_for_segment (first_address, seg));
13979
13980         uint8_t* end_address   = heap_segment_reserved (seg);
13981
13982         dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
13983             first_address, generation_allocation_limit (gen), end_address));
13984
13985         size_t mi = 0;
13986         mark* m = 0;
13987
13988         if (heap_segment_allocated (seg) != heap_segment_mem (seg))
13989         {
13990             assert (settings.condemned_generation == max_generation);
13991             //look through the pinned plugs for relevant ones.
13992             //Look for the right pinned plug to start from.
13993             while (mi != mark_stack_tos)
13994             {
13995                 m = pinned_plug_of (mi);
13996                 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
13997                 {
13998                     dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
13999                     break;
14000                 }
14001                 else
14002                     mi++;
14003             }
14004             if (mi != mark_stack_tos)
14005             {
14006                 //fix old free list.
14007                 size_t  hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
14008                 {
14009                     dprintf(3,("gc filling up hole"));
14010                     ptrdiff_t mi1 = (ptrdiff_t)mi;
14011                     while ((mi1 >= 0) &&
14012                            (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
14013                     {
14014                         dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
14015                         mi1--;
14016                     }
14017                     if (mi1 >= 0)
14018                     {
14019                         size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
14020                         pinned_len (pinned_plug_of(mi1)) = hsize;
14021                         dprintf (3, ("changing %Ix len %Ix->%Ix", 
14022                             pinned_plug (pinned_plug_of(mi1)), 
14023                             saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
14024                     }
14025                 }
14026             }
14027         }
14028         else
14029         {
14030             assert (generation_allocation_limit (gen) ==
14031                     generation_allocation_pointer (gen));
14032             mi = mark_stack_tos;
14033         }
14034
14035         while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
14036         {
14037             size_t len = pinned_len (m);
14038             uint8_t*  free_list = (pinned_plug (m) - len);
14039             dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)", 
14040                 free_list, (free_list + len), len));
14041             if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
14042             {
14043                 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
14044                             (size_t)free_list, len));
14045                 {
14046                     generation_allocation_pointer (gen) = free_list;
14047                     generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14048                     generation_allocation_limit (gen) = (free_list + len);
14049                 }
14050                 goto allocate_in_free;
14051             }
14052             mi++;
14053             m = pinned_plug_of (mi);
14054         }
14055
14056         //switch to the end of the segment.
14057         generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
14058         generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14059         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14060         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14061         dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)", 
14062             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14063             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
14064
14065         if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14066                          generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
14067         {
14068             dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
14069                 generation_allocation_limit (gen)));
14070             assert (!"Can't allocate if no free space");
14071             return 0;
14072         }
14073     }
14074     else
14075     {
14076         adjacentp = TRUE;
14077     }
14078
14079 allocate_in_free:
14080     {
14081         uint8_t*  result = generation_allocation_pointer (gen);
14082         size_t pad = 0;
14083
14084 #ifdef SHORT_PLUGS
14085         if ((pad_in_front & USE_PADDING_FRONT) &&
14086             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14087              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14088
14089         {
14090             pad = Align (min_obj_size);
14091             set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
14092         }
14093 #endif //SHORT_PLUGS
14094
14095 #ifdef FEATURE_STRUCTALIGN
14096         _ASSERTE(!old_loc || alignmentOffset != 0);
14097         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14098         if (old_loc != 0)
14099         {
14100             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14101             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14102             pad += pad1;
14103             adjacentp = FALSE;
14104         }
14105 #else // FEATURE_STRUCTALIGN
14106         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14107         {
14108             pad += switch_alignment_size (is_plug_padded (old_loc));
14109             set_node_realigned (old_loc);
14110             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14111                          (size_t)old_loc, (size_t)(result+pad)));
14112             assert (same_large_alignment_p (result + pad, old_loc));
14113             adjacentp = FALSE;
14114         }
14115 #endif // FEATURE_STRUCTALIGN
14116
14117         if ((old_loc == 0) || (pad != 0))
14118         {
14119             //allocating a non plug or a gap, so reset the start region
14120             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14121         }
14122
14123         generation_allocation_pointer (gen) += size + pad;
14124         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14125         dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
14126
14127         dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix", 
14128             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14129             generation_allocation_context_start_region (gen)));
14130
14131         return result + pad;
14132     }
14133 }
14134
14135 generation*  gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
14136 {
14137     heap_segment* seg = generation_allocation_segment (consing_gen);
14138     if (seg != ephemeral_heap_segment)
14139     {
14140         assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
14141         assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
14142
14143         //fix the allocated size of the segment.
14144         heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14145
14146         generation* new_consing_gen = generation_of (max_generation - 1);
14147         generation_allocation_pointer (new_consing_gen) =
14148                 heap_segment_mem (ephemeral_heap_segment);
14149         generation_allocation_limit (new_consing_gen) =
14150             generation_allocation_pointer (new_consing_gen);
14151         generation_allocation_context_start_region (new_consing_gen) = 
14152             generation_allocation_pointer (new_consing_gen);
14153         generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
14154
14155         return new_consing_gen;
14156     }
14157     else
14158         return consing_gen;
14159 }
14160
14161 uint8_t* gc_heap::allocate_in_condemned_generations (generation* gen,
14162                                                   size_t size,
14163                                                   int from_gen_number,
14164 #ifdef SHORT_PLUGS
14165                                                   BOOL* convert_to_pinned_p,
14166                                                   uint8_t* next_pinned_plug,
14167                                                   heap_segment* current_seg,
14168 #endif //SHORT_PLUGS
14169                                                   uint8_t* old_loc
14170                                                   REQD_ALIGN_AND_OFFSET_DCL)
14171 {
14172     // Make sure that the youngest generation gap hasn't been allocated
14173     if (settings.promotion)
14174     {
14175         assert (generation_plan_allocation_start (youngest_generation) == 0);
14176     }
14177
14178     size = Align (size);
14179     assert (size >= Align (min_obj_size));
14180     int to_gen_number = from_gen_number;
14181     if (from_gen_number != (int)max_generation)
14182     {
14183         to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
14184     }
14185
14186     dprintf (3, ("aic gen%d: s: %Id, %d->%d, %Ix->%Ix", gen->gen_num, size, from_gen_number, 
14187           to_gen_number, generation_allocation_pointer(gen), generation_allocation_limit(gen)));
14188
14189     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
14190
14191     if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
14192     {
14193         generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14194         generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14195     }
14196 retry:
14197     {
14198         heap_segment* seg = generation_allocation_segment (gen);
14199         if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14200                            generation_allocation_limit (gen), old_loc,
14201                            ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
14202         {
14203             if ((! (pinned_plug_que_empty_p()) &&
14204                  (generation_allocation_limit (gen) ==
14205                   pinned_plug (oldest_pin()))))
14206             {
14207                 size_t entry = deque_pinned_plug();
14208                 mark* pinned_plug_entry = pinned_plug_of (entry);
14209                 size_t len = pinned_len (pinned_plug_entry);
14210                 uint8_t* plug = pinned_plug (pinned_plug_entry);
14211                 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
14212
14213 #ifdef FREE_USAGE_STATS
14214                 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
14215                 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id", 
14216                     generation_allocated_since_last_pin (gen), 
14217                     plug,
14218                     generation_allocated_in_pinned_free (gen)));
14219                 generation_allocated_since_last_pin (gen) = 0;
14220
14221                 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
14222 #endif //FREE_USAGE_STATS
14223
14224                 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix", 
14225                     mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
14226
14227                 assert(mark_stack_array[entry].len == 0 ||
14228                        mark_stack_array[entry].len >= Align(min_obj_size));
14229                 generation_allocation_pointer (gen) = plug + len;
14230                 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14231                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14232                 set_allocator_next_pin (gen);
14233
14234                 //Add the size of the pinned plug to the right pinned allocations
14235                 //find out which gen this pinned plug came from 
14236                 int frgn = object_gennum (plug);
14237                 if ((frgn != (int)max_generation) && settings.promotion)
14238                 {
14239                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
14240                     int togn = object_gennum_plan (plug);
14241                     if (frgn < togn)
14242                     {
14243                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
14244                     }
14245                 }
14246                 goto retry;
14247             }
14248             
14249             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
14250             {
14251                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14252                 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
14253             }
14254             else
14255             {
14256                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
14257                 {
14258                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14259                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14260                     dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
14261                 }
14262                 else
14263                 {
14264 #ifndef RESPECT_LARGE_ALIGNMENT
14265                     assert (gen != youngest_generation);
14266 #endif //RESPECT_LARGE_ALIGNMENT
14267
14268                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14269                                     heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14270                         (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
14271                                             size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
14272                     {
14273                         dprintf (3, ("Expanded segment allocation by committing more memory"));
14274                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14275                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14276                     }
14277                     else
14278                     {
14279                         heap_segment*   next_seg = heap_segment_next (seg);
14280                         assert (generation_allocation_pointer (gen)>=
14281                                 heap_segment_mem (seg));
14282                         // Verify that all pinned plugs for this segment are consumed
14283                         if (!pinned_plug_que_empty_p() &&
14284                             ((pinned_plug (oldest_pin()) <
14285                               heap_segment_allocated (seg)) &&
14286                              (pinned_plug (oldest_pin()) >=
14287                               generation_allocation_pointer (gen))))
14288                         {
14289                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
14290                                          pinned_plug (oldest_pin())));
14291                             FATAL_GC_ERROR();
14292                         }
14293                         assert (generation_allocation_pointer (gen)>=
14294                                 heap_segment_mem (seg));
14295                         assert (generation_allocation_pointer (gen)<=
14296                                 heap_segment_committed (seg));
14297                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
14298
14299                         if (next_seg)
14300                         {
14301                             generation_allocation_segment (gen) = next_seg;
14302                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14303                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14304                             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14305                         }
14306                         else
14307                         {
14308                             return 0; //should only happen during allocation of generation 0 gap
14309                             // in that case we are going to grow the heap anyway
14310                         }
14311                     }
14312                 }
14313             }
14314             set_allocator_next_pin (gen);
14315
14316             goto retry;
14317         }
14318     }
14319
14320     {
14321         assert (generation_allocation_pointer (gen)>=
14322                 heap_segment_mem (generation_allocation_segment (gen)));
14323         uint8_t* result = generation_allocation_pointer (gen);
14324         size_t pad = 0;
14325 #ifdef SHORT_PLUGS
14326         if ((pad_in_front & USE_PADDING_FRONT) &&
14327             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14328              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14329         {
14330             ptrdiff_t dist = old_loc - result;
14331             if (dist == 0)
14332             {
14333                 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
14334                 pad = 0;
14335             }
14336             else
14337             {
14338                 if ((dist > 0) && (dist < (ptrdiff_t)Align (min_obj_size)))
14339                 {
14340                     dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
14341                     FATAL_GC_ERROR();
14342                 }
14343
14344                 pad = Align (min_obj_size);
14345                 set_plug_padded (old_loc);
14346             }
14347         }
14348 #endif //SHORT_PLUGS
14349 #ifdef FEATURE_STRUCTALIGN
14350         _ASSERTE(!old_loc || alignmentOffset != 0);
14351         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14352         if ((old_loc != 0))
14353         {
14354             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14355             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14356             pad += pad1;
14357         }
14358 #else // FEATURE_STRUCTALIGN
14359         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14360         {
14361             pad += switch_alignment_size (is_plug_padded (old_loc));
14362             set_node_realigned(old_loc);
14363             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14364                          (size_t)old_loc, (size_t)(result+pad)));
14365             assert (same_large_alignment_p (result + pad, old_loc));
14366         }
14367 #endif // FEATURE_STRUCTALIGN
14368
14369 #ifdef SHORT_PLUGS
14370         if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
14371         {
14372             assert (old_loc != 0);
14373             ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
14374             assert (dist_to_next_pin >= 0);
14375
14376             if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
14377             {
14378                 dprintf (3, ("%Ix->(%Ix,%Ix),%Ix(%Ix)(%Ix),NP->PP", 
14379                     old_loc, 
14380                     generation_allocation_pointer (gen),
14381                     generation_allocation_limit (gen),
14382                     next_pinned_plug,
14383                     size, 
14384                     dist_to_next_pin));
14385                 clear_plug_padded (old_loc);
14386                 pad = 0;
14387                 *convert_to_pinned_p = TRUE;
14388                 record_interesting_data_point (idp_converted_pin);
14389
14390                 return 0;
14391             }
14392         }
14393 #endif //SHORT_PLUGS
14394
14395         if ((old_loc == 0) || (pad != 0))
14396         {
14397             //allocating a non plug or a gap, so reset the start region
14398             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14399         }
14400
14401         generation_allocation_pointer (gen) += size + pad;
14402         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14403
14404 #ifdef FREE_USAGE_STATS
14405         generation_allocated_since_last_pin (gen) += size;
14406 #endif //FREE_USAGE_STATS
14407
14408         dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix", 
14409             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14410             generation_allocation_context_start_region (gen)));
14411
14412         assert (result + pad);
14413         return result + pad;
14414     }
14415 }
14416
14417 inline int power (int x, int y)
14418 {
14419     int z = 1;
14420     for (int i = 0; i < y; i++)
14421     {
14422         z = z*x;
14423     }
14424     return z;
14425 }
14426
14427 int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation, 
14428                                            int n_initial,
14429                                            BOOL* blocking_collection_p
14430                                            STRESS_HEAP_ARG(int n_original))
14431 {
14432     int n = n_initial;
14433 #ifdef MULTIPLE_HEAPS
14434     BOOL blocking_p = *blocking_collection_p;
14435     if (!blocking_p)
14436     {
14437         for (int i = 0; i < n_heaps; i++)
14438         {
14439             if (g_heaps[i]->last_gc_before_oom)
14440             {
14441                 dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
14442                 *blocking_collection_p = TRUE;
14443                 break;
14444             }
14445         }
14446     }
14447 #endif //MULTIPLE_HEAPS
14448
14449     if (should_evaluate_elevation && (n == max_generation))
14450     {
14451         dprintf (GTC_LOG, ("lock: %d(%d)", 
14452             (settings.should_lock_elevation ? 1 : 0), 
14453             settings.elevation_locked_count));
14454
14455         if (settings.should_lock_elevation)
14456         {
14457             settings.elevation_locked_count++;
14458             if (settings.elevation_locked_count == 6)
14459             {
14460                 settings.elevation_locked_count = 0;
14461             }
14462             else
14463             {
14464                 n = max_generation - 1;
14465                 settings.elevation_reduced = TRUE;
14466             }
14467         }
14468         else
14469         {
14470             settings.elevation_locked_count = 0;
14471         }
14472     }
14473     else
14474     {
14475         settings.should_lock_elevation = FALSE;
14476         settings.elevation_locked_count = 0;
14477     }
14478
14479 #ifdef STRESS_HEAP
14480 #ifdef BACKGROUND_GC
14481     // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14482     // generations to be collected,
14483
14484     // [LOCALGC TODO] STRESS_HEAP is not defined for a standalone GC so there are multiple
14485     // things that need to be fixed in this code block.
14486     if (n_original != max_generation &&
14487         g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
14488     {
14489 #ifndef FEATURE_REDHAWK
14490         // for the GC stress mix mode throttle down gen2 collections
14491         if (g_pConfig->IsGCStressMix())
14492         {
14493             size_t current_gc_count = 0;
14494
14495 #ifdef MULTIPLE_HEAPS
14496             current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
14497 #else
14498             current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
14499 #endif //MULTIPLE_HEAPS
14500             // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
14501             if ((current_gc_count % 10) == 0)
14502             {
14503                 n = max_generation;
14504             }
14505         }
14506         // for traditional GC stress
14507         else
14508 #endif // !FEATURE_REDHAWK
14509         if (*blocking_collection_p)
14510         {
14511             // We call StressHeap() a lot for Concurrent GC Stress. However,
14512             // if we can not do a concurrent collection, no need to stress anymore.
14513             // @TODO: Enable stress when the memory pressure goes down again
14514             GCStressPolicy::GlobalDisable();
14515         }
14516         else
14517         {
14518             n = max_generation;
14519         }
14520     }
14521 #endif //BACKGROUND_GC
14522 #endif //STRESS_HEAP
14523
14524     return n;
14525 }
14526
14527 inline
14528 size_t get_survived_size (gc_history_per_heap* hist)
14529 {
14530     size_t surv_size = 0;
14531     gc_generation_data* gen_data;
14532
14533     for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14534     {
14535         gen_data = &(hist->gen_data[gen_number]); 
14536         surv_size += (gen_data->size_after - 
14537                       gen_data->free_list_space_after - 
14538                       gen_data->free_obj_space_after);
14539     }
14540
14541     return surv_size;
14542 }
14543
14544 size_t gc_heap::get_total_survived_size()
14545 {
14546     size_t total_surv_size = 0;
14547 #ifdef MULTIPLE_HEAPS
14548     for (int i = 0; i < gc_heap::n_heaps; i++)
14549     {
14550         gc_heap* hp = gc_heap::g_heaps[i];
14551         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
14552         total_surv_size += get_survived_size (current_gc_data_per_heap);
14553     }
14554 #else
14555     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14556     total_surv_size = get_survived_size (current_gc_data_per_heap);
14557 #endif //MULTIPLE_HEAPS
14558     return total_surv_size;
14559 }
14560
14561 // Gets what's allocated on both SOH and LOH that hasn't been collected.
14562 size_t gc_heap::get_current_allocated()
14563 {
14564     dynamic_data* dd = dynamic_data_of (0);
14565     size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
14566     dd = dynamic_data_of (max_generation + 1);
14567     current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
14568
14569     return current_alloc;
14570 }
14571
14572 size_t gc_heap::get_total_allocated()
14573 {
14574     size_t total_current_allocated = 0;
14575 #ifdef MULTIPLE_HEAPS
14576     for (int i = 0; i < gc_heap::n_heaps; i++)
14577     {
14578         gc_heap* hp = gc_heap::g_heaps[i];
14579         total_current_allocated += hp->get_current_allocated();
14580     }
14581 #else
14582     total_current_allocated = get_current_allocated();
14583 #endif //MULTIPLE_HEAPS
14584     return total_current_allocated;
14585 }
14586
14587 size_t gc_heap::current_generation_size (int gen_number)
14588 {
14589     dynamic_data* dd = dynamic_data_of (gen_number);
14590     size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
14591                         - dd_new_allocation (dd));
14592
14593     return gen_size;
14594 }
14595
14596 #ifdef _PREFAST_
14597 #pragma warning(push)
14598 #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
14599 #endif //_PREFAST_
14600
14601 /*
14602     This is called by when we are actually doing a GC, or when we are just checking whether
14603     we would do a full blocking GC, in which case check_only_p is TRUE.
14604
14605     The difference between calling this with check_only_p TRUE and FALSE is that when it's
14606     TRUE: 
14607             settings.reason is ignored
14608             budgets are not checked (since they are checked before this is called)
14609             it doesn't change anything non local like generation_skip_ratio
14610 */
14611 int gc_heap::generation_to_condemn (int n_initial, 
14612                                     BOOL* blocking_collection_p, 
14613                                     BOOL* elevation_requested_p,
14614                                     BOOL check_only_p)
14615 {
14616     gc_mechanisms temp_settings = settings;
14617     gen_to_condemn_tuning temp_condemn_reasons;
14618     gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
14619     gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
14620     if (!check_only_p)
14621     {
14622         if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
14623         {
14624             assert (n_initial >= 1);
14625         }
14626
14627         assert (settings.reason != reason_empty);
14628     }
14629
14630     local_condemn_reasons->init();
14631
14632     int n = n_initial;
14633     int n_alloc = n;
14634     if (heap_number == 0)
14635     {
14636         dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
14637     }
14638     int i = 0;
14639     int temp_gen = 0;
14640     BOOL low_memory_detected = g_low_memory_status;
14641     uint32_t memory_load = 0;
14642     uint64_t available_physical = 0;
14643     uint64_t available_page_file = 0;
14644     BOOL check_memory = FALSE;
14645     BOOL high_fragmentation  = FALSE;
14646     BOOL v_high_memory_load  = FALSE;
14647     BOOL high_memory_load    = FALSE;
14648     BOOL low_ephemeral_space = FALSE;
14649     BOOL evaluate_elevation  = TRUE;
14650     *elevation_requested_p   = FALSE;
14651     *blocking_collection_p   = FALSE;
14652
14653     BOOL check_max_gen_alloc = TRUE;
14654
14655 #ifdef STRESS_HEAP
14656     int orig_gen = n;
14657 #endif //STRESS_HEAP
14658
14659     if (!check_only_p)
14660     {
14661         dd_fragmentation (dynamic_data_of (0)) = 
14662             generation_free_list_space (youngest_generation) + 
14663             generation_free_obj_space (youngest_generation);
14664
14665         dd_fragmentation (dynamic_data_of (max_generation + 1)) = 
14666             generation_free_list_space (large_object_generation) + 
14667             generation_free_obj_space (large_object_generation);
14668
14669         //save new_allocation
14670         for (i = 0; i <= max_generation+1; i++)
14671         {
14672             dynamic_data* dd = dynamic_data_of (i);
14673             dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)", 
14674                             heap_number, i,
14675                             dd_new_allocation (dd),
14676                             dd_desired_allocation (dd)));
14677             dd_gc_new_allocation (dd) = dd_new_allocation (dd);
14678         }
14679
14680         local_condemn_reasons->set_gen (gen_initial, n);
14681         temp_gen = n;
14682
14683 #ifdef BACKGROUND_GC
14684         if (recursive_gc_sync::background_running_p())
14685         {
14686             dprintf (GTC_LOG, ("bgc in prog, 1"));
14687             check_max_gen_alloc = FALSE;
14688         }
14689 #endif //BACKGROUND_GC
14690
14691         if (check_max_gen_alloc)
14692         {
14693             //figure out if large objects need to be collected.
14694             if (get_new_allocation (max_generation+1) <= 0)
14695             {
14696                 n = max_generation;
14697                 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14698             }
14699         }
14700
14701         //figure out which generation ran out of allocation
14702         for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
14703         {
14704             if (get_new_allocation (i) <= 0)
14705             {
14706                 n = i;
14707             }
14708             else
14709                 break;
14710         }
14711     }
14712
14713     if (n > temp_gen)
14714     {
14715         local_condemn_reasons->set_gen (gen_alloc_budget, n);
14716     }
14717
14718     dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
14719
14720     n_alloc = n;
14721
14722 #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
14723     //time based tuning
14724     // if enough time has elapsed since the last gc
14725     // and the number of gc is too low (1/10 of lower gen) then collect
14726     // This should also be enabled if we have memory concerns
14727     int n_time_max = max_generation;
14728
14729     if (!check_only_p)
14730     {
14731         if (recursive_gc_sync::background_running_p())
14732         {
14733             n_time_max = max_generation - 1;
14734         }
14735     }
14736
14737     if ((local_settings->pause_mode == pause_interactive) ||
14738         (local_settings->pause_mode == pause_sustained_low_latency))
14739     {
14740         dynamic_data* dd0 = dynamic_data_of (0);
14741         size_t now = GetHighPrecisionTimeStamp();
14742         temp_gen = n;
14743         for (i = (temp_gen+1); i <= n_time_max; i++)
14744         {
14745             dynamic_data* dd = dynamic_data_of (i);
14746             if ((now > dd_time_clock(dd) + dd_time_clock_interval(dd)) &&
14747                 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + dd_gc_clock_interval(dd))) &&
14748                 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
14749             {
14750                 n = min (i, n_time_max);
14751                 dprintf (GTC_LOG, ("time %d", n));
14752             }
14753         }
14754         if (n > temp_gen)
14755         {
14756             local_condemn_reasons->set_gen (gen_time_tuning, n);
14757         }
14758     }
14759
14760     if (n != n_alloc)
14761     {
14762         dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
14763     }
14764 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
14765
14766     if (n < (max_generation - 1))
14767     {
14768         if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
14769         {
14770             n = max (n, max_generation - 1);
14771             local_settings->promotion = TRUE;
14772             dprintf (GTC_LOG, ("h%d: skip %d, c %d",
14773                         heap_number, generation_skip_ratio, n));
14774             local_condemn_reasons->set_condition (gen_low_card_p);
14775         }
14776     }
14777
14778     if (!check_only_p)
14779     {
14780         generation_skip_ratio = 100;
14781     }
14782
14783     if (dt_low_ephemeral_space_p (check_only_p ? 
14784                                   tuning_deciding_full_gc : 
14785                                   tuning_deciding_condemned_gen))
14786     {
14787         low_ephemeral_space = TRUE;
14788
14789         n = max (n, max_generation - 1);
14790         local_condemn_reasons->set_condition (gen_low_ephemeral_p);
14791         dprintf (GTC_LOG, ("h%d: low eph", heap_number));
14792
14793 #ifdef BACKGROUND_GC
14794         if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
14795 #endif //BACKGROUND_GC
14796         {
14797             //It is better to defragment first if we are running out of space for
14798             //the ephemeral generation but we have enough fragmentation to make up for it
14799             //in the non ephemeral generation. Essentially we are trading a gen2 for 
14800             // having to expand heap in ephemeral collections.
14801             if (dt_high_frag_p (tuning_deciding_condemned_gen, 
14802                                 max_generation - 1, 
14803                                 TRUE))
14804             {
14805                 high_fragmentation = TRUE;
14806                 local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
14807                 dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
14808             }
14809         }
14810     }
14811
14812     //figure out which ephemeral generation is too fragramented
14813     temp_gen = n;
14814     for (i = n+1; i < max_generation; i++)
14815     {
14816         if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
14817         {
14818             dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
14819             n = i;
14820         }
14821         else
14822             break;
14823     }
14824
14825     if (low_ephemeral_space)
14826     {
14827         //enable promotion
14828         local_settings->promotion = TRUE;
14829     }
14830
14831     if (n > temp_gen)
14832     {
14833         local_condemn_reasons->set_condition (gen_eph_high_frag_p);
14834     }
14835
14836     if (!check_only_p)
14837     {
14838         if (settings.pause_mode == pause_low_latency)
14839         {
14840             if (!is_induced (settings.reason))
14841             {
14842                 n = min (n, max_generation - 1);
14843                 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
14844                 evaluate_elevation = FALSE;
14845                 goto exit;
14846             }
14847         }
14848     }
14849
14850     // It's hard to catch when we get to the point that the memory load is so high
14851     // we get an induced GC from the finalizer thread so we are checking the memory load
14852     // for every gen0 GC.
14853     check_memory = (check_only_p ? 
14854                     (n >= 0) : 
14855                     ((n >= 1) || low_memory_detected));
14856
14857     if (check_memory)
14858     {
14859         //find out if we are short on memory
14860         get_memory_info (&memory_load, &available_physical, &available_page_file);
14861         if (heap_number == 0)
14862         {
14863             dprintf (GTC_LOG, ("ml: %d", memory_load));
14864         }
14865         
14866         // Need to get it early enough for all heaps to use.
14867         entry_available_physical_mem = available_physical;
14868         local_settings->entry_memory_load = memory_load;
14869
14870         // @TODO: Force compaction more often under GCSTRESS
14871         if (memory_load >= high_memory_load_th || low_memory_detected)
14872         {
14873 #ifdef SIMPLE_DPRINTF
14874             // stress log can't handle any parameter that's bigger than a void*.
14875             if (heap_number == 0)
14876             {
14877                 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d", total_physical_mem, available_physical));
14878             }
14879 #endif //SIMPLE_DPRINTF
14880
14881             high_memory_load = TRUE;
14882
14883             if (memory_load >= v_high_memory_load_th || low_memory_detected)
14884             {
14885                 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
14886                 // gen1/gen0 may take a lot more memory than gen2.
14887                 if (!high_fragmentation)
14888                 {
14889                     high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation);
14890                 }
14891                 v_high_memory_load = TRUE;
14892             }
14893             else
14894             {
14895                 if (!high_fragmentation)
14896                 {
14897                     high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, available_physical);
14898                 }
14899             }
14900
14901             if (high_fragmentation)
14902             {
14903                 if (high_memory_load)
14904                 {
14905                     local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
14906                 }
14907                 else if (v_high_memory_load)
14908                 {
14909                     local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
14910                 }
14911             }
14912         }
14913     }
14914
14915     dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
14916                  heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
14917                  high_fragmentation));
14918
14919     if (should_expand_in_full_gc)
14920     {
14921         dprintf (GTC_LOG, ("h%d: expand_in_full - BLOCK", heap_number));
14922         *blocking_collection_p = TRUE;
14923         if (!check_only_p)
14924         {
14925             should_expand_in_full_gc = FALSE;
14926         }
14927         evaluate_elevation = FALSE;
14928         n = max_generation;
14929         local_condemn_reasons->set_condition (gen_expand_fullgc_p);
14930     }
14931
14932     if (last_gc_before_oom)
14933     {
14934         dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
14935         n = max_generation;
14936         *blocking_collection_p = TRUE;
14937         if ((local_settings->reason == reason_oos_loh) ||
14938             (local_settings->reason == reason_alloc_loh))
14939             evaluate_elevation = FALSE;
14940
14941         local_condemn_reasons->set_condition (gen_before_oom);
14942     }
14943
14944     if (!check_only_p)
14945     {
14946         if (is_induced_blocking (settings.reason) && 
14947             n_initial == max_generation
14948             IN_STRESS_HEAP( && !settings.stress_induced ))
14949         {
14950             if (heap_number == 0)
14951             {
14952                 dprintf (GTC_LOG, ("induced - BLOCK"));
14953             }
14954
14955             *blocking_collection_p = TRUE;
14956             local_condemn_reasons->set_condition (gen_induced_fullgc_p);
14957             evaluate_elevation = FALSE;
14958         }
14959
14960         if (settings.reason == reason_induced_noforce)
14961         {
14962             local_condemn_reasons->set_condition (gen_induced_noforce_p);
14963             evaluate_elevation = FALSE;
14964         }
14965     }
14966
14967     if (evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
14968     {
14969         *elevation_requested_p = TRUE;
14970 #ifdef BIT64
14971         // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
14972         if (high_memory_load || v_high_memory_load)
14973         {
14974             dynamic_data* dd_max = dynamic_data_of (max_generation);
14975             if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
14976             {
14977                 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)", 
14978                     dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
14979                 n = max_generation;
14980                 local_condemn_reasons->set_condition (gen_almost_max_alloc);
14981             }
14982         }
14983
14984         if (n <= max_generation)
14985         {
14986 #endif // BIT64
14987             if (high_fragmentation)
14988             {
14989                 //elevate to max_generation
14990                 n = max_generation;
14991                 dprintf (GTC_LOG, ("h%d: f full", heap_number));
14992
14993 #ifdef BACKGROUND_GC
14994                 if (high_memory_load || v_high_memory_load)
14995                 {
14996                     // For background GC we want to do blocking collections more eagerly because we don't
14997                     // want to get into the situation where the memory load becomes high while we are in
14998                     // a background GC and we'd have to wait for the background GC to finish to start
14999                     // a blocking collection (right now the implemenation doesn't handle converting 
15000                     // a background GC to a blocking collection midway.
15001                     dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
15002                     *blocking_collection_p = TRUE;
15003                 }
15004 #else
15005                 if (v_high_memory_load)
15006                 {
15007                     dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
15008                     *blocking_collection_p = TRUE;
15009                 }
15010 #endif //BACKGROUND_GC
15011             }
15012             else
15013             {
15014                 n = max (n, max_generation - 1);
15015                 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
15016             }
15017 #ifdef BIT64
15018         }
15019 #endif // BIT64
15020     }
15021
15022     if ((n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
15023     {
15024         dprintf (GTC_LOG, ("h%d: budget %d, check 2",
15025                       heap_number, n_alloc));
15026         if (get_new_allocation (max_generation) <= 0)
15027         {
15028             dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
15029             n = max_generation;
15030             local_condemn_reasons->set_condition (gen_max_gen1);
15031         }
15032     }
15033
15034     //figure out if max_generation is too fragmented -> blocking collection
15035     if (n == max_generation)
15036     {
15037         if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
15038         {
15039             dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
15040             local_condemn_reasons->set_condition (gen_max_high_frag_p);
15041             if (local_settings->pause_mode != pause_sustained_low_latency)
15042             {
15043                 *blocking_collection_p = TRUE;
15044             }
15045         }
15046     }
15047
15048 #ifdef BACKGROUND_GC
15049     if (n == max_generation)
15050     {
15051         if (heap_number == 0)
15052         {
15053             BOOL bgc_heap_too_small = TRUE;
15054             size_t gen2size = 0;
15055             size_t gen3size = 0;
15056 #ifdef MULTIPLE_HEAPS
15057             for (int i = 0; i < n_heaps; i++)
15058             {
15059                 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) || 
15060                     ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
15061                 {
15062                     bgc_heap_too_small = FALSE;
15063                     break;
15064                 }
15065             }
15066 #else //MULTIPLE_HEAPS
15067             if ((current_generation_size (max_generation) > bgc_min_per_heap) || 
15068                 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
15069             {
15070                 bgc_heap_too_small = FALSE;
15071             }            
15072 #endif //MULTIPLE_HEAPS
15073
15074             if (bgc_heap_too_small)
15075             {
15076                 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
15077
15078 #ifdef STRESS_HEAP
15079                 // do not turn stress-induced collections into blocking GCs
15080                 if (!settings.stress_induced)
15081 #endif //STRESS_HEAP
15082                 {
15083                     *blocking_collection_p = TRUE;
15084                 }
15085
15086                 local_condemn_reasons->set_condition (gen_gen2_too_small);
15087             }
15088         }
15089     }
15090 #endif //BACKGROUND_GC
15091
15092 exit:
15093     if (!check_only_p)
15094     {
15095 #ifdef STRESS_HEAP
15096 #ifdef BACKGROUND_GC
15097         // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
15098         // generations to be collected,
15099
15100         if (orig_gen != max_generation &&
15101             g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
15102         {
15103             *elevation_requested_p = FALSE;
15104         }
15105 #endif //BACKGROUND_GC
15106 #endif //STRESS_HEAP
15107
15108         if (check_memory)
15109         {
15110             fgm_result.available_pagefile_mb = (size_t)(available_page_file / (1024 * 1024));
15111         }
15112
15113         local_condemn_reasons->set_gen (gen_final_per_heap, n);
15114         get_gc_data_per_heap()->gen_to_condemn_reasons.init (local_condemn_reasons);
15115
15116 #ifdef DT_LOG
15117         local_condemn_reasons->print (heap_number);
15118 #endif //DT_LOG
15119
15120         if ((local_settings->reason == reason_oos_soh) || 
15121             (local_settings->reason == reason_oos_loh))
15122         {
15123             assert (n >= 1);
15124         }
15125     }
15126
15127     if (n == max_generation && GCToEEInterface::ForceFullGCToBeBlocking())
15128     {
15129 #ifdef BACKGROUND_GC
15130         // do not turn stress-induced collections into blocking GCs, unless there
15131         // have already been more full BGCs than full NGCs
15132 #if 0
15133         // This exposes DevDiv 94129, so we'll leave this out for now
15134         if (!settings.stress_induced ||
15135             full_gc_counts[gc_type_blocking] <= full_gc_counts[gc_type_background])
15136 #endif // 0
15137 #endif // BACKGROUND_GC
15138         {
15139             *blocking_collection_p = TRUE;
15140         }
15141     }
15142
15143     return n;
15144 }
15145
15146 #ifdef _PREFAST_
15147 #pragma warning(pop)
15148 #endif //_PREFAST_
15149
15150 inline
15151 size_t gc_heap::min_reclaim_fragmentation_threshold (uint32_t num_heaps)
15152 {
15153     // if the memory load is higher, the threshold we'd want to collect gets lower.
15154     size_t min_mem_based_on_available = 
15155         (500 - (settings.entry_memory_load - high_memory_load_th) * 40) * 1024 * 1024 / num_heaps;
15156     size_t ten_percent_size = (size_t)((float)generation_size (max_generation) * 0.10);
15157     uint64_t three_percent_mem = mem_one_percent * 3 / num_heaps;
15158
15159 #ifdef SIMPLE_DPRINTF
15160     dprintf (GTC_LOG, ("min av: %Id, 10%% gen2: %Id, 3%% mem: %I64d", 
15161         min_mem_based_on_available, ten_percent_size, three_percent_mem));
15162 #endif //SIMPLE_DPRINTF
15163     return (size_t)(min (min_mem_based_on_available, min (ten_percent_size, three_percent_mem)));
15164 }
15165
15166 inline
15167 uint64_t gc_heap::min_high_fragmentation_threshold(uint64_t available_mem, uint32_t num_heaps)
15168 {
15169     return min (available_mem, (256*1024*1024)) / num_heaps;
15170 }
15171
15172 enum {
15173 CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
15174 };
15175
15176
15177 #ifdef BACKGROUND_GC
15178 void gc_heap::init_background_gc ()
15179 {
15180     //reset the allocation so foreground gc can allocate into older (max_generation) generation
15181     generation* gen = generation_of (max_generation);
15182     generation_allocation_pointer (gen)= 0;
15183     generation_allocation_limit (gen) = 0;
15184     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
15185
15186     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
15187
15188     //reset the plan allocation for each segment
15189     for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
15190         seg = heap_segment_next_rw (seg))
15191     {
15192         heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
15193     }
15194
15195     if (heap_number == 0)
15196     {
15197         dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix", 
15198             heap_number,
15199             background_saved_lowest_address, 
15200             background_saved_highest_address));
15201     }
15202
15203     gc_lh_block_event.Reset();
15204 }
15205
15206 #endif //BACKGROUND_GC
15207
15208 inline
15209 void fire_drain_mark_list_event (size_t mark_list_objects)
15210 {
15211     FIRE_EVENT(BGCDrainMark, mark_list_objects);
15212 }
15213
15214 inline
15215 void fire_revisit_event (size_t dirtied_pages, 
15216                          size_t marked_objects,
15217                          BOOL large_objects_p)
15218 {
15219     FIRE_EVENT(BGCRevisit, dirtied_pages, marked_objects, large_objects_p);
15220 }
15221
15222 inline
15223 void fire_overflow_event (uint8_t* overflow_min,
15224                           uint8_t* overflow_max,
15225                           size_t marked_objects, 
15226                           int large_objects_p)
15227 {
15228     FIRE_EVENT(BGCOverflow, (uint64_t)overflow_min, (uint64_t)overflow_max, marked_objects, large_objects_p);
15229 }
15230
15231 void gc_heap::concurrent_print_time_delta (const char* msg)
15232 {
15233 #ifdef TRACE_GC
15234     size_t current_time = GetHighPrecisionTimeStamp();
15235     size_t elapsed_time = current_time - time_bgc_last;
15236     time_bgc_last = current_time;
15237
15238     dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
15239 #else
15240     UNREFERENCED_PARAMETER(msg);
15241 #endif //TRACE_GC
15242 }
15243
15244 void gc_heap::free_list_info (int gen_num, const char* msg)
15245 {
15246     UNREFERENCED_PARAMETER(gen_num);
15247 #if defined (BACKGROUND_GC) && defined (TRACE_GC)
15248     dprintf (3, ("h%d: %s", heap_number, msg));
15249     for (int i = 0; i <= (max_generation + 1); i++)
15250     {
15251         generation* gen = generation_of (i);
15252         if ((generation_allocation_size (gen) == 0) && 
15253             (generation_free_list_space (gen) == 0) && 
15254             (generation_free_obj_space (gen) == 0))
15255         {
15256             // don't print if everything is 0.
15257         }
15258         else
15259         {
15260             dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
15261                 heap_number, i, 
15262                 generation_allocation_size (gen), 
15263                 generation_free_list_space (gen), 
15264                 generation_free_obj_space (gen)));
15265         }
15266     }
15267 #else
15268     UNREFERENCED_PARAMETER(msg);
15269 #endif // BACKGROUND_GC && TRACE_GC
15270 }
15271
15272 void gc_heap::update_collection_counts_for_no_gc()
15273 {
15274     assert (settings.pause_mode == pause_no_gc);
15275
15276     settings.condemned_generation = max_generation;
15277 #ifdef MULTIPLE_HEAPS
15278     for (int i = 0; i < n_heaps; i++)
15279         g_heaps[i]->update_collection_counts();
15280 #else //MULTIPLE_HEAPS
15281     update_collection_counts();
15282 #endif //MULTIPLE_HEAPS
15283
15284     full_gc_counts[gc_type_blocking]++;
15285 }
15286
15287 BOOL gc_heap::should_proceed_with_gc()
15288 {
15289     if (gc_heap::settings.pause_mode == pause_no_gc)
15290     {
15291         if (current_no_gc_region_info.started)
15292         {
15293             // The no_gc mode was already in progress yet we triggered another GC,
15294             // this effectively exits the no_gc mode.
15295             restore_data_for_no_gc();
15296         }
15297         else
15298             return should_proceed_for_no_gc();
15299     }
15300
15301     return TRUE;
15302 }
15303
15304 //internal part of gc used by the serial and concurrent version
15305 void gc_heap::gc1()
15306 {
15307 #ifdef BACKGROUND_GC
15308     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15309 #endif //BACKGROUND_GC
15310
15311 #ifdef TIME_GC
15312     mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
15313 #endif //TIME_GC
15314
15315     verify_soh_segment_list();
15316
15317     int n = settings.condemned_generation;
15318
15319     update_collection_counts ();
15320
15321 #ifdef BACKGROUND_GC
15322     bgc_alloc_lock->check();
15323 #endif //BACKGROUND_GC
15324
15325     free_list_info (max_generation, "beginning");
15326
15327     vm_heap->GcCondemnedGeneration = settings.condemned_generation;
15328
15329     assert (g_gc_card_table == card_table);
15330
15331 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
15332     assert (g_gc_card_bundle_table == card_bundle_table);
15333 #endif    
15334
15335     {
15336         if (n == max_generation)
15337         {
15338             gc_low = lowest_address;
15339             gc_high = highest_address;
15340         }
15341         else
15342         {
15343             gc_low = generation_allocation_start (generation_of (n));
15344             gc_high = heap_segment_reserved (ephemeral_heap_segment);
15345         }   
15346 #ifdef BACKGROUND_GC
15347         if (settings.concurrent)
15348         {
15349 #ifdef TRACE_GC
15350             time_bgc_last = GetHighPrecisionTimeStamp();
15351 #endif //TRACE_GC
15352
15353             FIRE_EVENT(BGCBegin);
15354
15355             concurrent_print_time_delta ("BGC");
15356
15357 //#ifdef WRITE_WATCH
15358             //reset_write_watch (FALSE);
15359 //#endif //WRITE_WATCH
15360
15361             concurrent_print_time_delta ("RW");
15362             background_mark_phase();
15363             free_list_info (max_generation, "after mark phase");
15364             
15365             background_sweep();
15366             free_list_info (max_generation, "after sweep phase");
15367         }
15368         else
15369 #endif //BACKGROUND_GC
15370         {
15371             mark_phase (n, FALSE);
15372
15373             GCScan::GcRuntimeStructuresValid (FALSE);
15374             plan_phase (n);
15375             GCScan::GcRuntimeStructuresValid (TRUE);
15376         }
15377     }
15378
15379     size_t end_gc_time = GetHighPrecisionTimeStamp();
15380 //    printf ("generation: %d, elapsed time: %Id\n", n,  end_gc_time - dd_time_clock (dynamic_data_of (0)));
15381
15382     //adjust the allocation size from the pinned quantities. 
15383     for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
15384     {
15385         generation* gn = generation_of (gen_number);
15386         if (settings.compaction)
15387         {
15388             generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
15389             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
15390         }
15391         else
15392         {
15393             generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
15394             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
15395         }
15396         generation_pinned_allocation_sweep_size (gn) = 0;
15397         generation_pinned_allocation_compact_size (gn) = 0;
15398     }
15399
15400 #ifdef BACKGROUND_GC
15401     if (settings.concurrent)
15402     {
15403         dynamic_data* dd = dynamic_data_of (n);
15404         dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15405
15406         free_list_info (max_generation, "after computing new dynamic data");
15407
15408         gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
15409
15410         for (int gen_number = 0; gen_number < max_generation; gen_number++)
15411         {
15412             dprintf (2, ("end of BGC: gen%d new_alloc: %Id", 
15413                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15414             current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
15415             current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15416             current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15417         }
15418     }
15419     else
15420 #endif //BACKGROUND_GC
15421     {
15422         free_list_info (max_generation, "end");
15423         for (int gen_number = 0; gen_number <= n; gen_number++)
15424         {
15425             dynamic_data* dd = dynamic_data_of (gen_number);
15426             dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15427             compute_new_dynamic_data (gen_number);
15428         }
15429
15430         if (n != max_generation)
15431         {
15432             int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1));
15433             for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++)
15434             {
15435                 get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number);
15436                 get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15437                 get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15438             }
15439         }
15440
15441         get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100);
15442
15443         free_list_info (max_generation, "after computing new dynamic data");
15444         
15445         if (heap_number == 0)
15446         {
15447             dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms", 
15448                 dd_collection_count (dynamic_data_of (0)), 
15449                 settings.condemned_generation,
15450                 dd_gc_elapsed_time (dynamic_data_of (0))));
15451         }
15452
15453         for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
15454         {
15455             dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id", 
15456                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15457         }
15458     }
15459
15460     if (n < max_generation)
15461     {
15462         compute_promoted_allocation (1 + n);
15463
15464         dynamic_data* dd = dynamic_data_of (1 + n);
15465         size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) + 
15466                                    generation_free_obj_space (generation_of (1 + n));
15467
15468 #ifdef BACKGROUND_GC
15469         if (current_c_gc_state != c_gc_state_planning)
15470 #endif //BACKGROUND_GC
15471         {
15472             if (settings.promotion)
15473             {
15474                 dd_fragmentation (dd) = new_fragmentation;
15475             }
15476             else
15477             {
15478                 //assert (dd_fragmentation (dd) == new_fragmentation);
15479             }
15480         }
15481     }
15482
15483 #ifdef BACKGROUND_GC
15484     if (!settings.concurrent)
15485 #endif //BACKGROUND_GC
15486     {
15487 #ifndef FEATURE_REDHAWK
15488         // GCToEEInterface::IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
15489         assert(GCToEEInterface::IsGCThread());
15490 #endif // FEATURE_REDHAWK
15491         adjust_ephemeral_limits();
15492     }
15493
15494 #ifdef BACKGROUND_GC
15495     assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
15496     assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
15497 #endif //BACKGROUND_GC
15498
15499     if (fgn_maxgen_percent)
15500     {
15501         if (settings.condemned_generation == (max_generation - 1))
15502         {
15503             check_for_full_gc (max_generation - 1, 0);
15504         }
15505         else if (settings.condemned_generation == max_generation)
15506         {
15507             if (full_gc_approach_event_set 
15508 #ifdef MULTIPLE_HEAPS
15509                 && (heap_number == 0)
15510 #endif //MULTIPLE_HEAPS
15511                 )
15512             {
15513                 dprintf (2, ("FGN-GC: setting gen2 end event"));
15514
15515                 full_gc_approach_event.Reset();
15516 #ifdef BACKGROUND_GC
15517                 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
15518                 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
15519 #endif //BACKGROUND_GC
15520                 full_gc_end_event.Set();
15521                 full_gc_approach_event_set = false;            
15522             }
15523         }
15524     }
15525
15526 #ifdef BACKGROUND_GC
15527     if (!settings.concurrent)
15528 #endif //BACKGROUND_GC
15529     {
15530         //decide on the next allocation quantum
15531         if (alloc_contexts_used >= 1)
15532         {
15533             allocation_quantum = Align (min ((size_t)CLR_SIZE,
15534                                             (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
15535                                             get_alignment_constant(FALSE));
15536             dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
15537         }
15538     }
15539
15540     descr_generations (FALSE);
15541
15542     verify_soh_segment_list();
15543
15544 #ifdef BACKGROUND_GC
15545     add_to_history_per_heap();
15546     if (heap_number == 0)
15547     {
15548         add_to_history();
15549     }
15550 #endif // BACKGROUND_GC
15551
15552 #ifdef GC_STATS
15553     if (GCStatistics::Enabled() && heap_number == 0)
15554         g_GCStatistics.AddGCStats(settings, 
15555             dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
15556 #endif // GC_STATS
15557
15558 #ifdef TIME_GC
15559     fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
15560              n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
15561 #endif //TIME_GC
15562
15563 #ifdef BACKGROUND_GC
15564     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15565 #endif //BACKGROUND_GC
15566
15567 #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15568     if (FALSE 
15569 #ifdef VERIFY_HEAP
15570         // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same
15571         // value. If we ever allow randomly adjusting this as the process runs,
15572         // we cannot call it this way as joins need to match - we must have the same
15573         // value for all heaps like we do with bgc_heap_walk_for_etw_p.
15574         || (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15575 #endif
15576 #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
15577         || (bgc_heap_walk_for_etw_p && settings.concurrent)
15578 #endif
15579         )
15580     {
15581 #ifdef BACKGROUND_GC
15582         bool cooperative_mode = true;
15583
15584         if (settings.concurrent)
15585         {
15586             cooperative_mode = enable_preemptive ();
15587
15588 #ifdef MULTIPLE_HEAPS
15589             bgc_t_join.join(this, gc_join_suspend_ee_verify);
15590             if (bgc_t_join.joined())
15591             {
15592                 bgc_threads_sync_event.Reset();
15593
15594                 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
15595                 bgc_t_join.restart();
15596             }
15597             if (heap_number == 0)
15598             {
15599                 suspend_EE();
15600                 bgc_threads_sync_event.Set();
15601             }
15602             else
15603             {
15604                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15605                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15606             }
15607 #else
15608             suspend_EE();
15609 #endif //MULTIPLE_HEAPS
15610
15611             //fix the allocation area so verify_heap can proceed.
15612             fix_allocation_contexts (FALSE);
15613         }
15614 #endif //BACKGROUND_GC
15615
15616 #ifdef BACKGROUND_GC
15617         assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15618 #ifdef FEATURE_EVENT_TRACE
15619         if (bgc_heap_walk_for_etw_p && settings.concurrent)
15620         {
15621             GCToEEInterface::DiagWalkBGCSurvivors(__this);
15622
15623 #ifdef MULTIPLE_HEAPS
15624             bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
15625             if (bgc_t_join.joined())
15626             {
15627                 bgc_t_join.restart();
15628             }
15629 #endif // MULTIPLE_HEAPS
15630         }
15631 #endif // FEATURE_EVENT_TRACE
15632 #endif //BACKGROUND_GC
15633
15634 #ifdef VERIFY_HEAP
15635         if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15636             verify_heap (FALSE);
15637 #endif // VERIFY_HEAP
15638
15639 #ifdef BACKGROUND_GC
15640         if (settings.concurrent)
15641         {
15642             repair_allocation_contexts (TRUE);
15643
15644 #ifdef MULTIPLE_HEAPS
15645             bgc_t_join.join(this, gc_join_restart_ee_verify);
15646             if (bgc_t_join.joined())
15647             {
15648                 bgc_threads_sync_event.Reset();
15649
15650                 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
15651                 bgc_t_join.restart();
15652             }
15653             if (heap_number == 0)
15654             {
15655                 restart_EE();
15656                 bgc_threads_sync_event.Set();
15657             }
15658             else
15659             {
15660                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15661                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15662             }
15663 #else
15664             restart_EE();
15665 #endif //MULTIPLE_HEAPS
15666
15667             disable_preemptive (cooperative_mode);
15668         }
15669 #endif //BACKGROUND_GC
15670     }
15671 #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15672
15673 #ifdef MULTIPLE_HEAPS
15674     if (!settings.concurrent)
15675     {
15676         gc_t_join.join(this, gc_join_done);
15677         if (gc_t_join.joined ())
15678         {
15679             gc_heap::internal_gc_done = false;
15680
15681             //equalize the new desired size of the generations
15682             int limit = settings.condemned_generation;
15683             if (limit == max_generation)
15684             {
15685                 limit = max_generation+1;
15686             }
15687             for (int gen = 0; gen <= limit; gen++)
15688             {
15689                 size_t total_desired = 0;
15690
15691                 for (int i = 0; i < gc_heap::n_heaps; i++)
15692                 {
15693                     gc_heap* hp = gc_heap::g_heaps[i];
15694                     dynamic_data* dd = hp->dynamic_data_of (gen);
15695                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
15696                     if (temp_total_desired < total_desired)
15697                     {
15698                         // we overflowed.
15699                         total_desired = (size_t)MAX_PTR;
15700                         break;
15701                     }
15702                     total_desired = temp_total_desired;
15703                 }
15704
15705                 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
15706                                                     get_alignment_constant ((gen != (max_generation+1))));
15707
15708                 if (gen == 0)
15709                 {
15710 #if 1 //subsumed by the linear allocation model 
15711                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15712                     // apply some smoothing.
15713                     static size_t smoothed_desired_per_heap = 0;
15714                     size_t smoothing = 3; // exponential smoothing factor
15715                     if (smoothing  > VolatileLoad(&settings.gc_index))
15716                         smoothing  = VolatileLoad(&settings.gc_index);
15717                     smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
15718                     dprintf (1, ("sn = %Id  n = %Id", smoothed_desired_per_heap, desired_per_heap));
15719                     desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
15720 #endif //0
15721
15722                     // if desired_per_heap is close to min_gc_size, trim it
15723                     // down to min_gc_size to stay in the cache
15724                     gc_heap* hp = gc_heap::g_heaps[0];
15725                     dynamic_data* dd = hp->dynamic_data_of (gen);
15726                     size_t min_gc_size = dd_min_size(dd);
15727                     // if min GC size larger than true on die cache, then don't bother
15728                     // limiting the desired size
15729                     if ((min_gc_size <= GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)) &&
15730                         desired_per_heap <= 2*min_gc_size)
15731                     {
15732                         desired_per_heap = min_gc_size;
15733                     }
15734 #ifdef BIT64
15735                     desired_per_heap = joined_youngest_desired (desired_per_heap);
15736                     dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
15737 #endif // BIT64
15738
15739                     gc_data_global.final_youngest_desired = desired_per_heap;
15740                 }
15741 #if 1 //subsumed by the linear allocation model 
15742                 if (gen == (max_generation + 1))
15743                 {
15744                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15745                     // apply some smoothing.
15746                     static size_t smoothed_desired_per_heap_loh = 0;
15747                     size_t smoothing = 3; // exponential smoothing factor
15748                     size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
15749                     if (smoothing  > loh_count)
15750                         smoothing  = loh_count;
15751                     smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
15752                     dprintf( 2, ("smoothed_desired_per_heap_loh  = %Id  desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
15753                     desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
15754                 }
15755 #endif //0
15756                 for (int i = 0; i < gc_heap::n_heaps; i++)
15757                 {
15758                     gc_heap* hp = gc_heap::g_heaps[i];
15759                     dynamic_data* dd = hp->dynamic_data_of (gen);
15760                     dd_desired_allocation (dd) = desired_per_heap;
15761                     dd_gc_new_allocation (dd) = desired_per_heap;
15762                     dd_new_allocation (dd) = desired_per_heap;
15763
15764                     if (gen == 0)
15765                     {
15766                         hp->fgn_last_alloc = desired_per_heap;
15767                     }
15768                 }
15769             }
15770
15771 #ifdef FEATURE_LOH_COMPACTION
15772             BOOL all_heaps_compacted_p = TRUE;
15773 #endif //FEATURE_LOH_COMPACTION
15774             for (int i = 0; i < gc_heap::n_heaps; i++)
15775             {
15776                 gc_heap* hp = gc_heap::g_heaps[i];
15777                 hp->decommit_ephemeral_segment_pages();
15778                 hp->rearrange_large_heap_segments();
15779 #ifdef FEATURE_LOH_COMPACTION
15780                 all_heaps_compacted_p &= hp->loh_compacted_p;
15781 #endif //FEATURE_LOH_COMPACTION
15782             }
15783
15784 #ifdef FEATURE_LOH_COMPACTION
15785             check_loh_compact_mode (all_heaps_compacted_p);
15786 #endif //FEATURE_LOH_COMPACTION
15787
15788             fire_pevents();
15789
15790             gc_t_join.restart();
15791         }
15792         alloc_context_count = 0;
15793         heap_select::mark_heap (heap_number);
15794     }
15795
15796 #else
15797     gc_data_global.final_youngest_desired = 
15798         dd_desired_allocation (dynamic_data_of (0));
15799
15800     check_loh_compact_mode (loh_compacted_p);
15801
15802     decommit_ephemeral_segment_pages();
15803     fire_pevents();
15804
15805     if (!(settings.concurrent))
15806     {
15807         rearrange_large_heap_segments();
15808         do_post_gc();
15809     }
15810
15811 #ifdef BACKGROUND_GC
15812     recover_bgc_settings();
15813 #endif //BACKGROUND_GC
15814 #endif //MULTIPLE_HEAPS
15815 }
15816
15817 void gc_heap::save_data_for_no_gc()
15818 {
15819     current_no_gc_region_info.saved_pause_mode = settings.pause_mode;
15820 #ifdef MULTIPLE_HEAPS
15821     // This is to affect heap balancing. 
15822     for (int i = 0; i < n_heaps; i++)
15823     {
15824         current_no_gc_region_info.saved_gen0_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (0));
15825         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = min_balance_threshold;
15826         current_no_gc_region_info.saved_gen3_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1));
15827         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = 0;
15828     }
15829 #endif //MULTIPLE_HEAPS
15830 }
15831
15832 void gc_heap::restore_data_for_no_gc()
15833 {
15834     gc_heap::settings.pause_mode = current_no_gc_region_info.saved_pause_mode;
15835 #ifdef MULTIPLE_HEAPS
15836     for (int i = 0; i < n_heaps; i++)
15837     {
15838         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = current_no_gc_region_info.saved_gen0_min_size;
15839         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = current_no_gc_region_info.saved_gen3_min_size;
15840     }
15841 #endif //MULTIPLE_HEAPS
15842 }
15843
15844 start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size,
15845                                                              BOOL loh_size_known, 
15846                                                              uint64_t loh_size,
15847                                                              BOOL disallow_full_blocking)
15848 {
15849     if (current_no_gc_region_info.started)
15850     {
15851         return start_no_gc_in_progress;
15852     }
15853
15854     start_no_gc_region_status status = start_no_gc_success;
15855
15856     save_data_for_no_gc();
15857     settings.pause_mode = pause_no_gc;
15858     current_no_gc_region_info.start_status = start_no_gc_success;
15859
15860     uint64_t allocation_no_gc_loh = 0;
15861     uint64_t allocation_no_gc_soh = 0;
15862     assert(total_size != 0);
15863     if (loh_size_known)
15864     {
15865         assert(loh_size != 0);
15866         assert(loh_size <= total_size);
15867         allocation_no_gc_loh = loh_size;
15868         allocation_no_gc_soh = total_size - loh_size;
15869     }
15870     else
15871     {
15872         allocation_no_gc_soh = total_size;
15873         allocation_no_gc_loh = total_size;
15874     }
15875
15876     int soh_align_const = get_alignment_constant (TRUE);
15877     size_t max_soh_allocated = soh_segment_size - segment_info_size - eph_gen_starts_size;
15878     size_t size_per_heap = 0;
15879     const double scale_factor = 1.05;
15880
15881     int num_heaps = 1;
15882 #ifdef MULTIPLE_HEAPS
15883     num_heaps = n_heaps;
15884 #endif // MULTIPLE_HEAPS
15885
15886     uint64_t total_allowed_soh_allocation = max_soh_allocated * num_heaps;
15887     // [LOCALGC TODO]
15888     // In theory, the upper limit here is the physical memory of the machine, not
15889     // SIZE_T_MAX. This is not true today because total_physical_mem can be
15890     // larger than SIZE_T_MAX if running in wow64 on a machine with more than
15891     // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing
15892     // more freely between branches, it would be good to clean this up to use
15893     // total_physical_mem instead of SIZE_T_MAX.
15894     assert(total_allowed_soh_allocation <= SIZE_T_MAX);
15895     uint64_t total_allowed_loh_allocation = SIZE_T_MAX;
15896     uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast<uint64_t>(total_allowed_soh_allocation / scale_factor) : 0;
15897     uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast<uint64_t>(total_allowed_loh_allocation / scale_factor) : 0;
15898
15899     if (allocation_no_gc_soh > total_allowed_soh_alloc_scaled ||
15900         allocation_no_gc_loh > total_allowed_loh_alloc_scaled)
15901     {
15902         status = start_no_gc_too_large;
15903         goto done;
15904     }
15905
15906     if (allocation_no_gc_soh > 0)
15907     {
15908         allocation_no_gc_soh = static_cast<uint64_t>(allocation_no_gc_soh * scale_factor);
15909         allocation_no_gc_soh = min (allocation_no_gc_soh, total_allowed_soh_alloc_scaled);
15910     }
15911
15912     if (allocation_no_gc_loh > 0)
15913     {
15914         allocation_no_gc_loh = static_cast<uint64_t>(allocation_no_gc_loh * scale_factor);
15915         allocation_no_gc_loh = min (allocation_no_gc_loh, total_allowed_loh_alloc_scaled);
15916     }
15917
15918     if (disallow_full_blocking)
15919         current_no_gc_region_info.minimal_gc_p = TRUE;
15920
15921     if (allocation_no_gc_soh != 0)
15922     {
15923         current_no_gc_region_info.soh_allocation_size = static_cast<size_t>(allocation_no_gc_soh);
15924         size_per_heap = current_no_gc_region_info.soh_allocation_size;
15925 #ifdef MULTIPLE_HEAPS
15926         size_per_heap /= n_heaps;
15927         for (int i = 0; i < n_heaps; i++)
15928         {
15929             // due to heap balancing we need to allow some room before we even look to balance to another heap.
15930             g_heaps[i]->soh_allocation_no_gc = min (Align ((size_per_heap + min_balance_threshold), soh_align_const), max_soh_allocated);
15931         }
15932 #else //MULTIPLE_HEAPS
15933         soh_allocation_no_gc = min (Align (size_per_heap, soh_align_const), max_soh_allocated);
15934 #endif //MULTIPLE_HEAPS
15935     }
15936
15937     if (allocation_no_gc_loh != 0)
15938     {
15939         current_no_gc_region_info.loh_allocation_size = static_cast<size_t>(allocation_no_gc_loh);
15940         size_per_heap = current_no_gc_region_info.loh_allocation_size;
15941 #ifdef MULTIPLE_HEAPS
15942         size_per_heap /= n_heaps;
15943         for (int i = 0; i < n_heaps; i++)
15944             g_heaps[i]->loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
15945 #else //MULTIPLE_HEAPS
15946         loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
15947 #endif //MULTIPLE_HEAPS
15948     }
15949
15950 done:
15951     if (status != start_no_gc_success)
15952         restore_data_for_no_gc();
15953     return status;
15954 }
15955
15956 void gc_heap::handle_failure_for_no_gc()
15957 {
15958     gc_heap::restore_data_for_no_gc();
15959     // sets current_no_gc_region_info.started to FALSE here.
15960     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
15961 }
15962
15963 start_no_gc_region_status gc_heap::get_start_no_gc_region_status()
15964 {
15965     return current_no_gc_region_info.start_status;
15966 }
15967
15968 void gc_heap::record_gcs_during_no_gc()
15969 {
15970     if (current_no_gc_region_info.started)
15971     {
15972         current_no_gc_region_info.num_gcs++;
15973         if (is_induced (settings.reason))
15974             current_no_gc_region_info.num_gcs_induced++;
15975     }
15976 }
15977
15978 BOOL gc_heap::find_loh_free_for_no_gc()
15979 {
15980     allocator* loh_allocator = generation_allocator (generation_of (max_generation + 1));
15981     size_t sz_list = loh_allocator->first_bucket_size();
15982     size_t size = loh_allocation_no_gc;
15983     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
15984     {
15985         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
15986         {
15987             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
15988             while (free_list)
15989             {
15990                 size_t free_list_size = unused_array_size(free_list);
15991
15992                 if (free_list_size > loh_allocation_no_gc)
15993                 {
15994                     dprintf (3, ("free item %Ix(%Id) for no gc", (size_t)free_list, free_list_size));
15995                     return TRUE;
15996                 }
15997
15998                 free_list = free_list_slot (free_list); 
15999             }
16000         }
16001         sz_list = sz_list * 2;
16002     }
16003
16004     return FALSE;
16005 }
16006
16007 BOOL gc_heap::find_loh_space_for_no_gc()
16008 {
16009     saved_loh_segment_no_gc = 0;
16010
16011     if (find_loh_free_for_no_gc())
16012         return TRUE;
16013
16014     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16015
16016     while (seg)
16017     {
16018         size_t remaining = heap_segment_reserved (seg) - heap_segment_allocated (seg);
16019         if (remaining >= loh_allocation_no_gc)
16020         {
16021             saved_loh_segment_no_gc = seg;
16022             break;
16023         }
16024         seg = heap_segment_next (seg);
16025     }
16026
16027     if (!saved_loh_segment_no_gc && current_no_gc_region_info.minimal_gc_p)
16028     {
16029         // If no full GC is allowed, we try to get a new seg right away.
16030         saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc)
16031 #ifdef MULTIPLE_HEAPS
16032                                                       , this
16033 #endif //MULTIPLE_HEAPS
16034                                                       );
16035     }
16036
16037     return (saved_loh_segment_no_gc != 0);
16038 }
16039
16040 BOOL gc_heap::loh_allocated_for_no_gc()
16041 {
16042     if (!saved_loh_segment_no_gc)
16043         return FALSE;
16044
16045     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16046     do 
16047     {
16048         if (seg == saved_loh_segment_no_gc)
16049         {
16050             return FALSE;
16051         }
16052         seg = heap_segment_next (seg);
16053     } while (seg);
16054
16055     return TRUE;
16056 }
16057
16058 BOOL gc_heap::commit_loh_for_no_gc (heap_segment* seg)
16059 {
16060     uint8_t* end_committed = heap_segment_allocated (seg) + loh_allocation_no_gc;
16061     assert (end_committed <= heap_segment_reserved (seg));
16062     return (grow_heap_segment (seg, end_committed));
16063 }
16064
16065 void gc_heap::thread_no_gc_loh_segments()
16066 {
16067 #ifdef MULTIPLE_HEAPS
16068     for (int i = 0; i < n_heaps; i++)
16069     {
16070         gc_heap* hp = g_heaps[i];
16071         if (hp->loh_allocated_for_no_gc())
16072         {
16073             hp->thread_loh_segment (hp->saved_loh_segment_no_gc);
16074             hp->saved_loh_segment_no_gc = 0;
16075         }
16076     }
16077 #else //MULTIPLE_HEAPS
16078     if (loh_allocated_for_no_gc())
16079     {
16080         thread_loh_segment (saved_loh_segment_no_gc);
16081         saved_loh_segment_no_gc = 0;
16082     }
16083 #endif //MULTIPLE_HEAPS    
16084 }
16085
16086 void gc_heap::set_loh_allocations_for_no_gc()
16087 {
16088     if (current_no_gc_region_info.loh_allocation_size != 0)
16089     {
16090         dynamic_data* dd = dynamic_data_of (max_generation + 1);
16091         dd_new_allocation (dd) = loh_allocation_no_gc;
16092         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16093     }
16094 }
16095
16096 void gc_heap::set_soh_allocations_for_no_gc()
16097 {
16098     if (current_no_gc_region_info.soh_allocation_size != 0)
16099     {
16100         dynamic_data* dd = dynamic_data_of (0);
16101         dd_new_allocation (dd) = soh_allocation_no_gc;
16102         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16103 #ifdef MULTIPLE_HEAPS
16104         alloc_context_count = 0;
16105 #endif //MULTIPLE_HEAPS
16106     }
16107 }
16108
16109 void gc_heap::set_allocations_for_no_gc()
16110 {
16111 #ifdef MULTIPLE_HEAPS
16112     for (int i = 0; i < n_heaps; i++)
16113     {
16114         gc_heap* hp = g_heaps[i];
16115         hp->set_loh_allocations_for_no_gc();
16116         hp->set_soh_allocations_for_no_gc();
16117     }
16118 #else //MULTIPLE_HEAPS
16119     set_loh_allocations_for_no_gc();
16120     set_soh_allocations_for_no_gc();
16121 #endif //MULTIPLE_HEAPS
16122 }
16123
16124 BOOL gc_heap::should_proceed_for_no_gc()
16125 {
16126     BOOL gc_requested = FALSE;
16127     BOOL loh_full_gc_requested = FALSE;
16128     BOOL soh_full_gc_requested = FALSE;
16129     BOOL no_gc_requested = FALSE;
16130     BOOL get_new_loh_segments = FALSE;
16131
16132     if (current_no_gc_region_info.soh_allocation_size)
16133     {
16134 #ifdef MULTIPLE_HEAPS
16135         for (int i = 0; i < n_heaps; i++)
16136         {
16137             gc_heap* hp = g_heaps[i];
16138             if ((size_t)(heap_segment_reserved (hp->ephemeral_heap_segment) - hp->alloc_allocated) < hp->soh_allocation_no_gc)
16139             {
16140                 gc_requested = TRUE;
16141                 break;
16142             }
16143         }
16144 #else //MULTIPLE_HEAPS
16145         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated) < soh_allocation_no_gc)
16146             gc_requested = TRUE;
16147 #endif //MULTIPLE_HEAPS
16148
16149         if (!gc_requested)
16150         {
16151 #ifdef MULTIPLE_HEAPS
16152             for (int i = 0; i < n_heaps; i++)
16153             {
16154                 gc_heap* hp = g_heaps[i];
16155                 if (!(hp->grow_heap_segment (hp->ephemeral_heap_segment, (hp->alloc_allocated + hp->soh_allocation_no_gc))))
16156                 {
16157                     soh_full_gc_requested = TRUE;
16158                     break;
16159                 }
16160             }
16161 #else //MULTIPLE_HEAPS
16162             if (!grow_heap_segment (ephemeral_heap_segment, (alloc_allocated + soh_allocation_no_gc)))
16163                 soh_full_gc_requested = TRUE;
16164 #endif //MULTIPLE_HEAPS
16165         }
16166     }
16167
16168     if (!current_no_gc_region_info.minimal_gc_p && gc_requested)
16169     {
16170         soh_full_gc_requested = TRUE;
16171     }
16172
16173     no_gc_requested = !(soh_full_gc_requested || gc_requested);
16174
16175     if (soh_full_gc_requested && current_no_gc_region_info.minimal_gc_p)
16176     {
16177         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16178         goto done;
16179     }
16180
16181     if (!soh_full_gc_requested && current_no_gc_region_info.loh_allocation_size)
16182     {
16183         // Check to see if we have enough reserved space. 
16184 #ifdef MULTIPLE_HEAPS
16185         for (int i = 0; i < n_heaps; i++)
16186         {
16187             gc_heap* hp = g_heaps[i];
16188             if (!hp->find_loh_space_for_no_gc())
16189             {
16190                 loh_full_gc_requested = TRUE;
16191                 break;
16192             }
16193         }
16194 #else //MULTIPLE_HEAPS
16195         if (!find_loh_space_for_no_gc())
16196             loh_full_gc_requested = TRUE;
16197 #endif //MULTIPLE_HEAPS
16198
16199         // Check to see if we have committed space.
16200         if (!loh_full_gc_requested)
16201         {
16202 #ifdef MULTIPLE_HEAPS
16203             for (int i = 0; i < n_heaps; i++)
16204             {
16205                 gc_heap* hp = g_heaps[i];
16206                 if (hp->saved_loh_segment_no_gc &&!hp->commit_loh_for_no_gc (hp->saved_loh_segment_no_gc))
16207                 {
16208                     loh_full_gc_requested = TRUE;
16209                     break;
16210                 }
16211             }
16212 #else //MULTIPLE_HEAPS
16213             if (saved_loh_segment_no_gc && !commit_loh_for_no_gc (saved_loh_segment_no_gc))
16214                 loh_full_gc_requested = TRUE;
16215 #endif //MULTIPLE_HEAPS
16216         }
16217     }
16218
16219     if (loh_full_gc_requested || soh_full_gc_requested)
16220     {
16221         if (current_no_gc_region_info.minimal_gc_p)
16222             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16223     }
16224
16225     no_gc_requested = !(loh_full_gc_requested || soh_full_gc_requested || gc_requested);
16226
16227     if (current_no_gc_region_info.start_status == start_no_gc_success)
16228     {
16229         if (no_gc_requested)
16230             set_allocations_for_no_gc();
16231     }
16232
16233 done:
16234
16235     if ((current_no_gc_region_info.start_status == start_no_gc_success) && !no_gc_requested)
16236         return TRUE;
16237     else
16238     {
16239         // We are done with starting the no_gc_region.
16240         current_no_gc_region_info.started = TRUE;
16241         return FALSE;
16242     }
16243 }
16244
16245 end_no_gc_region_status gc_heap::end_no_gc_region()
16246 {
16247     dprintf (1, ("end no gc called"));
16248
16249     end_no_gc_region_status status = end_no_gc_success;
16250
16251     if (!(current_no_gc_region_info.started))
16252         status = end_no_gc_not_in_progress;
16253     if (current_no_gc_region_info.num_gcs_induced)
16254         status = end_no_gc_induced;
16255     else if (current_no_gc_region_info.num_gcs)
16256         status = end_no_gc_alloc_exceeded;
16257
16258     if (settings.pause_mode == pause_no_gc)
16259         restore_data_for_no_gc();
16260
16261     // sets current_no_gc_region_info.started to FALSE here.
16262     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16263
16264     return status;
16265 }
16266
16267 //update counters
16268 void gc_heap::update_collection_counts ()
16269 {
16270     dynamic_data* dd0 = dynamic_data_of (0);
16271     dd_gc_clock (dd0) += 1;
16272
16273     size_t now = GetHighPrecisionTimeStamp();
16274
16275     for (int i = 0; i <= settings.condemned_generation;i++)
16276     {
16277         dynamic_data* dd = dynamic_data_of (i);
16278         dd_collection_count (dd)++;
16279         //this is needed by the linear allocation model
16280         if (i == max_generation)
16281             dd_collection_count (dynamic_data_of (max_generation+1))++;
16282         dd_gc_clock (dd) = dd_gc_clock (dd0);
16283         dd_time_clock (dd) = now;
16284     }
16285 }
16286
16287 BOOL gc_heap::expand_soh_with_minimal_gc()
16288 {
16289     if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) >= soh_allocation_no_gc)
16290         return TRUE;
16291
16292     heap_segment* new_seg = soh_get_segment_to_expand();
16293     if (new_seg)
16294     {
16295         if (g_gc_card_table != card_table)
16296             copy_brick_card_table();
16297
16298         settings.promotion = TRUE;
16299         settings.demotion = FALSE;
16300         ephemeral_promotion = TRUE;
16301         int condemned_gen_number = max_generation - 1;
16302
16303         generation* gen = 0;
16304         int align_const = get_alignment_constant (TRUE);
16305
16306         for (int i = 0; i <= condemned_gen_number; i++)
16307         {
16308             gen = generation_of (i);
16309             saved_ephemeral_plan_start[i] = generation_allocation_start (gen);
16310             saved_ephemeral_plan_start_size[i] = Align (size (generation_allocation_start (gen)), align_const);
16311         }
16312
16313         // We do need to clear the bricks here as we are converting a bunch of ephemeral objects to gen2
16314         // and need to make sure that there are no left over bricks from the previous GCs for the space 
16315         // we just used for gen0 allocation. We will need to go through the bricks for these objects for 
16316         // ephemeral GCs later.
16317         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
16318              b < brick_of (align_on_brick (heap_segment_allocated (ephemeral_heap_segment)));
16319              b++)
16320         {
16321             set_brick (b, -1);
16322         }
16323
16324         size_t ephemeral_size = (heap_segment_allocated (ephemeral_heap_segment) - 
16325                                 generation_allocation_start (generation_of (max_generation - 1)));
16326         heap_segment_next (ephemeral_heap_segment) = new_seg;
16327         ephemeral_heap_segment = new_seg;
16328         uint8_t*  start = heap_segment_mem (ephemeral_heap_segment);
16329
16330         for (int i = condemned_gen_number; i >= 0; i--)
16331         {
16332             gen = generation_of (i);
16333             size_t gen_start_size = Align (min_obj_size);
16334             make_generation (generation_table[i], ephemeral_heap_segment, start, 0);
16335             generation_plan_allocation_start (gen) = start;
16336             generation_plan_allocation_start_size (gen) = gen_start_size;
16337             start += gen_start_size;
16338         }
16339         heap_segment_used (ephemeral_heap_segment) = start - plug_skew;
16340         heap_segment_plan_allocated (ephemeral_heap_segment) = start;
16341
16342         fix_generation_bounds (condemned_gen_number, generation_of (0));
16343
16344         dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
16345         dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));
16346
16347         adjust_ephemeral_limits();
16348         return TRUE;
16349     }
16350     else
16351         return FALSE;
16352 }
16353
16354 // Only to be done on the thread that calls restart in a join for server GC
16355 // and reset the oom status per heap.
16356 void gc_heap::check_and_set_no_gc_oom()
16357 {
16358 #ifdef MULTIPLE_HEAPS
16359     for (int i = 0; i < n_heaps; i++)
16360     {
16361         gc_heap* hp = g_heaps[i];
16362         if (hp->no_gc_oom_p)
16363         {
16364             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16365             hp->no_gc_oom_p = false;
16366         }
16367     }
16368 #else
16369     if (no_gc_oom_p)
16370     {
16371         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16372         no_gc_oom_p = false;
16373     }
16374 #endif //MULTIPLE_HEAPS
16375 }
16376
16377 void gc_heap::allocate_for_no_gc_after_gc()
16378 {
16379     if (current_no_gc_region_info.minimal_gc_p)
16380         repair_allocation_contexts (TRUE);
16381
16382     no_gc_oom_p = false;
16383
16384     if (current_no_gc_region_info.start_status != start_no_gc_no_memory)
16385     {
16386         if (current_no_gc_region_info.soh_allocation_size != 0)
16387         {
16388             if (((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) < soh_allocation_no_gc) ||
16389                 (!grow_heap_segment (ephemeral_heap_segment, (heap_segment_allocated (ephemeral_heap_segment) + soh_allocation_no_gc))))
16390             {
16391                 no_gc_oom_p = true;
16392             }
16393
16394 #ifdef MULTIPLE_HEAPS
16395             gc_t_join.join(this, gc_join_after_commit_soh_no_gc);
16396             if (gc_t_join.joined())
16397             {
16398 #endif //MULTIPLE_HEAPS
16399
16400                 check_and_set_no_gc_oom();
16401
16402 #ifdef MULTIPLE_HEAPS
16403                 gc_t_join.restart();
16404             }
16405 #endif //MULTIPLE_HEAPS
16406         }
16407
16408         if ((current_no_gc_region_info.start_status == start_no_gc_success) &&
16409             !(current_no_gc_region_info.minimal_gc_p) && 
16410             (current_no_gc_region_info.loh_allocation_size != 0))
16411         {
16412             gc_policy = policy_compact;
16413             saved_loh_segment_no_gc = 0;
16414
16415             if (!find_loh_free_for_no_gc())
16416             {
16417                 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16418                 BOOL found_seg_p = FALSE;
16419                 while (seg)
16420                 {
16421                     if ((size_t)(heap_segment_reserved (seg) - heap_segment_allocated (seg)) >= loh_allocation_no_gc)
16422                     {
16423                         found_seg_p = TRUE;
16424                         if (!commit_loh_for_no_gc (seg))
16425                         {
16426                             no_gc_oom_p = true;
16427                             break;
16428                         }
16429                     }
16430                     seg = heap_segment_next (seg);
16431                 }
16432
16433                 if (!found_seg_p)
16434                     gc_policy = policy_expand;
16435             }
16436
16437 #ifdef MULTIPLE_HEAPS
16438             gc_t_join.join(this, gc_join_expand_loh_no_gc);
16439             if (gc_t_join.joined())
16440             {
16441                 check_and_set_no_gc_oom();
16442
16443                 if (current_no_gc_region_info.start_status == start_no_gc_success)
16444                 {
16445                     for (int i = 0; i < n_heaps; i++)
16446                     {
16447                         gc_heap* hp = g_heaps[i];
16448                         if (hp->gc_policy == policy_expand)
16449                         {
16450                             hp->saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc), hp);
16451                             if (!(hp->saved_loh_segment_no_gc))
16452                             {
16453                                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16454                                 break;
16455                             }
16456                         }
16457                     }
16458                 }
16459
16460                 gc_t_join.restart();
16461             }
16462 #else //MULTIPLE_HEAPS
16463             check_and_set_no_gc_oom();
16464
16465             if ((current_no_gc_region_info.start_status == start_no_gc_success) && (gc_policy == policy_expand))
16466             {
16467                 saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc));
16468                 if (!saved_loh_segment_no_gc)
16469                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16470             }
16471 #endif //MULTIPLE_HEAPS
16472
16473             if ((current_no_gc_region_info.start_status == start_no_gc_success) && saved_loh_segment_no_gc)
16474             {
16475                 if (!commit_loh_for_no_gc (saved_loh_segment_no_gc))
16476                 {
16477                     no_gc_oom_p = true;
16478                 }
16479             }
16480         }
16481     }
16482
16483 #ifdef MULTIPLE_HEAPS
16484     gc_t_join.join(this, gc_join_final_no_gc);
16485     if (gc_t_join.joined())
16486     {
16487 #endif //MULTIPLE_HEAPS
16488
16489         check_and_set_no_gc_oom();
16490
16491         if (current_no_gc_region_info.start_status == start_no_gc_success)
16492         {
16493             set_allocations_for_no_gc();
16494             current_no_gc_region_info.started = TRUE;
16495         }
16496
16497 #ifdef MULTIPLE_HEAPS
16498         gc_t_join.restart();
16499     }
16500 #endif //MULTIPLE_HEAPS
16501 }
16502
16503 void gc_heap::init_records()
16504 {
16505     memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
16506     gc_data_per_heap.heap_index = heap_number;
16507     if (heap_number == 0)
16508         memset (&gc_data_global, 0, sizeof (gc_data_global));
16509
16510 #ifdef GC_CONFIG_DRIVEN
16511     memset (interesting_data_per_gc, 0, sizeof (interesting_data_per_gc));
16512 #endif //GC_CONFIG_DRIVEN
16513 }
16514
16515 int gc_heap::garbage_collect (int n)
16516 {
16517     //reset the number of alloc contexts
16518     alloc_contexts_used = 0;
16519
16520     fix_allocation_contexts (TRUE);
16521 #ifdef MULTIPLE_HEAPS
16522 #ifdef JOIN_STATS
16523     gc_t_join.start_ts(this);
16524 #endif //JOIN_STATS
16525     clear_gen0_bricks();
16526 #endif //MULTIPLE_HEAPS
16527
16528     if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p)
16529     {
16530 #ifdef MULTIPLE_HEAPS
16531         gc_t_join.join(this, gc_join_minimal_gc);
16532         if (gc_t_join.joined())
16533         {
16534 #endif //MULTIPLE_HEAPS
16535
16536 #ifdef MULTIPLE_HEAPS
16537             // this is serialized because we need to get a segment
16538             for (int i = 0; i < n_heaps; i++)
16539             {
16540                 if (!(g_heaps[i]->expand_soh_with_minimal_gc()))
16541                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16542             }
16543 #else
16544             if (!expand_soh_with_minimal_gc())
16545                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16546 #endif //MULTIPLE_HEAPS
16547
16548             update_collection_counts_for_no_gc();
16549
16550 #ifdef MULTIPLE_HEAPS
16551             gc_t_join.restart();
16552         }
16553 #endif //MULTIPLE_HEAPS
16554
16555         goto done;
16556     }
16557
16558     init_records();
16559     memset (&fgm_result, 0, sizeof (fgm_result));
16560
16561     settings.reason = gc_trigger_reason;
16562     verify_pinned_queue_p = FALSE;
16563
16564 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
16565         num_pinned_objects = 0;
16566 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
16567
16568 #ifdef STRESS_HEAP
16569     if (settings.reason == reason_gcstress)
16570     {
16571         settings.reason = reason_induced;
16572         settings.stress_induced = TRUE;
16573     }
16574 #endif // STRESS_HEAP
16575
16576 #ifdef MULTIPLE_HEAPS
16577     //align all heaps on the max generation to condemn
16578     dprintf (3, ("Joining for max generation to condemn"));
16579     condemned_generation_num = generation_to_condemn (n, 
16580                                                       &blocking_collection, 
16581                                                       &elevation_requested, 
16582                                                       FALSE);
16583     gc_t_join.join(this, gc_join_generation_determined);
16584     if (gc_t_join.joined())
16585 #endif //MULTIPLE_HEAPS
16586     {
16587 #ifdef MULTIPLE_HEAPS
16588 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16589         //delete old slots from the segment table
16590         seg_table->delete_old_slots();
16591 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16592         for (int i = 0; i < n_heaps; i++)
16593         {
16594             //copy the card and brick tables
16595             if (g_gc_card_table != g_heaps[i]->card_table)
16596             {
16597                 g_heaps[i]->copy_brick_card_table();
16598             }
16599
16600             g_heaps[i]->rearrange_large_heap_segments();
16601             if (!recursive_gc_sync::background_running_p())
16602             {
16603                 g_heaps[i]->rearrange_small_heap_segments();
16604             }
16605         }
16606 #else //MULTIPLE_HEAPS
16607 #ifdef BACKGROUND_GC
16608             //delete old slots from the segment table
16609 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16610             seg_table->delete_old_slots();
16611 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16612             rearrange_large_heap_segments();
16613             if (!recursive_gc_sync::background_running_p())
16614             {
16615                 rearrange_small_heap_segments();
16616             }
16617 #endif //BACKGROUND_GC
16618         // check for card table growth
16619         if (g_gc_card_table != card_table)
16620             copy_brick_card_table();
16621
16622 #endif //MULTIPLE_HEAPS
16623
16624         BOOL should_evaluate_elevation = FALSE;
16625         BOOL should_do_blocking_collection = FALSE;
16626
16627 #ifdef MULTIPLE_HEAPS
16628         int gen_max = condemned_generation_num;
16629         for (int i = 0; i < n_heaps; i++)
16630         {
16631             if (gen_max < g_heaps[i]->condemned_generation_num)
16632                 gen_max = g_heaps[i]->condemned_generation_num;
16633             if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested))
16634                 should_evaluate_elevation = TRUE;
16635             if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
16636                 should_do_blocking_collection = TRUE;
16637         }
16638
16639         settings.condemned_generation = gen_max;
16640 #else //MULTIPLE_HEAPS
16641         settings.condemned_generation = generation_to_condemn (n, 
16642                                                             &blocking_collection, 
16643                                                             &elevation_requested, 
16644                                                             FALSE);
16645         should_evaluate_elevation = elevation_requested;
16646         should_do_blocking_collection = blocking_collection;
16647 #endif //MULTIPLE_HEAPS
16648
16649         settings.condemned_generation = joined_generation_to_condemn (
16650                                             should_evaluate_elevation, 
16651                                             settings.condemned_generation,
16652                                             &should_do_blocking_collection
16653                                             STRESS_HEAP_ARG(n)
16654                                             );
16655
16656         STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, 
16657                 "condemned generation num: %d\n", settings.condemned_generation);
16658
16659         record_gcs_during_no_gc();
16660
16661         if (settings.condemned_generation > 1)
16662             settings.promotion = TRUE;
16663
16664 #ifdef HEAP_ANALYZE
16665         // At this point we've decided what generation is condemned
16666         // See if we've been requested to analyze survivors after the mark phase
16667         if (GCToEEInterface::AnalyzeSurvivorsRequested(settings.condemned_generation))
16668         {
16669             heap_analyze_enabled = TRUE;
16670         }
16671 #endif // HEAP_ANALYZE
16672
16673         GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced);
16674
16675 #ifdef BACKGROUND_GC
16676         if ((settings.condemned_generation == max_generation) &&
16677             (recursive_gc_sync::background_running_p()))
16678         {
16679             //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work
16680             // because we have to collect 0 and 1 properly
16681             // in particular, the allocation contexts are gone.
16682             // For now, it is simpler to collect max_generation-1
16683             settings.condemned_generation = max_generation - 1;
16684             dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
16685         }
16686
16687         if ((settings.condemned_generation == max_generation) &&
16688             (should_do_blocking_collection == FALSE) &&
16689             gc_can_use_concurrent &&
16690             !temp_disable_concurrent_p &&                 
16691             ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
16692         {
16693             keep_bgc_threads_p = TRUE;
16694             c_write (settings.concurrent,  TRUE);
16695         }
16696 #endif //BACKGROUND_GC
16697
16698         settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1;
16699
16700         // Call the EE for start of GC work
16701         // just one thread for MP GC
16702         GCToEEInterface::GcStartWork (settings.condemned_generation,
16703                                  max_generation);            
16704
16705         // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
16706         // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
16707         // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
16708         // fired in gc1.
16709         do_pre_gc();
16710
16711 #ifdef MULTIPLE_HEAPS
16712         gc_start_event.Reset();
16713         //start all threads on the roots.
16714         dprintf(3, ("Starting all gc threads for gc"));
16715         gc_t_join.restart();
16716 #endif //MULTIPLE_HEAPS
16717     }
16718
16719     {
16720         int gen_num_for_data = max_generation + 1;
16721         for (int i = 0; i <= gen_num_for_data; i++)
16722         {
16723             gc_data_per_heap.gen_data[i].size_before = generation_size (i);
16724             generation* gen = generation_of (i);
16725             gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
16726             gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
16727         }
16728     }
16729     descr_generations (TRUE);
16730 //    descr_card_table();
16731
16732 #ifdef VERIFY_HEAP
16733     if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
16734        !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_POST_GC_ONLY))
16735     {
16736         verify_heap (TRUE);
16737     }
16738     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK)
16739         checkGCWriteBarrier();
16740
16741 #endif // VERIFY_HEAP
16742
16743 #ifdef BACKGROUND_GC
16744     if (settings.concurrent)
16745     {
16746         // We need to save the settings because we'll need to restore it after each FGC.
16747         assert (settings.condemned_generation == max_generation);
16748         settings.compaction = FALSE;
16749         saved_bgc_settings = settings;
16750
16751 #ifdef MULTIPLE_HEAPS
16752         if (heap_number == 0)
16753         {
16754             for (int i = 0; i < n_heaps; i++)
16755             {
16756                 prepare_bgc_thread (g_heaps[i]);
16757             }
16758             dprintf (2, ("setting bgc_threads_sync_event"));
16759             bgc_threads_sync_event.Set();
16760         }
16761         else
16762         {
16763             bgc_threads_sync_event.Wait(INFINITE, FALSE);
16764             dprintf (2, ("bgc_threads_sync_event is signalled"));
16765         }
16766 #else
16767         prepare_bgc_thread(0);
16768 #endif //MULTIPLE_HEAPS
16769
16770 #ifdef MULTIPLE_HEAPS
16771         gc_t_join.join(this, gc_join_start_bgc);
16772         if (gc_t_join.joined())
16773 #endif //MULTIPLE_HEAPS
16774         {
16775             do_concurrent_p = TRUE;
16776             do_ephemeral_gc_p = FALSE;
16777 #ifdef MULTIPLE_HEAPS
16778             dprintf(2, ("Joined to perform a background GC"));
16779
16780             for (int i = 0; i < n_heaps; i++)
16781             {
16782                 gc_heap* hp = g_heaps[i];
16783                 if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
16784                 {
16785                     do_concurrent_p = FALSE;
16786                     break;
16787                 }
16788                 else
16789                 {
16790                     hp->background_saved_lowest_address = hp->lowest_address;
16791                     hp->background_saved_highest_address = hp->highest_address;
16792                 }
16793             }
16794 #else
16795             do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
16796             if (do_concurrent_p)
16797             {
16798                 background_saved_lowest_address = lowest_address;
16799                 background_saved_highest_address = highest_address;
16800             }
16801 #endif //MULTIPLE_HEAPS
16802
16803             if (do_concurrent_p)
16804             {
16805 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
16806                 SoftwareWriteWatch::EnableForGCHeap();
16807 #endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
16808
16809 #ifdef MULTIPLE_HEAPS
16810                 for (int i = 0; i < n_heaps; i++)
16811                     g_heaps[i]->current_bgc_state = bgc_initialized;
16812 #else
16813                 current_bgc_state = bgc_initialized;
16814 #endif //MULTIPLE_HEAPS
16815
16816                 int gen = check_for_ephemeral_alloc();
16817                 // always do a gen1 GC before we start BGC. 
16818                 // This is temporary for testing purpose.
16819                 //int gen = max_generation - 1;
16820                 dont_restart_ee_p = TRUE;
16821                 if (gen == -1)
16822                 {
16823                     // If we decide to not do a GC before the BGC we need to 
16824                     // restore the gen0 alloc context.
16825 #ifdef MULTIPLE_HEAPS
16826                     for (int i = 0; i < n_heaps; i++)
16827                     {
16828                         generation_allocation_pointer (g_heaps[i]->generation_of (0)) =  0;
16829                         generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
16830                     }
16831 #else
16832                     generation_allocation_pointer (youngest_generation) =  0;
16833                     generation_allocation_limit (youngest_generation) = 0;
16834 #endif //MULTIPLE_HEAPS
16835                 }
16836                 else
16837                 {
16838                     do_ephemeral_gc_p = TRUE;
16839
16840                     settings.init_mechanisms();
16841                     settings.condemned_generation = gen;
16842                     settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2;
16843                     do_pre_gc();
16844
16845                     // TODO BACKGROUND_GC need to add the profiling stuff here.
16846                     dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
16847                 }
16848
16849                 //clear the cards so they don't bleed in gen 1 during collection
16850                 // shouldn't this always be done at the beginning of any GC?
16851                 //clear_card_for_addresses (
16852                 //    generation_allocation_start (generation_of (0)),
16853                 //    heap_segment_allocated (ephemeral_heap_segment));
16854
16855                 if (!do_ephemeral_gc_p)
16856                 {
16857                     do_background_gc();
16858                 }
16859             }
16860             else
16861             {
16862                 settings.compaction = TRUE;
16863                 c_write (settings.concurrent, FALSE);
16864             }
16865
16866 #ifdef MULTIPLE_HEAPS
16867             gc_t_join.restart();
16868 #endif //MULTIPLE_HEAPS
16869         }
16870
16871         if (do_concurrent_p)
16872         {
16873             // At this point we are sure we'll be starting a BGC, so save its per heap data here.
16874             // global data is only calculated at the end of the GC so we don't need to worry about
16875             // FGCs overwriting it.
16876             memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap));
16877             memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
16878
16879             if (do_ephemeral_gc_p)
16880             {
16881                 dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
16882
16883                 gen_to_condemn_reasons.init();
16884                 gen_to_condemn_reasons.set_condition (gen_before_bgc);
16885                 gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
16886                 gc1();
16887 #ifdef MULTIPLE_HEAPS
16888                 gc_t_join.join(this, gc_join_bgc_after_ephemeral);
16889                 if (gc_t_join.joined())
16890 #endif //MULTIPLE_HEAPS
16891                 {
16892 #ifdef MULTIPLE_HEAPS
16893                     do_post_gc();
16894 #endif //MULTIPLE_HEAPS
16895                     settings = saved_bgc_settings;
16896                     assert (settings.concurrent);
16897
16898                     do_background_gc();
16899
16900 #ifdef MULTIPLE_HEAPS
16901                     gc_t_join.restart();
16902 #endif //MULTIPLE_HEAPS
16903                 }
16904             }
16905         }
16906         else
16907         {
16908             dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
16909             gc1();
16910         }
16911     }
16912     else
16913 #endif //BACKGROUND_GC
16914     {
16915         gc1();
16916     }
16917 #ifndef MULTIPLE_HEAPS
16918     allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp();
16919     allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
16920     fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
16921 #endif //MULTIPLE_HEAPS
16922
16923 done:
16924     if (settings.pause_mode == pause_no_gc)
16925         allocate_for_no_gc_after_gc();
16926
16927     int gn = settings.condemned_generation;
16928     return gn;
16929 }
16930
16931 #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
16932
16933 inline
16934 size_t& gc_heap::promoted_bytes(int thread)
16935 {
16936 #ifdef MULTIPLE_HEAPS
16937     return g_promoted [thread*16];
16938 #else //MULTIPLE_HEAPS
16939     UNREFERENCED_PARAMETER(thread);
16940     return g_promoted;
16941 #endif //MULTIPLE_HEAPS
16942 }
16943
16944 #ifdef INTERIOR_POINTERS
16945 heap_segment* gc_heap::find_segment (uint8_t* interior, BOOL small_segment_only_p)
16946 {
16947 #ifdef SEG_MAPPING_TABLE
16948     heap_segment* seg = seg_mapping_table_segment_of (interior);
16949     if (seg)
16950     {
16951         if (small_segment_only_p && heap_segment_loh_p (seg))
16952             return 0;
16953     }
16954     return seg;
16955 #else //SEG_MAPPING_TABLE
16956 #ifdef MULTIPLE_HEAPS
16957     for (int i = 0; i < gc_heap::n_heaps; i++)
16958     {
16959         gc_heap* h = gc_heap::g_heaps [i];
16960         hs = h->find_segment_per_heap (o, small_segment_only_p);
16961         if (hs)
16962         {
16963             break;
16964         }        
16965     }
16966 #else
16967     {
16968         gc_heap* h = pGenGCHeap;
16969         hs = h->find_segment_per_heap (o, small_segment_only_p);
16970     }
16971 #endif //MULTIPLE_HEAPS
16972 #endif //SEG_MAPPING_TABLE
16973 }
16974
16975 heap_segment* gc_heap::find_segment_per_heap (uint8_t* interior, BOOL small_segment_only_p)
16976 {
16977 #ifdef SEG_MAPPING_TABLE
16978     return find_segment (interior, small_segment_only_p);
16979 #else //SEG_MAPPING_TABLE
16980     if (in_range_for_segment (interior, ephemeral_heap_segment))
16981     {
16982         return ephemeral_heap_segment;
16983     }
16984     else
16985     {
16986         heap_segment* found_seg = 0;
16987
16988         {
16989             heap_segment* seg = generation_start_segment (generation_of (max_generation));
16990             do
16991             {
16992                 if (in_range_for_segment (interior, seg))
16993                 {
16994                     found_seg = seg;
16995                     goto end_find_segment;
16996                 }
16997
16998             } while ((seg = heap_segment_next (seg)) != 0);
16999         }
17000         if (!small_segment_only_p)
17001         {
17002 #ifdef BACKGROUND_GC
17003             {
17004                 ptrdiff_t delta = 0;
17005                 heap_segment* seg = segment_of (interior, delta);
17006                 if (seg && in_range_for_segment (interior, seg))
17007                 {
17008                     found_seg = seg;
17009                 }
17010                 goto end_find_segment;
17011             }
17012 #else //BACKGROUND_GC
17013             heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
17014             do
17015             {
17016                 if (in_range_for_segment(interior, seg))
17017                 {
17018                     found_seg = seg;
17019                     goto end_find_segment;
17020                 }
17021
17022             } while ((seg = heap_segment_next (seg)) != 0);
17023 #endif //BACKGROUND_GC
17024         }
17025 end_find_segment:
17026
17027         return found_seg;
17028     }
17029 #endif //SEG_MAPPING_TABLE
17030 }
17031 #endif //INTERIOR_POINTERS
17032
17033 #if !defined(_DEBUG) && !defined(__GNUC__)
17034 inline // This causes link errors if global optimization is off
17035 #endif //!_DEBUG && !__GNUC__
17036 gc_heap* gc_heap::heap_of (uint8_t* o)
17037 {
17038 #ifdef MULTIPLE_HEAPS
17039     if (o == 0)
17040         return g_heaps [0];
17041 #ifdef SEG_MAPPING_TABLE
17042     gc_heap* hp = seg_mapping_table_heap_of (o);
17043     return (hp ? hp : g_heaps[0]);
17044 #else //SEG_MAPPING_TABLE
17045     ptrdiff_t delta = 0;
17046     heap_segment* seg = segment_of (o, delta);
17047     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17048 #endif //SEG_MAPPING_TABLE
17049 #else //MULTIPLE_HEAPS
17050     UNREFERENCED_PARAMETER(o);
17051     return __this;
17052 #endif //MULTIPLE_HEAPS
17053 }
17054
17055 inline
17056 gc_heap* gc_heap::heap_of_gc (uint8_t* o)
17057 {
17058 #ifdef MULTIPLE_HEAPS
17059     if (o == 0)
17060         return g_heaps [0];
17061 #ifdef SEG_MAPPING_TABLE
17062     gc_heap* hp = seg_mapping_table_heap_of_gc (o);
17063     return (hp ? hp : g_heaps[0]);
17064 #else //SEG_MAPPING_TABLE
17065     ptrdiff_t delta = 0;
17066     heap_segment* seg = segment_of (o, delta);
17067     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17068 #endif //SEG_MAPPING_TABLE
17069 #else //MULTIPLE_HEAPS
17070     UNREFERENCED_PARAMETER(o);
17071     return __this;
17072 #endif //MULTIPLE_HEAPS
17073 }
17074
17075 #ifdef INTERIOR_POINTERS
17076 // will find all heap objects (large and small)
17077 uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
17078 {
17079     if (!gen0_bricks_cleared)
17080     {
17081 #ifdef MULTIPLE_HEAPS
17082         assert (!"Should have already been done in server GC");
17083 #endif //MULTIPLE_HEAPS
17084         gen0_bricks_cleared = TRUE;
17085         //initialize brick table for gen 0
17086         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
17087              b < brick_of (align_on_brick
17088                            (heap_segment_allocated (ephemeral_heap_segment)));
17089              b++)
17090         {
17091             set_brick (b, -1);
17092         }
17093     }
17094 #ifdef FFIND_OBJECT
17095     //indicate that in the future this needs to be done during allocation
17096 #ifdef MULTIPLE_HEAPS
17097     gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
17098 #else
17099     gen0_must_clear_bricks = FFIND_DECAY;
17100 #endif //MULTIPLE_HEAPS
17101 #endif //FFIND_OBJECT
17102
17103     int brick_entry = get_brick_entry(brick_of (interior));
17104     if (brick_entry == 0)
17105     {
17106         // this is a pointer to a large object
17107         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17108         if (seg
17109 #ifdef FEATURE_CONSERVATIVE_GC
17110             && (GCConfig::GetConservativeGC() || interior <= heap_segment_allocated(seg))
17111 #endif
17112             )
17113         {
17114             // If interior falls within the first free object at the beginning of a generation,
17115             // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
17116             int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
17117 #ifdef FEATURE_CONSERVATIVE_GC
17118                                                        || (GCConfig::GetConservativeGC() && !heap_segment_loh_p (seg))
17119 #endif
17120                                                       );
17121             //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
17122             assert (interior < heap_segment_allocated (seg));
17123
17124             uint8_t* o = heap_segment_mem (seg);
17125             while (o < heap_segment_allocated (seg))
17126             {
17127                 uint8_t* next_o = o + Align (size (o), align_const);
17128                 assert (next_o > o);
17129                 if ((o <= interior) && (interior < next_o))
17130                 return o;
17131                 o = next_o;
17132             }
17133             return 0;
17134         }
17135         else
17136         {
17137             return 0;
17138         }
17139     }
17140     else if (interior >= low)
17141     {
17142         heap_segment* seg = find_segment_per_heap (interior, TRUE);
17143         if (seg)
17144         {
17145 #ifdef FEATURE_CONSERVATIVE_GC
17146             if (interior >= heap_segment_allocated (seg))
17147                 return 0;
17148 #else
17149             assert (interior < heap_segment_allocated (seg));
17150 #endif
17151             uint8_t* o = find_first_object (interior, heap_segment_mem (seg));
17152             return o;
17153         }
17154         else
17155             return 0;
17156     }
17157     else
17158         return 0;
17159 }
17160
17161 uint8_t*
17162 gc_heap::find_object_for_relocation (uint8_t* interior, uint8_t* low, uint8_t* high)
17163 {
17164     uint8_t* old_address = interior;
17165     if (!((old_address >= low) && (old_address < high)))
17166         return 0;
17167     uint8_t* plug = 0;
17168     size_t  brick = brick_of (old_address);
17169     int    brick_entry =  brick_table [ brick ];
17170     if (brick_entry != 0)
17171     {
17172     retry:
17173         {
17174             while (brick_entry < 0)
17175             {
17176                 brick = (brick + brick_entry);
17177                 brick_entry =  brick_table [ brick ];
17178             }
17179             uint8_t* old_loc = old_address;
17180             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
17181                                       old_loc);
17182             if (node <= old_loc)
17183                 plug = node;
17184             else
17185             {
17186                 brick = brick - 1;
17187                 brick_entry =  brick_table [ brick ];
17188                 goto retry;
17189             }
17190
17191         }
17192         assert (plug);
17193         //find the object by going along the plug
17194         uint8_t* o = plug;
17195         while (o <= interior)
17196         {
17197             uint8_t* next_o = o + Align (size (o));
17198             assert (next_o > o);
17199             if (next_o > interior)
17200             {
17201                 break;
17202             }
17203             o = next_o;
17204         }
17205         assert ((o <= interior) && ((o + Align (size (o))) > interior));
17206         return o;
17207     }
17208     else
17209     {
17210         // this is a pointer to a large object
17211         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17212         if (seg)
17213         {
17214             assert (interior < heap_segment_allocated (seg));
17215
17216             uint8_t* o = heap_segment_mem (seg);
17217             while (o < heap_segment_allocated (seg))
17218             {
17219                 uint8_t* next_o = o + Align (size (o));
17220                 assert (next_o > o);
17221                 if ((o < interior) && (interior < next_o))
17222                 return o;
17223                 o = next_o;
17224             }
17225             return 0;
17226         }
17227         else
17228             {
17229             return 0;
17230         }
17231     }
17232 }
17233 #else //INTERIOR_POINTERS
17234 inline
17235 uint8_t* gc_heap::find_object (uint8_t* o, uint8_t* low)
17236 {
17237     return o;
17238 }
17239 #endif //INTERIOR_POINTERS
17240
17241 #ifdef MARK_LIST
17242 #ifdef GC_CONFIG_DRIVEN
17243 #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;}
17244 #else
17245 #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;}
17246 #endif //GC_CONFIG_DRIVEN
17247 #else //MARK_LIST
17248 #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17249 #endif //MARK_LIST
17250
17251 #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17252
17253 #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
17254
17255 inline
17256 BOOL gc_heap::gc_mark1 (uint8_t* o)
17257 {
17258     BOOL marked = !marked (o);
17259     set_marked (o);
17260     dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
17261     return marked;
17262 }
17263
17264 inline
17265 BOOL gc_heap::gc_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17266 {
17267     BOOL marked = FALSE;
17268     if ((o >= low) && (o < high))
17269         marked = gc_mark1 (o);
17270 #ifdef MULTIPLE_HEAPS
17271     else if (o)
17272     {
17273         //find the heap
17274         gc_heap* hp = heap_of_gc (o);
17275         assert (hp);
17276         if ((o >= hp->gc_low) && (o < hp->gc_high))
17277             marked = gc_mark1 (o);
17278     }
17279 #ifdef SNOOP_STATS
17280     snoop_stat.objects_checked_count++;
17281
17282     if (marked)
17283     {
17284         snoop_stat.objects_marked_count++;
17285     }
17286     if (!o)
17287     {
17288         snoop_stat.zero_ref_count++;
17289     }
17290
17291 #endif //SNOOP_STATS
17292 #endif //MULTIPLE_HEAPS
17293     return marked;
17294 }
17295
17296 #ifdef BACKGROUND_GC
17297
17298 inline
17299 BOOL gc_heap::background_marked (uint8_t* o)
17300 {
17301     return mark_array_marked (o);
17302 }
17303 inline
17304 BOOL gc_heap::background_mark1 (uint8_t* o)
17305 {
17306     BOOL to_mark = !mark_array_marked (o);
17307
17308     dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
17309     if (to_mark)
17310     {
17311         mark_array_set_marked (o);
17312         dprintf (4, ("n*%Ix*n", (size_t)o));
17313         return TRUE;
17314     }
17315     else
17316         return FALSE;
17317 }
17318
17319 // TODO: we could consider filtering out NULL's here instead of going to 
17320 // look for it on other heaps
17321 inline
17322 BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17323 {
17324     BOOL marked = FALSE;
17325     if ((o >= low) && (o < high))
17326         marked = background_mark1 (o);
17327 #ifdef MULTIPLE_HEAPS
17328     else if (o)
17329     {
17330         //find the heap
17331         gc_heap* hp = heap_of (o);
17332         assert (hp);
17333         if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
17334             marked = background_mark1 (o);
17335     }
17336 #endif //MULTIPLE_HEAPS
17337     return marked;
17338 }
17339
17340 #endif //BACKGROUND_GC
17341
17342 inline
17343 uint8_t* gc_heap::next_end (heap_segment* seg, uint8_t* f)
17344 {
17345     if (seg == ephemeral_heap_segment)
17346         return  f;
17347     else
17348         return  heap_segment_allocated (seg);
17349 }
17350
17351 #define new_start() {if (ppstop <= start) {break;} else {parm = start}}
17352 #define ignore_start 0
17353 #define use_start 1
17354
17355 #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp)      \
17356 {                                                                           \
17357     CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt));           \
17358     CGCDescSeries* cur = map->GetHighestSeries();                           \
17359     ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries();                        \
17360                                                                             \
17361     if (cnt >= 0)                                                           \
17362     {                                                                       \
17363         CGCDescSeries* last = map->GetLowestSeries();                       \
17364         uint8_t** parm = 0;                                                 \
17365         do                                                                  \
17366         {                                                                   \
17367             assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset()));     \
17368             parm = (uint8_t**)((o) + cur->GetSeriesOffset());               \
17369             uint8_t** ppstop =                                              \
17370                 (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\
17371             if (!start_useful || (uint8_t*)ppstop > (start))                \
17372             {                                                               \
17373                 if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\
17374                 while (parm < ppstop)                                       \
17375                 {                                                           \
17376                    {exp}                                                    \
17377                    parm++;                                                  \
17378                 }                                                           \
17379             }                                                               \
17380             cur--;                                                          \
17381                                                                             \
17382         } while (cur >= last);                                              \
17383     }                                                                       \
17384     else                                                                    \
17385     {                                                                       \
17386         /* Handle the repeating case - array of valuetypes */               \
17387         uint8_t** parm = (uint8_t**)((o) + cur->startoffset);               \
17388         if (start_useful && start > (uint8_t*)parm)                         \
17389         {                                                                   \
17390             ptrdiff_t cs = mt->RawGetComponentSize();                         \
17391             parm = (uint8_t**)((uint8_t*)parm + (((start) - (uint8_t*)parm)/cs)*cs); \
17392         }                                                                   \
17393         while ((uint8_t*)parm < ((o)+(size)-plug_skew))                     \
17394         {                                                                   \
17395             for (ptrdiff_t __i = 0; __i > cnt; __i--)                         \
17396             {                                                               \
17397                 HALF_SIZE_T skip =  cur->val_serie[__i].skip;               \
17398                 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs;              \
17399                 uint8_t** ppstop = parm + nptrs;                            \
17400                 if (!start_useful || (uint8_t*)ppstop > (start))            \
17401                 {                                                           \
17402                     if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);      \
17403                     do                                                      \
17404                     {                                                       \
17405                        {exp}                                                \
17406                        parm++;                                              \
17407                     } while (parm < ppstop);                                \
17408                 }                                                           \
17409                 parm = (uint8_t**)((uint8_t*)ppstop + skip);                \
17410             }                                                               \
17411         }                                                                   \
17412     }                                                                       \
17413 }
17414
17415 #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
17416
17417 // 1 thing to note about this macro:
17418 // 1) you can use *parm safely but in general you don't want to use parm 
17419 // because for the collectible types it's not an address on the managed heap.
17420 #ifndef COLLECTIBLE_CLASS
17421 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17422 {                                                                           \
17423     if (header(o)->ContainsPointers())                                      \
17424     {                                                                       \
17425         go_through_object_nostart(mt,o,size,parm,exp);                      \
17426     }                                                                       \
17427 }
17428 #else //COLLECTIBLE_CLASS
17429 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17430 {                                                                           \
17431     if (header(o)->Collectible())                                           \
17432     {                                                                       \
17433         uint8_t* class_obj = get_class_object (o);                             \
17434         uint8_t** parm = &class_obj;                                           \
17435         do {exp} while (false);                                             \
17436     }                                                                       \
17437     if (header(o)->ContainsPointers())                                      \
17438     {                                                                       \
17439         go_through_object_nostart(mt,o,size,parm,exp);                      \
17440     }                                                                       \
17441 }
17442 #endif //COLLECTIBLE_CLASS
17443
17444 // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
17445 void gc_heap::enque_pinned_plug (uint8_t* plug,
17446                                  BOOL save_pre_plug_info_p, 
17447                                  uint8_t* last_object_in_last_plug)
17448 {
17449     if (mark_stack_array_length <= mark_stack_tos)
17450     {
17451         if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
17452         {
17453             // we don't want to continue here due to security
17454             // risks. This happens very rarely and fixing it in the
17455             // way so that we can continue is a bit involved and will
17456             // not be done in Dev10.
17457             GCToEEInterface::HandleFatalError(CORINFO_EXCEPTION_GC);
17458         }
17459     }
17460
17461     dprintf (3, ("enqueuing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d", 
17462         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)));
17463     mark& m = mark_stack_array[mark_stack_tos];
17464     m.first = plug;
17465     // Must be set now because if we have a short object we'll need the value of saved_pre_p.
17466     m.saved_pre_p = save_pre_plug_info_p;
17467
17468     if (save_pre_plug_info_p)
17469     {
17470 #ifdef SHORT_PLUGS
17471         BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17472         if (is_padded)
17473             clear_plug_padded (last_object_in_last_plug);
17474 #endif //SHORT_PLUGS
17475         memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17476 #ifdef SHORT_PLUGS
17477         if (is_padded)
17478             set_plug_padded (last_object_in_last_plug);
17479 #endif //SHORT_PLUGS
17480
17481         memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17482
17483         // If the last object in the last plug is too short, it requires special handling.
17484         size_t last_obj_size = plug - last_object_in_last_plug;
17485         if (last_obj_size < min_pre_pin_obj_size)
17486         {
17487             record_interesting_data_point (idp_pre_short);
17488 #ifdef SHORT_PLUGS
17489             if (is_padded)
17490                 record_interesting_data_point (idp_pre_short_padded);
17491 #endif //SHORT_PLUGS
17492             dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!", 
17493                          last_object_in_last_plug, plug));
17494             // Need to set the short bit regardless of having refs or not because we need to 
17495             // indicate that this object is not walkable.
17496             m.set_pre_short();
17497
17498 #ifdef COLLECTIBLE_CLASS
17499             if (is_collectible (last_object_in_last_plug))
17500             {
17501                 m.set_pre_short_collectible();
17502             }
17503 #endif //COLLECTIBLE_CLASS
17504
17505             if (contain_pointers (last_object_in_last_plug))
17506             {
17507                 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17508
17509                 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17510                     {
17511                         size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17512                         dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17513                         m.set_pre_short_bit (gap_offset);
17514                     }
17515                 );
17516             }
17517         }
17518     }
17519
17520     m.saved_post_p = FALSE;
17521 }
17522
17523 void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_object_in_last_plug, uint8_t* post_plug)
17524 {
17525     UNREFERENCED_PARAMETER(last_pinned_plug);
17526
17527     mark& m = mark_stack_array[mark_stack_tos - 1];
17528     assert (last_pinned_plug == m.first);
17529     m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]);
17530
17531 #ifdef SHORT_PLUGS
17532     BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17533     if (is_padded)
17534         clear_plug_padded (last_object_in_last_plug);
17535 #endif //SHORT_PLUGS
17536     memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17537 #ifdef SHORT_PLUGS
17538     if (is_padded)
17539         set_plug_padded (last_object_in_last_plug);
17540 #endif //SHORT_PLUGS
17541
17542     memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17543
17544     // This is important - we need to clear all bits here except the last one.
17545     m.saved_post_p = TRUE;
17546
17547 #ifdef _DEBUG
17548     m.saved_post_plug_debug.gap = 1;
17549 #endif //_DEBUG
17550
17551     dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
17552
17553     size_t last_obj_size = post_plug - last_object_in_last_plug;
17554     if (last_obj_size < min_pre_pin_obj_size)
17555     {
17556         dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
17557         record_interesting_data_point (idp_post_short);
17558 #ifdef SHORT_PLUGS
17559         if (is_padded)
17560             record_interesting_data_point (idp_post_short_padded);
17561 #endif //SHORT_PLUGS
17562         m.set_post_short();
17563         verify_pinned_queue_p = TRUE;
17564
17565 #ifdef COLLECTIBLE_CLASS
17566         if (is_collectible (last_object_in_last_plug))
17567         {
17568             m.set_post_short_collectible();
17569         }
17570 #endif //COLLECTIBLE_CLASS
17571
17572         if (contain_pointers (last_object_in_last_plug))
17573         {
17574             dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17575
17576             // TODO: since we won't be able to walk this object in relocation, we still need to
17577             // take care of collectible assemblies here.
17578             go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17579                 {
17580                     size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17581                     dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17582                     m.set_post_short_bit (gap_offset);
17583                 }
17584             );
17585         }
17586     }
17587 }
17588
17589 //#define PREFETCH
17590 #ifdef PREFETCH
17591 __declspec(naked) void __fastcall Prefetch(void* addr)
17592 {
17593    __asm {
17594        PREFETCHT0 [ECX]
17595         ret
17596     };
17597 }
17598 #else //PREFETCH
17599 inline void Prefetch (void* addr)
17600 {
17601     UNREFERENCED_PARAMETER(addr);
17602 }
17603 #endif //PREFETCH
17604 #ifdef MH_SC_MARK
17605 inline
17606 VOLATILE(uint8_t*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
17607 {
17608     return ((VOLATILE(uint8_t*)*)(hp->mark_stack_array))[index];
17609 }
17610
17611 #endif //MH_SC_MARK
17612
17613 #define stolen 2
17614 #define partial 1
17615 #define partial_object 3
17616 inline 
17617 uint8_t* ref_from_slot (uint8_t* r)
17618 {
17619     return (uint8_t*)((size_t)r & ~(stolen | partial));
17620 }
17621 inline
17622 BOOL stolen_p (uint8_t* r)
17623 {
17624     return (((size_t)r&2) && !((size_t)r&1));
17625 }
17626 inline 
17627 BOOL ready_p (uint8_t* r)
17628 {
17629     return ((size_t)r != 1);
17630 }
17631 inline
17632 BOOL partial_p (uint8_t* r)
17633 {
17634     return (((size_t)r&1) && !((size_t)r&2));
17635 }
17636 inline 
17637 BOOL straight_ref_p (uint8_t* r)
17638 {
17639     return (!stolen_p (r) && !partial_p (r));
17640 }
17641 inline 
17642 BOOL partial_object_p (uint8_t* r)
17643 {
17644     return (((size_t)r & partial_object) == partial_object);
17645 }
17646 inline
17647 BOOL ref_p (uint8_t* r)
17648 {
17649     return (straight_ref_p (r) || partial_object_p (r));
17650 }
17651
17652 void gc_heap::mark_object_simple1 (uint8_t* oo, uint8_t* start THREAD_NUMBER_DCL)
17653 {
17654     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)mark_stack_array;
17655     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)&mark_stack_array[mark_stack_array_length];
17656     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_base = mark_stack_tos;
17657 #ifdef SORT_MARK_STACK
17658     SERVER_SC_MARK_VOLATILE(uint8_t*)* sorted_tos = mark_stack_base;
17659 #endif //SORT_MARK_STACK
17660
17661     // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't 
17662     // update mark list.
17663     BOOL  full_p = (settings.condemned_generation == max_generation);
17664
17665     assert ((start >= oo) && (start < oo+size(oo)));
17666
17667 #ifndef MH_SC_MARK
17668     *mark_stack_tos = oo;
17669 #endif //!MH_SC_MARK
17670
17671     while (1)
17672     {
17673 #ifdef MULTIPLE_HEAPS
17674 #else  //MULTIPLE_HEAPS
17675         const int thread = 0;
17676 #endif //MULTIPLE_HEAPS
17677
17678         if (oo && ((size_t)oo != 4))
17679         {
17680             size_t s = 0; 
17681             if (stolen_p (oo))
17682             {
17683                 --mark_stack_tos;
17684                 goto next_level;
17685             }
17686             else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
17687             {
17688                 BOOL overflow_p = FALSE;
17689
17690                 if (mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit  - 1))
17691                 {
17692                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
17693                     if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
17694                     {
17695                         overflow_p = TRUE;
17696                     }
17697                 }
17698                 
17699                 if (overflow_p == FALSE)
17700                 {
17701                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17702
17703                     go_through_object_cl (method_table(oo), oo, s, ppslot,
17704                                           {
17705                                               uint8_t* o = *ppslot;
17706                                               Prefetch(o);
17707                                               if (gc_mark (o, gc_low, gc_high))
17708                                               {
17709                                                   if (full_p)
17710                                                   {
17711                                                       m_boundary_fullgc (o);
17712                                                   }
17713                                                   else
17714                                                   {
17715                                                       m_boundary (o);
17716                                                   }
17717                                                   size_t obj_size = size (o);
17718                                                   promoted_bytes (thread) += obj_size;
17719                                                   if (contain_pointers_or_collectible (o))
17720                                                   {
17721                                                       *(mark_stack_tos++) = o;
17722                                                   }
17723                                               }
17724                                           }
17725                         );
17726                 }
17727                 else
17728                 {
17729                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17730                     min_overflow_address = min (min_overflow_address, oo);
17731                     max_overflow_address = max (max_overflow_address, oo);
17732                 }
17733             }
17734             else
17735             {
17736                 if (partial_p (oo))
17737                 {
17738                     start = ref_from_slot (oo);
17739                     oo = ref_from_slot (*(--mark_stack_tos));
17740                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
17741                     assert ((oo < start) && (start < (oo + size (oo))));
17742                 }
17743 #ifdef COLLECTIBLE_CLASS
17744                 else
17745                 {
17746                     // If there's a class object, push it now. We are guaranteed to have the slot since
17747                     // we just popped one object off.
17748                     if (is_collectible (oo))
17749                     {
17750                         uint8_t* class_obj = get_class_object (oo);
17751                         if (gc_mark (class_obj, gc_low, gc_high))
17752                         {
17753                             if (full_p)
17754                             {
17755                                 m_boundary_fullgc (class_obj);
17756                             }
17757                             else
17758                             {
17759                                 m_boundary (class_obj);
17760                             }
17761
17762                             size_t obj_size = size (class_obj);
17763                             promoted_bytes (thread) += obj_size;
17764                             *(mark_stack_tos++) = class_obj;
17765                             // The code below expects that the oo is still stored in the stack slot that was
17766                             // just popped and it "pushes" it back just by incrementing the mark_stack_tos. 
17767                             // But the class_obj has just overwritten that stack slot and so the oo needs to
17768                             // be stored to the new slot that's pointed to by the mark_stack_tos.
17769                             *mark_stack_tos = oo;
17770                         }
17771                     }
17772                 }
17773 #endif //COLLECTIBLE_CLASS
17774
17775                 s = size (oo);
17776                 
17777                 BOOL overflow_p = FALSE;
17778             
17779                 if (mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
17780                 {
17781                     overflow_p = TRUE;
17782                 }
17783                 if (overflow_p == FALSE)
17784                 {
17785                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17786
17787                     //push the object and its current 
17788                     SERVER_SC_MARK_VOLATILE(uint8_t*)* place = ++mark_stack_tos;
17789                     mark_stack_tos++;
17790 #ifdef MH_SC_MARK
17791                     *(place-1) = 0;
17792                     *(place) = (uint8_t*)partial;
17793 #endif //MH_SC_MARK
17794                     int i = num_partial_refs; 
17795                     uint8_t* ref_to_continue = 0;
17796
17797                     go_through_object (method_table(oo), oo, s, ppslot,
17798                                        start, use_start, (oo + s),
17799                                        {
17800                                            uint8_t* o = *ppslot;
17801                                            Prefetch(o);
17802                                            if (gc_mark (o, gc_low, gc_high))
17803                                            {
17804                                                 if (full_p)
17805                                                 {
17806                                                     m_boundary_fullgc (o);
17807                                                 }
17808                                                 else
17809                                                 {
17810                                                     m_boundary (o);
17811                                                 }
17812                                                 size_t obj_size = size (o);
17813                                                 promoted_bytes (thread) += obj_size;
17814                                                 if (contain_pointers_or_collectible (o))
17815                                                 {
17816                                                     *(mark_stack_tos++) = o;
17817                                                     if (--i == 0)
17818                                                     {
17819                                                         ref_to_continue = (uint8_t*)((size_t)(ppslot+1) | partial);
17820                                                         goto more_to_do;
17821                                                     }
17822
17823                                                 }
17824                                            }
17825
17826                                        }
17827                         );
17828                     //we are finished with this object
17829                     assert (ref_to_continue == 0);
17830 #ifdef MH_SC_MARK
17831                     assert ((*(place-1)) == (uint8_t*)0);
17832 #else //MH_SC_MARK
17833                     *(place-1) = 0;
17834 #endif //MH_SC_MARK
17835                     *place = 0; 
17836                     // shouldn't we decrease tos by 2 here??
17837
17838 more_to_do:
17839                     if (ref_to_continue)
17840                     {
17841                         //update the start
17842 #ifdef MH_SC_MARK
17843                         assert ((*(place-1)) == (uint8_t*)0);
17844                         *(place-1) = (uint8_t*)((size_t)oo | partial_object);
17845                         assert (((*place) == (uint8_t*)1) || ((*place) == (uint8_t*)2));
17846 #endif //MH_SC_MARK
17847                         *place = ref_to_continue;
17848                     }
17849                 }
17850                 else
17851                 {
17852                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17853                     min_overflow_address = min (min_overflow_address, oo);
17854                     max_overflow_address = max (max_overflow_address, oo);
17855                 }
17856             }
17857 #ifdef SORT_MARK_STACK
17858             if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
17859             {
17860                 rqsort1 (sorted_tos, mark_stack_tos-1);
17861                 sorted_tos = mark_stack_tos-1;
17862             }
17863 #endif //SORT_MARK_STACK
17864         }
17865     next_level:
17866         if (!(mark_stack_empty_p()))
17867         {
17868             oo = *(--mark_stack_tos);
17869             start = oo;
17870
17871 #ifdef SORT_MARK_STACK
17872             sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
17873 #endif //SORT_MARK_STACK
17874         }
17875         else
17876             break;
17877     }
17878 }
17879
17880 #ifdef MH_SC_MARK
17881 BOOL same_numa_node_p (int hn1, int hn2)
17882 {
17883     return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
17884 }
17885
17886 int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
17887 {
17888     int hn = (current_buddy+1)%n_heaps;
17889     while (hn != current_buddy)
17890     {
17891         if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
17892             return hn;
17893         hn = (hn+1)%n_heaps;
17894     }
17895     return current_buddy;
17896 }
17897
17898 void 
17899 gc_heap::mark_steal()
17900 {
17901     mark_stack_busy() = 0;
17902     //clear the mark stack in the snooping range
17903     for (int i = 0; i < max_snoop_level; i++)
17904     {
17905         ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
17906     }
17907
17908     //pick the next heap as our buddy
17909     int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
17910
17911 #ifdef SNOOP_STATS
17912         dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
17913         uint32_t begin_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
17914 #endif //SNOOP_STATS
17915
17916     int idle_loop_count = 0; 
17917     int first_not_ready_level = 0;
17918
17919     while (1)
17920     {
17921         gc_heap* hp = g_heaps [thpn];
17922         int level = first_not_ready_level;
17923         first_not_ready_level = 0; 
17924
17925         while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
17926         {
17927             idle_loop_count = 0; 
17928 #ifdef SNOOP_STATS
17929             snoop_stat.busy_count++;
17930             dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix", 
17931                                  heap_number, level, (int)((uint8_t**)(hp->mark_stack_array))[level]));
17932 #endif //SNOOP_STATS
17933
17934             uint8_t* o = ref_mark_stack (hp, level);
17935
17936             uint8_t* start = o;
17937             if (ref_p (o))
17938             {
17939                 mark_stack_busy() = 1;
17940
17941                 BOOL success = TRUE;
17942                 uint8_t* next = (ref_mark_stack (hp, level+1));
17943                 if (ref_p (next))
17944                 {
17945                     if (((size_t)o > 4) && !partial_object_p (o))
17946                     {
17947                         //this is a normal object, not a partial mark tuple
17948                         //success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
17949                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), (uint8_t*)4, o)==o);
17950 #ifdef SNOOP_STATS
17951                         snoop_stat.interlocked_count++;
17952                         if (success)
17953                             snoop_stat.normal_count++;
17954 #endif //SNOOP_STATS
17955                     }
17956                     else
17957                     {
17958                         //it is a stolen entry, or beginning/ending of a partial mark
17959                         level++;
17960 #ifdef SNOOP_STATS
17961                         snoop_stat.stolen_or_pm_count++;
17962 #endif //SNOOP_STATS
17963                         success = FALSE;
17964                     }
17965                 }
17966                 else if (stolen_p (next))
17967                 {
17968                     //ignore the stolen guy and go to the next level
17969                     success = FALSE;
17970                     level+=2;
17971 #ifdef SNOOP_STATS
17972                     snoop_stat.stolen_entry_count++;
17973 #endif //SNOOP_STATS
17974                 }
17975                 else
17976                 {
17977                     assert (partial_p (next));
17978                     start = ref_from_slot (next);
17979                     //re-read the object
17980                     o = ref_from_slot (ref_mark_stack (hp, level));
17981                     if (o && start)
17982                     {
17983                         //steal the object
17984                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level+1), (uint8_t*)stolen, next)==next);
17985 #ifdef SNOOP_STATS
17986                         snoop_stat.interlocked_count++;
17987                         if (success)
17988                         {
17989                             snoop_stat.partial_mark_parent_count++;                    
17990                         }
17991 #endif //SNOOP_STATS
17992                     }
17993                     else
17994                     {
17995                         // stack is not ready, or o is completely different from the last time we read from this stack level.
17996                         // go up 2 levels to steal children or totally unrelated objects.
17997                         success = FALSE;
17998                         if (first_not_ready_level == 0)
17999                         {
18000                             first_not_ready_level = level;
18001                         }
18002                         level+=2;
18003 #ifdef SNOOP_STATS
18004                         snoop_stat.pm_not_ready_count++;
18005 #endif //SNOOP_STATS                        
18006                     }
18007                 }
18008                 if (success)
18009                 {
18010
18011 #ifdef SNOOP_STATS
18012                     dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
18013                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18014                             (GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18015                     uint32_t start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
18016 #endif //SNOOP_STATS
18017
18018                     mark_object_simple1 (o, start, heap_number);
18019
18020 #ifdef SNOOP_STATS
18021                     dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
18022                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18023                             (GCToOSInterface::GetLowPrecisionTimeStamp()-start_tick),(GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18024 #endif //SNOOP_STATS
18025
18026                     mark_stack_busy() = 0;
18027
18028                     //clear the mark stack in snooping range
18029                     for (int i = 0; i < max_snoop_level; i++)
18030                     {
18031                         if (((uint8_t**)mark_stack_array)[i] != 0)
18032                         {
18033                             ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18034 #ifdef SNOOP_STATS
18035                             snoop_stat.stack_bottom_clear_count++;
18036 #endif //SNOOP_STATS
18037                         }
18038                     }
18039
18040                     level = 0; 
18041                 }
18042                 mark_stack_busy() = 0;
18043             }
18044             else
18045             {
18046                 //slot is either partial or stolen
18047                 level++;
18048             }
18049         }
18050         if ((first_not_ready_level != 0) && hp->mark_stack_busy())
18051         {
18052             continue;
18053         } 
18054         if (!hp->mark_stack_busy())
18055         {
18056             first_not_ready_level = 0; 
18057             idle_loop_count++;
18058
18059             if ((idle_loop_count % (6) )==1)
18060             {
18061 #ifdef SNOOP_STATS
18062                 snoop_stat.switch_to_thread_count++;
18063 #endif //SNOOP_STATS
18064                 GCToOSInterface::Sleep(1);
18065             }
18066             int free_count = 1;
18067 #ifdef SNOOP_STATS
18068             snoop_stat.stack_idle_count++;
18069             //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
18070 #endif //SNOOP_STATS
18071             for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
18072             {
18073                 if (!((g_heaps [hpn])->mark_stack_busy()))
18074                 {
18075                     free_count++;
18076 #ifdef SNOOP_STATS
18077                 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
18078 #endif //SNOOP_STATS
18079                 }
18080                 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
18081                 {
18082                     thpn = hpn;
18083                     break;
18084                 }
18085                 hpn = (hpn+1)%n_heaps;
18086                 YieldProcessor();
18087             }
18088             if (free_count == n_heaps)
18089             {
18090                 break;
18091             }
18092         }
18093     }
18094 }
18095
18096 inline
18097 BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
18098 {
18099 #ifdef SNOOP_STATS
18100     snoop_stat.check_level_count++;
18101 #endif //SNOOP_STATS
18102     return (next_heap->mark_stack_busy()>=1);
18103 }
18104 #endif //MH_SC_MARK
18105
18106 #ifdef SNOOP_STATS
18107 void gc_heap::print_snoop_stat()
18108 {
18109     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18110         "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
18111     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
18112         snoop_stat.heap_index,
18113         snoop_stat.objects_checked_count,
18114         snoop_stat.zero_ref_count,
18115         snoop_stat.objects_marked_count,
18116         snoop_stat.stolen_stack_count,
18117         snoop_stat.partial_stack_count,
18118         snoop_stat.normal_stack_count,
18119         snoop_stat.non_stack_count));
18120     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18121         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
18122     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18123         snoop_stat.heap_index,
18124         snoop_stat.check_level_count,
18125         snoop_stat.busy_count,
18126         snoop_stat.interlocked_count,
18127         snoop_stat.partial_mark_parent_count,
18128         snoop_stat.stolen_or_pm_count,
18129         snoop_stat.stolen_entry_count,
18130         snoop_stat.pm_not_ready_count,
18131         snoop_stat.normal_count,
18132         snoop_stat.stack_bottom_clear_count));
18133
18134     printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n", 
18135         "heap", "check", "zero", "mark", "idle", "switch");
18136     printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
18137         snoop_stat.heap_index,
18138         snoop_stat.objects_checked_count,
18139         snoop_stat.zero_ref_count,
18140         snoop_stat.objects_marked_count,
18141         snoop_stat.stack_idle_count,
18142         snoop_stat.switch_to_thread_count);
18143     printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
18144         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18145     printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18146         snoop_stat.heap_index,
18147         snoop_stat.check_level_count,
18148         snoop_stat.busy_count,
18149         snoop_stat.interlocked_count,
18150         snoop_stat.partial_mark_parent_count,
18151         snoop_stat.stolen_or_pm_count,
18152         snoop_stat.stolen_entry_count,
18153         snoop_stat.pm_not_ready_count,
18154         snoop_stat.normal_count,
18155         snoop_stat.stack_bottom_clear_count);
18156 }
18157 #endif //SNOOP_STATS
18158
18159 #ifdef HEAP_ANALYZE
18160 void
18161 gc_heap::ha_mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18162 {
18163     if (!internal_root_array)
18164     {
18165         internal_root_array = new (nothrow) uint8_t* [internal_root_array_length];
18166         if (!internal_root_array)
18167         {
18168             heap_analyze_success = FALSE;
18169         }
18170     }
18171
18172     if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
18173     {
18174         size_t new_size = 2*internal_root_array_length;
18175
18176         uint64_t available_physical = 0;
18177         get_memory_info (NULL, &available_physical);
18178         if (new_size > (size_t)(available_physical / 10))
18179         {
18180             heap_analyze_success = FALSE;
18181         }
18182         else
18183         {
18184             uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18185             if (tmp)
18186             {
18187                 memcpy (tmp, internal_root_array,
18188                         internal_root_array_length*sizeof (uint8_t*));
18189                 delete[] internal_root_array;
18190                 internal_root_array = tmp;
18191                 internal_root_array_length = new_size;
18192             }
18193             else
18194             {
18195                 heap_analyze_success = FALSE;
18196             }
18197         }
18198     }
18199
18200     if (heap_analyze_success)
18201     {
18202         PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
18203
18204         uint8_t* ref = (uint8_t*)po;
18205         if (!current_obj || 
18206             !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
18207         {
18208             gc_heap* hp = gc_heap::heap_of (ref);
18209             current_obj = hp->find_object (ref, hp->lowest_address);
18210             current_obj_size = size (current_obj);
18211
18212             internal_root_array[internal_root_array_index] = current_obj;
18213             internal_root_array_index++;
18214         }
18215     }
18216
18217     mark_object_simple (po THREAD_NUMBER_ARG);
18218 }
18219 #endif //HEAP_ANALYZE
18220
18221 //this method assumes that *po is in the [low. high[ range
18222 void
18223 gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18224 {
18225     uint8_t* o = *po;
18226 #ifdef MULTIPLE_HEAPS
18227 #else  //MULTIPLE_HEAPS
18228     const int thread = 0;
18229 #endif //MULTIPLE_HEAPS
18230     {
18231 #ifdef SNOOP_STATS
18232         snoop_stat.objects_checked_count++;
18233 #endif //SNOOP_STATS
18234
18235         if (gc_mark1 (o))
18236         {
18237             m_boundary (o);
18238             size_t s = size (o);
18239             promoted_bytes (thread) += s;
18240             {
18241                 go_through_object_cl (method_table(o), o, s, poo,
18242                                         {
18243                                             uint8_t* oo = *poo;
18244                                             if (gc_mark (oo, gc_low, gc_high))
18245                                             {
18246                                                 m_boundary (oo);
18247                                                 size_t obj_size = size (oo);
18248                                                 promoted_bytes (thread) += obj_size;
18249
18250                                                 if (contain_pointers_or_collectible (oo))
18251                                                     mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
18252                                             }
18253                                         }
18254                     );
18255             }
18256         }
18257     }
18258 }
18259
18260 inline
18261 uint8_t* gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL)
18262 {
18263     if ((o >= gc_low) && (o < gc_high))
18264         mark_object_simple (&o THREAD_NUMBER_ARG);
18265 #ifdef MULTIPLE_HEAPS
18266     else if (o)
18267     {
18268         //find the heap
18269         gc_heap* hp = heap_of (o);
18270         assert (hp);
18271         if ((o >= hp->gc_low) && (o < hp->gc_high))
18272             mark_object_simple (&o THREAD_NUMBER_ARG);
18273     }
18274 #endif //MULTIPLE_HEAPS
18275
18276     return o;
18277 }
18278
18279 #ifdef BACKGROUND_GC
18280
18281 void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL)
18282 {
18283     uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
18284
18285 #ifdef SORT_MARK_STACK
18286     uint8_t** sorted_tos = background_mark_stack_array;
18287 #endif //SORT_MARK_STACK
18288
18289     background_mark_stack_tos = background_mark_stack_array;
18290
18291     while (1)
18292     {
18293 #ifdef MULTIPLE_HEAPS
18294 #else  //MULTIPLE_HEAPS
18295         const int thread = 0;
18296 #endif //MULTIPLE_HEAPS
18297         if (oo)
18298         {
18299             size_t s = 0; 
18300             if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18301             {
18302                 BOOL overflow_p = FALSE;
18303             
18304                 if (background_mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1))
18305                 {
18306                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18307                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18308                     if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
18309                     {
18310                         dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs", 
18311                             heap_number,
18312                             (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
18313                             method_table(oo), 
18314                             num_pointers));
18315
18316                         bgc_overflow_count++;
18317                         overflow_p = TRUE;
18318                     }
18319                 }
18320             
18321                 if (overflow_p == FALSE)
18322                 {
18323                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18324
18325                     go_through_object_cl (method_table(oo), oo, s, ppslot,
18326                     {
18327                         uint8_t* o = *ppslot;
18328                         Prefetch(o);
18329                         if (background_mark (o, 
18330                                              background_saved_lowest_address, 
18331                                              background_saved_highest_address))
18332                         {
18333                             //m_boundary (o);
18334                             size_t obj_size = size (o);
18335                             bpromoted_bytes (thread) += obj_size;
18336                             if (contain_pointers_or_collectible (o))
18337                             {
18338                                 *(background_mark_stack_tos++) = o;
18339
18340                             }
18341                         }
18342                     }
18343                         );
18344                 }
18345                 else
18346                 {
18347                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18348                     background_min_overflow_address = min (background_min_overflow_address, oo);
18349                     background_max_overflow_address = max (background_max_overflow_address, oo);
18350                 }
18351             }
18352             else 
18353             {
18354                 uint8_t* start = oo;
18355                 if ((size_t)oo & 1)
18356                 {
18357                     oo = (uint8_t*)((size_t)oo & ~1);
18358                     start = *(--background_mark_stack_tos);
18359                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18360                 }
18361 #ifdef COLLECTIBLE_CLASS
18362                 else
18363                 {
18364                     // If there's a class object, push it now. We are guaranteed to have the slot since
18365                     // we just popped one object off.
18366                     if (is_collectible (oo))
18367                     {
18368                         uint8_t* class_obj = get_class_object (oo);
18369                         if (background_mark (class_obj, 
18370                                             background_saved_lowest_address, 
18371                                             background_saved_highest_address))
18372                         {
18373                             size_t obj_size = size (class_obj);
18374                             bpromoted_bytes (thread) += obj_size;
18375
18376                             *(background_mark_stack_tos++) = class_obj;
18377                         }
18378                     }
18379                 }
18380 #endif //COLLECTIBLE_CLASS
18381
18382                 s = size (oo);
18383                 
18384                 BOOL overflow_p = FALSE;
18385             
18386                 if (background_mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
18387                 {
18388                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18389                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18390
18391                     dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id", 
18392                         heap_number,
18393                         (size_t)(mark_stack_limit - background_mark_stack_tos),
18394                         oo,
18395                         method_table(oo), 
18396                         start,
18397                         num_pointers));
18398
18399                     bgc_overflow_count++;
18400                     overflow_p = TRUE;
18401                 }
18402                 if (overflow_p == FALSE)
18403                 {
18404                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18405
18406                     //push the object and its current 
18407                     uint8_t** place = background_mark_stack_tos++;
18408                     *(place) = start;
18409                     *(background_mark_stack_tos++) = (uint8_t*)((size_t)oo | 1);
18410
18411                     int i = num_partial_refs; 
18412
18413                     go_through_object (method_table(oo), oo, s, ppslot,
18414                                        start, use_start, (oo + s),
18415                     {
18416                         uint8_t* o = *ppslot;
18417                         Prefetch(o);
18418
18419                         if (background_mark (o, 
18420                                             background_saved_lowest_address, 
18421                                             background_saved_highest_address))
18422                         {
18423                             //m_boundary (o);
18424                             size_t obj_size = size (o);
18425                             bpromoted_bytes (thread) += obj_size;
18426                             if (contain_pointers_or_collectible (o))
18427                             {
18428                                 *(background_mark_stack_tos++) = o;
18429                                 if (--i == 0)
18430                                 {
18431                                     //update the start
18432                                     *place = (uint8_t*)(ppslot+1);
18433                                     goto more_to_do;
18434                                 }
18435
18436                             }
18437                         }
18438
18439                     }
18440                         );
18441                     //we are finished with this object
18442                     *place = 0; 
18443                     *(place+1) = 0;
18444
18445                 more_to_do:;
18446                 }
18447                 else
18448                 {
18449                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18450                     background_min_overflow_address = min (background_min_overflow_address, oo);
18451                     background_max_overflow_address = max (background_max_overflow_address, oo);
18452                 }
18453             }
18454         }
18455 #ifdef SORT_MARK_STACK
18456         if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18457         {
18458             rqsort1 (sorted_tos, background_mark_stack_tos-1);
18459             sorted_tos = background_mark_stack_tos-1;
18460         }
18461 #endif //SORT_MARK_STACK
18462
18463         allow_fgc();
18464
18465         if (!(background_mark_stack_tos == background_mark_stack_array))
18466         {
18467             oo = *(--background_mark_stack_tos);
18468
18469 #ifdef SORT_MARK_STACK
18470             sorted_tos = (uint8_t**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
18471 #endif //SORT_MARK_STACK
18472         }
18473         else
18474             break;
18475     }
18476
18477     assert (background_mark_stack_tos == background_mark_stack_array);
18478
18479
18480 }
18481
18482 //this version is different than the foreground GC because
18483 //it can't keep pointers to the inside of an object
18484 //while calling background_mark_simple1. The object could be moved
18485 //by an intervening foreground gc.
18486 //this method assumes that *po is in the [low. high[ range
18487 void
18488 gc_heap::background_mark_simple (uint8_t* o THREAD_NUMBER_DCL)
18489 {
18490 #ifdef MULTIPLE_HEAPS
18491 #else  //MULTIPLE_HEAPS
18492     const int thread = 0;
18493 #endif //MULTIPLE_HEAPS
18494     {
18495         dprintf (3, ("bmarking %Ix", o));
18496         
18497         if (background_mark1 (o))
18498         {
18499             //m_boundary (o);
18500             size_t s = size (o);
18501             bpromoted_bytes (thread) += s;
18502
18503             if (contain_pointers_or_collectible (o))
18504             {
18505                 background_mark_simple1 (o THREAD_NUMBER_ARG);
18506             }
18507         }
18508     }
18509 }
18510
18511 inline
18512 uint8_t* gc_heap::background_mark_object (uint8_t* o THREAD_NUMBER_DCL)
18513 {
18514     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
18515     {
18516         background_mark_simple (o THREAD_NUMBER_ARG);
18517     }
18518     else
18519     {
18520         if (o)
18521         {
18522             dprintf (3, ("or-%Ix", o));
18523         }
18524     }
18525     return o;
18526 }
18527
18528 void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, uint32_t flags)
18529 {
18530     UNREFERENCED_PARAMETER(sc);
18531
18532     assert (settings.concurrent);
18533     uint8_t* o = (uint8_t*)object;
18534
18535     gc_heap* hp = gc_heap::heap_of (o);
18536 #ifdef INTERIOR_POINTERS
18537     if (flags & GC_CALL_INTERIOR)
18538     {
18539         o = hp->find_object (o, background_saved_lowest_address);
18540     }
18541 #endif //INTERIOR_POINTERS
18542
18543     if (!background_object_marked (o, FALSE))
18544     {
18545         FATAL_GC_ERROR();
18546     }
18547 }
18548
18549 void gc_heap::background_promote (Object** ppObject, ScanContext* sc, uint32_t flags)
18550 {
18551     UNREFERENCED_PARAMETER(sc);
18552     //in order to save space on the array, mark the object,
18553     //knowing that it will be visited later
18554     assert (settings.concurrent);
18555
18556     THREAD_NUMBER_FROM_CONTEXT;
18557 #ifndef MULTIPLE_HEAPS
18558     const int thread = 0;
18559 #endif //!MULTIPLE_HEAPS
18560
18561     uint8_t* o = (uint8_t*)*ppObject;
18562
18563     if (o == 0)
18564         return;
18565
18566 #ifdef DEBUG_DestroyedHandleValue
18567     // we can race with destroy handle during concurrent scan
18568     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
18569         return;
18570 #endif //DEBUG_DestroyedHandleValue
18571
18572     HEAP_FROM_THREAD;
18573
18574     gc_heap* hp = gc_heap::heap_of (o);
18575
18576     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
18577     {
18578         return;
18579     }
18580
18581 #ifdef INTERIOR_POINTERS
18582     if (flags & GC_CALL_INTERIOR)
18583     {
18584         o = hp->find_object (o, hp->background_saved_lowest_address);
18585         if (o == 0)
18586             return;
18587     }
18588 #endif //INTERIOR_POINTERS
18589
18590 #ifdef FEATURE_CONSERVATIVE_GC
18591     // For conservative GC, a value on stack may point to middle of a free object.
18592     // In this case, we don't need to promote the pointer.
18593     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
18594     {
18595         return;
18596     }
18597 #endif //FEATURE_CONSERVATIVE_GC
18598
18599 #ifdef _DEBUG
18600     ((CObjectHeader*)o)->Validate();
18601 #endif //_DEBUG
18602
18603     dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
18604
18605     //needs to be called before the marking because it is possible for a foreground
18606     //gc to take place during the mark and move the object
18607     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
18608
18609     hpt->background_mark_simple (o THREAD_NUMBER_ARG);
18610 }
18611
18612 //used by the ephemeral collection to scan the local background structures
18613 //containing references.
18614 void
18615 gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
18616 {
18617     ScanContext sc;
18618     if (pSC == 0)
18619         pSC = &sc;
18620
18621     pSC->thread_number = hn;
18622
18623 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
18624     pSC->pCurrentDomain = 0;
18625 #endif
18626
18627     BOOL relocate_p = (fn == &GCHeap::Relocate);
18628
18629     dprintf (3, ("Scanning background mark list"));
18630
18631     //scan mark_list
18632     size_t mark_list_finger = 0;
18633     while (mark_list_finger < c_mark_list_index)
18634     {
18635         uint8_t** o = &c_mark_list [mark_list_finger];
18636         if (!relocate_p)
18637         {
18638             // We may not be able to calculate the size during relocate as POPO
18639             // may have written over the object.
18640             size_t s = size (*o);
18641             assert (Align (s) >= Align (min_obj_size));
18642             dprintf(3,("background root %Ix", (size_t)*o));
18643         }
18644         (*fn) ((Object**)o, pSC, 0);
18645         mark_list_finger++;
18646     }
18647
18648     //scan the mark stack
18649     dprintf (3, ("Scanning background mark stack"));
18650
18651     uint8_t** finger = background_mark_stack_array;
18652     while (finger < background_mark_stack_tos)
18653     {
18654         if ((finger + 1) < background_mark_stack_tos)
18655         {
18656             // We need to check for the partial mark case here.
18657             uint8_t* parent_obj = *(finger + 1);
18658             if ((size_t)parent_obj & 1)
18659             {
18660                 uint8_t* place = *finger;
18661                 size_t place_offset = 0;
18662                 uint8_t* real_parent_obj = (uint8_t*)((size_t)parent_obj & ~1);
18663
18664                 if (relocate_p)
18665                 {
18666                     *(finger + 1) = real_parent_obj;
18667                     place_offset = place - real_parent_obj;
18668                     dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
18669                     (*fn) ((Object**)(finger + 1), pSC, 0);
18670                     real_parent_obj = *(finger + 1);
18671                     *finger = real_parent_obj + place_offset;
18672                     *(finger + 1) = (uint8_t*)((size_t)real_parent_obj | 1);
18673                     dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
18674                 }
18675                 else
18676                 {
18677                     uint8_t** temp = &real_parent_obj;
18678                     dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
18679                     (*fn) ((Object**)temp, pSC, 0);
18680                 }
18681
18682                 finger += 2;
18683                 continue;
18684             }
18685         }
18686         dprintf(3,("background root %Ix", (size_t)*finger));
18687         (*fn) ((Object**)finger, pSC, 0);
18688         finger++;
18689     }
18690 }
18691
18692 inline
18693 void gc_heap::background_mark_through_object (uint8_t* oo THREAD_NUMBER_DCL)
18694 {
18695     if (contain_pointers (oo))
18696     {
18697         size_t total_refs = 0;
18698         size_t s = size (oo);
18699         go_through_object_nostart (method_table(oo), oo, s, po,
18700                           {
18701                             uint8_t* o = *po;
18702                             total_refs++;
18703                             background_mark_object (o THREAD_NUMBER_ARG);
18704                           }
18705             );
18706
18707         dprintf (3,("Background marking through %Ix went through %Id refs", 
18708                           (size_t)oo,
18709                            total_refs));
18710     }
18711 }
18712
18713 uint8_t* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
18714 {
18715     if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
18716     {
18717         // for now we stop at where gen1 started when we started processing 
18718         return background_min_soh_overflow_address;
18719     }
18720     else
18721     {
18722         return heap_segment_allocated (seg);
18723     }
18724 }
18725
18726 uint8_t* gc_heap::background_first_overflow (uint8_t* min_add,
18727                                           heap_segment* seg,
18728                                           BOOL concurrent_p, 
18729                                           BOOL small_object_p)
18730 {
18731     uint8_t* o = 0;
18732
18733     if (small_object_p)
18734     {
18735         if (in_range_for_segment (min_add, seg))
18736         {
18737             // min_add was the beginning of gen1 when we did the concurrent
18738             // overflow. Now we could be in a situation where min_add is
18739             // actually the same as allocated for that segment (because
18740             // we expanded heap), in which case we can not call 
18741             // find first on this address or we will AV.
18742             if (min_add >= heap_segment_allocated (seg))
18743             {
18744                 return min_add;
18745             }
18746             else
18747             {
18748                 if (concurrent_p && 
18749                     ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
18750                 {
18751                     return background_min_soh_overflow_address;
18752                 }
18753                 else
18754                 {
18755                     o = find_first_object (min_add, heap_segment_mem (seg));
18756                     return o;
18757                 }
18758             }
18759         }
18760     }
18761
18762     o = max (heap_segment_mem (seg), min_add);
18763     return o;
18764 }
18765
18766 void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
18767                                                          uint8_t* min_add, uint8_t* max_add,
18768                                                          BOOL concurrent_p)
18769 {
18770     if (concurrent_p)
18771     {
18772         current_bgc_state = bgc_overflow_soh;
18773     }
18774
18775     size_t total_marked_objects = 0;
18776
18777 #ifdef MULTIPLE_HEAPS
18778     int thread = heap_number;
18779 #endif //MULTIPLE_HEAPS
18780
18781     exclusive_sync* loh_alloc_lock = 0;
18782
18783     dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
18784 #ifdef MULTIPLE_HEAPS
18785     // We don't have each heap scan all heaps concurrently because we are worried about
18786     // multiple threads calling things like find_first_object.
18787     int h_start = (concurrent_p ? heap_number : 0);
18788     int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
18789     for (int hi = h_start; hi < h_end; hi++)
18790     {
18791         gc_heap*  hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
18792
18793 #else
18794     {
18795         gc_heap*  hp = 0;
18796
18797 #endif //MULTIPLE_HEAPS
18798         BOOL small_object_segments = TRUE;
18799         int align_const = get_alignment_constant (small_object_segments);
18800         generation* gen = hp->generation_of (condemned_gen_number);
18801         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
18802         PREFIX_ASSUME(seg != NULL);
18803         loh_alloc_lock = hp->bgc_alloc_lock;
18804
18805         uint8_t* o = hp->background_first_overflow (min_add,
18806                                                     seg, 
18807                                                     concurrent_p, 
18808                                                     small_object_segments);
18809
18810         while (1)
18811         {
18812             while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
18813             {
18814                 dprintf (3, ("considering %Ix", (size_t)o));
18815
18816                 size_t s;
18817
18818                 if (concurrent_p && !small_object_segments)
18819                 {
18820                     loh_alloc_lock->bgc_mark_set (o);
18821
18822                     if (((CObjectHeader*)o)->IsFree())
18823                     {
18824                         s = unused_array_size (o);
18825                     }
18826                     else
18827                     {
18828                         s = size (o);
18829                     }
18830                 }
18831                 else
18832                 {
18833                     s = size (o);
18834                 }
18835
18836                 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
18837                 {
18838                     total_marked_objects++;
18839                     go_through_object_cl (method_table(o), o, s, poo,
18840                                           uint8_t* oo = *poo;
18841                                           background_mark_object (oo THREAD_NUMBER_ARG);
18842                                          );
18843                 }
18844
18845                 if (concurrent_p && !small_object_segments)
18846                 {
18847                     loh_alloc_lock->bgc_mark_done ();
18848                 }
18849
18850                 o = o + Align (s, align_const);
18851
18852                 if (concurrent_p)
18853                 {
18854                     allow_fgc();
18855                 }
18856             }
18857
18858             dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)", 
18859                 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
18860
18861             if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
18862                 (seg = heap_segment_next_in_range (seg)) == 0)
18863             {
18864                 if (small_object_segments)
18865                 {
18866                     if (concurrent_p)
18867                     {
18868                         current_bgc_state = bgc_overflow_loh;
18869                     }
18870
18871                     dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
18872                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
18873                     concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
18874                     total_marked_objects = 0;
18875                     small_object_segments = FALSE;
18876                     align_const = get_alignment_constant (small_object_segments);
18877                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
18878
18879                     PREFIX_ASSUME(seg != NULL);
18880
18881                     o = max (heap_segment_mem (seg), min_add);
18882                     continue;
18883                 }
18884                 else
18885                 {
18886                     dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
18887                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
18888                     break;
18889                 }
18890             } 
18891             else
18892             {
18893                 o = hp->background_first_overflow (min_add, 
18894                                                    seg, 
18895                                                    concurrent_p, 
18896                                                    small_object_segments);
18897                 continue;
18898             }
18899         }
18900     }
18901 }
18902
18903 BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
18904 {
18905     BOOL grow_mark_array_p = TRUE;
18906
18907     if (concurrent_p)
18908     {
18909         assert (!processed_soh_overflow_p);
18910
18911         if ((background_max_overflow_address != 0) &&
18912             (background_min_overflow_address != MAX_PTR))
18913         {
18914             // We have overflow to process but we know we can't process the ephemeral generations
18915             // now (we actually could process till the current gen1 start but since we are going to 
18916             // make overflow per segment, for now I'll just stop at the saved gen1 start.
18917             saved_overflow_ephemeral_seg = ephemeral_heap_segment;
18918             background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
18919             background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
18920         }
18921     }
18922     else
18923     {
18924         assert ((saved_overflow_ephemeral_seg == 0) || 
18925                 ((background_max_soh_overflow_address != 0) &&
18926                  (background_min_soh_overflow_address != MAX_PTR)));
18927         
18928         if (!processed_soh_overflow_p)
18929         {
18930             // if there was no more overflow we just need to process what we didn't process 
18931             // on the saved ephemeral segment.
18932             if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
18933             {
18934                 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
18935                 grow_mark_array_p = FALSE;
18936             }
18937
18938             background_min_overflow_address = min (background_min_overflow_address, 
18939                                                 background_min_soh_overflow_address);
18940             background_max_overflow_address = max (background_max_overflow_address,
18941                                                 background_max_soh_overflow_address);
18942             processed_soh_overflow_p = TRUE;
18943         }
18944     }
18945
18946     BOOL  overflow_p = FALSE;
18947 recheck:
18948     if ((! ((background_max_overflow_address == 0)) ||
18949          ! ((background_min_overflow_address == MAX_PTR))))
18950     {
18951         overflow_p = TRUE;
18952
18953         if (grow_mark_array_p)
18954         {
18955             // Try to grow the array.
18956             size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
18957
18958             if ((new_size * sizeof(mark)) > 100*1024)
18959             {
18960                 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
18961
18962                 new_size = min(new_max_size, new_size);
18963             }
18964
18965             if ((background_mark_stack_array_length < new_size) && 
18966                 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
18967             {
18968                 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
18969
18970                 uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18971                 if (tmp)
18972                 {
18973                     delete background_mark_stack_array;
18974                     background_mark_stack_array = tmp;
18975                     background_mark_stack_array_length = new_size;
18976                     background_mark_stack_tos = background_mark_stack_array;
18977                 }
18978             }
18979         }
18980         else
18981         {
18982             grow_mark_array_p = TRUE;
18983         }
18984
18985         uint8_t*  min_add = background_min_overflow_address;
18986         uint8_t*  max_add = background_max_overflow_address;
18987
18988         background_max_overflow_address = 0;
18989         background_min_overflow_address = MAX_PTR;
18990
18991         background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
18992         if (!concurrent_p)
18993         {        
18994             goto recheck;
18995         }
18996     }
18997
18998     return overflow_p;
18999 }
19000
19001 #endif //BACKGROUND_GC
19002
19003 inline
19004 void gc_heap::mark_through_object (uint8_t* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
19005 {
19006 #ifndef COLLECTIBLE_CLASS
19007     UNREFERENCED_PARAMETER(mark_class_object_p);
19008     BOOL to_mark_class_object = FALSE;
19009 #else //COLLECTIBLE_CLASS
19010     BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
19011 #endif //COLLECTIBLE_CLASS
19012     if (contain_pointers (oo) || to_mark_class_object)
19013     {
19014         dprintf(3,( "Marking through %Ix", (size_t)oo));
19015         size_t s = size (oo);
19016
19017 #ifdef COLLECTIBLE_CLASS
19018         if (to_mark_class_object)
19019         {
19020             uint8_t* class_obj = get_class_object (oo);
19021             mark_object (class_obj THREAD_NUMBER_ARG);
19022         }
19023 #endif //COLLECTIBLE_CLASS
19024
19025         if (contain_pointers (oo))
19026         {
19027             go_through_object_nostart (method_table(oo), oo, s, po,
19028                                 uint8_t* o = *po;
19029                                 mark_object (o THREAD_NUMBER_ARG);
19030                                 );
19031         }
19032     }
19033 }
19034
19035 size_t gc_heap::get_total_heap_size()
19036 {
19037     size_t total_heap_size = 0;
19038
19039 #ifdef MULTIPLE_HEAPS
19040     int hn = 0;
19041
19042     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19043     {
19044         gc_heap* hp2 = gc_heap::g_heaps [hn];
19045         total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
19046     }
19047 #else
19048     total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
19049 #endif //MULTIPLE_HEAPS
19050
19051     return total_heap_size;
19052 }
19053
19054 size_t gc_heap::get_total_fragmentation()
19055 {
19056     size_t total_fragmentation = 0;
19057
19058 #ifdef MULTIPLE_HEAPS
19059     for (int i = 0; i < gc_heap::n_heaps; i++)
19060     {
19061         gc_heap* hp = gc_heap::g_heaps[i];
19062 #else //MULTIPLE_HEAPS
19063     {
19064         gc_heap* hp = pGenGCHeap;
19065 #endif //MULTIPLE_HEAPS
19066         for (int i = 0; i <= (max_generation + 1); i++)
19067         {
19068             generation* gen = hp->generation_of (i);
19069             total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen));
19070         }
19071     }
19072
19073     return total_fragmentation;
19074 }
19075
19076 size_t gc_heap::committed_size()
19077 {
19078     generation* gen = generation_of (max_generation);
19079     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19080     size_t total_committed = 0;
19081
19082     while (1)
19083     {
19084         total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19085
19086         seg = heap_segment_next (seg);
19087         if (!seg)
19088         {
19089             if (gen != large_object_generation)
19090             {
19091                 gen = generation_of (max_generation + 1);
19092                 seg = generation_start_segment (gen);
19093             }
19094             else
19095                 break;
19096         }
19097     }
19098
19099     return total_committed;
19100 }
19101
19102 size_t gc_heap::get_total_committed_size()
19103 {
19104     size_t total_committed = 0;
19105
19106 #ifdef MULTIPLE_HEAPS
19107     int hn = 0;
19108
19109     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19110     {
19111         gc_heap* hp = gc_heap::g_heaps [hn];
19112         total_committed += hp->committed_size();
19113     }
19114 #else
19115     total_committed = committed_size();
19116 #endif //MULTIPLE_HEAPS
19117
19118     return total_committed;
19119 }
19120
19121 void gc_heap::get_memory_info (uint32_t* memory_load, 
19122                                uint64_t* available_physical,
19123                                uint64_t* available_page_file)
19124 {
19125     GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file);
19126 }
19127
19128 void fire_mark_event (int heap_num, int root_type, size_t bytes_marked)
19129 {
19130     dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id", heap_num, root_type, bytes_marked));
19131     FIRE_EVENT(GCMarkWithType, heap_num, root_type, bytes_marked);
19132 }
19133
19134 //returns TRUE is an overflow happened.
19135 BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
19136 {
19137     size_t last_promoted_bytes = promoted_bytes (heap_number);
19138     BOOL  overflow_p = FALSE;
19139 recheck:
19140     if ((! (max_overflow_address == 0) ||
19141          ! (min_overflow_address == MAX_PTR)))
19142     {
19143         overflow_p = TRUE;
19144         // Try to grow the array.
19145         size_t new_size =
19146             max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
19147
19148         if ((new_size * sizeof(mark)) > 100*1024)
19149         {
19150             size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19151
19152             new_size = min(new_max_size, new_size);
19153         }
19154
19155         if ((mark_stack_array_length < new_size) && 
19156             ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
19157         {
19158             mark* tmp = new (nothrow) mark [new_size];
19159             if (tmp)
19160             {
19161                 delete mark_stack_array;
19162                 mark_stack_array = tmp;
19163                 mark_stack_array_length = new_size;
19164             }
19165         }
19166
19167         uint8_t*  min_add = min_overflow_address;
19168         uint8_t*  max_add = max_overflow_address;
19169         max_overflow_address = 0;
19170         min_overflow_address = MAX_PTR;
19171         process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
19172         goto recheck;
19173     }
19174
19175     size_t current_promoted_bytes = promoted_bytes (heap_number);
19176
19177     if (current_promoted_bytes != last_promoted_bytes)
19178         fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes));
19179     return overflow_p;
19180 }
19181
19182 void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
19183                                               uint8_t* min_add, uint8_t* max_add)
19184 {
19185 #ifdef MULTIPLE_HEAPS
19186     int thread = heap_number;
19187 #endif //MULTIPLE_HEAPS
19188     BOOL  full_p = (condemned_gen_number == max_generation);
19189
19190         dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19191 #ifdef MULTIPLE_HEAPS
19192             for (int hi = 0; hi < n_heaps; hi++)
19193             {
19194                 gc_heap*  hp = g_heaps [(heap_number + hi) % n_heaps];
19195
19196 #else
19197             {
19198                 gc_heap*  hp = 0;
19199
19200 #endif //MULTIPLE_HEAPS
19201         BOOL small_object_segments = TRUE;
19202         int align_const = get_alignment_constant (small_object_segments);
19203         generation* gen = hp->generation_of (condemned_gen_number);
19204         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19205         
19206         PREFIX_ASSUME(seg != NULL);
19207         uint8_t*  o = max (heap_segment_mem (seg), min_add);
19208         while (1)
19209         {
19210             uint8_t*  end = heap_segment_allocated (seg);
19211
19212             while ((o < end) && (o <= max_add))
19213             {
19214                 assert ((min_add <= o) && (max_add >= o));
19215                 dprintf (3, ("considering %Ix", (size_t)o));
19216                 if (marked (o))
19217                 {
19218                     mark_through_object (o, TRUE THREAD_NUMBER_ARG);
19219                 }
19220
19221                 o = o + Align (size (o), align_const);
19222             }
19223
19224             if (( seg = heap_segment_next_in_range (seg)) == 0)
19225             {
19226                 if (small_object_segments && full_p)
19227                 {
19228                     small_object_segments = FALSE;
19229                     align_const = get_alignment_constant (small_object_segments);
19230                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19231
19232                     PREFIX_ASSUME(seg != NULL);
19233
19234                     o = max (heap_segment_mem (seg), min_add);
19235                     continue;
19236                 }
19237                 else
19238                 {
19239                     break;
19240                 } 
19241             } 
19242             else
19243             {
19244                 o = max (heap_segment_mem (seg), min_add);
19245                 continue;
19246             }
19247         }
19248     }
19249 }
19250
19251 // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
19252 // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
19253 // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
19254 // promotion scan multiple times.
19255 // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
19256 // also has the effect of processing any mark stack overflow.
19257
19258 #ifdef MULTIPLE_HEAPS
19259 // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
19260 // worker threads synchronized. The algorithms are sufficiently divergent that we have different
19261 // implementations based on whether MULTIPLE_HEAPS is defined or not.
19262 //
19263 // Define some static variables used for synchronization in the method below. These should really be defined
19264 // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
19265 //
19266 // A note about the synchronization used within this method. Communication between the worker threads is
19267 // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
19268 // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
19269 // protection of a join.
19270 static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
19271 static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
19272 static VOLATILE(BOOL) s_fScanRequired;
19273 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19274 {
19275     // Whenever we call this method there may have been preceding object promotions. So set
19276     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19277     // based on the how the scanning proceeded).
19278     s_fUnscannedPromotions = TRUE;
19279
19280     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
19281     // the state of this thread's portion of the dependent handle table. That's because promotions on other
19282     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
19283     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
19284     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
19285     // as all the others or they'll get out of step).
19286     while (true)
19287     {
19288         // The various worker threads are all currently racing in this code. We need to work out if at least
19289         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
19290         // dependent handle table when both of the following conditions apply:
19291         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
19292         //     object happens to correspond to a primary in one of our handles we might potentially have to
19293         //     promote the associated secondary).
19294         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
19295         //
19296         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
19297         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
19298         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
19299         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
19300         // follows below. Note that we can't read this outside of the join since on any iteration apart from
19301         // the first threads will be racing between reading this value and completing their previous
19302         // iteration's table scan.
19303         //
19304         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
19305         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
19306         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
19307         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
19308         // we're safely joined.
19309         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19310             s_fUnpromotedHandles = TRUE;
19311
19312         // Synchronize all the threads so we can read our state variables safely. The shared variable
19313         // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
19314         // a single thread inside the join.
19315         gc_t_join.join(this, gc_join_scan_dependent_handles);
19316         if (gc_t_join.joined())
19317         {
19318             // We're synchronized so it's safe to read our shared state variables. We update another shared
19319             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
19320             // the loop. We scan if there has been at least one object promotion since last time and at least
19321             // one thread has a dependent handle table with a potential handle promotion possible.
19322             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
19323
19324             // Reset our shared state variables (ready to be set again on this scan or with a good initial
19325             // value for the next call if we're terminating the loop).
19326             s_fUnscannedPromotions = FALSE;
19327             s_fUnpromotedHandles = FALSE;
19328
19329             if (!s_fScanRequired)
19330             {
19331                 // We're terminating the loop. Perform any last operations that require single threaded access.
19332                 if (!initial_scan_p)
19333                 {
19334                     // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
19335                     // load balance if some of the heaps have an abnormally large workload.
19336                     uint8_t* all_heaps_max = 0;
19337                     uint8_t* all_heaps_min = MAX_PTR;
19338                     int i;
19339                     for (i = 0; i < n_heaps; i++)
19340                     {
19341                         if (all_heaps_max < g_heaps[i]->max_overflow_address)
19342                             all_heaps_max = g_heaps[i]->max_overflow_address;
19343                         if (all_heaps_min > g_heaps[i]->min_overflow_address)
19344                             all_heaps_min = g_heaps[i]->min_overflow_address;
19345                     }
19346                     for (i = 0; i < n_heaps; i++)
19347                     {
19348                         g_heaps[i]->max_overflow_address = all_heaps_max;
19349                         g_heaps[i]->min_overflow_address = all_heaps_min;
19350                     }
19351                 }
19352             }
19353
19354             // Restart all the workers.
19355             dprintf(3, ("Starting all gc thread mark stack overflow processing"));
19356             gc_t_join.restart();
19357         }
19358
19359         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19360         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
19361         // global flag indicating that at least one object promotion may have occurred (the usual comment
19362         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
19363         // exit the method since we unconditionally set this variable on method entry anyway).
19364         if (process_mark_overflow(condemned_gen_number))
19365             s_fUnscannedPromotions = TRUE;
19366
19367         // If we decided that no scan was required we can terminate the loop now.
19368         if (!s_fScanRequired)
19369             break;
19370
19371         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
19372         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
19373         // could miss noting the promotion of some primary objects).
19374         gc_t_join.join(this, gc_join_rescan_dependent_handles);
19375         if (gc_t_join.joined())
19376         {
19377             // Restart all the workers.
19378             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
19379             gc_t_join.restart();
19380         }
19381
19382         // If the portion of the dependent handle table managed by this worker has handles that could still be
19383         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
19384         // could require a rescan of handles on this or other workers.
19385         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19386             if (GCScan::GcDhReScan(sc))
19387                 s_fUnscannedPromotions = TRUE;
19388     }
19389 }
19390 #else //MULTIPLE_HEAPS
19391 // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
19392 // threads synchronized.
19393 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19394 {
19395     UNREFERENCED_PARAMETER(initial_scan_p);
19396
19397     // Whenever we call this method there may have been preceding object promotions. So set
19398     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19399     // based on the how the scanning proceeded).
19400     bool fUnscannedPromotions = true;
19401
19402     // Loop until there are either no more dependent handles that can have their secondary promoted or we've
19403     // managed to perform a scan without promoting anything new.
19404     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
19405     {
19406         // On each iteration of the loop start with the assumption that no further objects have been promoted.
19407         fUnscannedPromotions = false;
19408
19409         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19410         // being visible. If there was an overflow (process_mark_overflow returned true) then additional
19411         // objects now appear to be promoted and we should set the flag.
19412         if (process_mark_overflow(condemned_gen_number))
19413             fUnscannedPromotions = true;
19414
19415         // Perform the scan and set the flag if any promotions resulted.
19416         if (GCScan::GcDhReScan(sc))
19417             fUnscannedPromotions = true;
19418     }
19419
19420     // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
19421     // scan any handles at all this is the processing of overflows that may have occurred prior to this method
19422     // invocation).
19423     process_mark_overflow(condemned_gen_number);
19424 }
19425 #endif //MULTIPLE_HEAPS
19426
19427 void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
19428 {
19429     assert (settings.concurrent == FALSE);
19430
19431     ScanContext sc;
19432     sc.thread_number = heap_number;
19433     sc.promotion = TRUE;
19434     sc.concurrent = FALSE;
19435
19436     dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
19437     BOOL  full_p = (condemned_gen_number == max_generation);
19438
19439 #ifdef TIME_GC
19440     unsigned start;
19441     unsigned finish;
19442     start = GetCycleCount32();
19443 #endif //TIME_GC
19444
19445     int gen_to_init = condemned_gen_number;
19446     if (condemned_gen_number == max_generation)
19447     {
19448         gen_to_init = max_generation + 1;
19449     }
19450     for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
19451     {
19452         dynamic_data* dd = dynamic_data_of (gen_idx);
19453         dd_begin_data_size (dd) = generation_size (gen_idx) - 
19454                                    dd_fragmentation (dd) -
19455                                    Align (size (generation_allocation_start (generation_of (gen_idx))));
19456         dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
19457         dd_survived_size (dd) = 0;
19458         dd_pinned_survived_size (dd) = 0;
19459         dd_artificial_pinned_survived_size (dd) = 0;
19460         dd_added_pinned_size (dd) = 0;
19461 #ifdef SHORT_PLUGS
19462         dd_padding_size (dd) = 0;
19463 #endif //SHORT_PLUGS
19464 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
19465         dd_num_npinned_plugs (dd) = 0;
19466 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
19467     }
19468
19469 #ifdef FFIND_OBJECT
19470     if (gen0_must_clear_bricks > 0)
19471         gen0_must_clear_bricks--;
19472 #endif //FFIND_OBJECT
19473
19474     size_t last_promoted_bytes = 0;
19475
19476     promoted_bytes (heap_number) = 0;
19477     reset_mark_stack();
19478
19479 #ifdef SNOOP_STATS
19480     memset (&snoop_stat, 0, sizeof(snoop_stat));
19481     snoop_stat.heap_index = heap_number;
19482 #endif //SNOOP_STATS
19483
19484 #ifdef MH_SC_MARK
19485     if (full_p)
19486     {
19487         //initialize the mark stack
19488         for (int i = 0; i < max_snoop_level; i++)
19489         {
19490             ((uint8_t**)(mark_stack_array))[i] = 0;
19491         }
19492
19493         mark_stack_busy() = 1;
19494     }
19495 #endif //MH_SC_MARK
19496
19497     static uint32_t num_sizedrefs = 0;
19498
19499 #ifdef MH_SC_MARK
19500     static BOOL do_mark_steal_p = FALSE;
19501 #endif //MH_SC_MARK
19502
19503 #ifdef MULTIPLE_HEAPS
19504     gc_t_join.join(this, gc_join_begin_mark_phase);
19505     if (gc_t_join.joined())
19506     {
19507 #endif //MULTIPLE_HEAPS
19508
19509         num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
19510
19511 #ifdef MULTIPLE_HEAPS
19512
19513 #ifdef MH_SC_MARK
19514         if (full_p)
19515         {
19516             size_t total_heap_size = get_total_heap_size();
19517
19518             if (total_heap_size > (100 * 1024 * 1024))
19519             {
19520                 do_mark_steal_p = TRUE;
19521             }
19522             else
19523             {
19524                 do_mark_steal_p = FALSE;
19525             }
19526         }
19527         else
19528         {
19529             do_mark_steal_p = FALSE;
19530         }
19531 #endif //MH_SC_MARK
19532
19533         gc_t_join.restart();
19534     }
19535 #endif //MULTIPLE_HEAPS
19536
19537     {
19538
19539 #ifdef MARK_LIST
19540         //set up the mark lists from g_mark_list
19541         assert (g_mark_list);
19542 #ifdef MULTIPLE_HEAPS
19543         mark_list = &g_mark_list [heap_number*mark_list_size];
19544 #else
19545         mark_list = g_mark_list;
19546 #endif //MULTIPLE_HEAPS
19547         //dont use the mark list for full gc
19548         //because multiple segments are more complex to handle and the list
19549         //is likely to overflow
19550         if (condemned_gen_number != max_generation)
19551             mark_list_end = &mark_list [mark_list_size-1];
19552         else
19553             mark_list_end = &mark_list [0];
19554         mark_list_index = &mark_list [0];
19555 #endif //MARK_LIST
19556
19557         shigh = (uint8_t*) 0;
19558         slow  = MAX_PTR;
19559
19560         //%type%  category = quote (mark);
19561
19562         if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
19563         {
19564             GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19565             fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes));
19566             last_promoted_bytes = promoted_bytes (heap_number);
19567
19568 #ifdef MULTIPLE_HEAPS
19569             gc_t_join.join(this, gc_join_scan_sizedref_done);
19570             if (gc_t_join.joined())
19571             {
19572                 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
19573                 gc_t_join.restart();
19574             }
19575 #endif //MULTIPLE_HEAPS
19576         }
19577     
19578         dprintf(3,("Marking Roots"));
19579
19580         GCScan::GcScanRoots(GCHeap::Promote,
19581                                 condemned_gen_number, max_generation,
19582                                 &sc);
19583
19584         fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes));
19585         last_promoted_bytes = promoted_bytes (heap_number);
19586
19587 #ifdef BACKGROUND_GC
19588         if (recursive_gc_sync::background_running_p())
19589         {
19590             scan_background_roots (GCHeap::Promote, heap_number, &sc);
19591         }
19592 #endif //BACKGROUND_GC
19593
19594 #ifdef FEATURE_PREMORTEM_FINALIZATION
19595         dprintf(3, ("Marking finalization data"));
19596         finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
19597 #endif // FEATURE_PREMORTEM_FINALIZATION
19598
19599         fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes));
19600         last_promoted_bytes = promoted_bytes (heap_number);
19601
19602 // MTHTS
19603         {
19604
19605             dprintf(3,("Marking handle table"));
19606             GCScan::GcScanHandles(GCHeap::Promote,
19607                                       condemned_gen_number, max_generation,
19608                                       &sc);
19609             fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes));
19610             last_promoted_bytes = promoted_bytes (heap_number);
19611         }
19612
19613 #ifdef TRACE_GC
19614         size_t promoted_before_cards = promoted_bytes (heap_number);
19615 #endif //TRACE_GC
19616
19617         dprintf (3, ("before cards: %Id", promoted_before_cards));
19618         if (!full_p)
19619         {
19620 #ifdef CARD_BUNDLE
19621 #ifdef MULTIPLE_HEAPS
19622             if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
19623             {
19624 #endif //MULTIPLE_HEAPS
19625
19626 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
19627                 // If we are manually managing card bundles, every write to the card table should already be
19628                 // accounted for in the card bundle table so there's nothing to update here.
19629                 update_card_table_bundle();
19630 #endif
19631                 if (card_bundles_enabled())
19632                 {
19633                     verify_card_bundles();
19634                 }
19635
19636 #ifdef MULTIPLE_HEAPS
19637                 gc_t_join.r_restart();
19638             }
19639 #endif //MULTIPLE_HEAPS
19640 #endif //CARD_BUNDLE
19641
19642             card_fn mark_object_fn = &gc_heap::mark_object_simple;
19643 #ifdef HEAP_ANALYZE
19644             heap_analyze_success = TRUE;
19645             if (heap_analyze_enabled)
19646             {
19647                 internal_root_array_index = 0;
19648                 current_obj = 0;
19649                 current_obj_size = 0;
19650                 mark_object_fn = &gc_heap::ha_mark_object_simple;
19651             }
19652 #endif //HEAP_ANALYZE
19653
19654             dprintf(3,("Marking cross generation pointers"));
19655             mark_through_cards_for_segments (mark_object_fn, FALSE);
19656
19657             dprintf(3,("Marking cross generation pointers for large objects"));
19658             mark_through_cards_for_large_objects (mark_object_fn, FALSE);
19659
19660             dprintf (3, ("marked by cards: %Id", 
19661                 (promoted_bytes (heap_number) - promoted_before_cards)));
19662             fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes));
19663             last_promoted_bytes = promoted_bytes (heap_number);
19664         }
19665     }
19666
19667 #ifdef MH_SC_MARK
19668     if (do_mark_steal_p)
19669     {
19670         mark_steal();
19671     }
19672 #endif //MH_SC_MARK
19673
19674     // Dependent handles need to be scanned with a special algorithm (see the header comment on
19675     // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
19676     // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
19677     // but in a common case (where there are no dependent handles that are due to be collected) it allows us
19678     // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
19679     // iterations if required and will also perform processing of any mark stack overflow once the dependent
19680     // handle table has been fully promoted.
19681     GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19682     scan_dependent_handles(condemned_gen_number, &sc, true);
19683
19684 #ifdef MULTIPLE_HEAPS
19685     dprintf(3, ("Joining for short weak handle scan"));
19686     gc_t_join.join(this, gc_join_null_dead_short_weak);
19687     if (gc_t_join.joined())
19688 #endif //MULTIPLE_HEAPS
19689     {
19690 #ifdef HEAP_ANALYZE
19691         heap_analyze_enabled = FALSE;
19692         GCToEEInterface::AnalyzeSurvivorsFinished(condemned_gen_number);
19693 #endif // HEAP_ANALYZE
19694         GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
19695
19696 #ifdef MULTIPLE_HEAPS
19697         if (!full_p)
19698         {
19699             // we used r_join and need to reinitialize states for it here.
19700             gc_t_join.r_init();
19701         }
19702
19703         //start all threads on the roots.
19704         dprintf(3, ("Starting all gc thread for short weak handle scan"));
19705         gc_t_join.restart();
19706 #endif //MULTIPLE_HEAPS
19707
19708     }
19709
19710     // null out the target of short weakref that were not promoted.
19711     GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
19712
19713 // MTHTS: keep by single thread
19714 #ifdef MULTIPLE_HEAPS
19715     dprintf(3, ("Joining for finalization"));
19716     gc_t_join.join(this, gc_join_scan_finalization);
19717     if (gc_t_join.joined())
19718 #endif //MULTIPLE_HEAPS
19719
19720     {
19721 #ifdef MULTIPLE_HEAPS
19722         //start all threads on the roots.
19723         dprintf(3, ("Starting all gc thread for Finalization"));
19724         gc_t_join.restart();
19725 #endif //MULTIPLE_HEAPS
19726     }
19727
19728     //Handle finalization.
19729     size_t promoted_bytes_live = promoted_bytes (heap_number);
19730
19731 #ifdef FEATURE_PREMORTEM_FINALIZATION
19732     dprintf (3, ("Finalize marking"));
19733     finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
19734
19735     GCToEEInterface::DiagWalkFReachableObjects(__this);
19736 #endif // FEATURE_PREMORTEM_FINALIZATION
19737
19738     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
19739     // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
19740     scan_dependent_handles(condemned_gen_number, &sc, false);
19741
19742 #ifdef MULTIPLE_HEAPS
19743     dprintf(3, ("Joining for weak pointer deletion"));
19744     gc_t_join.join(this, gc_join_null_dead_long_weak);
19745     if (gc_t_join.joined())
19746     {
19747         //start all threads on the roots.
19748         dprintf(3, ("Starting all gc thread for weak pointer deletion"));
19749         gc_t_join.restart();
19750     }
19751 #endif //MULTIPLE_HEAPS
19752
19753     // null out the target of long weakref that were not promoted.
19754     GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19755
19756 // MTHTS: keep by single thread
19757 #ifdef MULTIPLE_HEAPS
19758 #ifdef MARK_LIST
19759 #ifdef PARALLEL_MARK_LIST_SORT
19760 //    unsigned long start = GetCycleCount32();
19761     sort_mark_list();
19762 //    printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
19763 #endif //PARALLEL_MARK_LIST_SORT
19764 #endif //MARK_LIST
19765
19766     dprintf (3, ("Joining for sync block cache entry scanning"));
19767     gc_t_join.join(this, gc_join_null_dead_syncblk);
19768     if (gc_t_join.joined())
19769 #endif //MULTIPLE_HEAPS
19770     {
19771         // scan for deleted entries in the syncblk cache
19772         GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
19773
19774 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
19775         if (g_fEnableARM)
19776         {
19777             size_t promoted_all_heaps = 0;
19778 #ifdef MULTIPLE_HEAPS
19779             for (int i = 0; i < n_heaps; i++)
19780             {
19781                 promoted_all_heaps += promoted_bytes (i);
19782             }
19783 #else
19784             promoted_all_heaps = promoted_bytes (heap_number);
19785 #endif //MULTIPLE_HEAPS
19786             SystemDomain::RecordTotalSurvivedBytes (promoted_all_heaps);
19787         }
19788 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
19789
19790 #ifdef MULTIPLE_HEAPS
19791
19792 #ifdef MARK_LIST
19793 #ifndef PARALLEL_MARK_LIST_SORT
19794         //compact g_mark_list and sort it.
19795         combine_mark_lists();
19796 #endif //PARALLEL_MARK_LIST_SORT
19797 #endif //MARK_LIST
19798
19799         //decide on promotion
19800         if (!settings.promotion)
19801         {
19802             size_t m = 0;
19803             for (int n = 0; n <= condemned_gen_number;n++)
19804             {
19805                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.1);
19806             }
19807
19808             for (int i = 0; i < n_heaps; i++)
19809             {
19810                 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
19811                                                                      max_generation));
19812                 size_t older_gen_size = (dd_current_size (dd) +
19813                                          (dd_desired_allocation (dd) -
19814                                          dd_new_allocation (dd)));
19815
19816                 if ((m > (older_gen_size)) ||
19817                     (promoted_bytes (i) > m))
19818                 {
19819                     settings.promotion = TRUE;
19820                 }
19821             }
19822         }
19823
19824 #ifdef SNOOP_STATS
19825         if (do_mark_steal_p)
19826         {
19827             size_t objects_checked_count = 0;
19828             size_t zero_ref_count = 0;
19829             size_t objects_marked_count = 0;
19830             size_t check_level_count = 0;
19831             size_t busy_count = 0;
19832             size_t interlocked_count = 0;
19833             size_t partial_mark_parent_count = 0;
19834             size_t stolen_or_pm_count = 0; 
19835             size_t stolen_entry_count = 0; 
19836             size_t pm_not_ready_count = 0; 
19837             size_t normal_count = 0;
19838             size_t stack_bottom_clear_count = 0;
19839
19840             for (int i = 0; i < n_heaps; i++)
19841             {
19842                 gc_heap* hp = g_heaps[i];
19843                 hp->print_snoop_stat();
19844                 objects_checked_count += hp->snoop_stat.objects_checked_count;
19845                 zero_ref_count += hp->snoop_stat.zero_ref_count;
19846                 objects_marked_count += hp->snoop_stat.objects_marked_count;
19847                 check_level_count += hp->snoop_stat.check_level_count;
19848                 busy_count += hp->snoop_stat.busy_count;
19849                 interlocked_count += hp->snoop_stat.interlocked_count;
19850                 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
19851                 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
19852                 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
19853                 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
19854                 normal_count += hp->snoop_stat.normal_count;
19855                 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
19856             }
19857
19858             fflush (stdout);
19859
19860             printf ("-------total stats-------\n");
19861             printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
19862                 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
19863             printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
19864                 objects_checked_count,
19865                 zero_ref_count,
19866                 objects_marked_count,
19867                 check_level_count,
19868                 busy_count,
19869                 interlocked_count,
19870                 partial_mark_parent_count,
19871                 stolen_or_pm_count,
19872                 stolen_entry_count,
19873                 pm_not_ready_count,
19874                 normal_count,
19875                 stack_bottom_clear_count);
19876         }
19877 #endif //SNOOP_STATS
19878
19879         //start all threads.
19880         dprintf(3, ("Starting all threads for end of mark phase"));
19881         gc_t_join.restart();
19882 #else //MULTIPLE_HEAPS
19883
19884         //decide on promotion
19885         if (!settings.promotion)
19886         {
19887             size_t m = 0;
19888             for (int n = 0; n <= condemned_gen_number;n++)
19889             {
19890                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06);
19891             }
19892             dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
19893                                                      max_generation));
19894             size_t older_gen_size = (dd_current_size (dd) +
19895                                      (dd_desired_allocation (dd) -
19896                                      dd_new_allocation (dd)));
19897
19898             dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
19899                          m, promoted_bytes (heap_number), older_gen_size));
19900
19901             if ((m > older_gen_size) ||
19902                     (promoted_bytes (heap_number) > m))
19903             {
19904                 settings.promotion = TRUE;
19905             }
19906         }
19907
19908 #endif //MULTIPLE_HEAPS
19909     }
19910
19911 #ifdef MULTIPLE_HEAPS
19912 #ifdef MARK_LIST
19913 #ifdef PARALLEL_MARK_LIST_SORT
19914 //    start = GetCycleCount32();
19915     merge_mark_lists();
19916 //    printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
19917 #endif //PARALLEL_MARK_LIST_SORT
19918 #endif //MARK_LIST
19919 #endif //MULTIPLE_HEAPS
19920
19921 #ifdef BACKGROUND_GC
19922     total_promoted_bytes = promoted_bytes (heap_number);
19923 #endif //BACKGROUND_GC
19924
19925     promoted_bytes (heap_number) -= promoted_bytes_live;
19926
19927 #ifdef TIME_GC
19928         finish = GetCycleCount32();
19929         mark_time = finish - start;
19930 #endif //TIME_GC
19931
19932     dprintf(2,("---- End of mark phase ----"));
19933 }
19934
19935 inline
19936 void gc_heap::pin_object (uint8_t* o, uint8_t** ppObject, uint8_t* low, uint8_t* high)
19937 {
19938     dprintf (3, ("Pinning %Ix", (size_t)o));
19939     if ((o >= low) && (o < high))
19940     {
19941         dprintf(3,("^%Ix^", (size_t)o));
19942         set_pinned (o);
19943
19944 #ifdef FEATURE_EVENT_TRACE        
19945         if(EVENT_ENABLED(PinObjectAtGCTime))
19946         {
19947             fire_etw_pin_object_event(o, ppObject);
19948         }
19949 #endif // FEATURE_EVENT_TRACE
19950
19951 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
19952         num_pinned_objects++;
19953 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
19954     }
19955 }
19956
19957 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
19958 size_t gc_heap::get_total_pinned_objects()
19959 {
19960 #ifdef MULTIPLE_HEAPS
19961     size_t total_num_pinned_objects = 0;
19962     for (int i = 0; i < gc_heap::n_heaps; i++)
19963     {
19964         gc_heap* hp = gc_heap::g_heaps[i];
19965         total_num_pinned_objects += hp->num_pinned_objects;
19966     }
19967     return total_num_pinned_objects;
19968 #else //MULTIPLE_HEAPS
19969     return num_pinned_objects;
19970 #endif //MULTIPLE_HEAPS
19971 }
19972 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
19973
19974 void gc_heap::reset_mark_stack ()
19975 {
19976     reset_pinned_queue();
19977     max_overflow_address = 0;
19978     min_overflow_address = MAX_PTR;
19979 }
19980
19981 #ifdef FEATURE_STRUCTALIGN
19982 //
19983 // The word with left child, right child, and align info is laid out as follows:
19984 //
19985 //      |   upper short word   |   lower short word   |
19986 //      |<------------> <----->|<------------> <----->|
19987 //      |  left child   info hi| right child   info lo|
19988 // x86: |    10 bits     6 bits|   10 bits      6 bits|
19989 //
19990 // where left/right child are signed values and concat(info hi, info lo) is unsigned.
19991 //
19992 // The "align info" encodes two numbers: the required alignment (a power of two)
19993 // and the misalignment (the number of machine words the destination address needs
19994 // to be adjusted by to provide alignment - so this number is always smaller than
19995 // the required alignment).  Thus, the two can be represented as the "logical or"
19996 // of the two numbers.  Note that the actual pad is computed from the misalignment
19997 // by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
19998 //
19999
20000 // The number of bits in a brick.
20001 #if defined (_TARGET_AMD64_)
20002 #define brick_bits (12)
20003 #else
20004 #define brick_bits (11)
20005 #endif //_TARGET_AMD64_
20006 C_ASSERT(brick_size == (1 << brick_bits));
20007
20008 // The number of bits needed to represent the offset to a child node.
20009 // "brick_bits + 1" allows us to represent a signed offset within a brick.
20010 #define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
20011
20012 // The number of bits in each of the pad hi, pad lo fields.
20013 #define pad_bits (sizeof(short) * 8 - child_bits)
20014
20015 #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
20016 #define pad_mask ((1 << pad_bits) - 1)
20017 #define pad_from_short(w) ((size_t)(w) & pad_mask)
20018 #else // FEATURE_STRUCTALIGN
20019 #define child_from_short(w) (w)
20020 #endif // FEATURE_STRUCTALIGN
20021
20022 inline
20023 short node_left_child(uint8_t* node)
20024 {
20025     return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20026 }
20027
20028 inline
20029 void set_node_left_child(uint8_t* node, ptrdiff_t val)
20030 {
20031     assert (val > -(ptrdiff_t)brick_size);
20032     assert (val < (ptrdiff_t)brick_size);
20033     assert (Aligned (val));
20034 #ifdef FEATURE_STRUCTALIGN
20035     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20036     ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20037 #else // FEATURE_STRUCTALIGN
20038     ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
20039 #endif // FEATURE_STRUCTALIGN
20040     assert (node_left_child (node) == val);
20041 }
20042
20043 inline
20044 short node_right_child(uint8_t* node)
20045 {
20046     return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20047 }
20048
20049 inline
20050 void set_node_right_child(uint8_t* node, ptrdiff_t val)
20051 {
20052     assert (val > -(ptrdiff_t)brick_size);
20053     assert (val < (ptrdiff_t)brick_size);
20054     assert (Aligned (val));
20055 #ifdef FEATURE_STRUCTALIGN
20056     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20057     ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20058 #else // FEATURE_STRUCTALIGN
20059     ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
20060 #endif // FEATURE_STRUCTALIGN
20061     assert (node_right_child (node) == val);
20062 }
20063
20064 #ifdef FEATURE_STRUCTALIGN
20065 void node_aligninfo (uint8_t* node, int& requiredAlignment, ptrdiff_t& pad)
20066 {
20067     // Extract the single-number aligninfo from the fields.
20068     short left = ((plug_and_pair*)node)[-1].m_pair.left;
20069     short right = ((plug_and_pair*)node)[-1].m_pair.right;
20070     ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
20071     ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
20072
20073     // Replicate the topmost bit into all lower bits.
20074     ptrdiff_t x = aligninfo;
20075     x |= x >> 8;
20076     x |= x >> 4;
20077     x |= x >> 2;
20078     x |= x >> 1;
20079
20080     // Clear all bits but the highest.
20081     requiredAlignment = (int)(x ^ (x >> 1));
20082     pad = aligninfo - requiredAlignment;
20083     pad += AdjustmentForMinPadSize(pad, requiredAlignment);
20084 }
20085
20086 inline
20087 ptrdiff_t node_alignpad (uint8_t* node)
20088 {
20089     int requiredAlignment;
20090     ptrdiff_t alignpad;
20091     node_aligninfo (node, requiredAlignment, alignpad);
20092     return alignpad;
20093 }
20094
20095 void clear_node_aligninfo (uint8_t* node)
20096 {
20097     ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
20098     ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
20099 }
20100
20101 void set_node_aligninfo (uint8_t* node, int requiredAlignment, ptrdiff_t pad)
20102 {
20103     // Encode the alignment requirement and alignment offset as a single number
20104     // as described above.
20105     ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
20106     assert (Aligned (aligninfo));
20107     ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
20108     assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
20109
20110     ptrdiff_t hi = aligninfo_shifted >> pad_bits;
20111     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
20112     ((plug_and_pair*)node)[-1].m_pair.left |= hi;
20113
20114     ptrdiff_t lo = aligninfo_shifted & pad_mask;
20115     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
20116     ((plug_and_pair*)node)[-1].m_pair.right |= lo;
20117
20118 #ifdef _DEBUG
20119     int requiredAlignment2;
20120     ptrdiff_t pad2;
20121     node_aligninfo (node, requiredAlignment2, pad2);
20122     assert (requiredAlignment == requiredAlignment2);
20123     assert (pad == pad2);
20124 #endif // _DEBUG
20125 }
20126 #endif // FEATURE_STRUCTALIGN
20127
20128 inline
20129 void loh_set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20130 {
20131     ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
20132     *place = val;
20133 }
20134
20135 inline
20136 ptrdiff_t loh_node_relocation_distance(uint8_t* node)
20137 {
20138     return (((loh_obj_and_pad*)node)[-1].reloc);
20139 }
20140
20141 inline
20142 ptrdiff_t node_relocation_distance (uint8_t* node)
20143 {
20144     return (((plug_and_reloc*)(node))[-1].reloc & ~3);
20145 }
20146
20147 inline
20148 void set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20149 {
20150     assert (val == (val & ~3));
20151     ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
20152     //clear the left bit and the relocation field
20153     *place &= 1;
20154     // store the value
20155     *place |= val;
20156 }
20157
20158 #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
20159
20160 #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
20161
20162 #ifndef FEATURE_STRUCTALIGN
20163 void set_node_realigned(uint8_t* node)
20164 {
20165     ((plug_and_reloc*)(node))[-1].reloc |= 1;
20166 }
20167
20168 void clear_node_realigned(uint8_t* node)
20169 {
20170 #ifdef RESPECT_LARGE_ALIGNMENT
20171     ((plug_and_reloc*)(node))[-1].reloc &= ~1;
20172 #else
20173     UNREFERENCED_PARAMETER(node);
20174 #endif //RESPECT_LARGE_ALIGNMENT
20175 }
20176 #endif // FEATURE_STRUCTALIGN
20177
20178 inline
20179 size_t  node_gap_size (uint8_t* node)
20180 {
20181     return ((plug_and_gap *)node)[-1].gap;
20182 }
20183
20184 void set_gap_size (uint8_t* node, size_t size)
20185 {
20186     assert (Aligned (size));
20187
20188     // clear the 2 uint32_t used by the node.
20189     ((plug_and_gap *)node)[-1].reloc = 0;
20190     ((plug_and_gap *)node)[-1].lr =0;
20191     ((plug_and_gap *)node)[-1].gap = size;
20192
20193     assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
20194
20195 }
20196
20197 uint8_t* gc_heap::insert_node (uint8_t* new_node, size_t sequence_number,
20198                    uint8_t* tree, uint8_t* last_node)
20199 {
20200     dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
20201                  (size_t)new_node, brick_of(new_node), 
20202                  (size_t)tree, brick_of(tree), 
20203                  (size_t)last_node, brick_of(last_node),
20204                  sequence_number));
20205     if (power_of_two_p (sequence_number))
20206     {
20207         set_node_left_child (new_node, (tree - new_node));
20208         dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
20209         tree = new_node;
20210     }
20211     else
20212     {
20213         if (oddp (sequence_number))
20214         {
20215             set_node_right_child (last_node, (new_node - last_node));
20216             dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
20217         }
20218         else
20219         {
20220             uint8_t*  earlier_node = tree;
20221             size_t imax = logcount(sequence_number) - 2;
20222             for (size_t i = 0; i != imax; i++)
20223             {
20224                 earlier_node = earlier_node + node_right_child (earlier_node);
20225             }
20226             int tmp_offset = node_right_child (earlier_node);
20227             assert (tmp_offset); // should never be empty
20228             set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
20229             set_node_right_child (earlier_node, (new_node - earlier_node));
20230
20231             dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix", 
20232                 new_node, ((earlier_node + tmp_offset ) - new_node),
20233                 earlier_node, (new_node - earlier_node)));
20234         }
20235     }
20236     return tree;
20237 }
20238
20239 size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick,
20240                                     uint8_t* x, uint8_t* plug_end)
20241 {
20242     dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
20243         tree, current_brick, x, plug_end));
20244
20245     if (tree != NULL)
20246     {
20247         dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix", 
20248             current_brick, (size_t)(tree - brick_address (current_brick)), tree));
20249         set_brick (current_brick, (tree - brick_address (current_brick)));
20250     }
20251     else
20252     {
20253         dprintf (3, ("b- %Ix->-1", current_brick));
20254         set_brick (current_brick, -1);
20255     }
20256     size_t  b = 1 + current_brick;
20257     ptrdiff_t  offset = 0;
20258     size_t last_br = brick_of (plug_end-1);
20259     current_brick = brick_of (x-1);
20260     dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
20261     while (b <= current_brick)
20262     {
20263         if (b <= last_br)
20264         {
20265             set_brick (b, --offset);
20266         }
20267         else
20268         {
20269             set_brick (b,-1);
20270         }
20271         b++;
20272     }
20273     return brick_of (x);
20274 }
20275
20276 void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate)
20277 {
20278 #ifdef BIT64
20279     // We should never demote big plugs to gen0.
20280     if (gen == youngest_generation)
20281     {
20282         heap_segment* seg = ephemeral_heap_segment;
20283         size_t mark_stack_large_bos = mark_stack_bos;
20284         size_t large_plug_pos = 0;
20285         while (mark_stack_large_bos < mark_stack_tos)
20286         {
20287             if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
20288             {
20289                 while (mark_stack_bos <= mark_stack_large_bos)
20290                 {
20291                     size_t entry = deque_pinned_plug();
20292                     size_t len = pinned_len (pinned_plug_of (entry));
20293                     uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20294                     if (len > demotion_plug_len_th)
20295                     {
20296                         dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
20297                     }
20298                     pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
20299                     assert(mark_stack_array[entry].len == 0 ||
20300                             mark_stack_array[entry].len >= Align(min_obj_size));
20301                     generation_allocation_pointer (consing_gen) = plug + len;
20302                     generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
20303                     set_allocator_next_pin (consing_gen);
20304                 }
20305             }
20306
20307             mark_stack_large_bos++;
20308         }
20309     }
20310 #endif // BIT64
20311
20312     generation_plan_allocation_start (gen) =
20313         allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
20314     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20315     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20316     if (next_plug_to_allocate)
20317     {
20318         size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
20319         if (allocation_left > dist_to_next_plug)
20320         {
20321             allocation_left = dist_to_next_plug;
20322         }
20323     }
20324     if (allocation_left < Align (min_obj_size))
20325     {
20326         generation_plan_allocation_start_size (gen) += allocation_left;
20327         generation_allocation_pointer (consing_gen) += allocation_left;
20328     }
20329
20330     dprintf (1, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)", gen->gen_num, 
20331         generation_plan_allocation_start (gen),
20332         generation_plan_allocation_start_size (gen),
20333         generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen),
20334         next_plug_to_allocate));
20335 }
20336
20337 void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
20338 {
20339     BOOL adjacentp = FALSE;
20340
20341     generation_plan_allocation_start (gen) =  
20342         allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0, 
20343 #ifdef SHORT_PLUGS
20344                                    FALSE, NULL, 
20345 #endif //SHORT_PLUGS
20346                                    FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
20347
20348     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20349     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20350     if ((allocation_left < Align (min_obj_size)) && 
20351          (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
20352     {
20353         generation_plan_allocation_start_size (gen) += allocation_left;
20354         generation_allocation_pointer (consing_gen) += allocation_left;
20355     }
20356
20357     dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num, 
20358         generation_plan_allocation_start (consing_gen),
20359         generation_allocation_pointer (consing_gen), 
20360         generation_allocation_limit (consing_gen))); 
20361 }
20362
20363 void gc_heap::plan_generation_starts (generation*& consing_gen)
20364 {
20365     //make sure that every generation has a planned allocation start
20366     int  gen_number = settings.condemned_generation;
20367     while (gen_number >= 0)
20368     {
20369         if (gen_number < max_generation)
20370         {
20371             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20372         }
20373         generation* gen = generation_of (gen_number);
20374         if (0 == generation_plan_allocation_start (gen))
20375         {
20376             plan_generation_start (gen, consing_gen, 0);
20377             assert (generation_plan_allocation_start (gen));
20378         }
20379         gen_number--;
20380     }
20381     // now we know the planned allocation size
20382     heap_segment_plan_allocated (ephemeral_heap_segment) =
20383         generation_allocation_pointer (consing_gen);
20384 }
20385
20386 void gc_heap::advance_pins_for_demotion (generation* gen)
20387 {
20388     uint8_t* original_youngest_start = generation_allocation_start (youngest_generation);
20389     heap_segment* seg = ephemeral_heap_segment;
20390
20391     if ((!(pinned_plug_que_empty_p())))
20392     {
20393         size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
20394         size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
20395         size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
20396         float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
20397         float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
20398         if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
20399         {
20400             while (!pinned_plug_que_empty_p() &&
20401                     (pinned_plug (oldest_pin()) < original_youngest_start))
20402             {
20403                 size_t entry = deque_pinned_plug();
20404                 size_t len = pinned_len (pinned_plug_of (entry));
20405                 uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20406                 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
20407                 assert(mark_stack_array[entry].len == 0 ||
20408                         mark_stack_array[entry].len >= Align(min_obj_size));
20409                 generation_allocation_pointer (gen) = plug + len;
20410                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20411                 set_allocator_next_pin (gen);
20412
20413                 //Add the size of the pinned plug to the right pinned allocations
20414                 //find out which gen this pinned plug came from 
20415                 int frgn = object_gennum (plug);
20416                 if ((frgn != (int)max_generation) && settings.promotion)
20417                 {
20418                     int togn = object_gennum_plan (plug);
20419                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20420                     if (frgn < togn)
20421                     {
20422                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
20423                     }
20424                 }
20425
20426                 dprintf (2, ("skipping gap %d, pin %Ix (%Id)", 
20427                     pinned_len (pinned_plug_of (entry)), plug, len));
20428             }
20429         }
20430         dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d", 
20431             gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
20432     }
20433 }
20434
20435 void gc_heap::process_ephemeral_boundaries (uint8_t* x,
20436                                             int& active_new_gen_number,
20437                                             int& active_old_gen_number,
20438                                             generation*& consing_gen,
20439                                             BOOL& allocate_in_condemned)
20440 {
20441 retry:
20442     if ((active_old_gen_number > 0) &&
20443         (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
20444     {
20445         dprintf (1, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
20446
20447         if (!pinned_plug_que_empty_p())
20448         {
20449             dprintf (1, ("oldest pin: %Ix(%Id)",
20450                 pinned_plug (oldest_pin()), 
20451                 (x - pinned_plug (oldest_pin()))));
20452         }
20453
20454         if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
20455         {
20456             active_new_gen_number--;
20457         }
20458
20459         active_old_gen_number--;
20460         assert ((!settings.promotion) || (active_new_gen_number>0));
20461
20462         if (active_new_gen_number == (max_generation - 1))
20463         {
20464 #ifdef FREE_USAGE_STATS
20465             if (settings.condemned_generation == max_generation)
20466             {
20467                 // We need to do this before we skip the rest of the pinned plugs.
20468                 generation* gen_2 = generation_of (max_generation);
20469                 generation* gen_1 = generation_of (max_generation - 1);
20470
20471                 size_t total_num_pinned_free_spaces_left = 0;
20472
20473                 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
20474                 for (int j = 0; j < NUM_GEN_POWER2; j++)
20475                 {
20476                     dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)", 
20477                         heap_number, 
20478                         settings.gc_index,
20479                         (j + 10), 
20480                         gen_2->gen_current_pinned_free_spaces[j],
20481                         gen_2->gen_plugs[j], gen_1->gen_plugs[j],
20482                         (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
20483
20484                     total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
20485                 }
20486
20487                 float pinned_free_list_efficiency = 0;
20488                 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
20489                 if (total_pinned_free_space != 0)
20490                 {
20491                     pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
20492                 }
20493
20494                 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
20495                             heap_number,
20496                             generation_allocated_in_pinned_free (gen_2),
20497                             total_pinned_free_space, 
20498                             (int)(pinned_free_list_efficiency * 100),
20499                             generation_pinned_free_obj_space (gen_2),
20500                             total_num_pinned_free_spaces_left));
20501             }
20502 #endif //FREE_USAGE_STATS
20503
20504             //Go past all of the pinned plugs for this generation.
20505             while (!pinned_plug_que_empty_p() &&
20506                    (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
20507             {
20508                 size_t  entry = deque_pinned_plug();
20509                 mark*  m = pinned_plug_of (entry);
20510                 uint8_t*  plug = pinned_plug (m);
20511                 size_t  len = pinned_len (m);
20512                 // detect pinned block in different segment (later) than
20513                 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
20514                 // adjust the allocation segment along the way (at the end it will
20515                 // be the ephemeral segment.
20516                 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
20517
20518                 PREFIX_ASSUME(nseg != NULL);
20519
20520                 while (!((plug >= generation_allocation_pointer (consing_gen))&&
20521                         (plug < heap_segment_allocated (nseg))))
20522                 {
20523                     //adjust the end of the segment to be the end of the plug
20524                     assert (generation_allocation_pointer (consing_gen)>=
20525                             heap_segment_mem (nseg));
20526                     assert (generation_allocation_pointer (consing_gen)<=
20527                             heap_segment_committed (nseg));
20528
20529                     heap_segment_plan_allocated (nseg) =
20530                         generation_allocation_pointer (consing_gen);
20531                     //switch allocation segment
20532                     nseg = heap_segment_next_rw (nseg);
20533                     generation_allocation_segment (consing_gen) = nseg;
20534                     //reset the allocation pointer and limits
20535                     generation_allocation_pointer (consing_gen) =
20536                         heap_segment_mem (nseg);
20537                 }
20538                 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
20539                 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
20540                 generation_allocation_pointer (consing_gen) = plug + len;
20541                 generation_allocation_limit (consing_gen) =
20542                     generation_allocation_pointer (consing_gen);
20543             }
20544             allocate_in_condemned = TRUE;
20545             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20546         }
20547
20548         if (active_new_gen_number != max_generation)
20549         {
20550             if (active_new_gen_number == (max_generation - 1))
20551             {
20552                 maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
20553                 if (!demote_gen1_p)
20554                     advance_pins_for_demotion (consing_gen);
20555             }
20556
20557             plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
20558                 
20559             dprintf (1, ("process eph: allocated gen%d start at %Ix", 
20560                 active_new_gen_number,
20561                 generation_plan_allocation_start (generation_of (active_new_gen_number))));
20562
20563             if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
20564             {
20565                 uint8_t* pplug = pinned_plug (oldest_pin());
20566                 if (object_gennum (pplug) > 0)
20567                 {
20568                     demotion_low = pplug;
20569                     dprintf (3, ("process eph: dlow->%Ix", demotion_low));
20570                 }
20571             }
20572
20573             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
20574         }
20575
20576         goto retry;
20577     }
20578 }
20579
20580 inline
20581 void gc_heap::seg_clear_mark_bits (heap_segment* seg)
20582 {
20583     uint8_t* o = heap_segment_mem (seg);
20584     while (o < heap_segment_allocated (seg))
20585     {
20586         if (marked (o))
20587         {
20588             clear_marked (o);
20589         }
20590         o = o  + Align (size (o));
20591     }
20592 }
20593
20594 #ifdef FEATURE_BASICFREEZE
20595 void gc_heap::sweep_ro_segments (heap_segment* start_seg)
20596 {
20597     //go through all of the segment in range and reset the mark bit
20598     //TODO works only on small object segments
20599
20600     heap_segment* seg = start_seg;
20601
20602     while (seg)
20603     {
20604         if (heap_segment_read_only_p (seg) &&
20605             heap_segment_in_range_p (seg))
20606         {
20607 #ifdef BACKGROUND_GC
20608             if (settings.concurrent)
20609             {
20610                 seg_clear_mark_array_bits_soh (seg);
20611             }
20612             else
20613             {
20614                 seg_clear_mark_bits (seg);
20615             }
20616 #else //BACKGROUND_GC
20617
20618 #ifdef MARK_ARRAY
20619             if(gc_can_use_concurrent)
20620             {
20621                 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
20622                               min (heap_segment_allocated (seg), highest_address),
20623                               FALSE); // read_only segments need the mark clear
20624             }
20625 #else //MARK_ARRAY
20626             seg_clear_mark_bits (seg);
20627 #endif //MARK_ARRAY
20628
20629 #endif //BACKGROUND_GC
20630         }
20631         seg = heap_segment_next (seg);
20632     }
20633 }
20634 #endif // FEATURE_BASICFREEZE
20635
20636 #ifdef FEATURE_LOH_COMPACTION
20637 inline
20638 BOOL gc_heap::loh_pinned_plug_que_empty_p()
20639 {
20640     return (loh_pinned_queue_bos == loh_pinned_queue_tos);
20641 }
20642
20643 void gc_heap::loh_set_allocator_next_pin()
20644 {
20645     if (!(loh_pinned_plug_que_empty_p()))
20646     {
20647         mark*  oldest_entry = loh_oldest_pin();
20648         uint8_t* plug = pinned_plug (oldest_entry);
20649         generation* gen = large_object_generation;
20650         if ((plug >= generation_allocation_pointer (gen)) &&
20651             (plug <  generation_allocation_limit (gen)))
20652         {
20653             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
20654         }
20655         else
20656             assert (!((plug < generation_allocation_pointer (gen)) &&
20657                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
20658     }
20659 }
20660
20661 size_t gc_heap::loh_deque_pinned_plug ()
20662 {
20663     size_t m = loh_pinned_queue_bos;
20664     loh_pinned_queue_bos++;
20665     return m;
20666 }
20667
20668 inline
20669 mark* gc_heap::loh_pinned_plug_of (size_t bos)
20670 {
20671     return &loh_pinned_queue[bos];
20672 }
20673
20674 inline
20675 mark* gc_heap::loh_oldest_pin()
20676 {
20677     return loh_pinned_plug_of (loh_pinned_queue_bos);
20678 }
20679
20680 // If we can't grow the queue, then don't compact.
20681 BOOL gc_heap::loh_enque_pinned_plug (uint8_t* plug, size_t len)
20682 {
20683     assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
20684
20685     if (loh_pinned_queue_length <= loh_pinned_queue_tos)
20686     {
20687         if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
20688         {
20689             return FALSE;
20690         }
20691     }
20692     dprintf (3, (" P: %Ix(%Id)", plug, len));
20693     mark& m = loh_pinned_queue[loh_pinned_queue_tos];
20694     m.first = plug;
20695     m.len = len;
20696     loh_pinned_queue_tos++;
20697     loh_set_allocator_next_pin();
20698     return TRUE;
20699 }
20700
20701 inline
20702 BOOL gc_heap::loh_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit)
20703 {
20704     dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)", 
20705         size, 
20706         (2* AlignQword (loh_padding_obj_size) +  size),
20707         alloc_pointer,
20708         alloc_limit,
20709         (alloc_limit - alloc_pointer)));
20710
20711     return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) +  size) <= alloc_limit);
20712 }
20713
20714 uint8_t* gc_heap::loh_allocate_in_condemned (uint8_t* old_loc, size_t size)
20715 {
20716     UNREFERENCED_PARAMETER(old_loc);
20717
20718     generation* gen = large_object_generation;
20719     dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id", 
20720         generation_allocation_pointer (gen),
20721         generation_allocation_limit (gen),
20722         size));
20723
20724 retry:
20725     {
20726         heap_segment* seg = generation_allocation_segment (gen);
20727         if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
20728         {
20729             if ((!(loh_pinned_plug_que_empty_p()) &&
20730                  (generation_allocation_limit (gen) ==
20731                   pinned_plug (loh_oldest_pin()))))
20732             {
20733                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
20734                 size_t len = pinned_len (m);
20735                 uint8_t* plug = pinned_plug (m);
20736                 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
20737                 pinned_len (m) = plug - generation_allocation_pointer (gen);
20738                 generation_allocation_pointer (gen) = plug + len;
20739                 
20740                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20741                 loh_set_allocator_next_pin();
20742                 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)", 
20743                     generation_allocation_pointer (gen), 
20744                     generation_allocation_limit (gen),
20745                     (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20746
20747                 goto retry;
20748             }
20749
20750             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
20751             {
20752                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20753                 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
20754             }
20755             else
20756             {
20757                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
20758                 {
20759                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
20760                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20761                     dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
20762                 }
20763                 else
20764                 {
20765                     if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
20766                         (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
20767                     {
20768                         dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
20769                                          (generation_allocation_pointer (gen) + size)));
20770
20771                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
20772                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20773
20774                         dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)", 
20775                             generation_allocation_pointer (gen), 
20776                             generation_allocation_limit (gen),
20777                             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20778                     }
20779                     else
20780                     {
20781                         heap_segment* next_seg = heap_segment_next (seg);
20782                         assert (generation_allocation_pointer (gen)>=
20783                                 heap_segment_mem (seg));
20784                         // Verify that all pinned plugs for this segment are consumed
20785                         if (!loh_pinned_plug_que_empty_p() &&
20786                             ((pinned_plug (loh_oldest_pin()) <
20787                               heap_segment_allocated (seg)) &&
20788                              (pinned_plug (loh_oldest_pin()) >=
20789                               generation_allocation_pointer (gen))))
20790                         {
20791                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
20792                                          pinned_plug (loh_oldest_pin())));
20793                             dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
20794                             FATAL_GC_ERROR();
20795                         }
20796                         assert (generation_allocation_pointer (gen)>=
20797                                 heap_segment_mem (seg));
20798                         assert (generation_allocation_pointer (gen)<=
20799                                 heap_segment_committed (seg));
20800                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
20801
20802                         if (next_seg)
20803                         {
20804                             // for LOH do we want to try starting from the first LOH every time though?
20805                             generation_allocation_segment (gen) = next_seg;
20806                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
20807                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
20808
20809                             dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)", 
20810                                 generation_allocation_pointer (gen), 
20811                                 generation_allocation_limit (gen),
20812                                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20813                         }
20814                         else
20815                         {
20816                             dprintf (1, ("We ran out of space compacting, shouldn't happen"));
20817                             FATAL_GC_ERROR();
20818                         }
20819                     }
20820                 }
20821             }
20822             loh_set_allocator_next_pin();
20823
20824             dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)", 
20825                 generation_allocation_pointer (gen), 
20826                 generation_allocation_limit (gen),
20827                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20828
20829             goto retry;
20830         }
20831     }
20832
20833     {
20834         assert (generation_allocation_pointer (gen)>=
20835                 heap_segment_mem (generation_allocation_segment (gen)));
20836         uint8_t* result = generation_allocation_pointer (gen);
20837         size_t loh_pad = AlignQword (loh_padding_obj_size);
20838
20839         generation_allocation_pointer (gen) += size + loh_pad;
20840         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
20841
20842         dprintf (1235, ("p: %Ix, l: %Ix (%Id)", 
20843             generation_allocation_pointer (gen), 
20844             generation_allocation_limit (gen),
20845             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20846
20847         assert (result + loh_pad);
20848         return result + loh_pad;
20849     }
20850 }
20851
20852 BOOL gc_heap::should_compact_loh()
20853 {
20854     return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
20855 }
20856
20857 inline
20858 void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
20859 {
20860     if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
20861     {
20862         if (all_heaps_compacted_p)
20863         {
20864             // If the compaction mode says to compact once and we are going to compact LOH, 
20865             // we need to revert it back to no compaction.
20866             loh_compaction_mode = loh_compaction_default;
20867         }
20868     }
20869 }
20870
20871 BOOL gc_heap::plan_loh()
20872 {
20873     if (!loh_pinned_queue)
20874     {
20875         loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
20876         if (!loh_pinned_queue)
20877         {
20878             dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction", 
20879                          LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
20880             return FALSE;
20881         }
20882
20883         loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
20884     }
20885
20886     if (heap_number == 0)
20887         loh_pinned_queue_decay = LOH_PIN_DECAY;
20888
20889     loh_pinned_queue_tos = 0;
20890     loh_pinned_queue_bos = 0;
20891     
20892     generation* gen        = large_object_generation;
20893     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
20894     PREFIX_ASSUME(start_seg != NULL);
20895     heap_segment* seg      = start_seg;
20896     uint8_t* o             = generation_allocation_start (gen);
20897
20898     dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n", 
20899         generation_size (max_generation + 1), 
20900         generation_free_list_space (gen),
20901         generation_free_obj_space (gen)));
20902
20903     while (seg)
20904     {
20905         heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
20906         seg = heap_segment_next (seg);
20907     }
20908
20909     seg = start_seg;
20910
20911     //Skip the generation gap object
20912     o = o + AlignQword (size (o));
20913     // We don't need to ever realloc gen3 start so don't touch it.
20914     heap_segment_plan_allocated (seg) = o;
20915     generation_allocation_pointer (gen) = o;
20916     generation_allocation_limit (gen) = generation_allocation_pointer (gen);
20917     generation_allocation_segment (gen) = start_seg;
20918
20919     uint8_t* free_space_start = o;
20920     uint8_t* free_space_end = o;
20921     uint8_t* new_address = 0;
20922
20923     while (1)
20924     {
20925         if (o >= heap_segment_allocated (seg))
20926         {
20927             seg = heap_segment_next (seg);
20928             if (seg == 0)
20929             {
20930                 break;
20931             }
20932
20933             o = heap_segment_mem (seg);
20934         }
20935
20936         if (marked (o))
20937         {
20938             free_space_end = o;
20939             size_t size = AlignQword (size (o));
20940             dprintf (1235, ("%Ix(%Id) M", o, size));
20941
20942             if (pinned (o))
20943             {
20944                 // We don't clear the pinned bit yet so we can check in 
20945                 // compact phase how big a free object we should allocate
20946                 // in front of the pinned object. We use the reloc address
20947                 // field to store this.
20948                 if (!loh_enque_pinned_plug (o, size))
20949                 {
20950                     return FALSE;
20951                 }
20952                 new_address = o;
20953             }
20954             else
20955             {
20956                 new_address = loh_allocate_in_condemned (o, size);
20957             }
20958
20959             loh_set_node_relocation_distance (o, (new_address - o));
20960             dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
20961
20962             o = o + size;
20963             free_space_start = o;
20964             if (o < heap_segment_allocated (seg))
20965             {
20966                 assert (!marked (o));
20967             }
20968         }
20969         else
20970         {
20971             while (o < heap_segment_allocated (seg) && !marked (o))
20972             {
20973                 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_gc_pFreeObjectMethodTable) ? 1 : 0)));
20974                 o = o + AlignQword (size (o));
20975             }
20976         }
20977     }
20978
20979     while (!loh_pinned_plug_que_empty_p())
20980     {
20981         mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
20982         size_t len = pinned_len (m);
20983         uint8_t* plug = pinned_plug (m);
20984
20985         // detect pinned block in different segment (later) than
20986         // allocation segment
20987         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
20988
20989         while ((plug < generation_allocation_pointer (gen)) ||
20990                (plug >= heap_segment_allocated (nseg)))
20991         {
20992             assert ((plug < heap_segment_mem (nseg)) ||
20993                     (plug > heap_segment_reserved (nseg)));
20994             //adjust the end of the segment to be the end of the plug
20995             assert (generation_allocation_pointer (gen)>=
20996                     heap_segment_mem (nseg));
20997             assert (generation_allocation_pointer (gen)<=
20998                     heap_segment_committed (nseg));
20999
21000             heap_segment_plan_allocated (nseg) =
21001                 generation_allocation_pointer (gen);
21002             //switch allocation segment
21003             nseg = heap_segment_next_rw (nseg);
21004             generation_allocation_segment (gen) = nseg;
21005             //reset the allocation pointer and limits
21006             generation_allocation_pointer (gen) =
21007                 heap_segment_mem (nseg);
21008         }
21009
21010         dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21011         pinned_len (m) = plug - generation_allocation_pointer (gen);
21012         generation_allocation_pointer (gen) = plug + len;
21013     }
21014
21015     heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
21016     generation_allocation_pointer (gen) = 0;
21017     generation_allocation_limit (gen) = 0;
21018
21019     return TRUE;
21020 }
21021
21022 void gc_heap::compact_loh()
21023 {
21024     assert (should_compact_loh());
21025
21026     generation* gen        = large_object_generation;
21027     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21028     PREFIX_ASSUME(start_seg != NULL);
21029     heap_segment* seg      = start_seg;
21030     heap_segment* prev_seg = 0;
21031     uint8_t* o             = generation_allocation_start (gen);
21032
21033     //Skip the generation gap object
21034     o = o + AlignQword (size (o));
21035     // We don't need to ever realloc gen3 start so don't touch it.
21036     uint8_t* free_space_start = o;
21037     uint8_t* free_space_end = o;
21038     generation_allocator (gen)->clear();
21039     generation_free_list_space (gen) = 0;
21040     generation_free_obj_space (gen) = 0;
21041
21042     loh_pinned_queue_bos = 0;
21043
21044     while (1)
21045     {
21046         if (o >= heap_segment_allocated (seg))
21047         {
21048             heap_segment* next_seg = heap_segment_next (seg);
21049
21050             if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
21051                 (seg != start_seg) && !heap_segment_read_only_p (seg))
21052             {
21053                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
21054                 assert (prev_seg);
21055                 heap_segment_next (prev_seg) = next_seg;
21056                 heap_segment_next (seg) = freeable_large_heap_segment;
21057                 freeable_large_heap_segment = seg;
21058             }
21059             else
21060             {
21061                 if (!heap_segment_read_only_p (seg))
21062                 {
21063                     // We grew the segment to accommodate allocations.
21064                     if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
21065                     {
21066                         if ((heap_segment_plan_allocated (seg) - plug_skew)  > heap_segment_used (seg))
21067                         {
21068                             heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
21069                         }
21070                     }
21071
21072                     heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
21073                     dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
21074                     decommit_heap_segment_pages (seg, 0);
21075                     dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
21076                         seg, 
21077                         heap_segment_allocated (seg),
21078                         heap_segment_used (seg),
21079                         heap_segment_committed (seg)));
21080                     //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
21081                     dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
21082                 }
21083                 prev_seg = seg;
21084             }
21085
21086             seg = next_seg;
21087             if (seg == 0)
21088                 break;
21089             else
21090             {
21091                 o = heap_segment_mem (seg);
21092             }
21093         }
21094
21095         if (marked (o))
21096         {
21097             free_space_end = o;
21098             size_t size = AlignQword (size (o));
21099
21100             size_t loh_pad;
21101             uint8_t* reloc = o;
21102             clear_marked (o);
21103
21104             if (pinned (o))
21105             {
21106                 // We are relying on the fact the pinned objects are always looked at in the same order 
21107                 // in plan phase and in compact phase.
21108                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21109                 uint8_t* plug = pinned_plug (m);
21110                 assert (plug == o);
21111
21112                 loh_pad = pinned_len (m);
21113                 clear_pinned (o);
21114             }
21115             else
21116             {
21117                 loh_pad = AlignQword (loh_padding_obj_size);
21118
21119                 reloc += loh_node_relocation_distance (o);
21120                 gcmemcopy (reloc, o, size, TRUE);
21121             }
21122
21123             thread_gap ((reloc - loh_pad), loh_pad, gen);
21124
21125             o = o + size;
21126             free_space_start = o;
21127             if (o < heap_segment_allocated (seg))
21128             {
21129                 assert (!marked (o));
21130             }
21131         }
21132         else
21133         {
21134             while (o < heap_segment_allocated (seg) && !marked (o))
21135             {
21136                 o = o + AlignQword (size (o));
21137             }
21138         }
21139     }
21140
21141     assert (loh_pinned_plug_que_empty_p());
21142
21143     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21144         generation_size (max_generation + 1), 
21145         generation_free_list_space (gen),
21146         generation_free_obj_space (gen)));
21147 }
21148
21149 void gc_heap::relocate_in_loh_compact()
21150 {
21151     generation* gen        = large_object_generation;
21152     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21153     uint8_t* o             = generation_allocation_start (gen);
21154
21155     //Skip the generation gap object
21156     o = o + AlignQword (size (o));
21157
21158     relocate_args args;
21159     args.low = gc_low;
21160     args.high = gc_high;
21161     args.last_plug = 0;
21162
21163     while (1)
21164     {
21165         if (o >= heap_segment_allocated (seg))
21166         {
21167             seg = heap_segment_next (seg);
21168             if (seg == 0)
21169             {
21170                 break;
21171             }
21172
21173             o = heap_segment_mem (seg);
21174         }
21175
21176         if (marked (o))
21177         {
21178             size_t size = AlignQword (size (o));
21179
21180             check_class_object_demotion (o);
21181             if (contain_pointers (o))
21182             {
21183                 go_through_object_nostart (method_table (o), o, size(o), pval,
21184                 {
21185                     reloc_survivor_helper (pval);
21186                 });
21187             }
21188
21189             o = o + size;
21190             if (o < heap_segment_allocated (seg))
21191             {
21192                 assert (!marked (o));
21193             }
21194         }
21195         else
21196         {
21197             while (o < heap_segment_allocated (seg) && !marked (o))
21198             {
21199                 o = o + AlignQword (size (o));
21200             }
21201         }
21202     }
21203
21204     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21205         generation_size (max_generation + 1), 
21206         generation_free_list_space (gen),
21207         generation_free_obj_space (gen)));
21208 }
21209
21210 void gc_heap::walk_relocation_for_loh (void* profiling_context, record_surv_fn fn)
21211 {
21212     generation* gen        = large_object_generation;
21213     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21214     uint8_t* o             = generation_allocation_start (gen);
21215
21216     //Skip the generation gap object
21217     o = o + AlignQword (size (o));
21218
21219     while (1)
21220     {
21221         if (o >= heap_segment_allocated (seg))
21222         {
21223             seg = heap_segment_next (seg);
21224             if (seg == 0)
21225             {
21226                 break;
21227             }
21228
21229             o = heap_segment_mem (seg);
21230         }
21231
21232         if (marked (o))
21233         {
21234             size_t size = AlignQword (size (o));
21235
21236             ptrdiff_t reloc = loh_node_relocation_distance (o);
21237
21238             STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
21239
21240             fn (o, (o + size), reloc, profiling_context, !!settings.compaction, false);
21241
21242             o = o + size;
21243             if (o < heap_segment_allocated (seg))
21244             {
21245                 assert (!marked (o));
21246             }
21247         }
21248         else
21249         {
21250             while (o < heap_segment_allocated (seg) && !marked (o))
21251             {
21252                 o = o + AlignQword (size (o));
21253             }
21254         }
21255     }
21256 }
21257
21258 BOOL gc_heap::loh_object_p (uint8_t* o)
21259 {
21260 #ifdef MULTIPLE_HEAPS
21261     gc_heap* hp = gc_heap::g_heaps [0];
21262     int brick_entry = hp->brick_table[hp->brick_of (o)];
21263 #else //MULTIPLE_HEAPS
21264     int brick_entry = brick_table[brick_of (o)];
21265 #endif //MULTIPLE_HEAPS
21266
21267     return (brick_entry == 0);
21268 }
21269 #endif //FEATURE_LOH_COMPACTION
21270
21271 void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p, 
21272                                       BOOL& last_pinned_plug_p, 
21273                                       BOOL& pinned_plug_p,
21274                                       size_t ps,
21275                                       size_t& artificial_pinned_size)
21276 {
21277     last_npinned_plug_p = FALSE;
21278     last_pinned_plug_p = TRUE;
21279     pinned_plug_p = TRUE;
21280     artificial_pinned_size = ps;
21281 }
21282
21283 // Because we have the artificial pinning, we can't guarantee that pinned and npinned
21284 // plugs are always interleaved.
21285 void gc_heap::store_plug_gap_info (uint8_t* plug_start,
21286                                    uint8_t* plug_end,
21287                                    BOOL& last_npinned_plug_p, 
21288                                    BOOL& last_pinned_plug_p, 
21289                                    uint8_t*& last_pinned_plug,
21290                                    BOOL& pinned_plug_p,
21291                                    uint8_t* last_object_in_last_plug,
21292                                    BOOL& merge_with_last_pin_p,
21293                                    // this is only for verification purpose
21294                                    size_t last_plug_len)
21295 {
21296     UNREFERENCED_PARAMETER(last_plug_len);
21297
21298     if (!last_npinned_plug_p && !last_pinned_plug_p)
21299     {
21300         //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
21301         dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
21302         assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
21303         set_gap_size (plug_start, plug_start - plug_end);
21304     }
21305
21306     if (pinned (plug_start))
21307     {
21308         BOOL save_pre_plug_info_p = FALSE;
21309
21310         if (last_npinned_plug_p || last_pinned_plug_p)
21311         {
21312             //if (last_plug_len == Align (min_obj_size))
21313             //{
21314             //    dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
21315             //    GCToOSInterface::DebugBreak();
21316             //}
21317             save_pre_plug_info_p = TRUE;
21318         }
21319
21320         pinned_plug_p = TRUE;
21321         last_npinned_plug_p = FALSE;
21322
21323         if (last_pinned_plug_p)
21324         {
21325             dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
21326             merge_with_last_pin_p = TRUE;
21327         }
21328         else
21329         {
21330             last_pinned_plug_p = TRUE;
21331             last_pinned_plug = plug_start;
21332                 
21333             enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
21334
21335             if (save_pre_plug_info_p)
21336             {
21337                 set_gap_size (plug_start, sizeof (gap_reloc_pair));
21338             }
21339         }
21340     }
21341     else
21342     {
21343         if (last_pinned_plug_p)
21344         {
21345             //if (Align (last_plug_len) < min_pre_pin_obj_size)
21346             //{
21347             //    dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
21348             //    GCToOSInterface::DebugBreak();
21349             //}
21350
21351             save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
21352             set_gap_size (plug_start, sizeof (gap_reloc_pair));
21353
21354             verify_pins_with_post_plug_info("after saving post plug info");
21355         }
21356         last_npinned_plug_p = TRUE;
21357         last_pinned_plug_p = FALSE;
21358     }
21359 }
21360
21361 void gc_heap::record_interesting_data_point (interesting_data_point idp)
21362 {
21363 #ifdef GC_CONFIG_DRIVEN
21364     (interesting_data_per_gc[idp])++;
21365 #else
21366     UNREFERENCED_PARAMETER(idp);
21367 #endif //GC_CONFIG_DRIVEN
21368 }
21369
21370 #ifdef _PREFAST_
21371 #pragma warning(push)
21372 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
21373 #endif //_PREFAST_
21374 void gc_heap::plan_phase (int condemned_gen_number)
21375 {
21376     size_t old_gen2_allocated = 0;
21377     size_t old_gen2_size = 0;
21378
21379     if (condemned_gen_number == (max_generation - 1))
21380     {
21381         old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
21382         old_gen2_size = generation_size (max_generation);
21383     }
21384
21385     assert (settings.concurrent == FALSE);
21386
21387     // %type%  category = quote (plan);
21388 #ifdef TIME_GC
21389     unsigned start;
21390     unsigned finish;
21391     start = GetCycleCount32();
21392 #endif //TIME_GC
21393
21394     dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
21395                 condemned_gen_number, settings.promotion ? 1 : 0));
21396
21397     generation*  condemned_gen1 = generation_of (condemned_gen_number);
21398
21399 #ifdef MARK_LIST
21400     BOOL use_mark_list = FALSE;
21401     uint8_t** mark_list_next = &mark_list[0];
21402 #ifdef GC_CONFIG_DRIVEN
21403     dprintf (3, ("total number of marked objects: %Id (%Id)",
21404                  (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0]))));
21405 #else
21406     dprintf (3, ("mark_list length: %Id",
21407                  (mark_list_index - &mark_list[0])));
21408 #endif //GC_CONFIG_DRIVEN
21409
21410     if ((condemned_gen_number < max_generation) &&
21411         (mark_list_index <= mark_list_end) 
21412 #ifdef BACKGROUND_GC        
21413         && (!recursive_gc_sync::background_running_p())
21414 #endif //BACKGROUND_GC
21415         )
21416     {
21417 #ifndef MULTIPLE_HEAPS
21418         _sort (&mark_list[0], mark_list_index-1, 0);
21419         //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
21420         //verify_qsort_array (&mark_list[0], mark_list_index-1);
21421 #endif //!MULTIPLE_HEAPS
21422         use_mark_list = TRUE;
21423         get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit);
21424     }
21425     else
21426     {
21427         dprintf (3, ("mark_list not used"));
21428     }
21429
21430 #endif //MARK_LIST
21431
21432 #ifdef FEATURE_BASICFREEZE
21433     if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
21434         ro_segments_in_range)
21435     {
21436         sweep_ro_segments (generation_start_segment (condemned_gen1));
21437     }
21438 #endif // FEATURE_BASICFREEZE
21439
21440 #ifndef MULTIPLE_HEAPS
21441     if (shigh != (uint8_t*)0)
21442     {
21443         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21444
21445         PREFIX_ASSUME(seg != NULL);
21446
21447         heap_segment* fseg = seg;
21448         do
21449         {
21450             if (slow > heap_segment_mem (seg) &&
21451                 slow < heap_segment_reserved (seg))
21452             {
21453                 if (seg == fseg)
21454                 {
21455                     uint8_t* o = generation_allocation_start (condemned_gen1) +
21456                         Align (size (generation_allocation_start (condemned_gen1)));
21457                     if (slow > o)
21458                     {
21459                         assert ((slow - o) >= (int)Align (min_obj_size));
21460 #ifdef BACKGROUND_GC
21461                         if (current_c_gc_state == c_gc_state_marking)
21462                         {
21463                             bgc_clear_batch_mark_array_bits (o, slow);
21464                         }
21465 #endif //BACKGROUND_GC
21466                         make_unused_array (o, slow - o);
21467                     }
21468                 } 
21469                 else
21470                 {
21471                     assert (condemned_gen_number == max_generation);
21472                     make_unused_array (heap_segment_mem (seg),
21473                                        slow - heap_segment_mem (seg));
21474                 }
21475             }
21476             if (in_range_for_segment (shigh, seg))
21477             {
21478 #ifdef BACKGROUND_GC
21479                 if (current_c_gc_state == c_gc_state_marking)
21480                 {
21481                     bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
21482                 }
21483 #endif //BACKGROUND_GC
21484                 heap_segment_allocated (seg) = shigh + Align (size (shigh));
21485             }
21486             // test if the segment is in the range of [slow, shigh]
21487             if (!((heap_segment_reserved (seg) >= slow) &&
21488                   (heap_segment_mem (seg) <= shigh)))
21489             {
21490                 // shorten it to minimum
21491                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
21492             }
21493             seg = heap_segment_next_rw (seg);
21494         } while (seg);
21495     }
21496     else
21497     {
21498         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21499
21500         PREFIX_ASSUME(seg != NULL);
21501
21502         heap_segment* sseg = seg;
21503         do
21504         {
21505             // shorten it to minimum
21506             if (seg == sseg)
21507             {
21508                 // no survivors make all generations look empty
21509                 uint8_t* o = generation_allocation_start (condemned_gen1) +
21510                     Align (size (generation_allocation_start (condemned_gen1)));
21511 #ifdef BACKGROUND_GC
21512                 if (current_c_gc_state == c_gc_state_marking)
21513                 {
21514                     bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
21515                 }
21516 #endif //BACKGROUND_GC
21517                 heap_segment_allocated (seg) = o;
21518             }
21519             else
21520             {
21521                 assert (condemned_gen_number == max_generation);
21522 #ifdef BACKGROUND_GC
21523                 if (current_c_gc_state == c_gc_state_marking)
21524                 {
21525                     bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
21526                 }
21527 #endif //BACKGROUND_GC
21528                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
21529             }
21530             seg = heap_segment_next_rw (seg);
21531         } while (seg);
21532     }
21533
21534 #endif //MULTIPLE_HEAPS
21535
21536     heap_segment*  seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
21537
21538     PREFIX_ASSUME(seg1 != NULL);
21539
21540     uint8_t*  end = heap_segment_allocated (seg1);
21541     uint8_t*  first_condemned_address = generation_allocation_start (condemned_gen1);
21542     uint8_t*  x = first_condemned_address;
21543
21544     assert (!marked (x));
21545     uint8_t*  plug_end = x;
21546     uint8_t*  tree = 0;
21547     size_t  sequence_number = 0;
21548     uint8_t*  last_node = 0;
21549     size_t  current_brick = brick_of (x);
21550     BOOL  allocate_in_condemned = ((condemned_gen_number == max_generation)||
21551                                    (settings.promotion == FALSE));
21552     int  active_old_gen_number = condemned_gen_number;
21553     int  active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
21554                                   (1 + condemned_gen_number));
21555     generation*  older_gen = 0;
21556     generation* consing_gen = condemned_gen1;
21557     alloc_list  r_free_list [MAX_BUCKET_COUNT];
21558
21559     size_t r_free_list_space = 0;
21560     size_t r_free_obj_space = 0;
21561     size_t r_older_gen_free_list_allocated = 0;
21562     size_t r_older_gen_condemned_allocated = 0;
21563     size_t r_older_gen_end_seg_allocated = 0;
21564     uint8_t*  r_allocation_pointer = 0;
21565     uint8_t*  r_allocation_limit = 0;
21566     uint8_t* r_allocation_start_region = 0;
21567     heap_segment*  r_allocation_segment = 0;
21568 #ifdef FREE_USAGE_STATS
21569     size_t r_older_gen_free_space[NUM_GEN_POWER2];
21570 #endif //FREE_USAGE_STATS
21571
21572     if ((condemned_gen_number < max_generation))
21573     {
21574         older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
21575         generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
21576
21577         r_free_list_space = generation_free_list_space (older_gen);
21578         r_free_obj_space = generation_free_obj_space (older_gen);
21579 #ifdef FREE_USAGE_STATS
21580         memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
21581 #endif //FREE_USAGE_STATS
21582         generation_allocate_end_seg_p (older_gen) = FALSE;
21583         r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
21584         r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
21585         r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
21586         r_allocation_limit = generation_allocation_limit (older_gen);
21587         r_allocation_pointer = generation_allocation_pointer (older_gen);
21588         r_allocation_start_region = generation_allocation_context_start_region (older_gen);
21589         r_allocation_segment = generation_allocation_segment (older_gen);
21590         heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
21591
21592         PREFIX_ASSUME(start_seg != NULL);
21593
21594         if (start_seg != ephemeral_heap_segment)
21595         {
21596             assert (condemned_gen_number == (max_generation - 1));
21597             while (start_seg && (start_seg != ephemeral_heap_segment))
21598             {
21599                 assert (heap_segment_allocated (start_seg) >=
21600                         heap_segment_mem (start_seg));
21601                 assert (heap_segment_allocated (start_seg) <=
21602                         heap_segment_reserved (start_seg));
21603                 heap_segment_plan_allocated (start_seg) =
21604                     heap_segment_allocated (start_seg);
21605                 start_seg = heap_segment_next_rw (start_seg);
21606             }
21607         }
21608     }
21609
21610     //reset all of the segment allocated sizes
21611     {
21612         heap_segment*  seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
21613
21614         PREFIX_ASSUME(seg2 != NULL);
21615
21616         while (seg2)
21617         {
21618             heap_segment_plan_allocated (seg2) =
21619                 heap_segment_mem (seg2);
21620             seg2 = heap_segment_next_rw (seg2);
21621         }
21622     }
21623     int  condemned_gn = condemned_gen_number;
21624
21625     int bottom_gen = 0;
21626     init_free_and_plug();
21627
21628     while (condemned_gn >= bottom_gen)
21629     {
21630         generation*  condemned_gen2 = generation_of (condemned_gn);
21631         generation_allocator (condemned_gen2)->clear();
21632         generation_free_list_space (condemned_gen2) = 0;
21633         generation_free_obj_space (condemned_gen2) = 0;
21634         generation_allocation_size (condemned_gen2) = 0;
21635         generation_condemned_allocated (condemned_gen2) = 0; 
21636         generation_pinned_allocated (condemned_gen2) = 0; 
21637         generation_free_list_allocated(condemned_gen2) = 0; 
21638         generation_end_seg_allocated (condemned_gen2) = 0; 
21639         generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
21640         generation_pinned_allocation_compact_size (condemned_gen2) = 0;
21641 #ifdef FREE_USAGE_STATS
21642         generation_pinned_free_obj_space (condemned_gen2) = 0;
21643         generation_allocated_in_pinned_free (condemned_gen2) = 0;
21644         generation_allocated_since_last_pin (condemned_gen2) = 0;
21645 #endif //FREE_USAGE_STATS
21646         generation_plan_allocation_start (condemned_gen2) = 0;
21647         generation_allocation_segment (condemned_gen2) =
21648             heap_segment_rw (generation_start_segment (condemned_gen2));
21649
21650         PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
21651
21652         if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
21653         {
21654             generation_allocation_pointer (condemned_gen2) =
21655                 heap_segment_mem (generation_allocation_segment (condemned_gen2));
21656         }
21657         else
21658         {
21659             generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
21660         }
21661
21662         generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21663         generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21664
21665         condemned_gn--;
21666     }
21667
21668     BOOL allocate_first_generation_start = FALSE;
21669     
21670     if (allocate_in_condemned)
21671     {
21672         allocate_first_generation_start = TRUE;
21673     }
21674
21675     dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21676
21677     demotion_low = MAX_PTR;
21678     demotion_high = heap_segment_allocated (ephemeral_heap_segment);
21679
21680     // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
21681     // from gen1. They should get promoted to gen2.
21682     demote_gen1_p = !(settings.promotion && 
21683                       (settings.condemned_generation == (max_generation - 1)) && 
21684                       gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
21685
21686     total_ephemeral_size = 0;
21687
21688     print_free_and_plug ("BP");
21689
21690     for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
21691     {
21692         generation* temp_gen = generation_of (gen_idx);
21693
21694         dprintf (2, ("gen%d start %Ix, plan start %Ix",
21695             gen_idx, 
21696             generation_allocation_start (temp_gen),
21697             generation_plan_allocation_start (temp_gen)));
21698     }
21699
21700     BOOL fire_pinned_plug_events_p = ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PinPlugAtGCTime);
21701     size_t last_plug_len = 0;
21702
21703     while (1)
21704     {
21705         if (x >= end)
21706         {
21707             assert (x == end);
21708             assert (heap_segment_allocated (seg1) == end);
21709             heap_segment_allocated (seg1) = plug_end;
21710
21711             current_brick = update_brick_table (tree, current_brick, x, plug_end);
21712             dprintf (3, ("end of seg: new tree, sequence# 0"));
21713             sequence_number = 0;
21714             tree = 0;
21715
21716             if (heap_segment_next_rw (seg1))
21717             {
21718                 seg1 = heap_segment_next_rw (seg1);
21719                 end = heap_segment_allocated (seg1);
21720                 plug_end = x = heap_segment_mem (seg1);
21721                 current_brick = brick_of (x);
21722                 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21723                 continue;
21724             }
21725             else
21726             {
21727                 break;
21728             }
21729         }
21730
21731         BOOL last_npinned_plug_p = FALSE;
21732         BOOL last_pinned_plug_p = FALSE;
21733
21734         // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
21735         // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
21736         // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
21737         uint8_t* last_pinned_plug = 0;
21738         size_t num_pinned_plugs_in_plug = 0;
21739
21740         uint8_t* last_object_in_plug = 0;
21741
21742         while ((x < end) && marked (x))
21743         {
21744             uint8_t*  plug_start = x;
21745             uint8_t*  saved_plug_end = plug_end;
21746             BOOL   pinned_plug_p = FALSE;
21747             BOOL   npin_before_pin_p = FALSE;
21748             BOOL   saved_last_npinned_plug_p = last_npinned_plug_p;
21749             uint8_t*  saved_last_object_in_plug = last_object_in_plug;
21750             BOOL   merge_with_last_pin_p = FALSE;
21751
21752             size_t added_pinning_size = 0;
21753             size_t artificial_pinned_size = 0;
21754
21755             store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p, 
21756                                  last_pinned_plug, pinned_plug_p, last_object_in_plug, 
21757                                  merge_with_last_pin_p, last_plug_len);
21758
21759 #ifdef FEATURE_STRUCTALIGN
21760             int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
21761             size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
21762 #endif // FEATURE_STRUCTALIGN
21763
21764             {
21765                 uint8_t* xl = x;
21766                 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
21767                 {
21768                     assert (xl < end);
21769                     if (pinned(xl))
21770                     {
21771                         clear_pinned (xl);
21772                     }
21773 #ifdef FEATURE_STRUCTALIGN
21774                     else
21775                     {
21776                         int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
21777                         if (obj_requiredAlignment > requiredAlignment)
21778                         {
21779                             requiredAlignment = obj_requiredAlignment;
21780                             alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
21781                         }
21782                     }
21783 #endif // FEATURE_STRUCTALIGN
21784
21785                     clear_marked (xl);
21786
21787                     dprintf(4, ("+%Ix+", (size_t)xl));
21788                     assert ((size (xl) > 0));
21789                     assert ((size (xl) <= LARGE_OBJECT_SIZE));
21790
21791                     last_object_in_plug = xl;
21792
21793                     xl = xl + Align (size (xl));
21794                     Prefetch (xl);
21795                 }
21796
21797                 BOOL next_object_marked_p = ((xl < end) && marked (xl));
21798
21799                 if (pinned_plug_p)
21800                 {
21801                     // If it is pinned we need to extend to the next marked object as we can't use part of
21802                     // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
21803                     // references but for now I am just using the next non pinned object for that).
21804                     if (next_object_marked_p) 
21805                     {
21806                         clear_marked (xl);
21807                         last_object_in_plug = xl;
21808                         size_t extra_size = Align (size (xl));
21809                         xl = xl + extra_size;
21810                         added_pinning_size = extra_size;
21811                     }
21812                 }
21813                 else
21814                 {
21815                     if (next_object_marked_p)
21816                         npin_before_pin_p = TRUE;
21817                 }
21818
21819                 assert (xl <= end);
21820                 x = xl;
21821             }
21822             dprintf (3, ( "%Ix[", (size_t)x));
21823             plug_end = x;
21824             size_t ps = plug_end - plug_start;
21825             last_plug_len = ps;
21826             dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
21827             uint8_t*  new_address = 0;
21828
21829             if (!pinned_plug_p)
21830             {
21831                 if (allocate_in_condemned &&
21832                     (settings.condemned_generation == max_generation) &&
21833                     (ps > OS_PAGE_SIZE))
21834                 {
21835                     ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
21836                     //reloc should >=0 except when we relocate
21837                     //across segments and the dest seg is higher then the src
21838
21839                     if ((ps > (8*OS_PAGE_SIZE)) &&
21840                         (reloc > 0) &&
21841                         ((size_t)reloc < (ps/16)))
21842                     {
21843                         dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
21844                                      (size_t)plug_start, reloc));
21845                         // The last plug couldn't have been a npinned plug or it would have
21846                         // included this plug.
21847                         assert (!saved_last_npinned_plug_p);
21848
21849                         if (last_pinned_plug)
21850                         {
21851                             dprintf (3, ("artificially pinned plug merged with last pinned plug"));
21852                             merge_with_last_pin_p = TRUE;
21853                         }
21854                         else
21855                         {
21856                             enque_pinned_plug (plug_start, FALSE, 0);
21857                             last_pinned_plug = plug_start;
21858                         }
21859
21860                         convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
21861                                                 ps, artificial_pinned_size);
21862                     }
21863                 }
21864             }
21865
21866             if (allocate_first_generation_start)
21867             {
21868                 allocate_first_generation_start = FALSE;
21869                 plan_generation_start (condemned_gen1, consing_gen, plug_start);
21870                 assert (generation_plan_allocation_start (condemned_gen1));
21871             }
21872
21873             if (seg1 == ephemeral_heap_segment)
21874             {
21875                 process_ephemeral_boundaries (plug_start, active_new_gen_number,
21876                                               active_old_gen_number,
21877                                               consing_gen,
21878                                               allocate_in_condemned);
21879             }
21880
21881             dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
21882
21883             dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
21884             dd_survived_size (dd_active_old) += ps;
21885
21886             BOOL convert_to_pinned_p = FALSE;
21887
21888             if (!pinned_plug_p)
21889             {
21890 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
21891                 dd_num_npinned_plugs (dd_active_old)++;
21892 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
21893
21894                 add_gen_plug (active_old_gen_number, ps);
21895
21896                 if (allocate_in_condemned)
21897                 {
21898                     verify_pins_with_post_plug_info("before aic");
21899
21900                     new_address =
21901                         allocate_in_condemned_generations (consing_gen,
21902                                                            ps,
21903                                                            active_old_gen_number,
21904 #ifdef SHORT_PLUGS
21905                                                            &convert_to_pinned_p,
21906                                                            (npin_before_pin_p ? plug_end : 0),
21907                                                            seg1,
21908 #endif //SHORT_PLUGS
21909                                                            plug_start REQD_ALIGN_AND_OFFSET_ARG);
21910                     verify_pins_with_post_plug_info("after aic");
21911                 }
21912                 else
21913                 {
21914                     new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
21915
21916                     if (new_address != 0)
21917                     {
21918                         if (settings.condemned_generation == (max_generation - 1))
21919                         {
21920                             dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
21921                                 plug_start, plug_end,
21922                                 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
21923                                 (size_t)(plug_end - plug_start)));
21924                         }
21925                     }
21926                     else
21927                     {
21928                         allocate_in_condemned = TRUE;
21929
21930                         new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number, 
21931 #ifdef SHORT_PLUGS
21932                                                                          &convert_to_pinned_p,
21933                                                                          (npin_before_pin_p ? plug_end : 0),
21934                                                                          seg1,
21935 #endif //SHORT_PLUGS
21936                                                                          plug_start REQD_ALIGN_AND_OFFSET_ARG);
21937                     }
21938                 }
21939
21940                 if (convert_to_pinned_p)
21941                 {
21942                     assert (last_npinned_plug_p != FALSE);
21943                     assert (last_pinned_plug_p == FALSE);
21944                     convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
21945                                             ps, artificial_pinned_size);
21946                     enque_pinned_plug (plug_start, FALSE, 0);
21947                     last_pinned_plug = plug_start;
21948                 }
21949                 else
21950                 {
21951                     if (!new_address)
21952                     {
21953                         //verify that we are at then end of the ephemeral segment
21954                         assert (generation_allocation_segment (consing_gen) ==
21955                                 ephemeral_heap_segment);
21956                         //verify that we are near the end
21957                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
21958                                 heap_segment_allocated (ephemeral_heap_segment));
21959                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
21960                                 (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
21961                     }
21962                     else
21963                     {
21964 #ifdef SIMPLE_DPRINTF
21965                         dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)",
21966                             (size_t)(node_gap_size (plug_start)), 
21967                             plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
21968                                 (size_t)new_address + ps, ps, 
21969                                 (is_plug_padded (plug_start) ? 1 : 0)));
21970 #endif //SIMPLE_DPRINTF
21971
21972 #ifdef SHORT_PLUGS
21973                         if (is_plug_padded (plug_start))
21974                         {
21975                             dprintf (3, ("%Ix was padded", plug_start));
21976                             dd_padding_size (dd_active_old) += Align (min_obj_size);
21977                         }
21978 #endif //SHORT_PLUGS
21979                     }
21980                 }
21981             }
21982
21983             if (pinned_plug_p)
21984             {
21985                 if (fire_pinned_plug_events_p)
21986                     FireEtwPinPlugAtGCTime(plug_start, plug_end, 
21987                                            (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start)),
21988                                            GetClrInstanceId());
21989
21990                 if (merge_with_last_pin_p)
21991                 {
21992                     merge_with_last_pinned_plug (last_pinned_plug, ps);
21993                 }
21994                 else
21995                 {
21996                     assert (last_pinned_plug == plug_start);
21997                     set_pinned_info (plug_start, ps, consing_gen);
21998                 }
21999
22000                 new_address = plug_start;
22001
22002                 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)",
22003                             (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
22004                             (size_t)plug_end, ps,
22005                             (merge_with_last_pin_p ? 1 : 0)));
22006
22007                 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
22008                 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
22009                 dd_added_pinned_size (dd_active_old) += added_pinning_size;
22010                 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
22011
22012                 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
22013                 {
22014                     last_gen1_pin_end = plug_end;
22015                 }
22016             }
22017
22018 #ifdef _DEBUG
22019             // detect forward allocation in the same segment
22020             assert (!((new_address > plug_start) &&
22021                 (new_address < heap_segment_reserved (seg1))));
22022 #endif //_DEBUG
22023
22024             if (!merge_with_last_pin_p)
22025             {
22026                 if (current_brick != brick_of (plug_start))
22027                 {
22028                     current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
22029                     sequence_number = 0;
22030                     tree = 0;
22031                 }
22032
22033                 set_node_relocation_distance (plug_start, (new_address - plug_start));
22034                 if (last_node && (node_relocation_distance (last_node) ==
22035                                   (node_relocation_distance (plug_start) +
22036                                    node_gap_size (plug_start))))
22037                 {
22038                     //dprintf(3,( " Lb"));
22039                     dprintf (3, ("%Ix Lb", plug_start));
22040                     set_node_left (plug_start);
22041                 }
22042                 if (0 == sequence_number)
22043                 {
22044                     dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
22045                     tree = plug_start;
22046                 }
22047
22048                 verify_pins_with_post_plug_info("before insert node");
22049
22050                 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
22051                 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
22052                 last_node = plug_start;
22053
22054 #ifdef _DEBUG
22055                 // If we detect if the last plug is pinned plug right before us, we should save this gap info
22056                 if (!pinned_plug_p)
22057                 {
22058                     if (mark_stack_tos > 0)
22059                     {
22060                         mark& m = mark_stack_array[mark_stack_tos - 1];
22061                         if (m.has_post_plug_info())
22062                         {
22063                             uint8_t* post_plug_info_start = m.saved_post_plug_info_start;
22064                             size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
22065                             if ((uint8_t*)current_plug_gap_start == post_plug_info_start)
22066                             {
22067                                 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
22068                                     *current_plug_gap_start, *(current_plug_gap_start + 1),
22069                                     *(current_plug_gap_start + 2)));
22070                                 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
22071                             }
22072                         }
22073                     }
22074                 }
22075 #endif //_DEBUG
22076
22077                 verify_pins_with_post_plug_info("after insert node");
22078             }
22079         }
22080         
22081         if (num_pinned_plugs_in_plug > 1)
22082         {
22083             dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
22084         }
22085
22086         {
22087 #ifdef MARK_LIST
22088             if (use_mark_list)
22089             {
22090                while ((mark_list_next < mark_list_index) &&
22091                       (*mark_list_next <= x))
22092                {
22093                    mark_list_next++;
22094                }
22095                if ((mark_list_next < mark_list_index)
22096 #ifdef MULTIPLE_HEAPS
22097                    && (*mark_list_next < end) //for multiple segments
22098 #endif //MULTIPLE_HEAPS
22099                    )
22100                    x = *mark_list_next;
22101                else
22102                    x = end;
22103             }
22104             else
22105 #endif //MARK_LIST
22106             {
22107                 uint8_t* xl = x;
22108 #ifdef BACKGROUND_GC
22109                 if (current_c_gc_state == c_gc_state_marking)
22110                 {
22111                     assert (recursive_gc_sync::background_running_p());
22112                     while ((xl < end) && !marked (xl))
22113                     {
22114                         dprintf (4, ("-%Ix-", (size_t)xl));
22115                         assert ((size (xl) > 0));
22116                         background_object_marked (xl, TRUE);
22117                         xl = xl + Align (size (xl));
22118                         Prefetch (xl);
22119                     }
22120                 }
22121                 else
22122 #endif //BACKGROUND_GC
22123                 {
22124                     while ((xl < end) && !marked (xl))
22125                     {
22126                         dprintf (4, ("-%Ix-", (size_t)xl));
22127                         assert ((size (xl) > 0));
22128                         xl = xl + Align (size (xl));
22129                         Prefetch (xl);
22130                     }
22131                 }
22132                 assert (xl <= end);
22133                 x = xl;
22134             }
22135         }
22136     }
22137
22138     while (!pinned_plug_que_empty_p())
22139     {
22140         if (settings.promotion)
22141         {
22142             uint8_t* pplug = pinned_plug (oldest_pin());
22143             if (in_range_for_segment (pplug, ephemeral_heap_segment))
22144             {
22145                 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
22146                 //allocate all of the generation gaps
22147                 while (active_new_gen_number > 0)
22148                 {
22149                     active_new_gen_number--;
22150
22151                     if (active_new_gen_number == (max_generation - 1))
22152                     {
22153                         maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
22154                         if (!demote_gen1_p)
22155                             advance_pins_for_demotion (consing_gen);
22156                     }
22157
22158                     generation* gen = generation_of (active_new_gen_number);
22159                     plan_generation_start (gen, consing_gen, 0);
22160
22161                     if (demotion_low == MAX_PTR)
22162                     {
22163                         demotion_low = pplug;
22164                         dprintf (3, ("end plan: dlow->%Ix", demotion_low));
22165                     }
22166
22167                     dprintf (2, ("(%d)gen%d plan start: %Ix", 
22168                                   heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
22169                     assert (generation_plan_allocation_start (gen));
22170                 }
22171             }
22172         }
22173
22174         if (pinned_plug_que_empty_p())
22175             break;
22176
22177         size_t  entry = deque_pinned_plug();
22178         mark*  m = pinned_plug_of (entry);
22179         uint8_t*  plug = pinned_plug (m);
22180         size_t  len = pinned_len (m);
22181
22182         // detect pinned block in different segment (later) than
22183         // allocation segment
22184         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
22185
22186         while ((plug < generation_allocation_pointer (consing_gen)) ||
22187                (plug >= heap_segment_allocated (nseg)))
22188         {
22189             assert ((plug < heap_segment_mem (nseg)) ||
22190                     (plug > heap_segment_reserved (nseg)));
22191             //adjust the end of the segment to be the end of the plug
22192             assert (generation_allocation_pointer (consing_gen)>=
22193                     heap_segment_mem (nseg));
22194             assert (generation_allocation_pointer (consing_gen)<=
22195                     heap_segment_committed (nseg));
22196
22197             heap_segment_plan_allocated (nseg) =
22198                 generation_allocation_pointer (consing_gen);
22199             //switch allocation segment
22200             nseg = heap_segment_next_rw (nseg);
22201             generation_allocation_segment (consing_gen) = nseg;
22202             //reset the allocation pointer and limits
22203             generation_allocation_pointer (consing_gen) =
22204                 heap_segment_mem (nseg);
22205         }
22206
22207         set_new_pin_info (m, generation_allocation_pointer (consing_gen));
22208         dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
22209             (size_t)(brick_table[brick_of (plug)])));
22210
22211         generation_allocation_pointer (consing_gen) = plug + len;
22212         generation_allocation_limit (consing_gen) =
22213             generation_allocation_pointer (consing_gen);
22214         //Add the size of the pinned plug to the right pinned allocations
22215         //find out which gen this pinned plug came from 
22216         int frgn = object_gennum (plug);
22217         if ((frgn != (int)max_generation) && settings.promotion)
22218         {
22219             generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
22220         }
22221
22222     }
22223
22224     plan_generation_starts (consing_gen);
22225     print_free_and_plug ("AP");
22226
22227     {
22228 #ifdef SIMPLE_DPRINTF
22229         for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22230         {
22231             generation* temp_gen = generation_of (gen_idx);
22232             dynamic_data* temp_dd = dynamic_data_of (gen_idx);
22233
22234             int added_pinning_ratio = 0;
22235             int artificial_pinned_ratio = 0;
22236
22237             if (dd_pinned_survived_size (temp_dd) != 0)
22238             {
22239                 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22240                 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22241             }
22242
22243             size_t padding_size = 
22244 #ifdef SHORT_PLUGS
22245                 dd_padding_size (temp_dd);
22246 #else
22247                 0;
22248 #endif //SHORT_PLUGS
22249             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",
22250                 gen_idx, 
22251                 generation_allocation_start (temp_gen),
22252                 generation_plan_allocation_start (temp_gen),
22253                 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
22254                 generation_allocation_size (temp_gen),
22255                 generation_pinned_allocation_compact_size (temp_gen),
22256                 generation_pinned_allocation_sweep_size (temp_gen),
22257                 dd_survived_size (temp_dd),
22258                 dd_pinned_survived_size (temp_dd),
22259                 added_pinning_ratio,
22260                 artificial_pinned_ratio,
22261                 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
22262                 padding_size));
22263         }
22264 #endif //SIMPLE_DPRINTF
22265     }
22266
22267     if (settings.condemned_generation == (max_generation - 1 ))
22268     {
22269         size_t plan_gen2_size = generation_plan_size (max_generation);
22270         size_t growth = plan_gen2_size - old_gen2_size;
22271
22272         if (growth > 0)
22273         {
22274             dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
22275                 growth, generation_end_seg_allocated (generation_of (max_generation)), 
22276                 generation_condemned_allocated (generation_of (max_generation - 1))));
22277         }
22278         else
22279         {
22280             dprintf (1, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
22281                 (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)), 
22282                 generation_condemned_allocated (generation_of (max_generation - 1))));
22283         }
22284
22285         generation* older_gen = generation_of (settings.condemned_generation + 1);
22286         size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
22287         size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
22288         size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated;
22289         size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated;
22290
22291         dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
22292                     r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
22293                     r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen), 
22294                     r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
22295
22296         dprintf (1, ("this GC did %Id free list alloc(%Id bytes free space rejected), %Id seg alloc and %Id condemned alloc, gen1 condemned alloc is %Id", 
22297             free_list_allocated, rejected_free_space, end_seg_allocated,
22298             condemned_allocated, generation_condemned_allocated (generation_of (settings.condemned_generation))));
22299
22300         maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info);
22301         maxgen_size_info->free_list_allocated = free_list_allocated;
22302         maxgen_size_info->free_list_rejected = rejected_free_space;
22303         maxgen_size_info->end_seg_allocated = end_seg_allocated;
22304         maxgen_size_info->condemned_allocated = condemned_allocated;
22305         maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance;
22306         maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance;
22307
22308 #ifdef FREE_USAGE_STATS
22309         int free_list_efficiency = 0;
22310         if ((free_list_allocated + rejected_free_space) != 0)
22311             free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100);
22312
22313         int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100);
22314
22315         dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%",
22316                     older_gen->gen_num,
22317                     free_list_efficiency, running_free_list_efficiency));
22318
22319         dprintf (1, ("gen2 free list change"));
22320         for (int j = 0; j < NUM_GEN_POWER2; j++)
22321         {
22322             dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id", 
22323                 heap_number, 
22324                 settings.gc_index,
22325                 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j], 
22326                 (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
22327                 (generation_of(max_generation - 1))->gen_plugs[j]));
22328         }
22329 #endif //FREE_USAGE_STATS
22330     }
22331
22332     size_t fragmentation =
22333         generation_fragmentation (generation_of (condemned_gen_number),
22334                                   consing_gen,
22335                                   heap_segment_allocated (ephemeral_heap_segment));
22336
22337     dprintf (2,("Fragmentation: %Id", fragmentation));
22338     dprintf (2,("---- End of Plan phase ----"));
22339
22340 #ifdef TIME_GC
22341     finish = GetCycleCount32();
22342     plan_time = finish - start;
22343 #endif //TIME_GC
22344
22345     // We may update write barrier code.  We assume here EE has been suspended if we are on a GC thread.
22346     assert(IsGCInProgress());
22347
22348     BOOL should_expand = FALSE;
22349     BOOL should_compact= FALSE;
22350     ephemeral_promotion = FALSE;
22351
22352 #ifdef BIT64
22353     if ((!settings.concurrent) &&
22354         ((condemned_gen_number < max_generation) && 
22355          ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
22356     {
22357         dprintf (2, ("gen0 reduction count is %d, condemning %d, mem load %d",
22358                      settings.gen0_reduction_count,
22359                      condemned_gen_number,
22360                      settings.entry_memory_load));
22361         should_compact = TRUE;
22362
22363         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, 
22364             ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load));
22365
22366         if ((condemned_gen_number >= (max_generation - 1)) && 
22367             dt_low_ephemeral_space_p (tuning_deciding_expansion))
22368         {
22369             dprintf (2, ("Not enough space for all ephemeral generations with compaction"));
22370             should_expand = TRUE;
22371         }
22372     }
22373     else
22374     {
22375 #endif // BIT64
22376         should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
22377 #ifdef BIT64
22378     }
22379 #endif // BIT64
22380
22381 #ifdef FEATURE_LOH_COMPACTION
22382     loh_compacted_p = FALSE;
22383 #endif //FEATURE_LOH_COMPACTION
22384
22385     if (condemned_gen_number == max_generation)
22386     {
22387 #ifdef FEATURE_LOH_COMPACTION
22388         if (settings.loh_compaction)
22389         {
22390             if (plan_loh())
22391             {
22392                 should_compact = TRUE;
22393                 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced);
22394                 loh_compacted_p = TRUE;
22395             }
22396         }
22397         else
22398         {
22399             if ((heap_number == 0) && (loh_pinned_queue))
22400             {
22401                 loh_pinned_queue_decay--;
22402
22403                 if (!loh_pinned_queue_decay)
22404                 {
22405                     delete loh_pinned_queue;
22406                     loh_pinned_queue = 0;
22407                 }
22408             }
22409         }
22410
22411         if (!loh_compacted_p)
22412 #endif //FEATURE_LOH_COMPACTION
22413         {
22414             GCToEEInterface::DiagWalkLOHSurvivors(__this);
22415             sweep_large_objects();
22416         }
22417     }
22418     else
22419     {
22420         settings.loh_compaction = FALSE;
22421     }
22422
22423 #ifdef MULTIPLE_HEAPS
22424
22425     new_heap_segment = NULL;
22426
22427     if (should_compact && should_expand)
22428         gc_policy = policy_expand;
22429     else if (should_compact)
22430         gc_policy = policy_compact;
22431     else
22432         gc_policy = policy_sweep;
22433
22434     //vote for result of should_compact
22435     dprintf (3, ("Joining for compaction decision"));
22436     gc_t_join.join(this, gc_join_decide_on_compaction);
22437     if (gc_t_join.joined())
22438     {
22439         //safe place to delete large heap segments
22440         if (condemned_gen_number == max_generation)
22441         {
22442             for (int i = 0; i < n_heaps; i++)
22443             {
22444                 g_heaps [i]->rearrange_large_heap_segments ();
22445             }
22446         }
22447
22448         settings.demotion = FALSE;
22449         int pol_max = policy_sweep;
22450 #ifdef GC_CONFIG_DRIVEN
22451         BOOL is_compaction_mandatory = FALSE;
22452 #endif //GC_CONFIG_DRIVEN
22453
22454         int i;
22455         for (i = 0; i < n_heaps; i++)
22456         {
22457             if (pol_max < g_heaps[i]->gc_policy)
22458                 pol_max = policy_compact;
22459             // set the demotion flag is any of the heap has demotion
22460             if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
22461             {
22462                 (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit);
22463                 settings.demotion = TRUE;
22464             }
22465
22466 #ifdef GC_CONFIG_DRIVEN
22467             if (!is_compaction_mandatory)
22468             {
22469                 int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact);
22470                 if (compact_reason >= 0)
22471                 {
22472                     if (gc_heap_compact_reason_mandatory_p[compact_reason])
22473                         is_compaction_mandatory = TRUE;
22474                 }
22475             }
22476 #endif //GC_CONFIG_DRIVEN
22477         }
22478
22479 #ifdef GC_CONFIG_DRIVEN
22480         if (!is_compaction_mandatory)
22481         {
22482             // If compaction is not mandatory we can feel free to change it to a sweeping GC.
22483             // Note that we may want to change this to only checking every so often instead of every single GC.
22484             if (should_do_sweeping_gc (pol_max >= policy_compact))
22485             {
22486                 pol_max = policy_sweep;
22487             }
22488             else
22489             {
22490                 if (pol_max == policy_sweep)
22491                     pol_max = policy_compact;
22492             }
22493         }
22494 #endif //GC_CONFIG_DRIVEN
22495
22496         for (i = 0; i < n_heaps; i++)
22497         {
22498             if (pol_max > g_heaps[i]->gc_policy)
22499                 g_heaps[i]->gc_policy = pol_max;
22500             //get the segment while we are serialized
22501             if (g_heaps[i]->gc_policy == policy_expand)
22502             {
22503                 g_heaps[i]->new_heap_segment =
22504                      g_heaps[i]->soh_get_segment_to_expand();
22505                 if (!g_heaps[i]->new_heap_segment)
22506                 {
22507                     set_expand_in_full_gc (condemned_gen_number);
22508                     //we are out of memory, cancel the expansion
22509                     g_heaps[i]->gc_policy = policy_compact;
22510                 }
22511             }
22512         }
22513
22514         BOOL is_full_compacting_gc = FALSE;
22515
22516         if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
22517         {
22518             full_gc_counts[gc_type_compacting]++;
22519             is_full_compacting_gc = TRUE;
22520         }
22521
22522         for (i = 0; i < n_heaps; i++)
22523         {
22524             //copy the card and brick tables
22525             if (g_gc_card_table!= g_heaps[i]->card_table)
22526             {
22527                 g_heaps[i]->copy_brick_card_table();
22528             }
22529
22530             if (is_full_compacting_gc)
22531             {
22532                 g_heaps[i]->loh_alloc_since_cg = 0;
22533             }
22534         }
22535
22536         //start all threads on the roots.
22537         dprintf(3, ("Starting all gc threads after compaction decision"));
22538         gc_t_join.restart();
22539     }
22540
22541     //reset the local variable accordingly
22542     should_compact = (gc_policy >= policy_compact);
22543     should_expand  = (gc_policy >= policy_expand);
22544
22545 #else //MULTIPLE_HEAPS
22546
22547     //safe place to delete large heap segments
22548     if (condemned_gen_number == max_generation)
22549     {
22550         rearrange_large_heap_segments ();
22551     }
22552
22553     settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
22554     if (settings.demotion)
22555         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
22556
22557 #ifdef GC_CONFIG_DRIVEN
22558     BOOL is_compaction_mandatory = FALSE;
22559     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
22560     if (compact_reason >= 0)
22561         is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason];
22562
22563     if (!is_compaction_mandatory)
22564     {
22565         if (should_do_sweeping_gc (should_compact))
22566             should_compact = FALSE;
22567         else
22568             should_compact = TRUE;
22569     }
22570 #endif //GC_CONFIG_DRIVEN
22571
22572     if (should_compact && (condemned_gen_number == max_generation))
22573     {
22574         full_gc_counts[gc_type_compacting]++;
22575         loh_alloc_since_cg = 0;
22576     }
22577 #endif //MULTIPLE_HEAPS
22578
22579     if (should_compact)
22580     {
22581         dprintf (2,( "**** Doing Compacting GC ****"));
22582
22583         if (should_expand)
22584         {
22585 #ifndef MULTIPLE_HEAPS
22586             heap_segment* new_heap_segment = soh_get_segment_to_expand();
22587 #endif //!MULTIPLE_HEAPS
22588             if (new_heap_segment)
22589             {
22590                 consing_gen = expand_heap(condemned_gen_number,
22591                                           consing_gen,
22592                                           new_heap_segment);
22593             }
22594
22595             // If we couldn't get a new segment, or we were able to 
22596             // reserve one but no space to commit, we couldn't
22597             // expand heap.
22598             if (ephemeral_heap_segment != new_heap_segment)
22599             {
22600                 set_expand_in_full_gc (condemned_gen_number);
22601                 should_expand = FALSE;
22602             }
22603         }
22604         generation_allocation_limit (condemned_gen1) =
22605             generation_allocation_pointer (condemned_gen1);
22606         if ((condemned_gen_number < max_generation))
22607         {
22608             generation_allocator (older_gen)->commit_alloc_list_changes();
22609
22610             // Fix the allocation area of the older generation
22611             fix_older_allocation_area (older_gen);
22612         }
22613         assert (generation_allocation_segment (consing_gen) ==
22614                 ephemeral_heap_segment);
22615
22616         GCToEEInterface::DiagWalkSurvivors(__this);
22617
22618         relocate_phase (condemned_gen_number, first_condemned_address);
22619         compact_phase (condemned_gen_number, first_condemned_address,
22620                        (!settings.demotion && settings.promotion));
22621         fix_generation_bounds (condemned_gen_number, consing_gen);
22622         assert (generation_allocation_limit (youngest_generation) ==
22623                 generation_allocation_pointer (youngest_generation));
22624         if (condemned_gen_number >= (max_generation -1))
22625         {
22626 #ifdef MULTIPLE_HEAPS
22627             // this needs be serialized just because we have one
22628             // segment_standby_list/seg_table for all heaps. We should make it at least
22629             // so that when hoarding is not on we don't need this join because
22630             // decommitting memory can take a long time.
22631             //must serialize on deleting segments
22632             gc_t_join.join(this, gc_join_rearrange_segs_compaction);
22633             if (gc_t_join.joined())
22634             {
22635                 for (int i = 0; i < n_heaps; i++)
22636                 {
22637                     g_heaps[i]->rearrange_heap_segments(TRUE);
22638                 }
22639                 gc_t_join.restart();
22640             }
22641 #else
22642             rearrange_heap_segments(TRUE);
22643 #endif //MULTIPLE_HEAPS
22644
22645             if (should_expand)
22646             {
22647                 //fix the start_segment for the ephemeral generations
22648                 for (int i = 0; i < max_generation; i++)
22649                 {
22650                     generation* gen = generation_of (i);
22651                     generation_start_segment (gen) = ephemeral_heap_segment;
22652                     generation_allocation_segment (gen) = ephemeral_heap_segment;
22653                 }
22654             }
22655         }
22656
22657         {
22658 #ifdef FEATURE_PREMORTEM_FINALIZATION
22659             finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
22660                                                        (!settings.demotion && settings.promotion));
22661 #endif // FEATURE_PREMORTEM_FINALIZATION
22662
22663 #ifdef MULTIPLE_HEAPS
22664             dprintf(3, ("Joining after end of compaction"));
22665             gc_t_join.join(this, gc_join_adjust_handle_age_compact);
22666             if (gc_t_join.joined())
22667 #endif //MULTIPLE_HEAPS
22668             {
22669 #ifdef MULTIPLE_HEAPS
22670                 //join all threads to make sure they are synchronized
22671                 dprintf(3, ("Restarting after Promotion granted"));
22672                 gc_t_join.restart();
22673 #endif //MULTIPLE_HEAPS
22674             }
22675
22676             ScanContext sc;
22677             sc.thread_number = heap_number;
22678             sc.promotion = FALSE;
22679             sc.concurrent = FALSE;
22680             // new generations bounds are set can call this guy
22681             if (settings.promotion && !settings.demotion)
22682             {
22683                 dprintf (2, ("Promoting EE roots for gen %d",
22684                              condemned_gen_number));
22685                 GCScan::GcPromotionsGranted(condemned_gen_number,
22686                                                 max_generation, &sc);
22687             }
22688             else if (settings.demotion)
22689             {
22690                 dprintf (2, ("Demoting EE roots for gen %d",
22691                              condemned_gen_number));
22692                 GCScan::GcDemote (condemned_gen_number, max_generation, &sc);
22693             }
22694         }
22695
22696         {
22697             gen0_big_free_spaces = 0;
22698
22699             reset_pinned_queue_bos();
22700             unsigned int  gen_number = min (max_generation, 1 + condemned_gen_number);
22701             generation*  gen = generation_of (gen_number);
22702             uint8_t*  low = generation_allocation_start (generation_of (gen_number-1));
22703             uint8_t*  high =  heap_segment_allocated (ephemeral_heap_segment);
22704             
22705             while (!pinned_plug_que_empty_p())
22706             {
22707                 mark*  m = pinned_plug_of (deque_pinned_plug());
22708                 size_t len = pinned_len (m);
22709                 uint8_t*  arr = (pinned_plug (m) - len);
22710                 dprintf(3,("free [%Ix %Ix[ pin",
22711                             (size_t)arr, (size_t)arr + len));
22712                 if (len != 0)
22713                 {
22714                     assert (len >= Align (min_obj_size));
22715                     make_unused_array (arr, len);
22716                     // fix fully contained bricks + first one
22717                     // if the array goes beyond the first brick
22718                     size_t start_brick = brick_of (arr);
22719                     size_t end_brick = brick_of (arr + len);
22720                     if (end_brick != start_brick)
22721                     {
22722                         dprintf (3,
22723                                     ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
22724                                     start_brick, end_brick, (size_t)arr));
22725                         set_brick (start_brick,
22726                                     arr - brick_address (start_brick));
22727                         size_t brick = start_brick+1;
22728                         while (brick < end_brick)
22729                         {
22730                             set_brick (brick, start_brick - brick);
22731                             brick++;
22732                         }
22733                     }
22734
22735                     //when we take an old segment to make the new
22736                     //ephemeral segment. we can have a bunch of
22737                     //pinned plugs out of order going to the new ephemeral seg
22738                     //and then the next plugs go back to max_generation
22739                     if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
22740                         (heap_segment_reserved (ephemeral_heap_segment) > arr))
22741                     {
22742
22743                         while ((low <= arr) && (high > arr))
22744                         {
22745                             gen_number--;
22746                             assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
22747                                     settings.demotion || !settings.promotion);
22748                             dprintf (3, ("new free list generation %d", gen_number));
22749
22750                             gen = generation_of (gen_number);
22751                             if (gen_number >= 1)
22752                                 low = generation_allocation_start (generation_of (gen_number-1));
22753                             else
22754                                 low = high;
22755                         }
22756                     }
22757                     else
22758                     {
22759                         dprintf (3, ("new free list generation %d", max_generation));
22760                         gen_number = max_generation;
22761                         gen = generation_of (gen_number);
22762                     }
22763
22764                     dprintf(3,("threading it into generation %d", gen_number));
22765                     thread_gap (arr, len, gen);
22766                     add_gen_free (gen_number, len);
22767                 }
22768             }
22769         }
22770
22771 #ifdef _DEBUG
22772         for (int x = 0; x <= max_generation; x++)
22773         {
22774             assert (generation_allocation_start (generation_of (x)));
22775         }
22776 #endif //_DEBUG
22777
22778         if (!settings.demotion && settings.promotion)
22779         {
22780             //clear card for generation 1. generation 0 is empty
22781             clear_card_for_addresses (
22782                 generation_allocation_start (generation_of (1)),
22783                 generation_allocation_start (generation_of (0)));
22784         }
22785         if (settings.promotion && !settings.demotion)
22786         {
22787             uint8_t* start = generation_allocation_start (youngest_generation);
22788             MAYBE_UNUSED_VAR(start);
22789             assert (heap_segment_allocated (ephemeral_heap_segment) ==
22790                     (start + Align (size (start))));
22791         }
22792     }
22793     else
22794     {
22795         //force promotion for sweep
22796         settings.promotion = TRUE;
22797         settings.compaction = FALSE;
22798
22799         ScanContext sc;
22800         sc.thread_number = heap_number;
22801         sc.promotion = FALSE;
22802         sc.concurrent = FALSE;
22803
22804         dprintf (2, ("**** Doing Mark and Sweep GC****"));
22805
22806         if ((condemned_gen_number < max_generation))
22807         {
22808             generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
22809             generation_free_list_space (older_gen) = r_free_list_space;
22810             generation_free_obj_space (older_gen) = r_free_obj_space;
22811             generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
22812             generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
22813             generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
22814             generation_allocation_limit (older_gen) = r_allocation_limit;
22815             generation_allocation_pointer (older_gen) = r_allocation_pointer;
22816             generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
22817             generation_allocation_segment (older_gen) = r_allocation_segment;
22818         }
22819
22820         if ((condemned_gen_number < max_generation))
22821         {
22822             // Fix the allocation area of the older generation
22823             fix_older_allocation_area (older_gen);
22824         }
22825
22826         GCToEEInterface::DiagWalkSurvivors(__this);
22827
22828         gen0_big_free_spaces = 0;
22829         make_free_lists (condemned_gen_number);
22830         recover_saved_pinned_info();
22831
22832 #ifdef FEATURE_PREMORTEM_FINALIZATION
22833         finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
22834 #endif // FEATURE_PREMORTEM_FINALIZATION
22835 // MTHTS: leave single thread for HT processing on plan_phase
22836 #ifdef MULTIPLE_HEAPS
22837         dprintf(3, ("Joining after end of sweep"));
22838         gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
22839         if (gc_t_join.joined())
22840 #endif //MULTIPLE_HEAPS
22841         {
22842             GCScan::GcPromotionsGranted(condemned_gen_number,
22843                                             max_generation, &sc);
22844             if (condemned_gen_number >= (max_generation -1))
22845             {
22846 #ifdef MULTIPLE_HEAPS
22847                 for (int i = 0; i < n_heaps; i++)
22848                 {
22849                     g_heaps[i]->rearrange_heap_segments(FALSE);
22850                 }
22851 #else
22852                 rearrange_heap_segments(FALSE);
22853 #endif //MULTIPLE_HEAPS
22854             }
22855
22856 #ifdef MULTIPLE_HEAPS
22857             //join all threads to make sure they are synchronized
22858             dprintf(3, ("Restarting after Promotion granted"));
22859             gc_t_join.restart();
22860 #endif //MULTIPLE_HEAPS
22861         }
22862
22863 #ifdef _DEBUG
22864         for (int x = 0; x <= max_generation; x++)
22865         {
22866             assert (generation_allocation_start (generation_of (x)));
22867         }
22868 #endif //_DEBUG
22869
22870         //clear card for generation 1. generation 0 is empty
22871         clear_card_for_addresses (
22872             generation_allocation_start (generation_of (1)),
22873             generation_allocation_start (generation_of (0)));
22874         assert ((heap_segment_allocated (ephemeral_heap_segment) ==
22875                  (generation_allocation_start (youngest_generation) +
22876                   Align (min_obj_size))));
22877     }
22878
22879     //verify_partial();
22880 }
22881 #ifdef _PREFAST_
22882 #pragma warning(pop)
22883 #endif //_PREFAST_
22884
22885
22886 /*****************************
22887 Called after compact phase to fix all generation gaps
22888 ********************************/
22889 void gc_heap::fix_generation_bounds (int condemned_gen_number,
22890                                      generation* consing_gen)
22891 {
22892     UNREFERENCED_PARAMETER(consing_gen);
22893
22894     assert (generation_allocation_segment (consing_gen) ==
22895             ephemeral_heap_segment);
22896
22897     //assign the planned allocation start to the generation
22898     int gen_number = condemned_gen_number;
22899     int bottom_gen = 0;
22900
22901     while (gen_number >= bottom_gen)
22902     {
22903         generation*  gen = generation_of (gen_number);
22904         dprintf(3,("Fixing generation pointers for %Ix", gen_number));
22905         if ((gen_number < max_generation) && ephemeral_promotion)
22906         {
22907             make_unused_array (saved_ephemeral_plan_start[gen_number], 
22908                                saved_ephemeral_plan_start_size[gen_number]);
22909         }
22910         reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
22911         make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
22912         dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
22913         gen_number--;
22914     }
22915 #ifdef MULTIPLE_HEAPS
22916     if (ephemeral_promotion)
22917     {
22918         //we are creating a generation fault. set the cards.
22919         // and we are only doing this for multiple heaps because in the single heap scenario the 
22920         // new ephemeral generations will be empty and there'll be no need to set cards for the
22921         // old ephemeral generations that got promoted into max_generation.
22922         ptrdiff_t delta = 0;
22923 #ifdef SEG_MAPPING_TABLE
22924         heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
22925 #else //SEG_MAPPING_TABLE
22926         heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
22927 #endif //SEG_MAPPING_TABLE
22928
22929         assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
22930         size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
22931         size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
22932         while (card != end_card)
22933         {
22934             set_card (card);
22935             card++;
22936         }
22937     }
22938 #endif //MULTIPLE_HEAPS
22939     {
22940         alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
22941         //reset the allocated size
22942         uint8_t* start = generation_allocation_start (youngest_generation);
22943         MAYBE_UNUSED_VAR(start);
22944         if (settings.promotion && !settings.demotion)
22945         {
22946             assert ((start + Align (size (start))) ==
22947                     heap_segment_plan_allocated(ephemeral_heap_segment));
22948         }
22949
22950         heap_segment_allocated(ephemeral_heap_segment)=
22951             heap_segment_plan_allocated(ephemeral_heap_segment);
22952     }
22953 }
22954
22955 uint8_t* gc_heap::generation_limit (int gen_number)
22956 {
22957     if (settings.promotion)
22958     {
22959         if (gen_number <= 1)
22960             return heap_segment_reserved (ephemeral_heap_segment);
22961         else
22962             return generation_allocation_start (generation_of ((gen_number - 2)));
22963     }
22964     else
22965     {
22966         if (gen_number <= 0)
22967             return heap_segment_reserved (ephemeral_heap_segment);
22968         else
22969             return generation_allocation_start (generation_of ((gen_number - 1)));
22970     }
22971 }
22972
22973 BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
22974 {
22975     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
22976     size_t size = Align (min_obj_size)*(condemned_gen_number+1);
22977     assert ((start + size) <=
22978             heap_segment_reserved (ephemeral_heap_segment));
22979     if ((start + size) >
22980         heap_segment_committed (ephemeral_heap_segment))
22981     {
22982         if (!grow_heap_segment (ephemeral_heap_segment, start + size))
22983         {
22984             return FALSE;
22985         }
22986     }
22987     return TRUE;
22988 }
22989
22990 uint8_t* gc_heap::allocate_at_end (size_t size)
22991 {
22992     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
22993     size = Align (size);
22994     uint8_t* result = start;
22995     // only called to allocate a min obj so can't overflow here.
22996     assert ((start + size) <=
22997             heap_segment_reserved (ephemeral_heap_segment));
22998     //ensure_gap_allocation took care of it
22999     assert ((start + size) <=
23000             heap_segment_committed (ephemeral_heap_segment));
23001     heap_segment_allocated (ephemeral_heap_segment) += size;
23002     return result;
23003 }
23004
23005
23006 void gc_heap::make_free_lists (int condemned_gen_number)
23007 {
23008 #ifdef TIME_GC
23009     unsigned start;
23010     unsigned finish;
23011     start = GetCycleCount32();
23012 #endif //TIME_GC
23013
23014     //Promotion has to happen in sweep case.
23015     assert (settings.promotion);
23016
23017     generation* condemned_gen = generation_of (condemned_gen_number);
23018     uint8_t* start_address = generation_allocation_start (condemned_gen);
23019
23020     size_t  current_brick = brick_of (start_address);
23021     heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23022
23023     PREFIX_ASSUME(current_heap_segment != NULL);
23024
23025     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
23026     size_t  end_brick = brick_of (end_address-1);
23027     make_free_args args;
23028     args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
23029     args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
23030                               MAX_PTR :
23031                               (generation_limit (args.free_list_gen_number)));
23032     args.free_list_gen = generation_of (args.free_list_gen_number);
23033     args.highest_plug = 0;
23034
23035     if ((start_address < end_address) ||
23036         (condemned_gen_number == max_generation))
23037     {
23038         while (1)
23039         {
23040             if ((current_brick > end_brick))
23041             {
23042                 if (args.current_gen_limit == MAX_PTR)
23043                 {
23044                     //We had an empty segment
23045                     //need to allocate the generation start
23046
23047                     generation* gen = generation_of (max_generation);
23048
23049                     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
23050
23051                     PREFIX_ASSUME(start_seg != NULL);
23052
23053                     uint8_t* gap = heap_segment_mem (start_seg);
23054
23055                     generation_allocation_start (gen) = gap;
23056                     heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
23057                     make_unused_array (gap, Align (min_obj_size));
23058                     reset_allocation_pointers (gen, gap);
23059                     dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
23060                                  max_generation, (size_t)gap));
23061                     args.current_gen_limit = generation_limit (args.free_list_gen_number);
23062                 }
23063                 if (heap_segment_next_rw (current_heap_segment))
23064                 {
23065                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
23066                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
23067                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23068
23069                     continue;
23070                 }
23071                 else
23072                 {
23073                     break;
23074                 }
23075             }
23076             {
23077                 int brick_entry =  brick_table [ current_brick ];
23078                 if ((brick_entry >= 0))
23079                 {
23080                     make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
23081                     dprintf(3,("Fixing brick entry %Ix to %Ix",
23082                                current_brick, (size_t)args.highest_plug));
23083                     set_brick (current_brick,
23084                                (args.highest_plug - brick_address (current_brick)));
23085                 }
23086                 else
23087                 {
23088                     if ((brick_entry > -32768))
23089                     {
23090
23091 #ifdef _DEBUG
23092                         ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
23093                         if ((brick_entry != -32767) && (! ((offset == brick_entry))))
23094                         {
23095                             assert ((brick_entry == -1));
23096                         }
23097 #endif //_DEBUG
23098                         //init to -1 for faster find_first_object
23099                         set_brick (current_brick, -1);
23100                     }
23101                 }
23102             }
23103             current_brick++;
23104         }
23105     }
23106     {
23107         int bottom_gen = 0;
23108         args.free_list_gen_number--;
23109         while (args.free_list_gen_number >= bottom_gen)
23110         {
23111             uint8_t*  gap = 0;
23112             generation* gen2 = generation_of (args.free_list_gen_number);
23113             gap = allocate_at_end (Align(min_obj_size));
23114             generation_allocation_start (gen2) = gap;
23115             reset_allocation_pointers (gen2, gap);
23116             dprintf(3,("Fixing generation start of %d to: %Ix",
23117                        args.free_list_gen_number, (size_t)gap));
23118             PREFIX_ASSUME(gap != NULL);
23119             make_unused_array (gap, Align (min_obj_size));
23120
23121             args.free_list_gen_number--;
23122         }
23123
23124         //reset the allocated size
23125         uint8_t* start2 = generation_allocation_start (youngest_generation);
23126         alloc_allocated = start2 + Align (size (start2));
23127     }
23128
23129 #ifdef TIME_GC
23130     finish = GetCycleCount32();
23131     sweep_time = finish - start;
23132 #endif //TIME_GC
23133 }
23134
23135 void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args)
23136 {
23137     assert ((tree != NULL));
23138     {
23139         int  right_node = node_right_child (tree);
23140         int left_node = node_left_child (tree);
23141         args->highest_plug = 0;
23142         if (! (0 == tree))
23143         {
23144             if (! (0 == left_node))
23145             {
23146                 make_free_list_in_brick (tree + left_node, args);
23147
23148             }
23149             {
23150                 uint8_t*  plug = tree;
23151                 size_t  gap_size = node_gap_size (tree);
23152                 uint8_t*  gap = (plug - gap_size);
23153                 dprintf (3,("Making free list %Ix len %d in %d",
23154                 //dprintf (3,("F: %Ix len %Ix in %d",
23155                         (size_t)gap, gap_size, args->free_list_gen_number));
23156                 args->highest_plug = tree;
23157 #ifdef SHORT_PLUGS
23158                 if (is_plug_padded (plug))
23159                 {
23160                     dprintf (3, ("%Ix padded", plug));
23161                     clear_plug_padded (plug);
23162                 }
23163 #endif //SHORT_PLUGS
23164             gen_crossing:
23165                 {
23166                     if ((args->current_gen_limit == MAX_PTR) ||
23167                         ((plug >= args->current_gen_limit) &&
23168                          ephemeral_pointer_p (plug)))
23169                     {
23170                         dprintf(3,(" Crossing Generation boundary at %Ix",
23171                                (size_t)args->current_gen_limit));
23172                         if (!(args->current_gen_limit == MAX_PTR))
23173                         {
23174                             args->free_list_gen_number--;
23175                             args->free_list_gen = generation_of (args->free_list_gen_number);
23176                         }
23177                         dprintf(3,( " Fixing generation start of %d to: %Ix",
23178                                 args->free_list_gen_number, (size_t)gap));
23179
23180                         reset_allocation_pointers (args->free_list_gen, gap);
23181                         args->current_gen_limit = generation_limit (args->free_list_gen_number);
23182
23183                         if ((gap_size >= (2*Align (min_obj_size))))
23184                         {
23185                             dprintf(3,(" Splitting the gap in two %Id left",
23186                                    gap_size));
23187                             make_unused_array (gap, Align(min_obj_size));
23188                             gap_size = (gap_size - Align(min_obj_size));
23189                             gap = (gap + Align(min_obj_size));
23190                         }
23191                         else
23192                         {
23193                             make_unused_array (gap, gap_size);
23194                             gap_size = 0;
23195                         }
23196                         goto gen_crossing;
23197                     }
23198                 }
23199
23200                 thread_gap (gap, gap_size, args->free_list_gen);
23201                 add_gen_free (args->free_list_gen->gen_num, gap_size);
23202             }
23203             if (! (0 == right_node))
23204             {
23205                 make_free_list_in_brick (tree + right_node, args);
23206             }
23207         }
23208     }
23209 }
23210
23211 void gc_heap::thread_gap (uint8_t* gap_start, size_t size, generation*  gen)
23212 {
23213     assert (generation_allocation_start (gen));
23214     if ((size > 0))
23215     {
23216         if ((gen->gen_num == 0) && (size > CLR_SIZE))
23217         {
23218             gen0_big_free_spaces += size;
23219         }
23220
23221         assert ((heap_segment_rw (generation_start_segment (gen))!=
23222                  ephemeral_heap_segment) ||
23223                 (gap_start > generation_allocation_start (gen)));
23224         // The beginning of a segment gap is not aligned
23225         assert (size >= Align (min_obj_size));
23226         make_unused_array (gap_start, size, 
23227                           (!settings.concurrent && (gen != youngest_generation)),
23228                           (gen->gen_num == max_generation));
23229         dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
23230
23231         if ((size >= min_free_list))
23232         {
23233             generation_free_list_space (gen) += size;
23234             generation_allocator (gen)->thread_item (gap_start, size);
23235         }
23236         else
23237         {
23238             generation_free_obj_space (gen) += size;
23239         }
23240     }
23241 }
23242
23243 void gc_heap::loh_thread_gap_front (uint8_t* gap_start, size_t size, generation*  gen)
23244 {
23245     assert (generation_allocation_start (gen));
23246     if (size >= min_free_list)
23247     {
23248         generation_free_list_space (gen) += size;
23249         generation_allocator (gen)->thread_item_front (gap_start, size);
23250     }
23251 }
23252
23253 void gc_heap::make_unused_array (uint8_t* x, size_t size, BOOL clearp, BOOL resetp)
23254 {
23255     dprintf (3, ("Making unused array [%Ix, %Ix[",
23256         (size_t)x, (size_t)(x+size)));
23257     assert (size >= Align (min_obj_size));
23258
23259 //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
23260 //    check_batch_mark_array_bits (x, x+size);
23261 //#endif //VERIFY_HEAP && BACKGROUND_GC
23262
23263     if (resetp)
23264         reset_memory (x, size);
23265
23266     ((CObjectHeader*)x)->SetFree(size);
23267
23268 #ifdef BIT64
23269
23270 #if BIGENDIAN
23271 #error "This won't work on big endian platforms"
23272 #endif
23273
23274     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23275
23276     if (size_as_object < size)
23277     {
23278         //
23279         // If the size is more than 4GB, we need to create multiple objects because of
23280         // the Array::m_NumComponents is uint32_t and the high 32 bits of unused array
23281         // size is ignored in regular object size computation.
23282         //
23283         uint8_t * tmp = x + size_as_object;
23284         size_t remaining_size = size - size_as_object;
23285
23286         while (remaining_size > UINT32_MAX)
23287         {
23288             // Make sure that there will be at least Align(min_obj_size) left
23289             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23290                 - Align (min_obj_size, get_alignment_constant (FALSE));
23291
23292             ((CObjectHeader*)tmp)->SetFree(current_size);
23293
23294             remaining_size -= current_size;
23295             tmp += current_size;
23296         }
23297
23298         ((CObjectHeader*)tmp)->SetFree(remaining_size);
23299     }
23300 #endif
23301
23302     if (clearp)
23303         clear_card_for_addresses (x, x + Align(size));
23304 }
23305
23306 // Clear memory set by make_unused_array.
23307 void gc_heap::clear_unused_array (uint8_t* x, size_t size)
23308 {
23309     // Also clear the sync block
23310     *(((PTR_PTR)x)-1) = 0;
23311
23312     ((CObjectHeader*)x)->UnsetFree();
23313
23314 #ifdef BIT64
23315
23316 #if BIGENDIAN
23317 #error "This won't work on big endian platforms"
23318 #endif
23319
23320     // The memory could have been cleared in the meantime. We have to mirror the algorithm
23321     // from make_unused_array since we cannot depend on the object sizes in memory.
23322     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23323
23324     if (size_as_object < size)
23325     {
23326         uint8_t * tmp = x + size_as_object;
23327         size_t remaining_size = size - size_as_object;
23328
23329         while (remaining_size > UINT32_MAX)
23330         {
23331             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23332                 - Align (min_obj_size, get_alignment_constant (FALSE));
23333
23334             ((CObjectHeader*)tmp)->UnsetFree();
23335
23336             remaining_size -= current_size;
23337             tmp += current_size;
23338         }
23339
23340         ((CObjectHeader*)tmp)->UnsetFree();
23341     }
23342 #else
23343     UNREFERENCED_PARAMETER(size);
23344 #endif
23345 }
23346
23347 inline
23348 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address)
23349 {
23350     uint8_t* candidate = 0;
23351     int cn;
23352     while (1)
23353     {
23354         if (tree < old_address)
23355         {
23356             if ((cn = node_right_child (tree)) != 0)
23357             {
23358                 assert (candidate < tree);
23359                 candidate = tree;
23360                 tree = tree + cn;
23361                 Prefetch (tree - 8);
23362                 continue;
23363             }
23364             else
23365                 break;
23366         }
23367         else if (tree > old_address)
23368         {
23369             if ((cn = node_left_child (tree)) != 0)
23370             {
23371                 tree = tree + cn;
23372                 Prefetch (tree - 8);
23373                 continue;
23374             }
23375             else
23376                 break;
23377         } else
23378             break;
23379     }
23380     if (tree <= old_address)
23381         return tree;
23382     else if (candidate)
23383         return candidate;
23384     else
23385         return tree;
23386 }
23387
23388 #ifdef FEATURE_BASICFREEZE
23389 bool gc_heap::frozen_object_p (Object* obj)
23390 {
23391     heap_segment* pSegment = gc_heap::find_segment ((uint8_t*)obj, FALSE);
23392     _ASSERTE(pSegment);
23393
23394     return heap_segment_read_only_p(pSegment);
23395 }
23396 #endif // FEATURE_BASICFREEZE
23397
23398 #ifdef FEATURE_REDHAWK
23399 // TODO: this was added on RH, we have not done perf runs to see if this is the right
23400 // thing to do for other versions of the CLR.
23401 inline
23402 #endif // FEATURE_REDHAWK
23403 void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL)
23404 {
23405     uint8_t* old_address = *pold_address;
23406     if (!((old_address >= gc_low) && (old_address < gc_high)))
23407 #ifdef MULTIPLE_HEAPS
23408     {
23409         UNREFERENCED_PARAMETER(thread);
23410         if (old_address == 0)
23411             return;
23412         gc_heap* hp = heap_of (old_address);
23413         if ((hp == this) ||
23414             !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
23415             return;
23416     }
23417 #else //MULTIPLE_HEAPS
23418         return ;
23419 #endif //MULTIPLE_HEAPS
23420     // delta translates old_address into address_gc (old_address);
23421     size_t  brick = brick_of (old_address);
23422     int    brick_entry =  brick_table [ brick ];
23423     uint8_t*  new_address = old_address;
23424     if (! ((brick_entry == 0)))
23425     {
23426     retry:
23427         {
23428             while (brick_entry < 0)
23429             {
23430                 brick = (brick + brick_entry);
23431                 brick_entry =  brick_table [ brick ];
23432             }
23433             uint8_t* old_loc = old_address;
23434
23435             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
23436                                       old_loc);
23437             if ((node <= old_loc))
23438                 new_address = (old_address + node_relocation_distance (node));
23439             else
23440             {
23441                 if (node_left_p (node))
23442                 {
23443                     dprintf(3,(" L: %Ix", (size_t)node));
23444                     new_address = (old_address +
23445                                    (node_relocation_distance (node) +
23446                                     node_gap_size (node)));
23447                 }
23448                 else
23449                 {
23450                     brick = brick - 1;
23451                     brick_entry =  brick_table [ brick ];
23452                     goto retry;
23453                 }
23454             }
23455         }
23456
23457         *pold_address = new_address;
23458         return;
23459     }
23460
23461 #ifdef FEATURE_LOH_COMPACTION
23462     if (loh_compacted_p
23463 #ifdef FEATURE_BASICFREEZE
23464         && !frozen_object_p((Object*)old_address)
23465 #endif // FEATURE_BASICFREEZE
23466         )
23467     {
23468         *pold_address = old_address + loh_node_relocation_distance (old_address);
23469     }
23470     else
23471 #endif //FEATURE_LOH_COMPACTION
23472     {
23473         *pold_address = new_address;
23474     }
23475 }
23476
23477 inline void 
23478 gc_heap::check_class_object_demotion (uint8_t* obj)
23479 {
23480 #ifdef COLLECTIBLE_CLASS
23481     if (is_collectible(obj))
23482     {
23483         check_class_object_demotion_internal (obj);
23484     }
23485 #else
23486     UNREFERENCED_PARAMETER(obj);
23487 #endif //COLLECTIBLE_CLASS
23488 }
23489
23490 #ifdef COLLECTIBLE_CLASS
23491 NOINLINE void 
23492 gc_heap::check_class_object_demotion_internal (uint8_t* obj)
23493 {
23494     if (settings.demotion)
23495     {
23496 #ifdef MULTIPLE_HEAPS
23497         // We set the card without checking the demotion range 'cause at this point
23498         // the handle that points to the loader allocator object may or may not have
23499         // been relocated by other GC threads. 
23500         set_card (card_of (obj));
23501 #else
23502         THREAD_FROM_HEAP;
23503         uint8_t* class_obj = get_class_object (obj);
23504         dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
23505         uint8_t* temp_class_obj = class_obj;
23506         uint8_t** temp = &temp_class_obj;
23507         relocate_address (temp THREAD_NUMBER_ARG);
23508
23509         check_demotion_helper (temp, obj);
23510 #endif //MULTIPLE_HEAPS
23511     }
23512 }
23513
23514 #endif //COLLECTIBLE_CLASS
23515
23516 inline void
23517 gc_heap::check_demotion_helper (uint8_t** pval, uint8_t* parent_obj)
23518 {
23519     // detect if we are demoting an object
23520     if ((*pval < demotion_high) &&
23521         (*pval >= demotion_low))
23522     {
23523         dprintf(3, ("setting card %Ix:%Ix",
23524                     card_of((uint8_t*)pval),
23525                     (size_t)pval));
23526
23527         set_card (card_of (parent_obj));
23528     }
23529 #ifdef MULTIPLE_HEAPS
23530     else if (settings.demotion)
23531     {
23532         dprintf (4, ("Demotion active, computing heap_of object"));
23533         gc_heap* hp = heap_of (*pval);
23534         if ((*pval < hp->demotion_high) &&
23535             (*pval >= hp->demotion_low))
23536         {
23537             dprintf(3, ("setting card %Ix:%Ix",
23538                         card_of((uint8_t*)pval),
23539                         (size_t)pval));
23540
23541             set_card (card_of (parent_obj));
23542         }
23543     }
23544 #endif //MULTIPLE_HEAPS
23545 }
23546
23547 inline void
23548 gc_heap::reloc_survivor_helper (uint8_t** pval)
23549 {
23550     THREAD_FROM_HEAP;
23551     relocate_address (pval THREAD_NUMBER_ARG);
23552
23553     check_demotion_helper (pval, (uint8_t*)pval);
23554 }
23555
23556 inline void
23557 gc_heap::relocate_obj_helper (uint8_t* x, size_t s)
23558 {
23559     THREAD_FROM_HEAP;
23560     if (contain_pointers (x))
23561     {
23562         dprintf (3, ("$%Ix$", (size_t)x));
23563
23564         go_through_object_nostart (method_table(x), x, s, pval,
23565                             {
23566                                 uint8_t* child = *pval;
23567                                 reloc_survivor_helper (pval);
23568                                 if (child)
23569                                 {
23570                                     dprintf (3, ("%Ix->%Ix->%Ix", (uint8_t*)pval, child, *pval));
23571                                 }
23572                             });
23573
23574     }
23575     check_class_object_demotion (x);
23576 }
23577
23578 inline 
23579 void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc)
23580 {
23581     THREAD_FROM_HEAP;
23582
23583     uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0);
23584     relocate_address (address_to_reloc THREAD_NUMBER_ARG);
23585     if (address_to_reloc)
23586     {
23587         dprintf (3, ("SR %Ix: %Ix->%Ix", (uint8_t*)address_to_reloc, old_val, *address_to_reloc));
23588     }
23589
23590     //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval);
23591     uint8_t* relocated_addr = *address_to_reloc;
23592     if ((relocated_addr < demotion_high) &&
23593         (relocated_addr >= demotion_low))
23594     {
23595         dprintf (3, ("set card for location %Ix(%Ix)",
23596                     (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23597
23598         set_card (card_of ((uint8_t*)address_to_set_card));
23599     }
23600 #ifdef MULTIPLE_HEAPS
23601     else if (settings.demotion)
23602     {
23603         gc_heap* hp = heap_of (relocated_addr);
23604         if ((relocated_addr < hp->demotion_high) &&
23605             (relocated_addr >= hp->demotion_low))
23606         {
23607             dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)",
23608                         relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23609
23610             set_card (card_of ((uint8_t*)address_to_set_card));
23611         }
23612     }
23613 #endif //MULTIPLE_HEAPS
23614 }
23615
23616 void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
23617 {
23618     THREAD_FROM_HEAP;
23619     uint8_t* plug = pinned_plug (pinned_plug_entry);
23620     uint8_t* pre_plug_start = plug - sizeof (plug_and_gap);
23621     // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
23622     // address. Consider this scenario: 
23623     // gen1 start | 3-ptr sized NP | PP
23624     // 0          | 0x18           | 0x30
23625     // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
23626     // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
23627     // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree). 
23628     pre_plug_start += sizeof (uint8_t*);
23629     uint8_t** old_address = &pre_plug_start;
23630
23631     uint8_t* old_val = (old_address ? *old_address : 0);
23632     relocate_address (old_address THREAD_NUMBER_ARG);
23633     if (old_address)
23634     {
23635         dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix", 
23636             (uint8_t*)old_address, old_val, *old_address, (pre_plug_start - sizeof (uint8_t*))));
23637     }
23638
23639     pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (uint8_t*));
23640 }
23641
23642 inline
23643 void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned)
23644 {
23645     THREAD_FROM_HEAP;
23646     uint8_t* plug = pinned_plug (pinned_plug_entry);
23647
23648     if (!is_pinned)
23649     {
23650         //// Temporary - we just wanna make sure we are doing things right when padding is needed.
23651         //if ((x + s) < plug)
23652         //{
23653         //    dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix", 
23654         //        x, (x + s), (plug- (x + s)), plug));
23655         //    GCToOSInterface::DebugBreak();
23656         //}
23657
23658         relocate_pre_plug_info (pinned_plug_entry);
23659     }
23660
23661     verify_pins_with_post_plug_info("after relocate_pre_plug_info");
23662
23663     uint8_t* saved_plug_info_start = 0;
23664     uint8_t** saved_info_to_relocate = 0;
23665
23666     if (is_pinned)
23667     {
23668         saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start());
23669         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23670     }
23671     else
23672     {
23673         saved_plug_info_start = (plug - sizeof (plug_and_gap));
23674         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
23675     }
23676     
23677     uint8_t** current_saved_info_to_relocate = 0;
23678     uint8_t* child = 0;
23679
23680     dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
23681
23682     if (contain_pointers (x))
23683     {
23684         dprintf (3,("$%Ix$", (size_t)x));
23685
23686         go_through_object_nostart (method_table(x), x, s, pval,
23687         {
23688             dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (uint8_t*)pval, *pval));
23689
23690             if ((uint8_t*)pval >= end)
23691             {
23692                 current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**);
23693                 child = *current_saved_info_to_relocate;
23694                 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
23695                 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
23696                     (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
23697             }
23698             else
23699             {
23700                 reloc_survivor_helper (pval);
23701             }
23702         });
23703     }
23704
23705     check_class_object_demotion (x);
23706 }
23707
23708 void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end)
23709 {
23710     uint8_t*  x = plug;
23711     while (x < plug_end)
23712     {
23713         size_t s = size (x);
23714         uint8_t* next_obj = x + Align (s);
23715         Prefetch (next_obj);
23716         relocate_obj_helper (x, s);
23717         assert (s > 0);
23718         x = next_obj;
23719     }
23720 }
23721
23722 // if we expanded, right now we are not handling it as We are not saving the new reloc info.
23723 void gc_heap::verify_pins_with_post_plug_info (const char* msg)
23724 {
23725 #if defined  (_DEBUG) && defined (VERIFY_HEAP)
23726     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
23727     {
23728         if (!verify_pinned_queue_p)
23729             return;
23730
23731         if (settings.heap_expansion)
23732             return;
23733
23734         for (size_t i = 0; i < mark_stack_tos; i++)
23735         {
23736             mark& m = mark_stack_array[i];
23737
23738             mark* pinned_plug_entry = pinned_plug_of(i);
23739
23740             if (pinned_plug_entry->has_post_plug_info() && 
23741                 pinned_plug_entry->post_short_p() && 
23742                 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
23743             {
23744                 uint8_t* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
23745                 // object after pin
23746                 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d", 
23747                     next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
23748                     (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
23749
23750                 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
23751
23752                 if (node_gap_size (next_obj) != *post_plug_debug)
23753                 {
23754                     dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix", 
23755                         next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
23756                     FATAL_GC_ERROR();
23757                 }
23758                 post_plug_debug++;
23759                 // can't do node_relocation_distance here as it clears the left bit.
23760                 //if (node_relocation_distance (next_obj) != *post_plug_debug)
23761                 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
23762                 {
23763                     dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix", 
23764                         next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
23765                     FATAL_GC_ERROR();
23766                 }
23767                 if (node_left_child (next_obj) > 0)
23768                 {
23769                     dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
23770                     FATAL_GC_ERROR();
23771                 }
23772             }
23773         }
23774
23775         dprintf (3, ("%s verified", msg));
23776     }
23777 #else // _DEBUG && VERIFY_HEAP
23778     UNREFERENCED_PARAMETER(msg);
23779 #endif // _DEBUG && VERIFY_HEAP
23780 }
23781
23782 #ifdef COLLECTIBLE_CLASS
23783 // We don't want to burn another ptr size space for pinned plugs to record this so just 
23784 // set the card unconditionally for collectible objects if we are demoting.
23785 inline void
23786 gc_heap::unconditional_set_card_collectible (uint8_t* obj)
23787 {
23788     if (settings.demotion)
23789     {
23790         set_card (card_of (obj));
23791     }
23792 }
23793 #endif //COLLECTIBLE_CLASS
23794
23795 void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry)
23796 {
23797     uint8_t*  x = plug;
23798     uint8_t* p_plug = pinned_plug (pinned_plug_entry);
23799     BOOL is_pinned = (plug == p_plug);
23800     BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
23801
23802     plug_end += sizeof (gap_reloc_pair);
23803
23804     //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
23805     dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
23806
23807     verify_pins_with_post_plug_info("begin reloc short surv");
23808
23809     while (x < plug_end)
23810     {
23811         if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
23812         {
23813             dprintf (3, ("last obj %Ix is short", x));
23814
23815             if (is_pinned)
23816             {
23817 #ifdef COLLECTIBLE_CLASS
23818                 if (pinned_plug_entry->post_short_collectible_p())
23819                     unconditional_set_card_collectible (x);
23820 #endif //COLLECTIBLE_CLASS
23821
23822                 // Relocate the saved references based on bits set.
23823                 uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start());
23824                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23825                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
23826                 {
23827                     if (pinned_plug_entry->post_short_bit_p (i))
23828                     {
23829                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
23830                     }
23831                 }
23832             }
23833             else
23834             {
23835 #ifdef COLLECTIBLE_CLASS
23836                 if (pinned_plug_entry->pre_short_collectible_p())
23837                     unconditional_set_card_collectible (x);
23838 #endif //COLLECTIBLE_CLASS
23839
23840                 relocate_pre_plug_info (pinned_plug_entry);
23841
23842                 // Relocate the saved references based on bits set.
23843                 uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap));
23844                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
23845                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
23846                 {
23847                     if (pinned_plug_entry->pre_short_bit_p (i))
23848                     {
23849                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
23850                     }
23851                 }
23852             }
23853
23854             break;
23855         }
23856
23857         size_t s = size (x);
23858         uint8_t* next_obj = x + Align (s);
23859         Prefetch (next_obj);
23860
23861         if (next_obj >= plug_end) 
23862         {
23863             dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix", 
23864                 next_obj, plug, plug_end));
23865
23866             verify_pins_with_post_plug_info("before reloc short obj");
23867
23868             relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
23869         }
23870         else
23871         {
23872             relocate_obj_helper (x, s);
23873         }
23874
23875         assert (s > 0);
23876         x = next_obj;
23877     }
23878
23879     verify_pins_with_post_plug_info("end reloc short surv");
23880 }
23881
23882 void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end,
23883                                           BOOL check_last_object_p, 
23884                                           mark* pinned_plug_entry)
23885 {
23886     //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
23887     dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
23888
23889     if (check_last_object_p)
23890     {
23891         relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
23892     }
23893     else
23894     {
23895         relocate_survivor_helper (plug, plug_end);
23896     }
23897 }
23898
23899 void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args)
23900 {
23901     assert ((tree != NULL));
23902
23903     dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
23904         tree, args->last_plug, 
23905         (tree + node_left_child (tree)),
23906         (tree + node_right_child (tree)),
23907         node_gap_size (tree)));
23908
23909     if (node_left_child (tree))
23910     {
23911         relocate_survivors_in_brick (tree + node_left_child (tree), args);
23912     }
23913     {
23914         uint8_t*  plug = tree;
23915         BOOL   has_post_plug_info_p = FALSE;
23916         BOOL   has_pre_plug_info_p = FALSE;
23917
23918         if (tree == oldest_pinned_plug)
23919         {
23920             args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
23921                                                                &has_post_plug_info_p);
23922             assert (tree == pinned_plug (args->pinned_plug_entry));
23923
23924             dprintf (3, ("tree is the oldest pin: %Ix", tree));
23925         }
23926         if (args->last_plug)
23927         {
23928             size_t  gap_size = node_gap_size (tree);
23929             uint8_t*  gap = (plug - gap_size);
23930             dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
23931             assert (gap_size >= Align (min_obj_size));
23932             uint8_t*  last_plug_end = gap;
23933
23934             BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
23935
23936             {
23937                 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
23938             }
23939         }
23940         else
23941         {
23942             assert (!has_pre_plug_info_p);
23943         }
23944
23945         args->last_plug = plug;
23946         args->is_shortened = has_post_plug_info_p;
23947         if (has_post_plug_info_p)
23948         {
23949             dprintf (3, ("setting %Ix as shortened", plug));
23950         }
23951         dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
23952     }
23953     if (node_right_child (tree))
23954     {
23955         relocate_survivors_in_brick (tree + node_right_child (tree), args);
23956     }
23957 }
23958
23959 inline
23960 void gc_heap::update_oldest_pinned_plug()
23961 {
23962     oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
23963 }
23964
23965 void gc_heap::relocate_survivors (int condemned_gen_number,
23966                                   uint8_t* first_condemned_address)
23967 {
23968     generation* condemned_gen = generation_of (condemned_gen_number);
23969     uint8_t*  start_address = first_condemned_address;
23970     size_t  current_brick = brick_of (start_address);
23971     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23972
23973     PREFIX_ASSUME(current_heap_segment != NULL);
23974
23975     uint8_t*  end_address = 0;
23976
23977     reset_pinned_queue_bos();
23978     update_oldest_pinned_plug();
23979     
23980     end_address = heap_segment_allocated (current_heap_segment);
23981
23982     size_t  end_brick = brick_of (end_address - 1);
23983     relocate_args args;
23984     args.low = gc_low;
23985     args.high = gc_high;
23986     args.is_shortened = FALSE;
23987     args.pinned_plug_entry = 0;
23988     args.last_plug = 0;
23989     while (1)
23990     {
23991         if (current_brick > end_brick)
23992         {
23993             if (args.last_plug)
23994             {
23995                 {
23996                     assert (!(args.is_shortened));
23997                     relocate_survivors_in_plug (args.last_plug,
23998                                                 heap_segment_allocated (current_heap_segment),
23999                                                 args.is_shortened, 
24000                                                 args.pinned_plug_entry);
24001                 }
24002
24003                 args.last_plug = 0;
24004             }
24005
24006             if (heap_segment_next_rw (current_heap_segment))
24007             {
24008                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24009                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24010                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24011                 continue;
24012             }
24013             else
24014             {
24015                 break;
24016             }
24017         }
24018         {
24019             int brick_entry =  brick_table [ current_brick ];
24020
24021             if (brick_entry >= 0)
24022             {
24023                 relocate_survivors_in_brick (brick_address (current_brick) +
24024                                              brick_entry -1,
24025                                              &args);
24026             }
24027         }
24028         current_brick++;
24029     }
24030 }
24031
24032 void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args)
24033 {
24034     if (check_last_object_p)
24035     {
24036         size += sizeof (gap_reloc_pair);
24037         mark* entry = args->pinned_plug_entry;
24038
24039         if (args->is_shortened)
24040         {
24041             assert (entry->has_post_plug_info());
24042             entry->swap_post_plug_and_saved_for_profiler();
24043         }
24044         else
24045         {
24046             assert (entry->has_pre_plug_info());
24047             entry->swap_pre_plug_and_saved_for_profiler();
24048         }
24049     }
24050
24051     ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
24052     STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
24053     ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
24054
24055     (args->fn) (plug, (plug + size), reloc, args->profiling_context, !!settings.compaction, false);
24056
24057     if (check_last_object_p)
24058     {
24059         mark* entry = args->pinned_plug_entry;
24060
24061         if (args->is_shortened)
24062         {
24063             entry->swap_post_plug_and_saved_for_profiler();
24064         }
24065         else
24066         {
24067             entry->swap_pre_plug_and_saved_for_profiler();
24068         }
24069     }
24070 }
24071
24072 void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args)
24073 {
24074     assert ((tree != NULL));
24075     if (node_left_child (tree))
24076     {
24077         walk_relocation_in_brick (tree + node_left_child (tree), args);
24078     }
24079
24080     uint8_t*  plug = tree;
24081     BOOL   has_pre_plug_info_p = FALSE;
24082     BOOL   has_post_plug_info_p = FALSE;
24083
24084     if (tree == oldest_pinned_plug)
24085     {
24086         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24087                                                            &has_post_plug_info_p);
24088         assert (tree == pinned_plug (args->pinned_plug_entry));
24089     }
24090
24091     if (args->last_plug != 0)
24092     {
24093         size_t gap_size = node_gap_size (tree);
24094         uint8_t*  gap = (plug - gap_size);
24095         uint8_t*  last_plug_end = gap;
24096         size_t last_plug_size = (last_plug_end - args->last_plug);
24097         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
24098             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24099         
24100         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24101         if (!check_last_object_p)
24102         {
24103             assert (last_plug_size >= Align (min_obj_size));
24104         }
24105
24106         walk_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24107     }
24108     else
24109     {
24110         assert (!has_pre_plug_info_p);
24111     }
24112
24113     dprintf (3, ("set args last plug to plug: %Ix", plug));
24114     args->last_plug = plug;
24115     args->is_shortened = has_post_plug_info_p;
24116
24117     if (node_right_child (tree))
24118     {
24119         walk_relocation_in_brick (tree + node_right_child (tree), args);
24120     }
24121 }
24122
24123 void gc_heap::walk_relocation (void* profiling_context, record_surv_fn fn)
24124 {
24125     generation* condemned_gen = generation_of (settings.condemned_generation);
24126     uint8_t*  start_address = generation_allocation_start (condemned_gen);
24127     size_t  current_brick = brick_of (start_address);
24128     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24129
24130     PREFIX_ASSUME(current_heap_segment != NULL);
24131
24132     reset_pinned_queue_bos();
24133     update_oldest_pinned_plug();
24134     size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24135     walk_relocate_args args;
24136     args.is_shortened = FALSE;
24137     args.pinned_plug_entry = 0;
24138     args.last_plug = 0;
24139     args.profiling_context = profiling_context;
24140     args.fn = fn;
24141
24142     while (1)
24143     {
24144         if (current_brick > end_brick)
24145         {
24146             if (args.last_plug)
24147             {
24148                 walk_plug (args.last_plug, 
24149                            (heap_segment_allocated (current_heap_segment) - args.last_plug), 
24150                            args.is_shortened,
24151                            &args);
24152                 args.last_plug = 0;
24153             }
24154             if (heap_segment_next_rw (current_heap_segment))
24155             {
24156                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24157                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24158                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24159                 continue;
24160             }
24161             else
24162             {
24163                 break;
24164             }
24165         }
24166         {
24167             int brick_entry =  brick_table [ current_brick ];
24168             if (brick_entry >= 0)
24169             {
24170                 walk_relocation_in_brick (brick_address (current_brick) +
24171                                           brick_entry - 1,
24172                                           &args);
24173             }
24174         }
24175         current_brick++;
24176     }
24177 }
24178
24179 void gc_heap::walk_survivors (record_surv_fn fn, void* context, walk_surv_type type)
24180 {
24181     if (type == walk_for_gc)
24182         walk_survivors_relocation (context, fn);
24183 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24184     else if (type == walk_for_bgc)
24185         walk_survivors_for_bgc (context, fn);
24186 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
24187     else if (type == walk_for_loh)
24188         walk_survivors_for_loh (context, fn);
24189     else
24190         assert (!"unknown type!");
24191 }
24192
24193 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24194 void gc_heap::walk_survivors_for_bgc (void* profiling_context, record_surv_fn fn)
24195 {
24196     // This should only be called for BGCs
24197     assert(settings.concurrent);
24198
24199     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24200
24201     BOOL small_object_segments = TRUE;
24202     int align_const = get_alignment_constant (small_object_segments);
24203
24204     while (1)
24205     {
24206         if (seg == 0)
24207         {
24208             if (small_object_segments)
24209             {
24210                 //switch to large segment
24211                 small_object_segments = FALSE;
24212
24213                 align_const = get_alignment_constant (small_object_segments);
24214                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24215
24216                 PREFIX_ASSUME(seg != NULL);
24217
24218                 continue;
24219             }
24220             else 
24221                 break;
24222         }
24223
24224         uint8_t* o = heap_segment_mem (seg);
24225         uint8_t* end = heap_segment_allocated (seg);
24226
24227         while (o < end)
24228         {
24229             if (method_table(o) == g_gc_pFreeObjectMethodTable)
24230             {
24231                 o += Align (size (o), align_const);
24232                 continue;
24233             }
24234
24235             // It's survived. Make a fake plug, starting at o,
24236             // and send the event
24237
24238             uint8_t* plug_start = o;
24239
24240             while (method_table(o) != g_gc_pFreeObjectMethodTable)
24241             {
24242                 o += Align (size (o), align_const);
24243                 if (o >= end)
24244                 {
24245                     break;
24246                 }
24247             }
24248                 
24249             uint8_t* plug_end = o;
24250
24251             fn (plug_start, 
24252                 plug_end,
24253                 0,              // Reloc distance == 0 as this is non-compacting
24254                 profiling_context,
24255                 false,          // Non-compacting
24256                 true);          // BGC
24257         }
24258
24259         seg = heap_segment_next (seg);
24260     }
24261 }
24262 #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24263
24264 void gc_heap::relocate_phase (int condemned_gen_number,
24265                               uint8_t* first_condemned_address)
24266 {
24267     ScanContext sc;
24268     sc.thread_number = heap_number;
24269     sc.promotion = FALSE;
24270     sc.concurrent = FALSE;
24271
24272
24273 #ifdef TIME_GC
24274         unsigned start;
24275         unsigned finish;
24276         start = GetCycleCount32();
24277 #endif //TIME_GC
24278
24279 //  %type%  category = quote (relocate);
24280     dprintf (2,("---- Relocate phase -----"));
24281
24282 #ifdef MULTIPLE_HEAPS
24283     //join all threads to make sure they are synchronized
24284     dprintf(3, ("Joining after end of plan"));
24285     gc_t_join.join(this, gc_join_begin_relocate_phase);
24286     if (gc_t_join.joined())
24287 #endif //MULTIPLE_HEAPS
24288
24289     {
24290 #ifdef MULTIPLE_HEAPS
24291
24292         //join all threads to make sure they are synchronized
24293         dprintf(3, ("Restarting for relocation"));
24294         gc_t_join.restart();
24295 #endif //MULTIPLE_HEAPS
24296     }
24297
24298     dprintf(3,("Relocating roots"));
24299     GCScan::GcScanRoots(GCHeap::Relocate,
24300                             condemned_gen_number, max_generation, &sc);
24301
24302     verify_pins_with_post_plug_info("after reloc stack");
24303
24304 #ifdef BACKGROUND_GC
24305     if (recursive_gc_sync::background_running_p())
24306     {
24307         scan_background_roots (GCHeap::Relocate, heap_number, &sc);
24308     }
24309 #endif //BACKGROUND_GC
24310
24311     if (condemned_gen_number != max_generation)
24312     {
24313         dprintf(3,("Relocating cross generation pointers"));
24314         mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
24315         verify_pins_with_post_plug_info("after reloc cards");
24316     }
24317     if (condemned_gen_number != max_generation)
24318     {
24319         dprintf(3,("Relocating cross generation pointers for large objects"));
24320         mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
24321     }
24322     else
24323     {
24324 #ifdef FEATURE_LOH_COMPACTION
24325         if (loh_compacted_p)
24326         {
24327             assert (settings.condemned_generation == max_generation);
24328             relocate_in_loh_compact();
24329         }
24330         else
24331 #endif //FEATURE_LOH_COMPACTION
24332         {
24333             relocate_in_large_objects ();
24334         }
24335     }
24336     {
24337         dprintf(3,("Relocating survivors"));
24338         relocate_survivors (condemned_gen_number,
24339                             first_condemned_address);
24340     }
24341
24342 #ifdef FEATURE_PREMORTEM_FINALIZATION
24343         dprintf(3,("Relocating finalization data"));
24344         finalize_queue->RelocateFinalizationData (condemned_gen_number,
24345                                                        __this);
24346 #endif // FEATURE_PREMORTEM_FINALIZATION
24347
24348
24349 // MTHTS
24350     {
24351         dprintf(3,("Relocating handle table"));
24352         GCScan::GcScanHandles(GCHeap::Relocate,
24353                                   condemned_gen_number, max_generation, &sc);
24354     }
24355
24356 #ifdef MULTIPLE_HEAPS
24357     //join all threads to make sure they are synchronized
24358     dprintf(3, ("Joining after end of relocation"));
24359     gc_t_join.join(this, gc_join_relocate_phase_done);
24360
24361 #endif //MULTIPLE_HEAPS
24362
24363 #ifdef TIME_GC
24364         finish = GetCycleCount32();
24365         reloc_time = finish - start;
24366 #endif //TIME_GC
24367
24368     dprintf(2,( "---- End of Relocate phase ----"));
24369 }
24370
24371 // This compares to see if tree is the current pinned plug and returns info
24372 // for this pinned plug. Also advances the pinned queue if that's the case.
24373 //
24374 // We don't change the values of the plug info if tree is not the same as 
24375 // the current pinned plug - the caller is responsible for setting the right
24376 // values to begin with.
24377 //
24378 // POPO TODO: We are keeping this temporarily as this is also used by realloc 
24379 // where it passes FALSE to deque_p, change it to use the same optimization 
24380 // as relocate. Not as essential since realloc is already a slow path.
24381 mark* gc_heap::get_next_pinned_entry (uint8_t* tree,
24382                                       BOOL* has_pre_plug_info_p, 
24383                                       BOOL* has_post_plug_info_p,
24384                                       BOOL deque_p)
24385 {
24386     if (!pinned_plug_que_empty_p())
24387     {
24388         mark* oldest_entry = oldest_pin();
24389         uint8_t* oldest_plug = pinned_plug (oldest_entry);
24390         if (tree == oldest_plug)
24391         {
24392             *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24393             *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24394
24395             if (deque_p)
24396             {
24397                 deque_pinned_plug();
24398             }
24399
24400             dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d", 
24401                 tree, 
24402                 (*has_pre_plug_info_p ? 1 : 0),
24403                 (*has_post_plug_info_p ? 1 : 0)));
24404
24405             return oldest_entry;
24406         }
24407     }
24408
24409     return NULL;
24410 }
24411
24412 // This also deques the oldest entry and update the oldest plug
24413 mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p, 
24414                                         BOOL* has_post_plug_info_p)
24415 {
24416     mark* oldest_entry = oldest_pin();
24417     *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24418     *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24419
24420     deque_pinned_plug();
24421     update_oldest_pinned_plug();
24422     return oldest_entry;
24423 }
24424
24425 inline
24426 void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24427 {
24428     if (copy_cards_p)
24429         copy_cards_for_addresses (dest, src, len);
24430     else
24431         clear_card_for_addresses (dest, dest + len);
24432 }
24433
24434 // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
24435 // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
24436 // we won't need to individually recover each overwritten part of plugs.
24437 inline
24438 void  gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24439 {
24440     if (dest != src)
24441     {
24442 #ifdef BACKGROUND_GC
24443         if (current_c_gc_state == c_gc_state_marking) 
24444         {
24445             //TODO: should look to see whether we should consider changing this
24446             // to copy a consecutive region of the mark array instead.
24447             copy_mark_bits_for_addresses (dest, src, len);
24448         }
24449 #endif //BACKGROUND_GC
24450         //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24451         dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24452         memcopy (dest - plug_skew, src - plug_skew, len);
24453 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24454         if (SoftwareWriteWatch::IsEnabledForGCHeap())
24455         {
24456             // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC
24457             // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the
24458             // object at (src + len), so it can be ignored anyway.
24459             SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew);
24460         }
24461 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24462         copy_cards_range (dest, src, len, copy_cards_p);
24463     }
24464 }
24465
24466 void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args)
24467 {
24468     args->print();
24469     uint8_t* reloc_plug = plug + args->last_plug_relocation;
24470
24471     if (check_last_object_p)
24472     {
24473         size += sizeof (gap_reloc_pair);
24474         mark* entry = args->pinned_plug_entry;
24475
24476         if (args->is_shortened)
24477         {
24478             assert (entry->has_post_plug_info());
24479             entry->swap_post_plug_and_saved();
24480         }
24481         else
24482         {
24483             assert (entry->has_pre_plug_info());
24484             entry->swap_pre_plug_and_saved();
24485         }
24486     }
24487
24488     int  old_brick_entry =  brick_table [brick_of (plug)];
24489
24490     assert (node_relocation_distance (plug) == args->last_plug_relocation);
24491
24492 #ifdef FEATURE_STRUCTALIGN
24493     ptrdiff_t alignpad = node_alignpad(plug);
24494     if (alignpad)
24495     {
24496         make_unused_array (reloc_plug - alignpad, alignpad);
24497         if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
24498         {
24499             // The alignment padding is straddling one or more bricks;
24500             // it has to be the last "object" of its first brick.
24501             fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
24502         }
24503     }
24504 #else // FEATURE_STRUCTALIGN
24505     size_t unused_arr_size = 0; 
24506     BOOL  already_padded_p = FALSE;
24507 #ifdef SHORT_PLUGS
24508     if (is_plug_padded (plug))
24509     {
24510         already_padded_p = TRUE;
24511         clear_plug_padded (plug);
24512         unused_arr_size = Align (min_obj_size);
24513     }
24514 #endif //SHORT_PLUGS
24515     if (node_realigned (plug))
24516     {
24517         unused_arr_size += switch_alignment_size (already_padded_p);
24518     }
24519
24520     if (unused_arr_size != 0) 
24521     {
24522         make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
24523
24524         if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
24525         {
24526             dprintf (3, ("fix B for padding: %Id: %Ix->%Ix", 
24527                 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
24528             // The alignment padding is straddling one or more bricks;
24529             // it has to be the last "object" of its first brick.
24530             fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
24531         }
24532     }
24533 #endif // FEATURE_STRUCTALIGN
24534
24535 #ifdef SHORT_PLUGS
24536     if (is_plug_padded (plug))
24537     {
24538         make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
24539
24540         if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
24541         {
24542             // The alignment padding is straddling one or more bricks;
24543             // it has to be the last "object" of its first brick.
24544             fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
24545         }
24546     }
24547 #endif //SHORT_PLUGS
24548
24549     gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
24550
24551     if (args->check_gennum_p)
24552     {
24553         int src_gennum = args->src_gennum;
24554         if (src_gennum == -1)
24555         {
24556             src_gennum = object_gennum (plug);
24557         }
24558
24559         int dest_gennum = object_gennum_plan (reloc_plug);
24560
24561         if (src_gennum < dest_gennum)
24562         {
24563             generation_allocation_size (generation_of (dest_gennum)) += size;
24564         }
24565     }
24566
24567     size_t current_reloc_brick = args->current_compacted_brick;
24568
24569     if (brick_of (reloc_plug) != current_reloc_brick)
24570     {
24571         dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix", 
24572             current_reloc_brick, brick_of (reloc_plug)));
24573
24574         if (args->before_last_plug)
24575         {
24576             dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
24577                      current_reloc_brick,
24578                      args->before_last_plug, 
24579                      (args->before_last_plug - brick_address (current_reloc_brick))));
24580
24581             {
24582                 set_brick (current_reloc_brick,
24583                         args->before_last_plug - brick_address (current_reloc_brick));
24584             }
24585         }
24586         current_reloc_brick = brick_of (reloc_plug);
24587     }
24588     size_t end_brick = brick_of (reloc_plug + size-1);
24589     if (end_brick != current_reloc_brick)
24590     {
24591         // The plug is straddling one or more bricks
24592         // It has to be the last plug of its first brick
24593         dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
24594                  current_reloc_brick, (size_t)reloc_plug,
24595                  (reloc_plug - brick_address (current_reloc_brick))));
24596
24597         {
24598             set_brick (current_reloc_brick,
24599                     reloc_plug - brick_address (current_reloc_brick));
24600         }
24601         // update all intervening brick
24602         size_t brick = current_reloc_brick + 1;
24603         dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
24604             brick, (end_brick - 1)));
24605         while (brick < end_brick)
24606         {
24607             set_brick (brick, -1);
24608             brick++;
24609         }
24610         // code last brick offset as a plug address
24611         args->before_last_plug = brick_address (end_brick) -1;
24612         current_reloc_brick = end_brick;
24613         dprintf (3, ("setting before last to %Ix, last brick to %Ix",
24614             args->before_last_plug, current_reloc_brick));
24615     } 
24616     else
24617     {
24618         dprintf (3, ("still in the same brick: %Ix", end_brick));
24619         args->before_last_plug = reloc_plug;
24620     }
24621     args->current_compacted_brick = current_reloc_brick;
24622
24623     if (check_last_object_p)
24624     {
24625         mark* entry = args->pinned_plug_entry;
24626
24627         if (args->is_shortened)
24628         {
24629             entry->swap_post_plug_and_saved();
24630         }
24631         else
24632         {
24633             entry->swap_pre_plug_and_saved();
24634         }
24635     }
24636 }
24637
24638 void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args)
24639 {
24640     assert (tree != NULL);
24641     int   left_node = node_left_child (tree);
24642     int   right_node = node_right_child (tree);
24643     ptrdiff_t relocation = node_relocation_distance (tree);
24644
24645     args->print();
24646
24647     if (left_node)
24648     {
24649         dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
24650         compact_in_brick ((tree + left_node), args);
24651     }
24652
24653     uint8_t*  plug = tree;
24654     BOOL   has_pre_plug_info_p = FALSE;
24655     BOOL   has_post_plug_info_p = FALSE;
24656
24657     if (tree == oldest_pinned_plug)
24658     {
24659         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24660                                                            &has_post_plug_info_p);
24661         assert (tree == pinned_plug (args->pinned_plug_entry));
24662     }
24663
24664     if (args->last_plug != 0)
24665     {
24666         size_t gap_size = node_gap_size (tree);
24667         uint8_t*  gap = (plug - gap_size);
24668         uint8_t*  last_plug_end = gap;
24669         size_t last_plug_size = (last_plug_end - args->last_plug);
24670         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
24671             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24672         
24673         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24674         if (!check_last_object_p)
24675         {
24676             assert (last_plug_size >= Align (min_obj_size));
24677         }
24678
24679         compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24680     }
24681     else
24682     {
24683         assert (!has_pre_plug_info_p);
24684     }
24685
24686     dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
24687     args->last_plug = plug;
24688     args->last_plug_relocation = relocation;
24689     args->is_shortened = has_post_plug_info_p;
24690
24691     if (right_node)
24692     {
24693         dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
24694         compact_in_brick ((tree + right_node), args);
24695     }
24696 }
24697
24698 void gc_heap::recover_saved_pinned_info()
24699 {
24700     reset_pinned_queue_bos();
24701
24702     while (!(pinned_plug_que_empty_p()))
24703     {
24704         mark* oldest_entry = oldest_pin();
24705         oldest_entry->recover_plug_info();
24706 #ifdef GC_CONFIG_DRIVEN
24707         if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info())
24708             record_interesting_data_point (idp_pre_and_post_pin);
24709         else if (oldest_entry->has_pre_plug_info())
24710             record_interesting_data_point (idp_pre_pin);
24711         else if (oldest_entry->has_post_plug_info())
24712             record_interesting_data_point (idp_post_pin);
24713 #endif //GC_CONFIG_DRIVEN
24714
24715         deque_pinned_plug();
24716     }
24717 }
24718
24719 void gc_heap::compact_phase (int condemned_gen_number,
24720                              uint8_t*  first_condemned_address,
24721                              BOOL clear_cards)
24722 {
24723 //  %type%  category = quote (compact);
24724 #ifdef TIME_GC
24725         unsigned start;
24726         unsigned finish;
24727         start = GetCycleCount32();
24728 #endif //TIME_GC
24729     generation*   condemned_gen = generation_of (condemned_gen_number);
24730     uint8_t*  start_address = first_condemned_address;
24731     size_t   current_brick = brick_of (start_address);
24732     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24733
24734     PREFIX_ASSUME(current_heap_segment != NULL);
24735
24736     reset_pinned_queue_bos();
24737     update_oldest_pinned_plug();
24738
24739     BOOL reused_seg = expand_reused_seg_p();
24740     if (reused_seg)
24741     {
24742         for (int i = 1; i <= max_generation; i++)
24743         {
24744             generation_allocation_size (generation_of (i)) = 0;
24745         }
24746     }
24747
24748     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
24749
24750     size_t  end_brick = brick_of (end_address-1);
24751     compact_args args;
24752     args.last_plug = 0;
24753     args.before_last_plug = 0;
24754     args.current_compacted_brick = ~((size_t)1);
24755     args.is_shortened = FALSE;
24756     args.pinned_plug_entry = 0;
24757     args.copy_cards_p =  (condemned_gen_number >= 1) || !clear_cards;
24758     args.check_gennum_p = reused_seg;
24759     if (args.check_gennum_p)
24760     {
24761         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
24762     }
24763
24764     dprintf (2,("---- Compact Phase: %Ix(%Ix)----", 
24765         first_condemned_address, brick_of (first_condemned_address)));
24766
24767 #ifdef MULTIPLE_HEAPS
24768     //restart
24769     if (gc_t_join.joined())
24770     {
24771 #endif //MULTIPLE_HEAPS
24772
24773 #ifdef MULTIPLE_HEAPS
24774         dprintf(3, ("Restarting for compaction"));
24775         gc_t_join.restart();
24776     }
24777 #endif //MULTIPLE_HEAPS
24778
24779     reset_pinned_queue_bos();
24780
24781 #ifdef FEATURE_LOH_COMPACTION
24782     if (loh_compacted_p)
24783     {
24784         compact_loh();
24785     }
24786 #endif //FEATURE_LOH_COMPACTION
24787
24788     if ((start_address < end_address) ||
24789         (condemned_gen_number == max_generation))
24790     {
24791         while (1)
24792         {
24793             if (current_brick > end_brick)
24794             {
24795                 if (args.last_plug != 0)
24796                 {
24797                     dprintf (3, ("compacting last plug: %Ix", args.last_plug))
24798                     compact_plug (args.last_plug,
24799                                   (heap_segment_allocated (current_heap_segment) - args.last_plug),
24800                                   args.is_shortened,
24801                                   &args);
24802                 }
24803
24804                 if (heap_segment_next_rw (current_heap_segment))
24805                 {
24806                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
24807                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
24808                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24809                     args.last_plug = 0;
24810                     if (args.check_gennum_p)
24811                     {
24812                         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
24813                     }
24814                     continue;
24815                 }
24816                 else
24817                 {
24818                     if (args.before_last_plug !=0)
24819                     {
24820                         dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
24821                                     args.current_compacted_brick, (size_t)args.before_last_plug));
24822                         assert (args.current_compacted_brick != ~1u);
24823                         set_brick (args.current_compacted_brick,
24824                                    args.before_last_plug - brick_address (args.current_compacted_brick));
24825                     }
24826                     break;
24827                 }
24828             }
24829             {
24830                 int  brick_entry =  brick_table [ current_brick ];
24831                 dprintf (3, ("B: %Ix(%Ix)->%Ix", 
24832                     current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
24833
24834                 if (brick_entry >= 0)
24835                 {
24836                     compact_in_brick ((brick_address (current_brick) + brick_entry -1),
24837                                       &args);
24838
24839                 }
24840             }
24841             current_brick++;
24842         }
24843     }
24844
24845     recover_saved_pinned_info();
24846
24847 #ifdef TIME_GC
24848     finish = GetCycleCount32();
24849     compact_time = finish - start;
24850 #endif //TIME_GC
24851
24852     concurrent_print_time_delta ("compact end");
24853
24854     dprintf(2,("---- End of Compact phase ----"));
24855 }
24856
24857 #ifdef MULTIPLE_HEAPS
24858
24859 #ifdef _MSC_VER
24860 #pragma warning(push)
24861 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
24862 #endif //_MSC_VER
24863 void gc_heap::gc_thread_stub (void* arg)
24864 {
24865     gc_heap* heap = (gc_heap*)arg;
24866     if (!gc_thread_no_affinitize_p)
24867     {
24868         GCThreadAffinity affinity;
24869         affinity.Group = GCThreadAffinity::None;
24870         affinity.Processor = GCThreadAffinity::None;
24871
24872         // We are about to set affinity for GC threads. It is a good place to set up NUMA and
24873         // CPU groups because the process mask, processor number, and group number are all
24874         // readily available.
24875         if (GCToOSInterface::CanEnableGCCPUGroups())
24876             set_thread_group_affinity_for_heap(heap->heap_number, &affinity);
24877         else
24878             set_thread_affinity_mask_for_heap(heap->heap_number, &affinity);
24879
24880         if (!GCToOSInterface::SetThreadAffinity(&affinity))
24881         {
24882             dprintf(1, ("Failed to set thread affinity for server GC thread"));
24883         }
24884     }
24885
24886     // server GC threads run at a higher priority than normal.
24887     GCToOSInterface::BoostThreadPriority();
24888     _alloca (256*heap->heap_number);
24889     heap->gc_thread_function();
24890 }
24891 #ifdef _MSC_VER
24892 #pragma warning(pop)
24893 #endif //_MSC_VER
24894
24895 #endif //MULTIPLE_HEAPS
24896
24897 #ifdef BACKGROUND_GC
24898
24899 #ifdef _MSC_VER
24900 #pragma warning(push)
24901 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
24902 #endif //_MSC_VER
24903 void gc_heap::bgc_thread_stub (void* arg)
24904 {
24905     gc_heap* heap = (gc_heap*)arg;
24906     heap->bgc_thread = GCToEEInterface::GetThread();
24907     assert(heap->bgc_thread != nullptr);
24908     heap->bgc_thread_function();
24909 }
24910 #ifdef _MSC_VER
24911 #pragma warning(pop)
24912 #endif //_MSC_VER
24913
24914 #endif //BACKGROUND_GC
24915
24916 /*------------------ Background GC ----------------------------*/
24917
24918 #ifdef BACKGROUND_GC
24919
24920 void gc_heap::background_drain_mark_list (int thread)
24921 {
24922     UNREFERENCED_PARAMETER(thread);
24923
24924     size_t saved_c_mark_list_index = c_mark_list_index;
24925
24926     if (saved_c_mark_list_index)
24927     {
24928         concurrent_print_time_delta ("SML");
24929     }
24930     while (c_mark_list_index != 0)
24931     {
24932         size_t current_index = c_mark_list_index - 1;
24933         uint8_t* o = c_mark_list [current_index];
24934         background_mark_object (o THREAD_NUMBER_ARG);
24935         c_mark_list_index--;
24936     }
24937     if (saved_c_mark_list_index)
24938     {
24939
24940         concurrent_print_time_delta ("EML");
24941     }
24942
24943     fire_drain_mark_list_event (saved_c_mark_list_index);
24944 }
24945
24946
24947 // The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
24948 #ifdef MULTIPLE_HEAPS
24949 // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
24950 // them. So we can use the same static variables.
24951 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
24952 {
24953     // Whenever we call this method there may have been preceding object promotions. So set
24954     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
24955     // based on the how the scanning proceeded).
24956     s_fUnscannedPromotions = TRUE;
24957
24958     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
24959     // the state of this thread's portion of the dependent handle table. That's because promotions on other
24960     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
24961     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
24962     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
24963     // as all the others or they'll get out of step).
24964     while (true)
24965     {
24966         // The various worker threads are all currently racing in this code. We need to work out if at least
24967         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
24968         // dependent handle table when both of the following conditions apply:
24969         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
24970         //     object happens to correspond to a primary in one of our handles we might potentially have to
24971         //     promote the associated secondary).
24972         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
24973         //
24974         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
24975         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
24976         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
24977         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
24978         // follows below. Note that we can't read this outside of the join since on any iteration apart from
24979         // the first threads will be racing between reading this value and completing their previous
24980         // iteration's table scan.
24981         //
24982         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
24983         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
24984         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
24985         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
24986         // we're safely joined.
24987         if (GCScan::GcDhUnpromotedHandlesExist(sc))
24988             s_fUnpromotedHandles = TRUE;
24989
24990         // Synchronize all the threads so we can read our state variables safely. The following shared
24991         // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
24992         // single thread inside the join.
24993         bgc_t_join.join(this, gc_join_scan_dependent_handles);
24994         if (bgc_t_join.joined())
24995         {
24996             // We're synchronized so it's safe to read our shared state variables. We update another shared
24997             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
24998             // the loop. We scan if there has been at least one object promotion since last time and at least
24999             // one thread has a dependent handle table with a potential handle promotion possible.
25000             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
25001
25002             // Reset our shared state variables (ready to be set again on this scan or with a good initial
25003             // value for the next call if we're terminating the loop).
25004             s_fUnscannedPromotions = FALSE;
25005             s_fUnpromotedHandles = FALSE;
25006
25007             if (!s_fScanRequired)
25008             {
25009                 uint8_t* all_heaps_max = 0;
25010                 uint8_t* all_heaps_min = MAX_PTR;
25011                 int i;
25012                 for (i = 0; i < n_heaps; i++)
25013                 {
25014                     if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25015                         all_heaps_max = g_heaps[i]->background_max_overflow_address;
25016                     if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25017                         all_heaps_min = g_heaps[i]->background_min_overflow_address;
25018                 }
25019                 for (i = 0; i < n_heaps; i++)
25020                 {
25021                     g_heaps[i]->background_max_overflow_address = all_heaps_max;
25022                     g_heaps[i]->background_min_overflow_address = all_heaps_min;
25023                 }
25024             }
25025
25026             // Restart all the workers.
25027             dprintf(2, ("Starting all gc thread mark stack overflow processing"));
25028             bgc_t_join.restart();
25029         }
25030
25031         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25032         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
25033         // global flag indicating that at least one object promotion may have occurred (the usual comment
25034         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
25035         // exit the method since we unconditionally set this variable on method entry anyway).
25036         if (background_process_mark_overflow (sc->concurrent))
25037             s_fUnscannedPromotions = TRUE;
25038
25039         // If we decided that no scan was required we can terminate the loop now.
25040         if (!s_fScanRequired)
25041             break;
25042
25043         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
25044         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
25045         // could miss noting the promotion of some primary objects).
25046         bgc_t_join.join(this, gc_join_rescan_dependent_handles);
25047         if (bgc_t_join.joined())
25048         {
25049             // Restart all the workers.
25050             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
25051             bgc_t_join.restart();
25052         }
25053
25054         // If the portion of the dependent handle table managed by this worker has handles that could still be
25055         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
25056         // could require a rescan of handles on this or other workers.
25057         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25058             if (GCScan::GcDhReScan(sc))
25059                 s_fUnscannedPromotions = TRUE;
25060     }
25061 }
25062 #else
25063 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25064 {
25065     // Whenever we call this method there may have been preceding object promotions. So set
25066     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25067     // based on the how the scanning proceeded).
25068     bool fUnscannedPromotions = true;
25069
25070     // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
25071     // scan without performing any new promotions.
25072     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
25073     {
25074         // On each iteration of the loop start with the assumption that no further objects have been promoted.
25075         fUnscannedPromotions = false;
25076
25077         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25078         // being visible. If there was an overflow (background_process_mark_overflow returned true) then
25079         // additional objects now appear to be promoted and we should set the flag.
25080         if (background_process_mark_overflow (sc->concurrent))
25081             fUnscannedPromotions = true;
25082
25083         // Perform the scan and set the flag if any promotions resulted.
25084         if (GCScan::GcDhReScan (sc))
25085             fUnscannedPromotions = true;
25086     }
25087
25088     // Perform a last processing of any overflowed mark stack.
25089     background_process_mark_overflow (sc->concurrent);
25090 }
25091 #endif //MULTIPLE_HEAPS
25092
25093 void gc_heap::recover_bgc_settings()
25094 {
25095     if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
25096     {
25097         dprintf (2, ("restoring bgc settings"));
25098         settings = saved_bgc_settings;
25099         GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
25100     }
25101 }
25102
25103 void gc_heap::allow_fgc()
25104 {
25105     assert (bgc_thread == GCToEEInterface::GetThread());
25106     bool bToggleGC = false;
25107
25108     if (g_fSuspensionPending > 0)
25109     {
25110         bToggleGC = GCToEEInterface::EnablePreemptiveGC();
25111         if (bToggleGC)
25112         {
25113             GCToEEInterface::DisablePreemptiveGC();
25114         }
25115     }
25116 }
25117
25118 BOOL gc_heap::should_commit_mark_array()
25119 {
25120     return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
25121 }
25122
25123 void gc_heap::clear_commit_flag()
25124 {
25125     generation* gen = generation_of (max_generation);
25126     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25127     while (1)
25128     {
25129         if (seg == 0)
25130         {
25131             if (gen != large_object_generation)
25132             {
25133                 gen = large_object_generation;
25134                 seg = heap_segment_in_range (generation_start_segment (gen));
25135             }
25136             else
25137             {
25138                 break;
25139             }
25140         }
25141
25142         if (seg->flags & heap_segment_flags_ma_committed)
25143         {
25144             seg->flags &= ~heap_segment_flags_ma_committed;
25145         }
25146
25147         if (seg->flags & heap_segment_flags_ma_pcommitted)
25148         {
25149             seg->flags &= ~heap_segment_flags_ma_pcommitted;
25150         }
25151
25152         seg = heap_segment_next (seg);
25153     }
25154 }
25155
25156 void gc_heap::clear_commit_flag_global()
25157 {
25158 #ifdef MULTIPLE_HEAPS
25159     for (int i = 0; i < n_heaps; i++)
25160     {
25161         g_heaps[i]->clear_commit_flag();
25162     }
25163 #else
25164     clear_commit_flag();
25165 #endif //MULTIPLE_HEAPS
25166 }
25167
25168 void gc_heap::verify_mark_array_cleared (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25169 {
25170 #ifdef _DEBUG
25171     size_t  markw = mark_word_of (begin);
25172     size_t  markw_end = mark_word_of (end);
25173
25174     while (markw < markw_end)
25175     {
25176         if (mark_array_addr[markw])
25177         {
25178             dprintf  (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
25179                             markw, mark_array_addr[markw], mark_word_address (markw)));
25180             FATAL_GC_ERROR();
25181         }
25182         markw++;
25183     }
25184 #else // _DEBUG
25185     UNREFERENCED_PARAMETER(begin);
25186     UNREFERENCED_PARAMETER(end);
25187     UNREFERENCED_PARAMETER(mark_array_addr);
25188 #endif //_DEBUG
25189 }
25190
25191 void gc_heap::verify_mark_array_cleared (heap_segment* seg, uint32_t* mark_array_addr)
25192 {
25193     verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
25194 }
25195
25196 BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, 
25197                                          heap_segment* seg,
25198                                          uint32_t* new_card_table,
25199                                          uint8_t* new_lowest_address)
25200 {
25201     UNREFERENCED_PARAMETER(hp); // compiler bug? -- this *is*, indeed, referenced
25202
25203     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25204     uint8_t* end = heap_segment_reserved (seg);
25205
25206     uint8_t* lowest = hp->background_saved_lowest_address;
25207     uint8_t* highest = hp->background_saved_highest_address;
25208
25209     uint8_t* commit_start = NULL;
25210     uint8_t* commit_end = NULL;
25211     size_t commit_flag = 0;
25212
25213     if ((highest >= start) &&
25214         (lowest <= end))
25215     {
25216         if ((start >= lowest) && (end <= highest))
25217         {
25218             dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25219                                     start, end, lowest, highest));
25220             commit_flag = heap_segment_flags_ma_committed;
25221         }
25222         else
25223         {
25224             dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25225                                     start, end, lowest, highest));
25226             commit_flag = heap_segment_flags_ma_pcommitted;
25227         }
25228
25229         commit_start = max (lowest, start);
25230         commit_end = min (highest, end);
25231
25232         if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
25233         {
25234             return FALSE;
25235         }
25236
25237         if (new_card_table == 0)
25238         {
25239             new_card_table = g_gc_card_table;
25240         }
25241
25242         if (hp->card_table != new_card_table)
25243         {
25244             if (new_lowest_address == 0)
25245             {
25246                 new_lowest_address = g_gc_lowest_address;
25247             }
25248
25249             uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))];
25250             uint32_t* ma = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
25251
25252             dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix", 
25253                                     hp->card_table, new_card_table,
25254                                     hp->mark_array, ma));
25255
25256             if (!commit_mark_array_by_range (commit_start, commit_end, ma))
25257             {
25258                 return FALSE;
25259             }
25260         }
25261
25262         seg->flags |= commit_flag;
25263     }
25264
25265     return TRUE;
25266 }
25267
25268 BOOL gc_heap::commit_mark_array_by_range (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25269 {
25270     size_t beg_word = mark_word_of (begin);
25271     size_t end_word = mark_word_of (align_on_mark_word (end));
25272     uint8_t* commit_start = align_lower_page ((uint8_t*)&mark_array_addr[beg_word]);
25273     uint8_t* commit_end = align_on_page ((uint8_t*)&mark_array_addr[end_word]);
25274     size_t size = (size_t)(commit_end - commit_start);
25275
25276 #ifdef SIMPLE_DPRINTF
25277     dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
25278                             begin, end,
25279                             beg_word, end_word,
25280                             (end_word - beg_word) * sizeof (uint32_t),
25281                             &mark_array_addr[beg_word],
25282                             &mark_array_addr[end_word],
25283                             (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
25284                             commit_start, commit_end,
25285                             size));
25286 #endif //SIMPLE_DPRINTF
25287
25288     if (GCToOSInterface::VirtualCommit (commit_start, size))
25289     {
25290         // We can only verify the mark array is cleared from begin to end, the first and the last
25291         // page aren't necessarily all cleared 'cause they could be used by other segments or 
25292         // card bundle.
25293         verify_mark_array_cleared (begin, end, mark_array_addr);
25294         return TRUE;
25295     }
25296     else
25297     {
25298         dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (uint32_t)));
25299         return FALSE;
25300     }
25301 }
25302
25303 BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, uint32_t* new_mark_array_addr)
25304 {
25305     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25306     uint8_t* end = heap_segment_reserved (seg);
25307
25308 #ifdef MULTIPLE_HEAPS
25309     uint8_t* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
25310     uint8_t* highest = heap_segment_heap (seg)->background_saved_highest_address;
25311 #else
25312     uint8_t* lowest = background_saved_lowest_address;
25313     uint8_t* highest = background_saved_highest_address;
25314 #endif //MULTIPLE_HEAPS
25315
25316     if ((highest >= start) &&
25317         (lowest <= end))
25318     {
25319         start = max (lowest, start);
25320         end = min (highest, end);
25321         if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
25322         {
25323             return FALSE;
25324         }
25325     }
25326
25327     return TRUE;
25328 }
25329
25330 BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, uint32_t* mark_array_addr)
25331 {
25332     dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix",
25333         seg,
25334         heap_segment_reserved (seg),
25335         mark_array_addr));
25336     uint8_t* start = (heap_segment_read_only_p (seg) ? heap_segment_mem (seg) : (uint8_t*)seg);
25337
25338     return commit_mark_array_by_range (start, heap_segment_reserved (seg), mark_array_addr);
25339 }
25340
25341 BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr)
25342 {
25343     UNREFERENCED_PARAMETER(mark_array_addr);
25344
25345     dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix", 
25346                             lowest_address, highest_address, mark_array));
25347
25348     generation* gen = generation_of (max_generation);
25349     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25350     while (1)
25351     {
25352         if (seg == 0)
25353         {
25354             if (gen != large_object_generation)
25355             {
25356                 gen = large_object_generation;
25357                 seg = heap_segment_in_range (generation_start_segment (gen));
25358             }
25359             else
25360             {
25361                 break;
25362             }
25363         }
25364
25365         dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
25366
25367         if (!(seg->flags & heap_segment_flags_ma_committed))
25368         {
25369             // For ro segments they could always be only partially in range so we'd
25370             // be calling this at the beginning of every BGC. We are not making this 
25371             // more efficient right now - ro segments are currently only used by redhawk.
25372             if (heap_segment_read_only_p (seg))
25373             {
25374                 if ((heap_segment_mem (seg) >= lowest_address) && 
25375                     (heap_segment_reserved (seg) <= highest_address))
25376                 {
25377                     if (commit_mark_array_by_seg (seg, mark_array))
25378                     {
25379                         seg->flags |= heap_segment_flags_ma_committed;
25380                     }
25381                     else
25382                     {
25383                         return FALSE;
25384                     }
25385                 }
25386                 else
25387                 {
25388                     uint8_t* start = max (lowest_address, heap_segment_mem (seg));
25389                     uint8_t* end = min (highest_address, heap_segment_reserved (seg));
25390                     if (commit_mark_array_by_range (start, end, mark_array))
25391                     {
25392                         seg->flags |= heap_segment_flags_ma_pcommitted;
25393                     }
25394                     else
25395                     {
25396                         return FALSE;
25397                     }
25398                 }
25399             }
25400             else
25401             {
25402                 // For normal segments they are by design completely in range so just 
25403                 // commit the whole mark array for each seg.
25404                 if (commit_mark_array_by_seg (seg, mark_array))
25405                 {
25406                     if (seg->flags & heap_segment_flags_ma_pcommitted)
25407                     {
25408                         seg->flags &= ~heap_segment_flags_ma_pcommitted;
25409                     }
25410                     seg->flags |= heap_segment_flags_ma_committed;
25411                 }
25412                 else
25413                 {
25414                     return FALSE;
25415                 }
25416             }
25417         }
25418
25419         seg = heap_segment_next (seg);
25420     }
25421
25422     return TRUE;
25423 }
25424
25425 // This function doesn't check the commit flag since it's for a new array -
25426 // the mark_array flag for these segments will remain the same.
25427 BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr)
25428 {
25429     dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
25430     generation* gen = generation_of (max_generation);
25431     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25432     while (1)
25433     {
25434         if (seg == 0)
25435         {
25436             if (gen != large_object_generation)
25437             {
25438                 gen = large_object_generation;
25439                 seg = heap_segment_in_range (generation_start_segment (gen));
25440             }
25441             else
25442             {
25443                 break;
25444             }
25445         }
25446
25447         if (!commit_mark_array_with_check (seg, new_mark_array_addr))
25448         {
25449             return FALSE;
25450         }
25451
25452         seg = heap_segment_next (seg);
25453     }
25454
25455 #ifdef MULTIPLE_HEAPS
25456     if (new_heap_segment)
25457     {
25458         if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
25459         {
25460             return FALSE;
25461         }        
25462     }
25463 #endif //MULTIPLE_HEAPS
25464
25465     return TRUE;
25466 }
25467
25468 BOOL gc_heap::commit_new_mark_array_global (uint32_t* new_mark_array)
25469 {
25470 #ifdef MULTIPLE_HEAPS
25471     for (int i = 0; i < n_heaps; i++)
25472     {
25473         if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
25474         {
25475             return FALSE;
25476         }
25477     }
25478 #else
25479     if (!commit_new_mark_array (new_mark_array))
25480     {
25481         return FALSE;
25482     }
25483 #endif //MULTIPLE_HEAPS
25484
25485     return TRUE;
25486 }
25487
25488 void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
25489 {
25490     // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
25491     // been set to NULL. 
25492     if (mark_array == NULL)
25493     {
25494         return;
25495     }
25496
25497     dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
25498
25499     size_t flags = seg->flags;
25500
25501     if ((flags & heap_segment_flags_ma_committed) ||
25502         (flags & heap_segment_flags_ma_pcommitted))
25503     {
25504         uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25505         uint8_t* end = heap_segment_reserved (seg);
25506
25507         if (flags & heap_segment_flags_ma_pcommitted)
25508         {
25509             start = max (lowest_address, start);
25510             end = min (highest_address, end);
25511         }
25512
25513         size_t beg_word = mark_word_of (start);
25514         size_t end_word = mark_word_of (align_on_mark_word (end));
25515         uint8_t* decommit_start = align_on_page ((uint8_t*)&mark_array[beg_word]);
25516         uint8_t* decommit_end = align_lower_page ((uint8_t*)&mark_array[end_word]);
25517         size_t size = (size_t)(decommit_end - decommit_start);
25518
25519 #ifdef SIMPLE_DPRINTF
25520         dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
25521                                 seg,
25522                                 beg_word, end_word,
25523                                 (end_word - beg_word) * sizeof (uint32_t),
25524                                 &mark_array[beg_word],
25525                                 &mark_array[end_word],
25526                                 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
25527                                 decommit_start, decommit_end,
25528                                 size));
25529 #endif //SIMPLE_DPRINTF
25530         
25531         if (decommit_start < decommit_end)
25532         {
25533             if (!GCToOSInterface::VirtualDecommit (decommit_start, size))
25534             {
25535                 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualDecommit on %Ix for %Id bytes failed", 
25536                                         decommit_start, size));
25537                 assert (!"decommit failed");
25538             }
25539         }
25540
25541         dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
25542     }
25543 }
25544
25545 void gc_heap::background_mark_phase ()
25546 {
25547     verify_mark_array_cleared();
25548
25549     ScanContext sc;
25550     sc.thread_number = heap_number;
25551     sc.promotion = TRUE;
25552     sc.concurrent = FALSE;
25553
25554     THREAD_FROM_HEAP;
25555     BOOL cooperative_mode = TRUE;
25556 #ifndef MULTIPLE_HEAPS
25557     const int thread = heap_number;
25558 #endif //!MULTIPLE_HEAPS
25559
25560     dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
25561
25562     assert (settings.concurrent);
25563
25564 #ifdef TIME_GC
25565     unsigned start;
25566     unsigned finish;
25567     start = GetCycleCount32();
25568 #endif //TIME_GC
25569
25570 #ifdef FFIND_OBJECT
25571     if (gen0_must_clear_bricks > 0)
25572         gen0_must_clear_bricks--;
25573 #endif //FFIND_OBJECT
25574
25575     background_soh_alloc_count = 0;
25576     background_loh_alloc_count = 0;
25577     bgc_overflow_count = 0;
25578
25579     bpromoted_bytes (heap_number) = 0;
25580     static uint32_t num_sizedrefs = 0;
25581
25582     background_min_overflow_address = MAX_PTR;
25583     background_max_overflow_address = 0;
25584     background_min_soh_overflow_address = MAX_PTR;
25585     background_max_soh_overflow_address = 0;
25586     processed_soh_overflow_p = FALSE;
25587
25588     {
25589         //set up the mark lists from g_mark_list
25590         assert (g_mark_list);
25591         mark_list = g_mark_list;
25592         //dont use the mark list for full gc
25593         //because multiple segments are more complex to handle and the list
25594         //is likely to overflow
25595         mark_list_end = &mark_list [0];
25596         mark_list_index = &mark_list [0];
25597
25598         c_mark_list_index = 0;
25599
25600         shigh = (uint8_t*) 0;
25601         slow  = MAX_PTR;
25602
25603         generation*   gen = generation_of (max_generation);
25604
25605         dprintf(3,("BGC: stack marking"));
25606         sc.concurrent = TRUE;
25607
25608         GCScan::GcScanRoots(background_promote_callback,
25609                                 max_generation, max_generation,
25610                                 &sc);
25611     }
25612
25613     {
25614         dprintf(3,("BGC: finalization marking"));
25615         finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
25616     }
25617
25618     size_t total_loh_size = generation_size (max_generation + 1);
25619     bgc_begin_loh_size = total_loh_size;
25620     bgc_alloc_spin_loh = 0;
25621     bgc_loh_size_increased = 0;
25622     bgc_loh_allocated_in_free = 0;
25623     size_t total_soh_size = generation_sizes (generation_of (max_generation));
25624
25625     dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25626
25627     {
25628         //concurrent_print_time_delta ("copying stack roots");
25629         concurrent_print_time_delta ("CS");
25630
25631         FIRE_EVENT(BGC1stNonConEnd);
25632
25633         expanded_in_fgc = FALSE;
25634         saved_overflow_ephemeral_seg = 0;
25635         current_bgc_state = bgc_reset_ww;
25636
25637         // we don't need a join here - just whichever thread that gets here
25638         // first can change the states and call restart_vm.
25639         // this is not true - we can't let the EE run when we are scanning stack.
25640         // since we now allow reset ww to run concurrently and have a join for it,
25641         // we can do restart ee on the 1st thread that got here. Make sure we handle the 
25642         // sizedref handles correctly.
25643 #ifdef MULTIPLE_HEAPS
25644         bgc_t_join.join(this, gc_join_restart_ee);
25645         if (bgc_t_join.joined())
25646 #endif //MULTIPLE_HEAPS
25647         {
25648 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25649             // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset
25650             // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while
25651             // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below.
25652 #ifdef WRITE_WATCH
25653             concurrent_print_time_delta ("CRWW begin");
25654
25655 #ifdef MULTIPLE_HEAPS
25656             for (int i = 0; i < n_heaps; i++)
25657             {
25658                 g_heaps[i]->reset_write_watch (FALSE);
25659             }
25660 #else
25661             reset_write_watch (FALSE);
25662 #endif //MULTIPLE_HEAPS
25663
25664             concurrent_print_time_delta ("CRWW");
25665 #endif //WRITE_WATCH
25666 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25667
25668             num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
25669
25670             // this c_write is not really necessary because restart_vm
25671             // has an instruction that will flush the cpu cache (interlocked
25672             // or whatever) but we don't want to rely on that.
25673             dprintf (BGC_LOG, ("setting cm_in_progress"));
25674             c_write (cm_in_progress, TRUE);
25675
25676             //restart all thread, doing the marking from the array
25677             assert (dont_restart_ee_p);
25678             dont_restart_ee_p = FALSE;
25679
25680             restart_vm();
25681             GCToOSInterface::YieldThread (0);
25682 #ifdef MULTIPLE_HEAPS
25683             dprintf(3, ("Starting all gc threads for gc"));
25684             bgc_t_join.restart();
25685 #endif //MULTIPLE_HEAPS
25686         }
25687
25688 #ifdef MULTIPLE_HEAPS
25689         bgc_t_join.join(this, gc_join_after_reset);
25690         if (bgc_t_join.joined())
25691 #endif //MULTIPLE_HEAPS
25692         {
25693             disable_preemptive (true);
25694
25695 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25696             // When software write watch is enabled, resetting write watch is done while the runtime is suspended above. The
25697             // post-reset call to revisit_written_pages is only necessary for concurrent reset_write_watch, to discard dirtied
25698             // pages during the concurrent reset.
25699
25700 #ifdef WRITE_WATCH
25701             concurrent_print_time_delta ("CRWW begin");
25702
25703 #ifdef MULTIPLE_HEAPS
25704             for (int i = 0; i < n_heaps; i++)
25705             {
25706                 g_heaps[i]->reset_write_watch (TRUE);
25707             }
25708 #else
25709             reset_write_watch (TRUE);
25710 #endif //MULTIPLE_HEAPS
25711
25712             concurrent_print_time_delta ("CRWW");
25713 #endif //WRITE_WATCH
25714
25715 #ifdef MULTIPLE_HEAPS
25716             for (int i = 0; i < n_heaps; i++)
25717             {
25718                 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
25719             }
25720 #else
25721             revisit_written_pages (TRUE, TRUE);
25722 #endif //MULTIPLE_HEAPS
25723
25724             concurrent_print_time_delta ("CRW");
25725 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25726
25727 #ifdef MULTIPLE_HEAPS
25728             for (int i = 0; i < n_heaps; i++)
25729             {
25730                 g_heaps[i]->current_bgc_state = bgc_mark_handles;
25731             }
25732 #else
25733             current_bgc_state = bgc_mark_handles;
25734 #endif //MULTIPLE_HEAPS
25735
25736             current_c_gc_state = c_gc_state_marking;
25737
25738             enable_preemptive ();
25739
25740 #ifdef MULTIPLE_HEAPS
25741             dprintf(3, ("Joining BGC threads after resetting writewatch"));
25742             bgc_t_join.restart();
25743 #endif //MULTIPLE_HEAPS
25744         }
25745
25746         disable_preemptive (true);
25747
25748         if (num_sizedrefs > 0)
25749         {
25750             GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
25751
25752             enable_preemptive ();
25753
25754 #ifdef MULTIPLE_HEAPS
25755             bgc_t_join.join(this, gc_join_scan_sizedref_done);
25756             if (bgc_t_join.joined())
25757             {
25758                 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
25759                 bgc_t_join.restart();
25760             }
25761 #endif //MULTIPLE_HEAPS
25762
25763             disable_preemptive (true);
25764         }
25765
25766         dprintf (3,("BGC: handle table marking"));
25767         GCScan::GcScanHandles(background_promote,
25768                                   max_generation, max_generation,
25769                                   &sc);
25770         //concurrent_print_time_delta ("concurrent marking handle table");
25771         concurrent_print_time_delta ("CRH");
25772
25773         current_bgc_state = bgc_mark_stack;
25774         dprintf (2,("concurrent draining mark list"));
25775         background_drain_mark_list (thread);
25776         //concurrent_print_time_delta ("concurrent marking stack roots");
25777         concurrent_print_time_delta ("CRS");
25778
25779         dprintf (2,("concurrent revisiting dirtied pages"));
25780         revisit_written_pages (TRUE);
25781         revisit_written_pages (TRUE);
25782         //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
25783         concurrent_print_time_delta ("CRre");
25784
25785         enable_preemptive ();
25786
25787 #ifdef MULTIPLE_HEAPS
25788         bgc_t_join.join(this, gc_join_concurrent_overflow);
25789         if (bgc_t_join.joined())
25790         {
25791             uint8_t* all_heaps_max = 0;
25792             uint8_t* all_heaps_min = MAX_PTR;
25793             int i;
25794             for (i = 0; i < n_heaps; i++)
25795             {
25796                 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix", 
25797                     i,
25798                     g_heaps[i]->background_max_overflow_address,
25799                     g_heaps[i]->background_min_overflow_address));
25800                 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25801                     all_heaps_max = g_heaps[i]->background_max_overflow_address;
25802                 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25803                     all_heaps_min = g_heaps[i]->background_min_overflow_address;
25804             }
25805             for (i = 0; i < n_heaps; i++)
25806             {
25807                 g_heaps[i]->background_max_overflow_address = all_heaps_max;
25808                 g_heaps[i]->background_min_overflow_address = all_heaps_min;
25809             }
25810             dprintf(3, ("Starting all bgc threads after updating the overflow info"));
25811             bgc_t_join.restart();
25812         }
25813 #endif //MULTIPLE_HEAPS
25814
25815         disable_preemptive (true);
25816
25817         dprintf (2, ("before CRov count: %d", bgc_overflow_count));
25818         bgc_overflow_count = 0;
25819         background_process_mark_overflow (TRUE);
25820         dprintf (2, ("after CRov count: %d", bgc_overflow_count));
25821         bgc_overflow_count = 0;
25822         //concurrent_print_time_delta ("concurrent processing mark overflow");
25823         concurrent_print_time_delta ("CRov");
25824
25825         // Stop all threads, crawl all stacks and revisit changed pages.
25826         FIRE_EVENT(BGC1stConEnd);
25827
25828         dprintf (2, ("Stopping the EE"));
25829
25830         enable_preemptive ();
25831
25832 #ifdef MULTIPLE_HEAPS
25833         bgc_t_join.join(this, gc_join_suspend_ee);
25834         if (bgc_t_join.joined())
25835         {
25836             bgc_threads_sync_event.Reset();
25837
25838             dprintf(3, ("Joining BGC threads for non concurrent final marking"));
25839             bgc_t_join.restart();
25840         }
25841 #endif //MULTIPLE_HEAPS
25842
25843         if (heap_number == 0)
25844         {
25845             enter_spin_lock (&gc_lock);
25846
25847             bgc_suspend_EE ();
25848             //suspend_EE ();
25849             bgc_threads_sync_event.Set();
25850         }
25851         else
25852         {
25853             bgc_threads_sync_event.Wait(INFINITE, FALSE);
25854             dprintf (2, ("bgc_threads_sync_event is signalled"));
25855         }
25856
25857         assert (settings.concurrent);
25858         assert (settings.condemned_generation == max_generation);
25859
25860         dprintf (2, ("clearing cm_in_progress"));
25861         c_write (cm_in_progress, FALSE);
25862
25863         bgc_alloc_lock->check();
25864
25865         current_bgc_state = bgc_final_marking;
25866
25867         //concurrent_print_time_delta ("concurrent marking ended");
25868         concurrent_print_time_delta ("CR");
25869
25870         FIRE_EVENT(BGC2ndNonConBegin);
25871
25872         mark_absorb_new_alloc();
25873
25874         // We need a join here 'cause find_object would complain if the gen0
25875         // bricks of another heap haven't been fixed up. So we need to make sure
25876         // that every heap's gen0 bricks are fixed up before we proceed.
25877 #ifdef MULTIPLE_HEAPS
25878         bgc_t_join.join(this, gc_join_after_absorb);
25879         if (bgc_t_join.joined())
25880         {
25881             dprintf(3, ("Joining BGC threads after absorb"));
25882             bgc_t_join.restart();
25883         }
25884 #endif //MULTIPLE_HEAPS
25885
25886         // give VM a chance to do work
25887         GCToEEInterface::GcBeforeBGCSweepWork();
25888
25889         //reset the flag, indicating that the EE no longer expect concurrent
25890         //marking
25891         sc.concurrent = FALSE;
25892
25893         total_loh_size = generation_size (max_generation + 1);
25894         total_soh_size = generation_sizes (generation_of (max_generation));
25895
25896         dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25897
25898         dprintf (2, ("nonconcurrent marking stack roots"));
25899         GCScan::GcScanRoots(background_promote,
25900                                 max_generation, max_generation,
25901                                 &sc);
25902         //concurrent_print_time_delta ("nonconcurrent marking stack roots");
25903         concurrent_print_time_delta ("NRS");
25904
25905 //        finalize_queue->EnterFinalizeLock();
25906         finalize_queue->GcScanRoots(background_promote, heap_number, 0);
25907 //        finalize_queue->LeaveFinalizeLock();
25908
25909         dprintf (2, ("nonconcurrent marking handle table"));
25910         GCScan::GcScanHandles(background_promote,
25911                                   max_generation, max_generation,
25912                                   &sc);
25913         //concurrent_print_time_delta ("nonconcurrent marking handle table");
25914         concurrent_print_time_delta ("NRH");
25915
25916         dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
25917         revisit_written_pages (FALSE);
25918         //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
25919         concurrent_print_time_delta ("NRre LOH");
25920
25921 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25922 #ifdef MULTIPLE_HEAPS
25923         bgc_t_join.join(this, gc_join_disable_software_write_watch);
25924         if (bgc_t_join.joined())
25925 #endif // MULTIPLE_HEAPS
25926         {
25927             // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to
25928             // avoid further perf penalty after the runtime is restarted
25929             SoftwareWriteWatch::DisableForGCHeap();
25930
25931 #ifdef MULTIPLE_HEAPS
25932             dprintf(3, ("Restarting BGC threads after disabling software write watch"));
25933             bgc_t_join.restart();
25934 #endif // MULTIPLE_HEAPS
25935         }
25936 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25937
25938         dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
25939         bgc_overflow_count = 0;
25940
25941         // Dependent handles need to be scanned with a special algorithm (see the header comment on
25942         // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
25943         // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
25944         // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
25945         // The call to background_scan_dependent_handles is what will cycle through more iterations if
25946         // required and will also perform processing of any mark stack overflow once the dependent handle
25947         // table has been fully promoted.
25948         dprintf (2, ("1st dependent handle scan and process mark overflow"));
25949         GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
25950         background_scan_dependent_handles (&sc);
25951         //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
25952         concurrent_print_time_delta ("NR 1st Hov");
25953
25954         dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
25955         bgc_overflow_count = 0;
25956
25957 #ifdef MULTIPLE_HEAPS
25958         bgc_t_join.join(this, gc_join_null_dead_short_weak);
25959         if (bgc_t_join.joined())
25960 #endif //MULTIPLE_HEAPS
25961         {
25962             GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
25963
25964 #ifdef MULTIPLE_HEAPS
25965             dprintf(3, ("Joining BGC threads for short weak handle scan"));
25966             bgc_t_join.restart();
25967 #endif //MULTIPLE_HEAPS
25968         }
25969
25970         // null out the target of short weakref that were not promoted.
25971         GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
25972
25973         //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
25974         concurrent_print_time_delta ("NR GcShortWeakPtrScan");
25975     }
25976
25977     {
25978 #ifdef MULTIPLE_HEAPS
25979         bgc_t_join.join(this, gc_join_scan_finalization);
25980         if (bgc_t_join.joined())
25981         {
25982             dprintf(3, ("Joining BGC threads for finalization"));
25983             bgc_t_join.restart();
25984         }
25985 #endif //MULTIPLE_HEAPS
25986
25987         //Handle finalization.
25988         dprintf(3,("Marking finalization data"));
25989         //concurrent_print_time_delta ("bgc joined to mark finalization");
25990         concurrent_print_time_delta ("NRj");
25991
25992 //        finalize_queue->EnterFinalizeLock();
25993         finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
25994 //        finalize_queue->LeaveFinalizeLock();
25995
25996         concurrent_print_time_delta ("NRF");
25997     }
25998
25999     dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
26000     bgc_overflow_count = 0;
26001
26002     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
26003     // for finalization. As before background_scan_dependent_handles will also process any mark stack
26004     // overflow.
26005     dprintf (2, ("2nd dependent handle scan and process mark overflow"));
26006     background_scan_dependent_handles (&sc);
26007     //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
26008     concurrent_print_time_delta ("NR 2nd Hov");
26009
26010 #ifdef MULTIPLE_HEAPS
26011     bgc_t_join.join(this, gc_join_null_dead_long_weak);
26012     if (bgc_t_join.joined())
26013     {
26014         dprintf(2, ("Joining BGC threads for weak pointer deletion"));
26015         bgc_t_join.restart();
26016     }
26017 #endif //MULTIPLE_HEAPS
26018
26019     // null out the target of long weakref that were not promoted.
26020     GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
26021     concurrent_print_time_delta ("NR GcWeakPtrScan");
26022
26023 #ifdef MULTIPLE_HEAPS
26024     bgc_t_join.join(this, gc_join_null_dead_syncblk);
26025     if (bgc_t_join.joined())
26026 #endif //MULTIPLE_HEAPS
26027     {
26028         dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
26029         // scan for deleted entries in the syncblk cache
26030         GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
26031         concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
26032 #ifdef MULTIPLE_HEAPS
26033         dprintf(2, ("Starting BGC threads for end of background mark phase"));
26034         bgc_t_join.restart();
26035 #endif //MULTIPLE_HEAPS
26036     }
26037
26038     gen0_bricks_cleared = FALSE;
26039
26040     dprintf (2, ("end of bgc mark: loh: %d, soh: %d", 
26041                  generation_size (max_generation + 1), 
26042                  generation_sizes (generation_of (max_generation))));
26043
26044     for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
26045     {
26046         generation* gen = generation_of (gen_idx);
26047         dynamic_data* dd = dynamic_data_of (gen_idx);
26048         dd_begin_data_size (dd) = generation_size (gen_idx) - 
26049                                    (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
26050                                    Align (size (generation_allocation_start (gen)));
26051         dd_survived_size (dd) = 0;
26052         dd_pinned_survived_size (dd) = 0;
26053         dd_artificial_pinned_survived_size (dd) = 0;
26054         dd_added_pinned_size (dd) = 0;
26055     }
26056
26057     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26058     PREFIX_ASSUME(seg != NULL);
26059
26060     while (seg)
26061     {
26062         seg->flags &= ~heap_segment_flags_swept;
26063
26064         if (heap_segment_allocated (seg) == heap_segment_mem (seg))
26065         {
26066             // This can't happen...
26067             FATAL_GC_ERROR();
26068         }
26069
26070         if (seg == ephemeral_heap_segment)
26071         {
26072             heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
26073         }
26074         else
26075         {
26076             heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
26077         }
26078
26079         dprintf (2, ("seg %Ix background allocated is %Ix", 
26080                       heap_segment_mem (seg), 
26081                       heap_segment_background_allocated (seg)));
26082         seg = heap_segment_next_rw (seg);
26083     }
26084
26085     // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
26086     // we can't let the user code consume the left over parts in these alloc contexts.
26087     repair_allocation_contexts (FALSE);
26088
26089 #ifdef TIME_GC
26090         finish = GetCycleCount32();
26091         mark_time = finish - start;
26092 #endif //TIME_GC
26093
26094     dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d", 
26095         generation_free_list_space (generation_of (max_generation)), 
26096         generation_free_obj_space (generation_of (max_generation))));
26097
26098     dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
26099 }
26100
26101 void
26102 gc_heap::suspend_EE ()
26103 {
26104     dprintf (2, ("suspend_EE"));
26105 #ifdef MULTIPLE_HEAPS
26106     gc_heap* hp = gc_heap::g_heaps[0];
26107     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26108 #else
26109     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26110 #endif //MULTIPLE_HEAPS
26111 }
26112
26113 #ifdef MULTIPLE_HEAPS
26114 void
26115 gc_heap::bgc_suspend_EE ()
26116 {
26117     for (int i = 0; i < n_heaps; i++)
26118     {
26119         gc_heap::g_heaps[i]->reset_gc_done();
26120     }
26121     gc_started = TRUE;
26122     dprintf (2, ("bgc_suspend_EE"));
26123     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26124
26125     gc_started = FALSE;
26126     for (int i = 0; i < n_heaps; i++)
26127     {
26128         gc_heap::g_heaps[i]->set_gc_done();
26129     }
26130 }
26131 #else
26132 void
26133 gc_heap::bgc_suspend_EE ()
26134 {
26135     reset_gc_done();
26136     gc_started = TRUE;
26137     dprintf (2, ("bgc_suspend_EE"));
26138     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26139     gc_started = FALSE;
26140     set_gc_done();
26141 }
26142 #endif //MULTIPLE_HEAPS
26143
26144 void
26145 gc_heap::restart_EE ()
26146 {
26147     dprintf (2, ("restart_EE"));
26148 #ifdef MULTIPLE_HEAPS
26149     GCToEEInterface::RestartEE(FALSE);
26150 #else
26151     GCToEEInterface::RestartEE(FALSE);
26152 #endif //MULTIPLE_HEAPS
26153 }
26154
26155 inline uint8_t* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
26156 {
26157     if (concurrent_p)
26158     {
26159         uint8_t* end = ((seg == ephemeral_heap_segment) ?
26160                      generation_allocation_start (generation_of (max_generation-1)) :
26161                      heap_segment_allocated (seg));
26162         return align_lower_page (end);
26163     }
26164     else 
26165     {
26166         return heap_segment_allocated (seg);
26167     }
26168 }
26169
26170 void gc_heap::revisit_written_page (uint8_t* page,
26171                                     uint8_t* end,
26172                                     BOOL concurrent_p,
26173                                     heap_segment* seg,
26174                                     uint8_t*& last_page,
26175                                     uint8_t*& last_object,
26176                                     BOOL large_objects_p,
26177                                     size_t& num_marked_objects)
26178 {
26179     UNREFERENCED_PARAMETER(seg);
26180
26181     uint8_t*   start_address = page;
26182     uint8_t*   o             = 0;
26183     int align_const = get_alignment_constant (!large_objects_p);
26184     uint8_t* high_address = end;
26185     uint8_t* current_lowest_address = background_saved_lowest_address;
26186     uint8_t* current_highest_address = background_saved_highest_address;
26187     BOOL no_more_loop_p = FALSE;
26188
26189     THREAD_FROM_HEAP;
26190 #ifndef MULTIPLE_HEAPS
26191     const int thread = heap_number;
26192 #endif //!MULTIPLE_HEAPS
26193
26194     if (large_objects_p)
26195     {
26196         o = last_object;
26197     }
26198     else
26199     {
26200         if (((last_page + WRITE_WATCH_UNIT_SIZE) == page)
26201             || (start_address <= last_object))
26202         {
26203             o = last_object;
26204         }
26205         else
26206         {
26207             o = find_first_object (start_address, last_object);
26208             // We can visit the same object again, but on a different page.
26209             assert (o >= last_object);
26210         }
26211     }
26212
26213     dprintf (3,("page %Ix start: %Ix, %Ix[ ",
26214                (size_t)page, (size_t)o,
26215                (size_t)(min (high_address, page + WRITE_WATCH_UNIT_SIZE))));
26216
26217     while (o < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26218     {
26219         size_t s;
26220
26221         if (concurrent_p && large_objects_p)
26222         {
26223             bgc_alloc_lock->bgc_mark_set (o);
26224
26225             if (((CObjectHeader*)o)->IsFree())
26226             {
26227                 s = unused_array_size (o);
26228             }
26229             else
26230             {
26231                 s = size (o);
26232             }
26233         }
26234         else
26235         {
26236             s = size (o);
26237         }
26238
26239         dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
26240
26241         assert (Align (s) >= Align (min_obj_size));
26242
26243         uint8_t* next_o =  o + Align (s, align_const);
26244
26245         if (next_o >= start_address) 
26246         {
26247 #ifdef MULTIPLE_HEAPS
26248             if (concurrent_p)
26249             {
26250                 // We set last_object here for SVR BGC here because SVR BGC has more than 
26251                 // one GC thread. When we have more than one GC thread we would run into this 
26252                 // situation if we skipped unmarked objects:
26253                 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it 
26254                 // for revisit. 
26255                 // bgc thread 2 marks X and all its current children.
26256                 // user thread comes along and dirties more (and later) pages in X.
26257                 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
26258                 // on them because it had already skipped X. We need to detect that this object is now
26259                 // marked and mark the children on the dirtied pages.
26260                 // In the future if we have less BGC threads than we have heaps we should add
26261                 // the check to the number of BGC threads.
26262                 last_object = o;
26263             }
26264 #endif //MULTIPLE_HEAPS
26265
26266             if (contain_pointers (o) &&
26267                 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
26268                 background_marked (o)))
26269             {
26270                 dprintf (3, ("going through %Ix", (size_t)o));
26271                 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
26272                                     if ((uint8_t*)poo >= min (high_address, page + WRITE_WATCH_UNIT_SIZE))
26273                                     {
26274                                         no_more_loop_p = TRUE;
26275                                         goto end_limit;
26276                                     }
26277                                     uint8_t* oo = *poo;
26278
26279                                     num_marked_objects++;
26280                                     background_mark_object (oo THREAD_NUMBER_ARG);
26281                                 );
26282             }
26283             else if (
26284                 concurrent_p &&
26285 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // see comment below
26286                 large_objects_p &&
26287 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26288                 ((CObjectHeader*)o)->IsFree() &&
26289                 (next_o > min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26290             {
26291                 // We need to not skip the object here because of this corner scenario:
26292                 // A large object was being allocated during BGC mark so we first made it 
26293                 // into a free object, then cleared its memory. In this loop we would detect
26294                 // that it's a free object which normally we would skip. But by the next time
26295                 // we call GetWriteWatch we could still be on this object and the object had
26296                 // been made into a valid object and some of its memory was changed. We need
26297                 // to be sure to process those written pages so we can't skip the object just
26298                 // yet.
26299                 //
26300                 // Similarly, when using software write watch, don't advance last_object when
26301                 // the current object is a free object that spans beyond the current page or
26302                 // high_address. Software write watch acquires gc_lock before the concurrent
26303                 // GetWriteWatch() call during revisit_written_pages(). A foreground GC may
26304                 // happen at that point and allocate from this free region, so when
26305                 // revisit_written_pages() continues, it cannot skip now-valid objects in this
26306                 // region.
26307                 no_more_loop_p = TRUE;
26308                 goto end_limit;                
26309             }
26310         }
26311 end_limit:
26312         if (concurrent_p && large_objects_p)
26313         {
26314             bgc_alloc_lock->bgc_mark_done ();
26315         }
26316         if (no_more_loop_p)
26317         {
26318             break;
26319         }
26320         o = next_o;
26321     }
26322
26323 #ifdef MULTIPLE_HEAPS
26324     if (concurrent_p)
26325     {
26326         assert (last_object < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)));
26327     }
26328     else
26329 #endif //MULTIPLE_HEAPS
26330     {
26331         last_object = o;
26332     }
26333
26334     dprintf (3,("Last object: %Ix", (size_t)last_object));
26335     last_page = align_write_watch_lower_page (o);
26336 }
26337
26338 // When reset_only_p is TRUE, we should only reset pages that are in range
26339 // because we need to consider the segments or part of segments that were
26340 // allocated out of range all live.
26341 void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
26342 {
26343 #ifdef WRITE_WATCH
26344     if (concurrent_p && !reset_only_p)
26345     {
26346         current_bgc_state = bgc_revisit_soh;
26347     }
26348
26349     size_t total_dirtied_pages = 0;
26350     size_t total_marked_objects = 0;
26351
26352     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26353
26354     PREFIX_ASSUME(seg != NULL);
26355
26356     bool reset_watch_state = !!concurrent_p;
26357     bool is_runtime_suspended = !concurrent_p;
26358     BOOL small_object_segments = TRUE;
26359     int align_const = get_alignment_constant (small_object_segments);
26360
26361     while (1)
26362     {
26363         if (seg == 0)
26364         {
26365             if (small_object_segments)
26366             {
26367                 //switch to large segment
26368                 if (concurrent_p && !reset_only_p)
26369                 {
26370                     current_bgc_state = bgc_revisit_loh;
26371                 }
26372
26373                 if (!reset_only_p)
26374                 {
26375                     dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26376                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26377                     concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
26378                     total_dirtied_pages = 0;
26379                     total_marked_objects = 0;
26380                 }
26381
26382                 small_object_segments = FALSE;
26383                 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
26384
26385                 dprintf (3, ("now revisiting large object segments"));
26386                 align_const = get_alignment_constant (small_object_segments);
26387                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
26388
26389                 PREFIX_ASSUME(seg != NULL);
26390
26391                 continue;
26392             }
26393             else
26394             {
26395                 if (reset_only_p)
26396                 {
26397                     dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
26398                 } 
26399                 else
26400                 {
26401                     dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26402                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26403                 }
26404                 break;
26405             }
26406         }
26407         uint8_t* base_address = (uint8_t*)heap_segment_mem (seg);
26408         //we need to truncate to the base of the page because
26409         //some newly allocated could exist beyond heap_segment_allocated
26410         //and if we reset the last page write watch status,
26411         // they wouldn't be guaranteed to be visited -> gc hole.
26412         uintptr_t bcount = array_size;
26413         uint8_t* last_page = 0;
26414         uint8_t* last_object = heap_segment_mem (seg);
26415         uint8_t* high_address = 0;
26416
26417         BOOL skip_seg_p = FALSE;
26418
26419         if (reset_only_p)
26420         {
26421             if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
26422                 (heap_segment_reserved (seg) <= background_saved_highest_address))
26423             {
26424                 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number, 
26425                     heap_segment_mem (seg), heap_segment_reserved (seg)));
26426                 skip_seg_p = TRUE;
26427             }
26428         }
26429
26430         if (!skip_seg_p)
26431         {
26432             dprintf (3, ("looking at seg %Ix", (size_t)last_object));
26433
26434             if (reset_only_p)
26435             {
26436                 base_address = max (base_address, background_saved_lowest_address);
26437                 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
26438             }
26439
26440             dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address, 
26441                 heap_segment_mem (seg), heap_segment_reserved (seg)));
26442
26443
26444             while (1)
26445             {
26446                 if (reset_only_p)
26447                 {
26448                     high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
26449                     high_address = min (high_address, background_saved_highest_address);
26450                 }
26451                 else
26452                 {
26453                     high_address = high_page (seg, concurrent_p);
26454                 }
26455
26456                 if ((base_address < high_address) &&
26457                     (bcount >= array_size))
26458                 {
26459                     ptrdiff_t region_size = high_address - base_address;
26460                     dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
26461
26462 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26463                     // When the runtime is not suspended, it's possible for the table to be resized concurrently with the scan
26464                     // for dirty pages below. Prevent that by synchronizing with grow_brick_card_tables(). When the runtime is
26465                     // suspended, it's ok to scan for dirty pages concurrently from multiple background GC threads for disjoint
26466                     // memory regions.
26467                     if (!is_runtime_suspended)
26468                     {
26469                         enter_spin_lock(&gc_lock);
26470                     }
26471 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26472
26473                     get_write_watch_for_gc_heap (reset_watch_state, base_address, region_size,
26474                                                  (void**)background_written_addresses,
26475                                                  &bcount, is_runtime_suspended);
26476
26477 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26478                     if (!is_runtime_suspended)
26479                     {
26480                         leave_spin_lock(&gc_lock);
26481                     }
26482 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26483
26484                     if (bcount != 0)
26485                     {
26486                         total_dirtied_pages += bcount;
26487
26488                         dprintf (3, ("Found %d pages [%Ix, %Ix[", 
26489                                         bcount, (size_t)base_address, (size_t)high_address));
26490                     }
26491
26492                     if (!reset_only_p)
26493                     {
26494                         for (unsigned i = 0; i < bcount; i++)
26495                         {
26496                             uint8_t* page = (uint8_t*)background_written_addresses[i];
26497                             dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i, 
26498                                 (size_t)page, (size_t)high_address));
26499                             if (page < high_address)
26500                             {
26501                                 //search for marked objects in the page
26502                                 revisit_written_page (page, high_address, concurrent_p,
26503                                                     seg, last_page, last_object,
26504                                                     !small_object_segments,
26505                                                     total_marked_objects);
26506                             }
26507                             else
26508                             {
26509                                 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
26510                                 assert (!"page shouldn't have exceeded limit");
26511                             }
26512                         }
26513                     }
26514
26515                     if (bcount >= array_size){
26516                         base_address = background_written_addresses [array_size-1] + WRITE_WATCH_UNIT_SIZE;
26517                         bcount = array_size;
26518                     }
26519                 }
26520                 else
26521                 {
26522                     break;
26523                 }
26524             }
26525         }
26526
26527         seg = heap_segment_next_rw (seg);
26528     }
26529
26530 #endif //WRITE_WATCH
26531 }
26532
26533 void gc_heap::background_grow_c_mark_list()
26534 {
26535     assert (c_mark_list_index >= c_mark_list_length);
26536     BOOL should_drain_p = FALSE;
26537     THREAD_FROM_HEAP;
26538 #ifndef MULTIPLE_HEAPS
26539     const int thread = heap_number;
26540 #endif //!MULTIPLE_HEAPS
26541
26542     dprintf (2, ("stack copy buffer overflow"));
26543     uint8_t** new_c_mark_list = 0;
26544     {
26545         FAULT_NOT_FATAL();
26546         if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (uint8_t*))))
26547         {
26548             should_drain_p = TRUE;
26549         }
26550         else
26551         {
26552             new_c_mark_list = new (nothrow) uint8_t*[c_mark_list_length*2];
26553             if (new_c_mark_list == 0)
26554             {
26555                 should_drain_p = TRUE;
26556             }
26557         }
26558     }
26559     if (should_drain_p)
26560
26561     {
26562         dprintf (2, ("No more memory for the stacks copy, draining.."));
26563         //drain the list by marking its elements
26564         background_drain_mark_list (thread);
26565     }
26566     else
26567     {
26568         assert (new_c_mark_list);
26569         memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(uint8_t*));
26570         c_mark_list_length = c_mark_list_length*2;
26571         delete c_mark_list;
26572         c_mark_list = new_c_mark_list;
26573     }
26574 }
26575
26576 void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
26577                                   uint32_t flags)
26578 {
26579     UNREFERENCED_PARAMETER(sc);
26580     //in order to save space on the array, mark the object,
26581     //knowing that it will be visited later
26582     assert (settings.concurrent);
26583
26584     THREAD_NUMBER_FROM_CONTEXT;
26585 #ifndef MULTIPLE_HEAPS
26586     const int thread = 0;
26587 #endif //!MULTIPLE_HEAPS
26588
26589     uint8_t* o = (uint8_t*)*ppObject;
26590
26591     if (o == 0)
26592         return;
26593
26594     HEAP_FROM_THREAD;
26595
26596     gc_heap* hp = gc_heap::heap_of (o);
26597
26598     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
26599     {
26600         return;
26601     }
26602
26603 #ifdef INTERIOR_POINTERS
26604     if (flags & GC_CALL_INTERIOR)
26605     {
26606         o = hp->find_object (o, hp->background_saved_lowest_address);
26607         if (o == 0)
26608             return;
26609     }
26610 #endif //INTERIOR_POINTERS
26611
26612 #ifdef FEATURE_CONSERVATIVE_GC
26613     // For conservative GC, a value on stack may point to middle of a free object.
26614     // In this case, we don't need to promote the pointer.
26615     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
26616     {
26617         return;
26618     }
26619 #endif //FEATURE_CONSERVATIVE_GC
26620
26621 #ifdef _DEBUG
26622     ((CObjectHeader*)o)->Validate();
26623 #endif //_DEBUG
26624
26625     dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
26626     if (o && (size (o) > LARGE_OBJECT_SIZE))
26627     {
26628         dprintf (3, ("Brc %Ix", (size_t)o));
26629     }
26630
26631     if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
26632     {
26633         hpt->background_grow_c_mark_list();
26634     }
26635     dprintf (3, ("pushing %08x into mark_list", (size_t)o));
26636     hpt->c_mark_list [hpt->c_mark_list_index++] = o;
26637
26638     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);
26639 }
26640
26641 void gc_heap::mark_absorb_new_alloc()
26642 {
26643     fix_allocation_contexts (FALSE);
26644     
26645     gen0_bricks_cleared = FALSE;
26646
26647     clear_gen0_bricks();
26648 }
26649
26650 BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
26651 {
26652     BOOL success = FALSE;
26653     BOOL thread_created = FALSE;
26654     dprintf (2, ("Preparing gc thread"));
26655     gh->bgc_threads_timeout_cs.Enter();
26656     if (!(gh->bgc_thread_running))
26657     {
26658         dprintf (2, ("GC thread not runnning"));
26659         if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
26660         {
26661             success = TRUE;
26662             thread_created = TRUE;
26663         }
26664     }
26665     else
26666     {
26667         dprintf (3, ("GC thread already running"));
26668         success = TRUE;
26669     }
26670     gh->bgc_threads_timeout_cs.Leave();
26671
26672     if(thread_created)
26673         FIRE_EVENT(GCCreateConcurrentThread_V1);
26674
26675     return success;
26676 }
26677
26678 BOOL gc_heap::create_bgc_thread(gc_heap* gh)
26679 {
26680     assert (background_gc_done_event.IsValid());
26681
26682     //dprintf (2, ("Creating BGC thread"));
26683
26684     gh->bgc_thread_running = GCToEEInterface::CreateThread(gh->bgc_thread_stub, gh, true, ".NET Background GC");
26685     return gh->bgc_thread_running;
26686 }
26687
26688 BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
26689 {
26690     BOOL ret = FALSE;
26691     dprintf (3, ("Creating concurrent GC thread for the first time"));
26692     if (!background_gc_done_event.CreateManualEventNoThrow(TRUE))
26693     {
26694         goto cleanup;
26695     }
26696     if (!bgc_threads_sync_event.CreateManualEventNoThrow(FALSE))
26697     {
26698         goto cleanup;
26699     }
26700     if (!ee_proceed_event.CreateAutoEventNoThrow(FALSE))
26701     {
26702         goto cleanup;
26703     }
26704     if (!bgc_start_event.CreateManualEventNoThrow(FALSE))
26705     {
26706         goto cleanup;
26707     }
26708
26709 #ifdef MULTIPLE_HEAPS
26710     bgc_t_join.init (number_of_heaps, join_flavor_bgc);
26711 #else
26712     UNREFERENCED_PARAMETER(number_of_heaps);
26713 #endif //MULTIPLE_HEAPS
26714
26715     ret = TRUE;
26716
26717 cleanup:
26718
26719     if (!ret)
26720     {
26721         if (background_gc_done_event.IsValid())
26722         {
26723             background_gc_done_event.CloseEvent();
26724         }
26725         if (bgc_threads_sync_event.IsValid())
26726         {
26727             bgc_threads_sync_event.CloseEvent();
26728         }
26729         if (ee_proceed_event.IsValid())
26730         {
26731             ee_proceed_event.CloseEvent();
26732         }
26733         if (bgc_start_event.IsValid())
26734         {
26735             bgc_start_event.CloseEvent();
26736         }
26737     }
26738
26739     return ret;
26740 }
26741
26742 BOOL gc_heap::create_bgc_thread_support()
26743 {
26744     BOOL ret = FALSE;
26745     uint8_t** parr;
26746     
26747     if (!gc_lh_block_event.CreateManualEventNoThrow(FALSE))
26748     {
26749         goto cleanup;
26750     }
26751
26752     //needs to have room for enough smallest objects fitting on a page
26753     parr = new (nothrow) uint8_t*[1 + OS_PAGE_SIZE / MIN_OBJECT_SIZE];
26754     if (!parr)
26755     {
26756         goto cleanup;
26757     }
26758
26759     make_c_mark_list (parr);
26760
26761     ret = TRUE;
26762
26763 cleanup:
26764
26765     if (!ret)
26766     {
26767         if (gc_lh_block_event.IsValid())
26768         {
26769             gc_lh_block_event.CloseEvent();
26770         }
26771     }
26772
26773     return ret;
26774 }
26775
26776 int gc_heap::check_for_ephemeral_alloc()
26777 {
26778     int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
26779
26780     if (gen == -1)
26781     {
26782 #ifdef MULTIPLE_HEAPS
26783         for (int heap_index = 0; heap_index < n_heaps; heap_index++)
26784 #endif //MULTIPLE_HEAPS
26785         {
26786             for (int i = 0; i <= (max_generation - 1); i++)
26787             {
26788 #ifdef MULTIPLE_HEAPS
26789                 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
26790 #else
26791                 if (get_new_allocation (i) <= 0)
26792 #endif //MULTIPLE_HEAPS
26793                 {
26794                     gen = max (gen, i);
26795                 }
26796                 else
26797                     break;
26798             }
26799         }
26800     }
26801
26802     return gen;
26803 }
26804
26805 // Wait for gc to finish sequential part
26806 void gc_heap::wait_to_proceed()
26807 {
26808     assert (background_gc_done_event.IsValid());
26809     assert (bgc_start_event.IsValid());
26810
26811     user_thread_wait(&ee_proceed_event, FALSE);
26812 }
26813
26814 // Start a new concurrent gc
26815 void gc_heap::start_c_gc()
26816 {
26817     assert (background_gc_done_event.IsValid());
26818     assert (bgc_start_event.IsValid());
26819
26820 //Need to make sure that the gc thread is in the right place.
26821     background_gc_done_event.Wait(INFINITE, FALSE);
26822     background_gc_done_event.Reset();
26823     bgc_start_event.Set();
26824 }
26825
26826 void gc_heap::do_background_gc()
26827 {
26828     dprintf (2, ("starting a BGC"));
26829 #ifdef MULTIPLE_HEAPS
26830     for (int i = 0; i < n_heaps; i++)
26831     {
26832         g_heaps[i]->init_background_gc();
26833     }
26834 #else
26835     init_background_gc();
26836 #endif //MULTIPLE_HEAPS
26837     //start the background gc
26838     start_c_gc ();
26839
26840     //wait until we get restarted by the BGC.
26841     wait_to_proceed();
26842 }
26843
26844 void gc_heap::kill_gc_thread()
26845 {
26846     //assert (settings.concurrent == FALSE);
26847
26848     // We are doing a two-stage shutdown now.
26849     // In the first stage, we do minimum work, and call ExitProcess at the end.
26850     // In the secodn stage, we have the Loader lock and only one thread is
26851     // alive.  Hence we do not need to kill gc thread.
26852     background_gc_done_event.CloseEvent();
26853     gc_lh_block_event.CloseEvent();
26854     bgc_start_event.CloseEvent();
26855     bgc_threads_timeout_cs.Destroy();
26856     bgc_thread = 0;
26857     recursive_gc_sync::shutdown();
26858 }
26859
26860 void gc_heap::bgc_thread_function()
26861 {
26862     assert (background_gc_done_event.IsValid());
26863     assert (bgc_start_event.IsValid());
26864
26865     dprintf (3, ("gc_thread thread starting..."));
26866
26867     BOOL do_exit = FALSE;
26868
26869     bool cooperative_mode = true;
26870     bgc_thread_id.SetToCurrentThread();
26871     dprintf (1, ("bgc_thread_id is set to %x", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()));
26872     while (1)
26873     {
26874         // Wait for work to do...
26875         dprintf (3, ("bgc thread: waiting..."));
26876
26877         cooperative_mode = enable_preemptive ();
26878         //current_thread->m_fPreemptiveGCDisabled = 0;
26879
26880         uint32_t result = bgc_start_event.Wait(
26881 #ifdef _DEBUG
26882 #ifdef MULTIPLE_HEAPS
26883                                              INFINITE,
26884 #else
26885                                              2000,
26886 #endif //MULTIPLE_HEAPS
26887 #else //_DEBUG
26888 #ifdef MULTIPLE_HEAPS
26889                                              INFINITE,
26890 #else
26891                                              20000,
26892 #endif //MULTIPLE_HEAPS
26893 #endif //_DEBUG
26894             FALSE);
26895         dprintf (2, ("gc thread: finished waiting"));
26896
26897         // not calling disable_preemptive here 'cause we 
26898         // can't wait for GC complete here - RestartEE will be called 
26899         // when we've done the init work.
26900
26901         if (result == WAIT_TIMEOUT)
26902         {
26903             // Should join the bgc threads and terminate all of them
26904             // at once.
26905             dprintf (1, ("GC thread timeout"));
26906             bgc_threads_timeout_cs.Enter();
26907             if (!keep_bgc_threads_p)
26908             {
26909                 dprintf (2, ("GC thread exiting"));
26910                 bgc_thread_running = FALSE;
26911                 bgc_thread = 0;
26912                 bgc_thread_id.Clear();
26913                 do_exit = TRUE;
26914             }
26915             bgc_threads_timeout_cs.Leave();
26916             if (do_exit)
26917                 break;
26918             else
26919             {
26920                 dprintf (3, ("GC thread needed, not exiting"));
26921                 continue;
26922             }
26923         }
26924         // if we signal the thread with no concurrent work to do -> exit
26925         if (!settings.concurrent)
26926         {
26927             dprintf (3, ("no concurrent GC needed, exiting"));
26928             break;
26929         }
26930 #ifdef TRACE_GC
26931         //trace_gc = TRUE;
26932 #endif //TRACE_GC
26933         recursive_gc_sync::begin_background();
26934         dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d", 
26935             generation_free_list_space (generation_of (max_generation)),
26936             generation_free_obj_space (generation_of (max_generation)),
26937             dd_fragmentation (dynamic_data_of (max_generation))));
26938
26939         gc1();
26940
26941         current_bgc_state = bgc_not_in_process;
26942
26943 #ifdef TRACE_GC
26944         //trace_gc = FALSE;
26945 #endif //TRACE_GC
26946
26947         enable_preemptive ();
26948 #ifdef MULTIPLE_HEAPS
26949         bgc_t_join.join(this, gc_join_done);
26950         if (bgc_t_join.joined())
26951 #endif //MULTIPLE_HEAPS
26952         {
26953             enter_spin_lock (&gc_lock);
26954             dprintf (SPINLOCK_LOG, ("bgc Egc"));
26955             
26956             bgc_start_event.Reset();
26957             do_post_gc();
26958 #ifdef MULTIPLE_HEAPS
26959             for (int gen = max_generation; gen <= (max_generation + 1); gen++)
26960             {
26961                 size_t desired_per_heap = 0;
26962                 size_t total_desired = 0;
26963                 gc_heap* hp = 0;
26964                 dynamic_data* dd;
26965                 for (int i = 0; i < n_heaps; i++)
26966                 {
26967                     hp = g_heaps[i];
26968                     dd = hp->dynamic_data_of (gen);
26969                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
26970                     if (temp_total_desired < total_desired)
26971                     {
26972                         // we overflowed.
26973                         total_desired = (size_t)MAX_PTR;
26974                         break;
26975                     }
26976                     total_desired = temp_total_desired;
26977                 }
26978
26979                 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
26980
26981                 for (int i = 0; i < n_heaps; i++)
26982                 {
26983                     hp = gc_heap::g_heaps[i];
26984                     dd = hp->dynamic_data_of (gen);
26985                     dd_desired_allocation (dd) = desired_per_heap;
26986                     dd_gc_new_allocation (dd) = desired_per_heap;
26987                     dd_new_allocation (dd) = desired_per_heap;
26988                 }
26989             }
26990 #endif //MULTIPLE_HEAPS
26991 #ifdef MULTIPLE_HEAPS
26992             fire_pevents();
26993 #endif //MULTIPLE_HEAPS
26994
26995             c_write (settings.concurrent, FALSE);
26996             recursive_gc_sync::end_background();
26997             keep_bgc_threads_p = FALSE;
26998             background_gc_done_event.Set();
26999
27000             dprintf (SPINLOCK_LOG, ("bgc Lgc"));
27001             leave_spin_lock (&gc_lock);
27002 #ifdef MULTIPLE_HEAPS
27003             dprintf(1, ("End of BGC - starting all BGC threads"));
27004             bgc_t_join.restart();
27005 #endif //MULTIPLE_HEAPS
27006         }
27007         // We can't disable preempt here because there might've been a GC already
27008         // started and decided to do a BGC and waiting for a BGC thread to restart 
27009         // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
27010         // to restart the VM so we deadlock.
27011         //gc_heap::disable_preemptive (current_thread, TRUE);
27012     }
27013
27014     FIRE_EVENT(GCTerminateConcurrentThread_V1);
27015
27016     dprintf (3, ("bgc_thread thread exiting"));
27017     return;
27018 }
27019
27020 #endif //BACKGROUND_GC
27021
27022 //Clear the cards [start_card, end_card[
27023 void gc_heap::clear_cards (size_t start_card, size_t end_card)
27024 {
27025     if (start_card < end_card)
27026     {
27027         size_t start_word = card_word (start_card);
27028         size_t end_word = card_word (end_card);
27029         if (start_word < end_word)
27030         {
27031             // Figure out the bit positions of the cards within their words
27032             unsigned bits = card_bit (start_card);
27033             card_table [start_word] &= lowbits (~0, bits);
27034             for (size_t i = start_word+1; i < end_word; i++)
27035                 card_table [i] = 0;
27036             bits = card_bit (end_card);
27037             // Don't write beyond end_card (and possibly uncommitted card table space).
27038             if (bits != 0)
27039             {
27040                 card_table [end_word] &= highbits (~0, bits);
27041             }
27042         }
27043         else
27044         {
27045             // If the start and end cards are in the same word, just clear the appropriate card
27046             // bits in that word.
27047             card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
27048                                         highbits (~0, card_bit (end_card)));
27049         }
27050 #ifdef VERYSLOWDEBUG
27051         size_t  card = start_card;
27052         while (card < end_card)
27053         {
27054             assert (! (card_set_p (card)));
27055             card++;
27056         }
27057 #endif //VERYSLOWDEBUG
27058         dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
27059                   start_card, (size_t)card_address (start_card),
27060                   end_card, (size_t)card_address (end_card)));
27061     }
27062 }
27063
27064 void gc_heap::clear_card_for_addresses (uint8_t* start_address, uint8_t* end_address)
27065 {
27066     size_t   start_card = card_of (align_on_card (start_address));
27067     size_t   end_card = card_of (align_lower_card (end_address));
27068     clear_cards (start_card, end_card);
27069 }
27070
27071 // copy [srccard, ...[ to [dst_card, end_card[
27072 // This will set the same bit twice. Can be optimized.
27073 inline
27074 void gc_heap::copy_cards (size_t dst_card,
27075                           size_t src_card,
27076                           size_t end_card, 
27077                           BOOL nextp)
27078 {
27079     // If the range is empty, this function is a no-op - with the subtlety that
27080     // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
27081     // outside the committed region.  To avoid the access, leave early.
27082     if (!(dst_card < end_card))
27083         return;
27084
27085     unsigned int srcbit = card_bit (src_card);
27086     unsigned int dstbit = card_bit (dst_card);
27087     size_t srcwrd = card_word (src_card);
27088     size_t dstwrd = card_word (dst_card);
27089     unsigned int srctmp = card_table[srcwrd];
27090     unsigned int dsttmp = card_table[dstwrd];
27091
27092     for (size_t card = dst_card; card < end_card; card++)
27093     {
27094         if (srctmp & (1 << srcbit))
27095             dsttmp |= 1 << dstbit;
27096         else
27097             dsttmp &= ~(1 << dstbit);
27098
27099         if (!(++srcbit % 32))
27100         {
27101             srctmp = card_table[++srcwrd];
27102             srcbit = 0;
27103         }
27104
27105         if (nextp)
27106         {
27107             if (srctmp & (1 << srcbit))
27108                 dsttmp |= 1 << dstbit;
27109         }
27110
27111         if (!(++dstbit % 32))
27112         {
27113             card_table[dstwrd] = dsttmp;
27114
27115 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27116             if (dsttmp != 0)
27117             {
27118                 card_bundle_set(cardw_card_bundle(dstwrd));
27119             }
27120 #endif
27121
27122             dstwrd++;
27123             dsttmp = card_table[dstwrd];
27124             dstbit = 0;
27125         }
27126     }
27127
27128     card_table[dstwrd] = dsttmp;
27129
27130 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27131     if (dsttmp != 0)
27132     {
27133         card_bundle_set(cardw_card_bundle(dstwrd));
27134     }
27135 #endif
27136 }
27137
27138 void gc_heap::copy_cards_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27139 {
27140     ptrdiff_t relocation_distance = src - dest;
27141     size_t start_dest_card = card_of (align_on_card (dest));
27142     size_t end_dest_card = card_of (dest + len - 1);
27143     size_t dest_card = start_dest_card;
27144     size_t src_card = card_of (card_address (dest_card)+relocation_distance);
27145     dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
27146                  src_card, (size_t)src, dest_card, (size_t)dest));
27147     dprintf (3,(" %Ix->%Ix:%Ix[",
27148               (size_t)src+len, end_dest_card, (size_t)dest+len));
27149
27150     dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
27151         dest, src, len, relocation_distance, (align_on_card (dest))));
27152
27153     dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
27154         start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
27155
27156     //First card has two boundaries
27157     if (start_dest_card != card_of (dest))
27158     {
27159         if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
27160             card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
27161         {
27162             dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
27163                     (card_address (start_dest_card) + relocation_distance),
27164                     card_of (card_address (start_dest_card) + relocation_distance),
27165                     (src + len - 1),
27166                     card_of (src + len - 1)));
27167
27168             dprintf (3, ("setting card: %Ix", card_of (dest)));
27169             set_card (card_of (dest));
27170         }
27171     }
27172
27173     if (card_set_p (card_of (src)))
27174         set_card (card_of (dest));
27175
27176
27177     copy_cards (dest_card, src_card, end_dest_card,
27178                 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
27179
27180     //Last card has two boundaries.
27181     if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
27182         card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
27183     {
27184         dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
27185                 (card_address (end_dest_card) + relocation_distance),
27186                 card_of (card_address (end_dest_card) + relocation_distance),
27187                 src,
27188                 card_of (src)));
27189
27190         dprintf (3, ("setting card: %Ix", end_dest_card));
27191         set_card (end_dest_card);
27192     }
27193
27194     if (card_set_p (card_of (src + len - 1)))
27195         set_card (end_dest_card);
27196
27197 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27198     card_bundles_set(cardw_card_bundle(card_word(card_of(dest))), cardw_card_bundle(align_cardw_on_bundle(card_word(end_dest_card))));
27199 #endif
27200 }
27201
27202 #ifdef BACKGROUND_GC
27203 // this does not need the Interlocked version of mark_array_set_marked.
27204 void gc_heap::copy_mark_bits_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27205 {
27206     dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
27207                  (size_t)src, (size_t)dest,
27208                  (size_t)src+len, (size_t)dest+len));
27209
27210     uint8_t* src_o = src;
27211     uint8_t* dest_o;
27212     uint8_t* src_end = src + len;
27213     int align_const = get_alignment_constant (TRUE);
27214     ptrdiff_t reloc = dest - src;
27215
27216     while (src_o < src_end)
27217     {
27218         uint8_t*  next_o = src_o + Align (size (src_o), align_const);
27219
27220         if (background_object_marked (src_o, TRUE))
27221         {
27222             dest_o = src_o + reloc;
27223
27224             //if (background_object_marked (dest_o, FALSE))
27225             //{
27226             //    dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
27227             //    FATAL_GC_ERROR();
27228             //}
27229
27230             background_mark (dest_o, 
27231                              background_saved_lowest_address, 
27232                              background_saved_highest_address);
27233             dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
27234         }
27235
27236         src_o = next_o;
27237     }
27238 }
27239 #endif //BACKGROUND_GC
27240
27241 void gc_heap::fix_brick_to_highest (uint8_t* o, uint8_t* next_o)
27242 {
27243     size_t new_current_brick = brick_of (o);
27244     set_brick (new_current_brick,
27245                (o - brick_address (new_current_brick)));
27246     size_t b = 1 + new_current_brick;
27247     size_t limit = brick_of (next_o);
27248     //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
27249     dprintf(3,("b:%Ix->%Ix-%Ix", 
27250                new_current_brick, (size_t)o, (size_t)next_o));
27251     while (b < limit)
27252     {
27253         set_brick (b,(new_current_brick - b));
27254         b++;
27255     }
27256 }
27257
27258 // start can not be >= heap_segment_allocated for the segment.
27259 uint8_t* gc_heap::find_first_object (uint8_t* start, uint8_t* first_object)
27260 {
27261     size_t brick = brick_of (start);
27262     uint8_t* o = 0;
27263     //last_object == null -> no search shortcut needed
27264     if ((brick == brick_of (first_object) || (start <= first_object)))
27265     {
27266         o = first_object;
27267     }
27268     else
27269     {
27270         ptrdiff_t  min_brick = (ptrdiff_t)brick_of (first_object);
27271         ptrdiff_t  prev_brick = (ptrdiff_t)brick - 1;
27272         int         brick_entry = 0;
27273         while (1)
27274         {
27275             if (prev_brick < min_brick)
27276             {
27277                 break;
27278             }
27279             if ((brick_entry = get_brick_entry(prev_brick)) >= 0)
27280             {
27281                 break;
27282             }
27283             assert (! ((brick_entry == 0)));
27284             prev_brick = (brick_entry + prev_brick);
27285
27286         }
27287         o = ((prev_brick < min_brick) ? first_object :
27288                       brick_address (prev_brick) + brick_entry - 1);
27289         assert (o <= start);
27290     }
27291
27292     assert (Align (size (o)) >= Align (min_obj_size));
27293     uint8_t*  next_o = o + Align (size (o));
27294     size_t curr_cl = (size_t)next_o / brick_size;
27295     size_t min_cl = (size_t)first_object / brick_size;
27296
27297     //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
27298 #ifdef TRACE_GC
27299     unsigned int n_o = 1;
27300 #endif //TRACE_GC
27301
27302     uint8_t* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27303
27304     while (next_o <= start)
27305     {
27306         do
27307         {
27308 #ifdef TRACE_GC
27309             n_o++;
27310 #endif //TRACE_GC
27311             o = next_o;
27312             assert (Align (size (o)) >= Align (min_obj_size));
27313             next_o = o + Align (size (o));
27314             Prefetch (next_o);
27315         }while (next_o < next_b);
27316
27317         if (((size_t)next_o / brick_size) != curr_cl)
27318         {
27319             if (curr_cl >= min_cl)
27320             {
27321                 fix_brick_to_highest (o, next_o);
27322             }
27323             curr_cl = (size_t) next_o / brick_size;
27324         }
27325         next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27326     }
27327
27328     size_t bo = brick_of (o);
27329     //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix", 
27330     dprintf (3, ("%Id o, [%Ix-[%Ix", 
27331         n_o, bo, brick));
27332     if (bo < brick)
27333     {
27334         set_brick (bo, (o - brick_address(bo)));
27335         size_t b = 1 + bo;
27336         int x = -1;
27337         while (b < brick)
27338         {
27339             set_brick (b,x--);
27340             b++;
27341         }
27342     }
27343
27344     return o;
27345 }
27346
27347 #ifdef CARD_BUNDLE
27348
27349 // Find the first non-zero card word between cardw and cardw_end.
27350 // The index of the word we find is returned in cardw.
27351 BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
27352 {
27353     dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
27354                  dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
27355
27356     if (card_bundles_enabled())
27357     {
27358         size_t cardb = cardw_card_bundle (cardw);
27359         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
27360         while (1)
27361         {
27362             // Find a non-zero bundle
27363             while ((cardb < end_cardb) && (card_bundle_set_p (cardb) == 0))
27364             {
27365                 cardb++;
27366             }
27367
27368             if (cardb == end_cardb)
27369                 return FALSE;
27370
27371             // We found a bundle, so go through its words and find a non-zero card word
27372             uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
27373             uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
27374             while ((card_word < card_word_end) && !(*card_word))
27375             {
27376                 card_word++;
27377             }
27378
27379             if (card_word != card_word_end)
27380             {
27381                 cardw = (card_word - &card_table[0]);
27382                 return TRUE;
27383             }
27384             else if ((cardw <= card_bundle_cardw (cardb)) &&
27385                      (card_word == &card_table [card_bundle_cardw (cardb+1)]))
27386             {
27387                 // a whole bundle was explored and is empty
27388                 dprintf  (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
27389                         dd_collection_count (dynamic_data_of (0)), 
27390                         cardb, card_bundle_cardw (cardb),
27391                         card_bundle_cardw (cardb+1)));
27392                 card_bundle_clear (cardb);
27393             }
27394
27395             cardb++;
27396         }
27397     }
27398     else
27399     {
27400         uint32_t* card_word = &card_table[cardw];
27401         uint32_t* card_word_end = &card_table [cardw_end];
27402
27403         while (card_word < card_word_end)
27404         {
27405             if (*card_word != 0)
27406             {
27407                 cardw = (card_word - &card_table [0]);
27408                 return TRUE;
27409             }
27410
27411             card_word++;
27412         }
27413
27414         return FALSE;
27415     }
27416 }
27417
27418 #endif //CARD_BUNDLE
27419
27420 // Find cards that are set between two points in a card table.
27421 // Parameters
27422 //     card_table    : The card table.
27423 //     card          : [in/out] As input, the card to start searching from.
27424 //                              As output, the first card that's set.
27425 //     card_word_end : The card word at which to stop looking.
27426 //     end_card      : [out] The last card which is set.
27427 BOOL gc_heap::find_card(uint32_t* card_table,
27428                         size_t&   card,
27429                         size_t    card_word_end,
27430                         size_t&   end_card)
27431 {
27432     uint32_t* last_card_word;
27433     uint32_t card_word_value;
27434     uint32_t bit_position;
27435     
27436     // Find the first card which is set
27437     last_card_word = &card_table [card_word (card)];
27438     bit_position = card_bit (card);
27439     card_word_value = (*last_card_word) >> bit_position;
27440     if (!card_word_value)
27441     {
27442         bit_position = 0;
27443 #ifdef CARD_BUNDLE
27444         // Using the card bundle, go through the remaining card words between here and 
27445         // card_word_end until we find one that is non-zero.
27446         size_t lcw = card_word(card) + 1;
27447         if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
27448         {
27449             return FALSE;
27450         }
27451         else
27452         {
27453             last_card_word = &card_table [lcw];
27454             card_word_value = *last_card_word;
27455         }
27456
27457 #else //CARD_BUNDLE
27458         // Go through the remaining card words between here and card_word_end until we find
27459         // one that is non-zero.
27460         do
27461         {
27462             ++last_card_word;
27463         }
27464         while ((last_card_word < &card_table [card_word_end]) && !(*last_card_word));
27465
27466         if (last_card_word < &card_table [card_word_end])
27467         {
27468             card_word_value = *last_card_word;
27469         }
27470         else
27471         {
27472             // We failed to find any non-zero card words before we got to card_word_end
27473             return FALSE;
27474         }
27475 #endif //CARD_BUNDLE
27476     }
27477
27478     // Look for the lowest bit set
27479     if (card_word_value)
27480     {
27481         while (!(card_word_value & 1))
27482         {
27483             bit_position++;
27484             card_word_value = card_word_value / 2;
27485         }
27486     }
27487     
27488     // card is the card word index * card size + the bit index within the card
27489     card = (last_card_word - &card_table[0]) * card_word_width + bit_position;
27490
27491     do
27492     {
27493         // Keep going until we get to an un-set card.
27494         bit_position++;
27495         card_word_value = card_word_value / 2;
27496
27497         // If we reach the end of the card word and haven't hit a 0 yet, start going
27498         // card word by card word until we get to one that's not fully set (0xFFFF...)
27499         // or we reach card_word_end.
27500         if ((bit_position == card_word_width) && (last_card_word < &card_table [card_word_end]))
27501         {
27502             do
27503             {
27504                 card_word_value = *(++last_card_word);
27505             } while ((last_card_word < &card_table [card_word_end]) &&
27506
27507 #ifdef _MSC_VER
27508                      (card_word_value == (1 << card_word_width)-1)
27509 #else
27510                      // if left shift count >= width of type,
27511                      // gcc reports error.
27512                      (card_word_value == ~0u)
27513 #endif // _MSC_VER
27514                 );
27515             bit_position = 0;
27516         }
27517     } while (card_word_value & 1);
27518
27519     end_card = (last_card_word - &card_table [0])* card_word_width + bit_position;
27520     
27521     //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
27522     dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
27523     return TRUE;
27524 }
27525
27526
27527     //because of heap expansion, computing end is complicated.
27528 uint8_t* compute_next_end (heap_segment* seg, uint8_t* low)
27529 {
27530     if ((low >=  heap_segment_mem (seg)) &&
27531         (low < heap_segment_allocated (seg)))
27532         return low;
27533     else
27534         return heap_segment_allocated (seg);
27535 }
27536
27537 uint8_t*
27538 gc_heap::compute_next_boundary (uint8_t* low, int gen_number,
27539                                 BOOL relocating)
27540 {
27541     UNREFERENCED_PARAMETER(low);
27542
27543     //when relocating, the fault line is the plan start of the younger
27544     //generation because the generation is promoted.
27545     if (relocating && (gen_number == (settings.condemned_generation + 1)))
27546     {
27547         generation* gen = generation_of (gen_number - 1);
27548         uint8_t* gen_alloc = generation_plan_allocation_start (gen);
27549         assert (gen_alloc);
27550         return gen_alloc;
27551     }
27552     else
27553     {
27554         assert (gen_number > settings.condemned_generation);
27555         return generation_allocation_start (generation_of (gen_number - 1 ));
27556     }
27557
27558 }
27559
27560 inline void
27561 gc_heap::keep_card_live (uint8_t* o, size_t& n_gen,
27562                          size_t& cg_pointers_found)
27563 {
27564     THREAD_FROM_HEAP;
27565     if ((gc_low <= o) && (gc_high > o))
27566     {
27567         n_gen++;
27568     }
27569 #ifdef MULTIPLE_HEAPS
27570     else if (o)
27571     {
27572         gc_heap* hp = heap_of (o);
27573         if (hp != this)
27574         {
27575             if ((hp->gc_low <= o) &&
27576                 (hp->gc_high > o))
27577             {
27578                 n_gen++;
27579             }
27580         }
27581     }
27582 #endif //MULTIPLE_HEAPS
27583     cg_pointers_found ++;
27584     dprintf (4, ("keep card live for %Ix", o));
27585 }
27586
27587 inline void
27588 gc_heap::mark_through_cards_helper (uint8_t** poo, size_t& n_gen,
27589                                     size_t& cg_pointers_found,
27590                                     card_fn fn, uint8_t* nhigh,
27591                                     uint8_t* next_boundary)
27592 {
27593     THREAD_FROM_HEAP;
27594     if ((gc_low <= *poo) && (gc_high > *poo))
27595     {
27596         n_gen++;
27597         call_fn(fn) (poo THREAD_NUMBER_ARG);
27598     }
27599 #ifdef MULTIPLE_HEAPS
27600     else if (*poo)
27601     {
27602         gc_heap* hp = heap_of_gc (*poo);
27603         if (hp != this)
27604         {
27605             if ((hp->gc_low <= *poo) &&
27606                 (hp->gc_high > *poo))
27607             {
27608                 n_gen++;
27609                 call_fn(fn) (poo THREAD_NUMBER_ARG);
27610             }
27611             if ((fn == &gc_heap::relocate_address) ||
27612                 ((hp->ephemeral_low <= *poo) &&
27613                  (hp->ephemeral_high > *poo)))
27614             {
27615                 cg_pointers_found++;
27616             }
27617         }
27618     }
27619 #endif //MULTIPLE_HEAPS
27620     if ((next_boundary <= *poo) && (nhigh > *poo))
27621     {
27622         cg_pointers_found ++;
27623         dprintf (4, ("cg pointer %Ix found, %Id so far",
27624                      (size_t)*poo, cg_pointers_found ));
27625
27626     }
27627 }
27628
27629 BOOL gc_heap::card_transition (uint8_t* po, uint8_t* end, size_t card_word_end,
27630                                size_t& cg_pointers_found, 
27631                                size_t& n_eph, size_t& n_card_set,
27632                                size_t& card, size_t& end_card,
27633                                BOOL& foundp, uint8_t*& start_address,
27634                                uint8_t*& limit, size_t& n_cards_cleared)
27635 {
27636     dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
27637     dprintf (3, ("ct: %Id cg", cg_pointers_found));
27638     BOOL passed_end_card_p = FALSE;
27639     foundp = FALSE;
27640
27641     if (cg_pointers_found == 0)
27642     {
27643         //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
27644         dprintf(3,(" CC [%Ix, %Ix[ ",
27645                 (size_t)card_address(card), (size_t)po));
27646         clear_cards (card, card_of(po));
27647         n_card_set -= (card_of (po) - card);
27648         n_cards_cleared += (card_of (po) - card);
27649
27650     }
27651     n_eph +=cg_pointers_found;
27652     cg_pointers_found = 0;
27653     card = card_of (po);
27654     if (card >= end_card)
27655     {
27656         passed_end_card_p = TRUE;
27657         dprintf (3, ("card %Ix exceeding end_card %Ix",
27658                     (size_t)card, (size_t)end_card));
27659         foundp = find_card (card_table, card, card_word_end, end_card);
27660         if (foundp)
27661         {
27662             n_card_set+= end_card - card;
27663             start_address = card_address (card);
27664             dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
27665                         (size_t)card, (size_t)start_address,
27666                         (size_t)card_address (end_card)));
27667         }
27668         limit = min (end, card_address (end_card));
27669
27670         assert (!((limit == card_address (end_card))&&
27671                 card_set_p (end_card)));
27672     }
27673
27674     return passed_end_card_p;
27675 }
27676
27677 void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
27678 {
27679 #ifdef BACKGROUND_GC
27680     dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
27681                  current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
27682
27683     heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
27684     PREFIX_ASSUME(soh_seg != NULL);
27685
27686     while (soh_seg)
27687     {
27688         dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix", 
27689             soh_seg, 
27690             heap_segment_background_allocated (soh_seg),
27691             heap_segment_allocated (soh_seg)));
27692
27693         soh_seg = heap_segment_next_rw (soh_seg);
27694     }
27695 #endif //BACKGROUND_GC
27696
27697     uint8_t* low = gc_low;
27698     uint8_t* high = gc_high;
27699     size_t end_card = 0;
27700
27701     generation*   oldest_gen        = generation_of (max_generation);
27702     int           curr_gen_number   = max_generation;
27703     uint8_t*      gen_boundary      = generation_allocation_start(generation_of(curr_gen_number - 1));
27704     uint8_t*      next_boundary     = compute_next_boundary(gc_low, curr_gen_number, relocating);
27705     
27706     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
27707     PREFIX_ASSUME(seg != NULL);
27708
27709     uint8_t*      beg               = generation_allocation_start (oldest_gen);
27710     uint8_t*      end               = compute_next_end (seg, low);
27711     uint8_t*      last_object       = beg;
27712
27713     size_t  cg_pointers_found = 0;
27714
27715     size_t  card_word_end = (card_of (align_on_card_word (end)) / card_word_width);
27716
27717     size_t        n_eph             = 0;
27718     size_t        n_gen             = 0;
27719     size_t        n_card_set        = 0;
27720     uint8_t*      nhigh             = (relocating ? heap_segment_plan_allocated (ephemeral_heap_segment) : high);
27721
27722     BOOL          foundp            = FALSE;
27723     uint8_t*      start_address     = 0;
27724     uint8_t*      limit             = 0;
27725     size_t        card              = card_of (beg);
27726 #ifdef BACKGROUND_GC
27727     BOOL consider_bgc_mark_p        = FALSE;
27728     BOOL check_current_sweep_p      = FALSE;
27729     BOOL check_saved_sweep_p        = FALSE;
27730     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
27731 #endif //BACKGROUND_GC
27732
27733     dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
27734     size_t total_cards_cleared = 0;
27735
27736     while (1)
27737     {
27738         if (card_of(last_object) > card)
27739         {
27740             // cg means cross-generational
27741             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
27742             if (cg_pointers_found == 0)
27743             {
27744                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
27745                 clear_cards (card, card_of(last_object));
27746                 n_card_set -= (card_of (last_object) - card);
27747                 total_cards_cleared += (card_of (last_object) - card);
27748             }
27749
27750             n_eph += cg_pointers_found;
27751             cg_pointers_found = 0;
27752             card = card_of (last_object);
27753         }
27754
27755         if (card >= end_card)
27756         {
27757             // Find the first card that's set (between card and card_word_end)
27758             foundp = find_card(card_table, card, card_word_end, end_card);
27759             if (foundp)
27760             {
27761                 // We found card(s) set. 
27762                 n_card_set += end_card - card;
27763                 start_address = max (beg, card_address (card));
27764             }
27765
27766             limit = min (end, card_address (end_card));
27767         }
27768
27769         if (!foundp || (last_object >= end) || (card_address (card) >= end))
27770         {
27771             if (foundp && (cg_pointers_found == 0))
27772             {
27773                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
27774                            (size_t)end));
27775                 clear_cards (card, card_of (end));
27776                 n_card_set -= (card_of (end) - card);
27777                 total_cards_cleared += (card_of (end) - card);
27778             }
27779
27780             n_eph += cg_pointers_found;
27781             cg_pointers_found = 0;
27782
27783             if ((seg = heap_segment_next_in_range (seg)) != 0)
27784             {
27785 #ifdef BACKGROUND_GC
27786                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
27787 #endif //BACKGROUND_GC
27788                 beg = heap_segment_mem (seg);
27789                 end = compute_next_end (seg, low);
27790                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
27791                 card = card_of (beg);
27792                 last_object = beg;
27793                 end_card = 0;
27794                 continue;
27795             }
27796             else
27797             {
27798                 break;
27799             }
27800         }
27801
27802         // We've found a card and will now go through the objects in it.
27803         assert (card_set_p (card));
27804         {
27805             uint8_t* o = last_object;
27806             o = find_first_object (start_address, last_object);
27807             // Never visit an object twice.
27808             assert (o >= last_object);
27809
27810             //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
27811             dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
27812                    card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
27813
27814             while (o < limit)
27815             {
27816                 assert (Align (size (o)) >= Align (min_obj_size));
27817                 size_t s = size (o);
27818
27819                 uint8_t* next_o =  o + Align (s);
27820                 Prefetch (next_o);
27821
27822                 if ((o >= gen_boundary) &&
27823                     (seg == ephemeral_heap_segment))
27824                 {
27825                     dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
27826                     curr_gen_number--;
27827                     assert ((curr_gen_number > 0));
27828                     gen_boundary = generation_allocation_start
27829                         (generation_of (curr_gen_number - 1));
27830                     next_boundary = (compute_next_boundary
27831                                      (low, curr_gen_number, relocating));
27832                 }
27833
27834                 dprintf (4, ("|%Ix|", (size_t)o));
27835
27836                 if (next_o < start_address)
27837                 {
27838                     goto end_object;
27839                 }
27840
27841 #ifdef BACKGROUND_GC
27842                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
27843                 {
27844                     goto end_object;
27845                 }
27846 #endif //BACKGROUND_GC
27847
27848 #ifdef COLLECTIBLE_CLASS
27849                 if (is_collectible(o))
27850                 {
27851                     BOOL passed_end_card_p = FALSE;
27852
27853                     if (card_of (o) > card)
27854                     {
27855                         passed_end_card_p = card_transition (o, end, card_word_end,
27856                             cg_pointers_found, 
27857                             n_eph, n_card_set,
27858                             card, end_card,
27859                             foundp, start_address,
27860                             limit, total_cards_cleared);
27861                     }
27862
27863                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
27864                     {
27865                         // card is valid and it covers the head of the object
27866                         if (fn == &gc_heap::relocate_address)
27867                         {
27868                             keep_card_live (o, n_gen, cg_pointers_found);
27869                         }
27870                         else
27871                         {
27872                             uint8_t* class_obj = get_class_object (o);
27873                             mark_through_cards_helper (&class_obj, n_gen,
27874                                                     cg_pointers_found, fn,
27875                                                     nhigh, next_boundary);
27876                         }
27877                     }
27878
27879                     if (passed_end_card_p)
27880                     {
27881                         if (foundp && (card_address (card) < next_o))
27882                         {
27883                             goto go_through_refs;
27884                         }
27885                         else if (foundp && (start_address < limit))
27886                         {
27887                             next_o = find_first_object (start_address, o);
27888                             goto end_object;
27889                         }
27890                         else
27891                             goto end_limit;                            
27892                     }
27893                 }
27894
27895 go_through_refs:
27896 #endif //COLLECTIBLE_CLASS
27897
27898                 if (contain_pointers (o))
27899                 {
27900                     dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
27901
27902                     {
27903                         dprintf (4, ("normal object path"));
27904                         go_through_object
27905                             (method_table(o), o, s, poo,
27906                              start_address, use_start, (o + s),
27907                              {
27908                                  dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
27909                                  if (card_of ((uint8_t*)poo) > card)
27910                                  {
27911                                     BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
27912                                             card_word_end,
27913                                             cg_pointers_found, 
27914                                             n_eph, n_card_set,
27915                                             card, end_card,
27916                                             foundp, start_address,
27917                                             limit, total_cards_cleared);
27918
27919                                      if (passed_end_card_p)
27920                                      {
27921                                         if (foundp && (card_address (card) < next_o))
27922                                         {
27923                                              //new_start();
27924                                              {
27925                                                  if (ppstop <= (uint8_t**)start_address)
27926                                                      {break;}
27927                                                  else if (poo < (uint8_t**)start_address)
27928                                                      {poo = (uint8_t**)start_address;}
27929                                              }
27930                                         }
27931                                         else if (foundp && (start_address < limit))
27932                                         {
27933                                             next_o = find_first_object (start_address, o);
27934                                             goto end_object;
27935                                         }
27936                                          else
27937                                             goto end_limit;
27938                                      }
27939                                  }
27940
27941                                  mark_through_cards_helper (poo, n_gen,
27942                                                             cg_pointers_found, fn,
27943                                                             nhigh, next_boundary);
27944                              }
27945                             );
27946                     }
27947                 }
27948
27949             end_object:
27950                 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
27951                 {
27952                     if (brick_table [brick_of (o)] <0)
27953                         fix_brick_to_highest (o, next_o);
27954                 }
27955                 o = next_o;
27956             }
27957         end_limit:
27958             last_object = o;
27959         }
27960     }
27961     // compute the efficiency ratio of the card table
27962     if (!relocating)
27963     {
27964         generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
27965         dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
27966             n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
27967     }
27968     else
27969     {
27970         dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
27971             n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
27972     }
27973 }
27974
27975 #ifdef SEG_REUSE_STATS
27976 size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
27977 {
27978     size_t total_items = 0;
27979     *total_size = 0;
27980     for (int i = 0; i < count; i++)
27981     {
27982         total_items += ordered_indices[i];
27983         *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
27984         dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
27985     } 
27986     dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
27987     return total_items;
27988 }
27989 #endif // SEG_REUSE_STATS
27990
27991 void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug)
27992 {
27993     // detect pinned plugs
27994     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
27995     {
27996         deque_pinned_plug();
27997         update_oldest_pinned_plug();
27998         dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
27999     }
28000     else
28001     {
28002         size_t plug_size = last_plug_size + Align(min_obj_size);
28003         BOOL is_padded = FALSE;
28004
28005 #ifdef SHORT_PLUGS
28006         plug_size += Align (min_obj_size);
28007         is_padded = TRUE;
28008 #endif //SHORT_PLUGS
28009
28010 #ifdef RESPECT_LARGE_ALIGNMENT
28011         plug_size += switch_alignment_size (is_padded);
28012 #endif //RESPECT_LARGE_ALIGNMENT
28013
28014         total_ephemeral_plugs += plug_size;
28015         size_t plug_size_power2 = round_up_power2 (plug_size);
28016         ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
28017         dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array", 
28018             heap_number, 
28019             last_plug, 
28020             plug_size, 
28021             (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
28022     }
28023 }
28024
28025 void gc_heap::count_plugs_in_brick (uint8_t* tree, uint8_t*& last_plug)
28026 {
28027     assert ((tree != NULL));
28028     if (node_left_child (tree))
28029     {
28030         count_plugs_in_brick (tree + node_left_child (tree), last_plug);
28031     }
28032
28033     if (last_plug != 0)
28034     {
28035         uint8_t*  plug = tree;
28036         size_t gap_size = node_gap_size (plug);
28037         uint8_t*   gap = (plug - gap_size);
28038         uint8_t*  last_plug_end = gap;
28039         size_t  last_plug_size = (last_plug_end - last_plug);
28040         dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
28041             tree, last_plug, gap_size, gap, last_plug_size));
28042
28043         if (tree == oldest_pinned_plug)
28044         {
28045             dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
28046                 tree, last_plug, last_plug_size));
28047             mark* m = oldest_pin();
28048             if (m->has_pre_plug_info())
28049             {
28050                 last_plug_size += sizeof (gap_reloc_pair);
28051                 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
28052             }
28053         }
28054         // Can't assert here - if it's a pinned plug it can be less.
28055         //assert (last_plug_size >= Align (min_obj_size));
28056
28057         count_plug (last_plug_size, last_plug);
28058     }
28059
28060     last_plug = tree;
28061
28062     if (node_right_child (tree))
28063     {
28064         count_plugs_in_brick (tree + node_right_child (tree), last_plug);
28065     }
28066 }
28067
28068 void gc_heap::build_ordered_plug_indices ()
28069 {
28070     memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
28071     memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
28072
28073     uint8_t*  start_address = generation_limit (max_generation);
28074     uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment);
28075     size_t  current_brick = brick_of (start_address);
28076     size_t  end_brick = brick_of (end_address - 1);
28077     uint8_t* last_plug = 0;
28078
28079     //Look for the right pinned plug to start from.
28080     reset_pinned_queue_bos();
28081     while (!pinned_plug_que_empty_p())
28082     {
28083         mark* m = oldest_pin();
28084         if ((m->first >= start_address) && (m->first < end_address))
28085         {
28086             dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
28087
28088             break;
28089         }
28090         else
28091             deque_pinned_plug();
28092     }
28093     
28094     update_oldest_pinned_plug();
28095
28096     while (current_brick <= end_brick)
28097     {
28098         int brick_entry =  brick_table [ current_brick ];
28099         if (brick_entry >= 0)
28100         {
28101             count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
28102         }
28103
28104         current_brick++;
28105     }
28106
28107     if (last_plug !=0)
28108     {
28109         count_plug (end_address - last_plug, last_plug);
28110     }
28111
28112     // we need to make sure that after fitting all the existing plugs, we
28113     // have big enough free space left to guarantee that the next allocation
28114     // will succeed.
28115     size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
28116     total_ephemeral_plugs += extra_size;
28117     dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
28118     ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
28119     
28120     memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
28121
28122 #ifdef SEG_REUSE_STATS
28123     dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
28124     size_t total_plug_power2 = 0;
28125     dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
28126     dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))", 
28127                 total_ephemeral_plugs, 
28128                 total_plug_power2, 
28129                 (total_ephemeral_plugs ? 
28130                     (total_plug_power2 * 100 / total_ephemeral_plugs) :
28131                     0)));
28132     dprintf (SEG_REUSE_LOG_0, ("-------------------"));
28133 #endif // SEG_REUSE_STATS
28134 }
28135
28136 void gc_heap::init_ordered_free_space_indices ()
28137 {
28138     memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
28139     memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
28140 }
28141
28142 void gc_heap::trim_free_spaces_indices ()
28143 {
28144     trimmed_free_space_index = -1;
28145     size_t max_count = max_free_space_items - 1;
28146     size_t count = 0;
28147     int i = 0;
28148     for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
28149     {
28150         count += ordered_free_space_indices[i];
28151
28152         if (count >= max_count)
28153         {
28154             break;
28155         }
28156     }
28157
28158     ptrdiff_t extra_free_space_items = count - max_count;
28159
28160     if (extra_free_space_items > 0)
28161     {
28162         ordered_free_space_indices[i] -= extra_free_space_items;
28163         free_space_items = max_count;
28164         trimmed_free_space_index = i;
28165     }
28166     else
28167     {
28168         free_space_items = count;
28169     }
28170
28171     if (i == -1)
28172     {
28173         i = 0;
28174     }
28175
28176     free_space_buckets = MAX_NUM_BUCKETS - i;
28177
28178     for (--i; i >= 0; i--)
28179     {
28180         ordered_free_space_indices[i] = 0;
28181     }
28182
28183     memcpy (saved_ordered_free_space_indices, 
28184             ordered_free_space_indices,
28185             sizeof(ordered_free_space_indices));
28186 }
28187
28188 // We fit as many plugs as we can and update the number of plugs left and the number
28189 // of free spaces left.
28190 BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
28191 {
28192     assert (small_index <= big_index);
28193     assert (big_index < MAX_NUM_BUCKETS);
28194
28195     size_t small_blocks = ordered_blocks[small_index];
28196
28197     if (small_blocks == 0)
28198     {
28199         return TRUE;
28200     }
28201
28202     size_t big_spaces = ordered_spaces[big_index];
28203
28204     if (big_spaces == 0)
28205     {
28206         return FALSE;
28207     }
28208
28209     dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces", 
28210         heap_number,
28211         small_blocks, (small_index + MIN_INDEX_POWER2),
28212         big_spaces, (big_index + MIN_INDEX_POWER2)));
28213
28214     size_t big_to_small = big_spaces << (big_index - small_index);
28215
28216     ptrdiff_t extra_small_spaces = big_to_small - small_blocks;
28217     dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks", 
28218         heap_number,
28219         big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
28220     BOOL can_fit = (extra_small_spaces >= 0);
28221
28222     if (can_fit) 
28223     {
28224         dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks", 
28225             heap_number,
28226             extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
28227     }
28228
28229     int i = 0;
28230
28231     dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
28232     ordered_spaces[big_index] = 0;
28233     if (extra_small_spaces > 0)
28234     {
28235         dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
28236         ordered_blocks[small_index] = 0;
28237         for (i = small_index; i < big_index; i++)
28238         {
28239             if (extra_small_spaces & 1)
28240             {
28241                 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d", 
28242                     heap_number,
28243                     (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
28244                 ordered_spaces[i] += 1;
28245             }
28246             extra_small_spaces >>= 1;
28247         }
28248
28249         dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d", 
28250             heap_number,
28251             (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
28252         ordered_spaces[i] += extra_small_spaces;
28253     }
28254     else
28255     {
28256         dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d", 
28257             heap_number,
28258             (small_index + MIN_INDEX_POWER2), 
28259             ordered_blocks[small_index], 
28260             (ordered_blocks[small_index] - big_to_small)));
28261         ordered_blocks[small_index] -= big_to_small;
28262     }
28263
28264 #ifdef SEG_REUSE_STATS
28265     size_t temp;
28266     dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
28267     dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
28268
28269     dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
28270     dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
28271 #endif //SEG_REUSE_STATS
28272
28273     return can_fit;
28274 }
28275
28276 // space_index gets updated to the biggest available space index.
28277 BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
28278 {
28279     assert (*space_index >= block_index);
28280
28281     while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
28282     {
28283         (*space_index)--;
28284         if (*space_index < block_index)
28285         {
28286             return FALSE;
28287         }
28288     }
28289
28290     return TRUE;
28291 }
28292
28293 BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
28294 {
28295 #ifdef FEATURE_STRUCTALIGN
28296     // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
28297     return FALSE;
28298 #endif // FEATURE_STRUCTALIGN
28299     int space_index = count - 1;
28300     for (int block_index = (count - 1); block_index >= 0; block_index--)
28301     {
28302         if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
28303         {
28304             return FALSE;
28305         }
28306     }
28307
28308     return TRUE;
28309 }
28310
28311 void gc_heap::build_ordered_free_spaces (heap_segment* seg)
28312 {
28313     assert (bestfit_seg);
28314
28315     //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2, 
28316     //                    ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets), 
28317     //                    free_space_buckets, 
28318     //                    free_space_items);
28319
28320     bestfit_seg->add_buckets (MIN_INDEX_POWER2, 
28321                         ordered_free_space_indices, 
28322                         MAX_NUM_BUCKETS, 
28323                         free_space_items);
28324
28325     assert (settings.condemned_generation == max_generation);
28326
28327     uint8_t* first_address = heap_segment_mem (seg);
28328     uint8_t* end_address   = heap_segment_reserved (seg);
28329     //look through the pinned plugs for relevant ones.
28330     //Look for the right pinned plug to start from.
28331     reset_pinned_queue_bos();
28332     mark* m = 0;
28333     // See comment in can_expand_into_p why we need (max_generation + 1).
28334     size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
28335     BOOL has_fit_gen_starts = FALSE;
28336
28337     while (!pinned_plug_que_empty_p())
28338     {
28339         m = oldest_pin();
28340         if ((pinned_plug (m) >= first_address) && 
28341             (pinned_plug (m) < end_address) &&
28342             (pinned_len (m) >= eph_gen_starts))
28343         {
28344
28345             assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
28346             break;
28347         }
28348         else
28349         {
28350             deque_pinned_plug();
28351         }
28352     }
28353
28354     if (!pinned_plug_que_empty_p())
28355     {
28356         bestfit_seg->add ((void*)m, TRUE, TRUE);
28357         deque_pinned_plug();
28358         m = oldest_pin();
28359         has_fit_gen_starts = TRUE;
28360     }
28361
28362     while (!pinned_plug_que_empty_p() &&
28363             ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28364     {
28365         bestfit_seg->add ((void*)m, TRUE, FALSE);
28366         deque_pinned_plug();
28367         m = oldest_pin();
28368     }
28369
28370     if (commit_end_of_seg)
28371     {
28372         if (!has_fit_gen_starts)
28373         {
28374             assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
28375         }
28376         bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
28377     }
28378
28379 #ifdef _DEBUG
28380     bestfit_seg->check();
28381 #endif //_DEBUG
28382 }
28383
28384 BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
28385 {
28386     if (!end_of_segment_p)
28387     {
28388         trim_free_spaces_indices ();
28389     }
28390
28391     BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices, 
28392                                              ordered_free_space_indices, 
28393                                              MAX_NUM_BUCKETS);
28394
28395     return can_bestfit;
28396 }
28397
28398 BOOL gc_heap::best_fit (size_t free_space, 
28399                         size_t largest_free_space, 
28400                         size_t additional_space, 
28401                         BOOL* use_additional_space)
28402 {
28403     dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
28404
28405     assert (!additional_space || (additional_space && use_additional_space));
28406     if (use_additional_space)
28407     {
28408         *use_additional_space = FALSE;
28409     }
28410
28411     if (ordered_plug_indices_init == FALSE)
28412     {
28413         total_ephemeral_plugs = 0;
28414         build_ordered_plug_indices();
28415         ordered_plug_indices_init = TRUE;
28416     }
28417     else
28418     {
28419         memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
28420     }
28421
28422     if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
28423     {
28424         dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
28425         size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
28426         BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
28427         if (!can_fit_empty_eph)
28428         {
28429             can_fit_empty_eph = (additional_space >= empty_eph);
28430
28431             if (can_fit_empty_eph)
28432             {
28433                 *use_additional_space = TRUE;
28434             }
28435         }
28436
28437         return can_fit_empty_eph;
28438     }
28439
28440     if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
28441     {
28442         dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
28443         return FALSE;
28444     }
28445
28446     if ((free_space + additional_space) == 0)
28447     {
28448         dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
28449         return FALSE;
28450     }
28451
28452 #ifdef SEG_REUSE_STATS
28453     dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
28454     size_t total_free_space_power2 = 0;
28455     size_t total_free_space_items = 
28456         dump_buckets (ordered_free_space_indices, 
28457                       MAX_NUM_BUCKETS,
28458                       &total_free_space_power2);
28459     dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
28460
28461     dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
28462                 total_ephemeral_plugs, 
28463                 free_space, 
28464                 total_free_space_power2, 
28465                 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
28466                 additional_space));
28467
28468     size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
28469     memcpy (saved_all_free_space_indices, 
28470             ordered_free_space_indices, 
28471             sizeof(saved_all_free_space_indices));
28472
28473 #endif // SEG_REUSE_STATS
28474
28475     if (total_ephemeral_plugs > (free_space + additional_space))
28476     {
28477         return FALSE;
28478     }
28479
28480     use_bestfit = try_best_fit(FALSE);
28481
28482     if (!use_bestfit && additional_space)
28483     {
28484         int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
28485
28486         if (relative_free_space_index != -1)
28487         {
28488             int relative_plug_index = 0;
28489             size_t plugs_to_fit = 0;
28490
28491             for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
28492             {
28493                 plugs_to_fit = ordered_plug_indices[relative_plug_index];
28494                 if (plugs_to_fit != 0)
28495                 {
28496                     break;
28497                 }
28498             }
28499
28500             if ((relative_plug_index > relative_free_space_index) ||
28501                 ((relative_plug_index == relative_free_space_index) &&
28502                 (plugs_to_fit > 1)))
28503             {
28504 #ifdef SEG_REUSE_STATS
28505                 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
28506                             (relative_free_space_index + MIN_INDEX_POWER2),
28507                             plugs_to_fit,
28508                             (relative_plug_index + MIN_INDEX_POWER2)));
28509 #endif // SEG_REUSE_STATS
28510                 goto adjust;
28511             }
28512             
28513             dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
28514             ordered_free_space_indices[relative_free_space_index]++;
28515             use_bestfit = try_best_fit(TRUE);
28516             if (use_bestfit)
28517             {
28518                 free_space_items++;
28519                 // Since we might've trimmed away some of the free spaces we had, we should see
28520                 // if we really need to use end of seg space - if it's the same or smaller than
28521                 // the largest space we trimmed we can just add that one back instead of 
28522                 // using end of seg.
28523                 if (relative_free_space_index > trimmed_free_space_index)
28524                 {
28525                     *use_additional_space = TRUE;
28526                 }
28527                 else 
28528                 {
28529                     // If the addition space is <= than the last trimmed space, we
28530                     // should just use that last trimmed space instead.
28531                     saved_ordered_free_space_indices[trimmed_free_space_index]++;
28532                 }
28533             }
28534         }
28535     }
28536
28537 adjust:
28538
28539     if (!use_bestfit)
28540     {
28541         dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
28542
28543 #ifdef SEG_REUSE_STATS
28544         size_t saved_max = max_free_space_items;
28545         BOOL temp_bestfit = FALSE;
28546
28547         dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
28548         dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
28549
28550         // TODO: need to take the end of segment into consideration.
28551         while (max_free_space_items <= total_free_space_items)
28552         {
28553             max_free_space_items += max_free_space_items / 2;
28554             dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
28555             memcpy (ordered_free_space_indices, 
28556                     saved_all_free_space_indices,
28557                     sizeof(ordered_free_space_indices));
28558             if (try_best_fit(FALSE))
28559             {
28560                 temp_bestfit = TRUE;
28561                 break;
28562             }
28563         }
28564
28565         if (temp_bestfit)
28566         {
28567             dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
28568         }
28569         else
28570         {
28571             dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
28572         }
28573
28574         dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
28575         max_free_space_items = saved_max;
28576 #endif // SEG_REUSE_STATS
28577         if (free_space_items)
28578         {
28579             max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
28580             max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
28581         }
28582         else
28583         {
28584             max_free_space_items = MAX_NUM_FREE_SPACES;
28585         }
28586     }
28587
28588     dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
28589     dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
28590
28591     return use_bestfit;
28592 }
28593
28594 BOOL gc_heap::process_free_space (heap_segment* seg, 
28595                          size_t free_space,
28596                          size_t min_free_size, 
28597                          size_t min_cont_size,
28598                          size_t* total_free_space,
28599                          size_t* largest_free_space)
28600 {
28601     *total_free_space += free_space;
28602     *largest_free_space = max (*largest_free_space, free_space);
28603
28604 #ifdef SIMPLE_DPRINTF
28605     dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix", 
28606                 free_space, *total_free_space, *largest_free_space));
28607 #endif //SIMPLE_DPRINTF
28608
28609     if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
28610     {
28611 #ifdef SIMPLE_DPRINTF
28612         dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit", 
28613             settings.condemned_generation,
28614             *total_free_space, min_free_size, *largest_free_space, min_cont_size,
28615             (size_t)seg));
28616 #else
28617         UNREFERENCED_PARAMETER(seg);
28618 #endif //SIMPLE_DPRINTF
28619         return TRUE;
28620     }
28621
28622     int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
28623     if (free_space_index != -1)
28624     {
28625         ordered_free_space_indices[free_space_index]++;
28626     }
28627     return FALSE;
28628 }
28629
28630 BOOL gc_heap::expand_reused_seg_p()
28631 {
28632     BOOL reused_seg = FALSE;
28633     int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
28634     if ((heap_expand_mechanism == expand_reuse_bestfit) || 
28635         (heap_expand_mechanism == expand_reuse_normal))
28636     {
28637         reused_seg = TRUE;
28638     }
28639
28640     return reused_seg;
28641 }
28642
28643 BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
28644                                  allocator* gen_allocator)
28645 {
28646     min_cont_size += END_SPACE_AFTER_GC;
28647     use_bestfit = FALSE;
28648     commit_end_of_seg = FALSE;
28649     bestfit_first_pin = 0;
28650     uint8_t* first_address = heap_segment_mem (seg);
28651     uint8_t* end_address   = heap_segment_reserved (seg);
28652     size_t end_extra_space = end_space_after_gc();
28653
28654     if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
28655     {
28656         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
28657                                    first_address, end_address, end_extra_space));
28658         return FALSE;
28659     }
28660
28661     end_address -= end_extra_space;
28662
28663     dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix", 
28664         settings.condemned_generation, min_free_size, min_cont_size));
28665     size_t eph_gen_starts = eph_gen_starts_size;
28666
28667     if (settings.condemned_generation == max_generation)
28668     {
28669         size_t free_space = 0;
28670         size_t largest_free_space = free_space;
28671         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
28672         //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from. 
28673         //We are going to allocate the generation starts in the 1st free space,
28674         //so start from the first free space that's big enough for gen starts and a min object size.
28675         // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it - 
28676         // we could use it by allocating the last generation start a bit bigger but 
28677         // the complexity isn't worth the effort (those plugs are from gen2 
28678         // already anyway).
28679         reset_pinned_queue_bos();
28680         mark* m = 0;
28681         BOOL has_fit_gen_starts = FALSE;
28682
28683         init_ordered_free_space_indices ();
28684         while (!pinned_plug_que_empty_p())
28685         {
28686             m = oldest_pin();
28687             if ((pinned_plug (m) >= first_address) && 
28688                 (pinned_plug (m) < end_address) &&
28689                 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
28690             {
28691                 break;
28692             }
28693             else
28694             {
28695                 deque_pinned_plug();
28696             }
28697         }
28698
28699         if (!pinned_plug_que_empty_p())
28700         {
28701             bestfit_first_pin = pinned_plug (m) - pinned_len (m);
28702
28703             if (process_free_space (seg, 
28704                                     pinned_len (m) - eph_gen_starts, 
28705                                     min_free_size, min_cont_size, 
28706                                     &free_space, &largest_free_space))
28707             {
28708                 return TRUE;
28709             }
28710
28711             deque_pinned_plug();
28712             m = oldest_pin();
28713             has_fit_gen_starts = TRUE;
28714         }
28715
28716         dprintf (3, ("first pin is %Ix", pinned_plug (m)));
28717
28718         //tally up free space
28719         while (!pinned_plug_que_empty_p() &&
28720                ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28721         {
28722             dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
28723             if (process_free_space (seg, 
28724                                     pinned_len (m), 
28725                                     min_free_size, min_cont_size, 
28726                                     &free_space, &largest_free_space))
28727             {
28728                 return TRUE;
28729             }
28730
28731             deque_pinned_plug();
28732             m = oldest_pin();
28733         }
28734
28735         //try to find space at the end of the segment. 
28736         size_t end_space = (end_address - heap_segment_plan_allocated (seg)); 
28737         size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0); 
28738         dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
28739         if (end_space >= additional_space)
28740         {
28741             BOOL can_fit = TRUE;
28742             commit_end_of_seg = TRUE;
28743
28744             if (largest_free_space < min_cont_size)
28745             {
28746                 if (end_space >= min_cont_size)
28747                 {
28748                     additional_space = max (min_cont_size, additional_space);
28749                     dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph", 
28750                         seg));
28751                 }
28752                 else 
28753                 {
28754                     if (settings.concurrent)
28755                     {
28756                         can_fit = FALSE;
28757                         commit_end_of_seg = FALSE;
28758                     }
28759                     else
28760                     {
28761                         size_t additional_space_bestfit = additional_space;
28762                         if (!has_fit_gen_starts)
28763                         {
28764                             if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
28765                             {
28766                                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
28767                                         additional_space_bestfit));
28768                                 return FALSE;
28769                             }
28770
28771                             bestfit_first_pin = heap_segment_plan_allocated (seg);
28772                             additional_space_bestfit -= eph_gen_starts;
28773                         }
28774
28775                         can_fit = best_fit (free_space, 
28776                                             largest_free_space,
28777                                             additional_space_bestfit, 
28778                                             &commit_end_of_seg);
28779
28780                         if (can_fit)
28781                         {
28782                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg", 
28783                                 seg, (commit_end_of_seg ? "with" : "without")));
28784                         }
28785                         else
28786                         {
28787                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
28788                         }
28789                     }
28790                 }
28791             }
28792             else
28793             {
28794                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
28795             }
28796
28797             assert (additional_space <= end_space);
28798             if (commit_end_of_seg)
28799             {
28800                 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
28801                 {
28802                     dprintf (2, ("Couldn't commit end of segment?!"));
28803                     use_bestfit = FALSE;
28804  
28805                     return FALSE;
28806                 }
28807
28808                 if (use_bestfit)
28809                 {
28810                     // We increase the index here because growing heap segment could create a discrepency with 
28811                     // the additional space we used (could be bigger).
28812                     size_t free_space_end_of_seg = 
28813                         heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
28814                     int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
28815                     saved_ordered_free_space_indices[relative_free_space_index]++;
28816                 }
28817             }
28818         
28819             if (use_bestfit)
28820             {
28821                 memcpy (ordered_free_space_indices, 
28822                         saved_ordered_free_space_indices, 
28823                         sizeof(ordered_free_space_indices));
28824                 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
28825                 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
28826                 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
28827             }
28828
28829             return can_fit;
28830         }
28831
28832         dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
28833         return FALSE;
28834     }
28835     else
28836     {
28837         assert (settings.condemned_generation == (max_generation-1));
28838         size_t free_space = (end_address - heap_segment_plan_allocated (seg));
28839         size_t largest_free_space = free_space;
28840         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
28841         //find the first free list in range of the current segment
28842         size_t sz_list = gen_allocator->first_bucket_size();
28843         unsigned int a_l_idx = 0;
28844         uint8_t* free_list = 0;
28845         for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
28846         {
28847             if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
28848             {
28849                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
28850                 while (free_list)
28851                 {
28852                     if ((free_list >= first_address) && 
28853                         (free_list < end_address) && 
28854                         (unused_array_size (free_list) >= eph_gen_starts))
28855                     {
28856                         goto next;
28857                     }
28858                     else
28859                     {
28860                         free_list = free_list_slot (free_list);
28861                     }
28862                 }
28863             }
28864         }
28865 next:
28866         if (free_list)
28867         {
28868             init_ordered_free_space_indices ();
28869             if (process_free_space (seg, 
28870                                     unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size), 
28871                                     min_free_size, min_cont_size, 
28872                                     &free_space, &largest_free_space))
28873             {
28874                 return TRUE;
28875             }
28876
28877             free_list = free_list_slot (free_list);
28878         }
28879         else
28880         {
28881             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
28882             return FALSE;
28883         }
28884
28885        //tally up free space
28886
28887         while (1)
28888         {
28889             while (free_list)
28890             {
28891                 if ((free_list >= first_address) && (free_list < end_address) &&
28892                     process_free_space (seg, 
28893                                         unused_array_size (free_list), 
28894                                         min_free_size, min_cont_size, 
28895                                         &free_space, &largest_free_space))
28896                 {
28897                     return TRUE;
28898                 }
28899
28900                 free_list = free_list_slot (free_list);
28901             }
28902             a_l_idx++;
28903             if (a_l_idx < gen_allocator->number_of_buckets())
28904             {
28905                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
28906             }
28907             else
28908                 break;
28909         } 
28910
28911         dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
28912         return FALSE;
28913
28914         /*
28915         BOOL can_fit = best_fit (free_space, 0, NULL);
28916         if (can_fit)
28917         {
28918             dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
28919         }
28920         else
28921         {
28922             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
28923         }
28924
28925         return can_fit;
28926         */
28927     }
28928 }
28929
28930 void gc_heap::realloc_plug (size_t last_plug_size, uint8_t*& last_plug,
28931                             generation* gen, uint8_t* start_address,
28932                             unsigned int& active_new_gen_number,
28933                             uint8_t*& last_pinned_gap, BOOL& leftp,
28934                             BOOL shortened_p
28935 #ifdef SHORT_PLUGS
28936                             , mark* pinned_plug_entry
28937 #endif //SHORT_PLUGS
28938                             )
28939 {
28940     // detect generation boundaries
28941     // make sure that active_new_gen_number is not the youngest generation.
28942     // because the generation_limit wouldn't return the right thing in this case.
28943     if (!use_bestfit)
28944     {
28945         if ((active_new_gen_number > 1) &&
28946             (last_plug >= generation_limit (active_new_gen_number)))
28947         {
28948             assert (last_plug >= start_address);
28949             active_new_gen_number--;
28950             realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
28951             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
28952             leftp = FALSE;
28953         }
28954     }
28955
28956     // detect pinned plugs
28957     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
28958     {
28959         size_t  entry = deque_pinned_plug();
28960         mark*  m = pinned_plug_of (entry);
28961
28962         size_t saved_pinned_len = pinned_len(m);
28963         pinned_len(m) = last_plug - last_pinned_gap;
28964         //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
28965
28966         if (m->has_post_plug_info())
28967         {
28968             last_plug_size += sizeof (gap_reloc_pair);
28969             dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
28970         }
28971
28972         last_pinned_gap = last_plug + last_plug_size;
28973         dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
28974             pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
28975         leftp = FALSE;
28976
28977         //we are creating a generation fault. set the cards.
28978         {
28979             size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
28980             size_t card = card_of (last_plug);
28981             while (card != end_card)
28982             {
28983                 set_card (card);
28984                 card++;
28985             }
28986         }
28987     }
28988     else if (last_plug >= start_address)
28989     {
28990 #ifdef FEATURE_STRUCTALIGN
28991         int requiredAlignment;
28992         ptrdiff_t pad;
28993         node_aligninfo (last_plug, requiredAlignment, pad);
28994
28995         // from how we previously aligned the plug's destination address,
28996         // compute the actual alignment offset.
28997         uint8_t* reloc_plug = last_plug + node_relocation_distance (last_plug);
28998         ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
28999         if (!alignmentOffset)
29000         {
29001             // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
29002             alignmentOffset = requiredAlignment;
29003         }
29004
29005         //clear the alignment info because we are reallocating
29006         clear_node_aligninfo (last_plug);
29007 #else // FEATURE_STRUCTALIGN
29008         //clear the realignment flag because we are reallocating
29009         clear_node_realigned (last_plug);
29010 #endif // FEATURE_STRUCTALIGN
29011         BOOL adjacentp = FALSE;
29012         BOOL set_padding_on_saved_p = FALSE;
29013
29014         if (shortened_p)
29015         {
29016             last_plug_size += sizeof (gap_reloc_pair);
29017
29018 #ifdef SHORT_PLUGS
29019             assert (pinned_plug_entry != NULL);
29020             if (last_plug_size <= sizeof (plug_and_gap))
29021             {
29022                 set_padding_on_saved_p = TRUE;
29023             }
29024 #endif //SHORT_PLUGS
29025
29026             dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29027         }
29028
29029 #ifdef SHORT_PLUGS
29030         clear_padding_in_expand (last_plug, set_padding_on_saved_p, pinned_plug_entry);
29031 #endif //SHORT_PLUGS
29032
29033         uint8_t* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug,
29034 #ifdef SHORT_PLUGS
29035                                      set_padding_on_saved_p,
29036                                      pinned_plug_entry,
29037 #endif //SHORT_PLUGS
29038                                      TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
29039
29040         dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
29041         assert (new_address);
29042         set_node_relocation_distance (last_plug, new_address - last_plug);
29043 #ifdef FEATURE_STRUCTALIGN
29044         if (leftp && node_alignpad (last_plug) == 0)
29045 #else // FEATURE_STRUCTALIGN
29046         if (leftp && !node_realigned (last_plug))
29047 #endif // FEATURE_STRUCTALIGN
29048         {
29049             // TODO - temporarily disable L optimization because of a bug in it.
29050             //set_node_left (last_plug);
29051         }
29052         dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
29053         leftp = adjacentp;
29054     }
29055 }
29056
29057 void gc_heap::realloc_in_brick (uint8_t* tree, uint8_t*& last_plug,
29058                                 uint8_t* start_address,
29059                                 generation* gen,
29060                                 unsigned int& active_new_gen_number,
29061                                 uint8_t*& last_pinned_gap, BOOL& leftp)
29062 {
29063     assert (tree != NULL);
29064     int   left_node = node_left_child (tree);
29065     int   right_node = node_right_child (tree);
29066
29067     dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d", 
29068         tree, last_pinned_gap, last_plug, left_node, right_node));
29069
29070     if (left_node)
29071     {
29072         dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
29073         realloc_in_brick ((tree + left_node), last_plug, start_address,
29074                           gen, active_new_gen_number, last_pinned_gap,
29075                           leftp);
29076     }
29077
29078     if (last_plug != 0)
29079     {
29080         uint8_t*  plug = tree;
29081
29082         BOOL has_pre_plug_info_p = FALSE;
29083         BOOL has_post_plug_info_p = FALSE;
29084         mark* pinned_plug_entry = get_next_pinned_entry (tree, 
29085                                                          &has_pre_plug_info_p,
29086                                                          &has_post_plug_info_p, 
29087                                                          FALSE);
29088
29089         // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
29090         // The pinned plugs are handled in realloc_plug.
29091         size_t gap_size = node_gap_size (plug);
29092         uint8_t*   gap = (plug - gap_size);
29093         uint8_t*  last_plug_end = gap;
29094         size_t  last_plug_size = (last_plug_end - last_plug);
29095         // Cannot assert this - a plug could be less than that due to the shortened ones.
29096         //assert (last_plug_size >= Align (min_obj_size));
29097         dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
29098             plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
29099         realloc_plug (last_plug_size, last_plug, gen, start_address,
29100                       active_new_gen_number, last_pinned_gap,
29101                       leftp, has_pre_plug_info_p
29102 #ifdef SHORT_PLUGS
29103                       , pinned_plug_entry
29104 #endif //SHORT_PLUGS
29105                       );
29106     }
29107
29108     last_plug = tree;
29109
29110     if (right_node)
29111     {
29112         dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
29113         realloc_in_brick ((tree + right_node), last_plug, start_address,
29114                           gen, active_new_gen_number, last_pinned_gap,
29115                           leftp);
29116     }
29117 }
29118
29119 void
29120 gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
29121                         uint8_t* start_address, uint8_t* end_address,
29122                         unsigned active_new_gen_number)
29123 {
29124     dprintf (3, ("--- Reallocing ---"));
29125
29126     if (use_bestfit)
29127     {
29128         //make sure that every generation has a planned allocation start
29129         int  gen_number = max_generation - 1;
29130         while (gen_number >= 0)
29131         {
29132             generation* gen = generation_of (gen_number);
29133             if (0 == generation_plan_allocation_start (gen))
29134             {
29135                 generation_plan_allocation_start (gen) = 
29136                     bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
29137                 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
29138                 assert (generation_plan_allocation_start (gen));
29139             }
29140             gen_number--;
29141         }
29142     }
29143
29144     uint8_t* first_address = start_address;
29145     //Look for the right pinned plug to start from.
29146     reset_pinned_queue_bos();
29147     uint8_t* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
29148     while (!pinned_plug_que_empty_p())
29149     {
29150         mark* m = oldest_pin();
29151         if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
29152         {
29153             if (pinned_plug (m) < first_address)
29154             {
29155                 first_address = pinned_plug (m);
29156             }
29157             break;
29158         }
29159         else
29160             deque_pinned_plug();
29161     }
29162
29163     size_t  current_brick = brick_of (first_address);
29164     size_t  end_brick = brick_of (end_address-1);
29165     uint8_t*  last_plug = 0;
29166
29167     uint8_t* last_pinned_gap = heap_segment_plan_allocated (seg);
29168     BOOL leftp = FALSE;
29169
29170     dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
29171         start_address, first_address, pinned_plug (oldest_pin())));
29172
29173     while (current_brick <= end_brick)
29174     {
29175         int   brick_entry =  brick_table [ current_brick ];
29176         if (brick_entry >= 0)
29177         {
29178             realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
29179                               last_plug, start_address, consing_gen,
29180                               active_new_gen_number, last_pinned_gap,
29181                               leftp);
29182         }
29183         current_brick++;
29184     }
29185
29186     if (last_plug != 0)
29187     {
29188         realloc_plug (end_address - last_plug, last_plug, consing_gen,
29189                       start_address,
29190                       active_new_gen_number, last_pinned_gap,
29191                       leftp, FALSE
29192 #ifdef SHORT_PLUGS
29193                       , NULL
29194 #endif //SHORT_PLUGS
29195                       );
29196     }
29197
29198     //Fix the old segment allocated size
29199     assert (last_pinned_gap >= heap_segment_mem (seg));
29200     assert (last_pinned_gap <= heap_segment_committed (seg));
29201     heap_segment_plan_allocated (seg) = last_pinned_gap;
29202 }
29203
29204 void gc_heap::verify_no_pins (uint8_t* start, uint8_t* end)
29205 {
29206 #ifdef VERIFY_HEAP
29207     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
29208     {
29209         BOOL contains_pinned_plugs = FALSE;
29210         size_t mi = 0;
29211         mark* m = 0;
29212         while (mi != mark_stack_tos)
29213         {
29214             m = pinned_plug_of (mi);
29215             if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
29216             {
29217                 contains_pinned_plugs = TRUE;
29218                 break;
29219             }
29220             else
29221                 mi++;
29222         }
29223
29224         if (contains_pinned_plugs)
29225         {
29226             FATAL_GC_ERROR();
29227         }
29228     }
29229 #endif //VERIFY_HEAP
29230 }
29231
29232 void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
29233 {
29234     if (!should_expand_in_full_gc)
29235     {
29236         if ((condemned_gen_number != max_generation) && 
29237             (settings.pause_mode != pause_low_latency) &&
29238             (settings.pause_mode != pause_sustained_low_latency))
29239         {
29240             should_expand_in_full_gc = TRUE;
29241         }
29242     }
29243 }
29244
29245 void gc_heap::save_ephemeral_generation_starts()
29246 {
29247     for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
29248     {
29249         saved_ephemeral_plan_start[ephemeral_generation] = 
29250             generation_plan_allocation_start (generation_of (ephemeral_generation));
29251         saved_ephemeral_plan_start_size[ephemeral_generation] = 
29252             generation_plan_allocation_start_size (generation_of (ephemeral_generation));
29253     }
29254 }
29255
29256 generation* gc_heap::expand_heap (int condemned_generation,
29257                                   generation* consing_gen,
29258                                   heap_segment* new_heap_segment)
29259 {
29260     UNREFERENCED_PARAMETER(condemned_generation);
29261     assert (condemned_generation >= (max_generation -1));
29262     unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
29263     uint8_t*  start_address = generation_limit (max_generation);
29264     uint8_t*  end_address = heap_segment_allocated (ephemeral_heap_segment);
29265     BOOL should_promote_ephemeral = FALSE;
29266     ptrdiff_t eph_size = total_ephemeral_size;
29267 #ifdef BACKGROUND_GC
29268     dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
29269 #endif //BACKGROUND_GC
29270     settings.heap_expansion = TRUE;
29271
29272 #ifdef BACKGROUND_GC
29273     if (cm_in_progress)
29274     {
29275         if (!expanded_in_fgc)
29276         {
29277             expanded_in_fgc = TRUE;
29278         }
29279     }
29280 #endif //BACKGROUND_GC
29281
29282     //reset the elevation state for next time.
29283     dprintf (2, ("Elevation: elevation = el_none"));
29284     if (settings.should_lock_elevation && !expand_reused_seg_p())
29285         settings.should_lock_elevation = FALSE;
29286
29287     heap_segment* new_seg = new_heap_segment;
29288
29289     if (!new_seg)
29290         return consing_gen;
29291
29292     //copy the card and brick tables
29293     if (g_gc_card_table!= card_table)
29294         copy_brick_card_table();
29295
29296     BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
29297     dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
29298
29299     assert (generation_plan_allocation_start (generation_of (max_generation-1)));
29300     assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
29301             heap_segment_mem (ephemeral_heap_segment));
29302     assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
29303             heap_segment_committed (ephemeral_heap_segment));
29304
29305     assert (generation_plan_allocation_start (youngest_generation));
29306     assert (generation_plan_allocation_start (youngest_generation) <
29307             heap_segment_plan_allocated (ephemeral_heap_segment));
29308
29309     if (settings.pause_mode == pause_no_gc)
29310     {
29311         // We don't reuse for no gc, so the size used on the new eph seg is eph_size.
29312         if ((size_t)(heap_segment_reserved (new_seg) - heap_segment_mem (new_seg)) < (eph_size + soh_allocation_no_gc))
29313             should_promote_ephemeral = TRUE;
29314     }
29315     else
29316     {
29317         if (!use_bestfit)
29318         {
29319             should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
29320         }
29321     }
29322
29323     if (should_promote_ephemeral)
29324     {
29325         ephemeral_promotion = TRUE;
29326         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_new_seg_ep);
29327         dprintf (2, ("promoting ephemeral"));
29328         save_ephemeral_generation_starts();
29329     }
29330     else
29331     {
29332         // commit the new ephemeral segment all at once if it is a new one.
29333         if ((eph_size > 0) && new_segment_p)
29334         {
29335 #ifdef FEATURE_STRUCTALIGN
29336             // The destination may require a larger alignment padding than the source.
29337             // Assume the worst possible alignment padding.
29338             eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
29339 #endif // FEATURE_STRUCTALIGN
29340 #ifdef RESPECT_LARGE_ALIGNMENT
29341             //Since the generation start can be larger than min_obj_size
29342             //The alignment could be switched. 
29343             eph_size += switch_alignment_size(FALSE);
29344 #endif //RESPECT_LARGE_ALIGNMENT
29345             //Since the generation start can be larger than min_obj_size
29346             //Compare the alignment of the first object in gen1 
29347             if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
29348             {
29349                 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
29350                 return consing_gen;
29351             }
29352             heap_segment_used (new_seg) = heap_segment_committed (new_seg);
29353         }
29354
29355         //Fix the end of the old ephemeral heap segment
29356         heap_segment_plan_allocated (ephemeral_heap_segment) =
29357             generation_plan_allocation_start (generation_of (max_generation-1));
29358
29359         dprintf (3, ("Old ephemeral allocated set to %Ix",
29360                     (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
29361     }
29362
29363     if (new_segment_p)
29364     {
29365         // TODO - Is this really necessary? We should think about it.
29366         //initialize the first brick
29367         size_t first_brick = brick_of (heap_segment_mem (new_seg));
29368         set_brick (first_brick,
29369                 heap_segment_mem (new_seg) - brick_address (first_brick));
29370     }
29371
29372     //From this point on, we cannot run out of memory
29373
29374     //reset the allocation of the consing generation back to the end of the
29375     //old ephemeral segment
29376     generation_allocation_limit (consing_gen) =
29377         heap_segment_plan_allocated (ephemeral_heap_segment);
29378     generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
29379     generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
29380
29381     //clear the generation gap for all of the ephemeral generations
29382     {
29383         int generation_num = max_generation-1;
29384         while (generation_num >= 0)
29385         {
29386             generation* gen = generation_of (generation_num);
29387             generation_plan_allocation_start (gen) = 0;
29388             generation_num--;
29389         }
29390     }
29391
29392     heap_segment* old_seg = ephemeral_heap_segment;
29393     ephemeral_heap_segment = new_seg;
29394
29395     //Note: the ephemeral segment shouldn't be threaded onto the segment chain
29396     //because the relocation and compact phases shouldn't see it
29397
29398     // set the generation members used by allocate_in_expanded_heap
29399     // and switch to ephemeral generation
29400     consing_gen = ensure_ephemeral_heap_segment (consing_gen);
29401
29402     if (!should_promote_ephemeral)
29403     {
29404         realloc_plugs (consing_gen, old_seg, start_address, end_address,
29405                     active_new_gen_number);
29406     }
29407
29408     if (!use_bestfit)
29409     {
29410         repair_allocation_in_expanded_heap (consing_gen);
29411     }
29412
29413     // assert that the generation gap for all of the ephemeral generations were allocated.
29414 #ifdef _DEBUG
29415     {
29416         int generation_num = max_generation-1;
29417         while (generation_num >= 0)
29418         {
29419             generation* gen = generation_of (generation_num);
29420             assert (generation_plan_allocation_start (gen));
29421             generation_num--;
29422         }
29423     }
29424 #endif // _DEBUG
29425
29426     if (!new_segment_p)
29427     {
29428         dprintf (2, ("Demoting ephemeral segment"));
29429         //demote the entire segment.
29430         settings.demotion = TRUE;
29431         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
29432         demotion_low = heap_segment_mem (ephemeral_heap_segment);
29433         demotion_high = heap_segment_reserved (ephemeral_heap_segment);
29434     }
29435     else
29436     {
29437         demotion_low = MAX_PTR;
29438         demotion_high = 0;
29439 #ifndef MULTIPLE_HEAPS
29440         settings.demotion = FALSE;
29441         get_gc_data_per_heap()->clear_mechanism_bit (gc_demotion_bit);
29442 #endif //!MULTIPLE_HEAPS
29443     }
29444     ptrdiff_t eph_size1 = total_ephemeral_size;
29445     MAYBE_UNUSED_VAR(eph_size1);
29446
29447     if (!should_promote_ephemeral && new_segment_p)
29448     {
29449         assert (eph_size1 <= eph_size);
29450     }
29451
29452     if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
29453     {
29454         // This is to catch when we accidently delete a segment that has pins.
29455         verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
29456     }
29457
29458     verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
29459
29460     dprintf(2,("---- End of Heap Expansion ----"));
29461     return consing_gen;
29462 }
29463
29464 void gc_heap::set_static_data()
29465 {
29466     static_data* pause_mode_sdata = static_data_table[latency_level];
29467     for (int i = 0; i < NUMBERGENERATIONS; i++)
29468     {
29469         dynamic_data* dd = dynamic_data_of (i);
29470         static_data* sdata = &pause_mode_sdata[i];
29471
29472         dd->sdata = sdata;
29473         dd->min_size = sdata->min_size;
29474
29475         dprintf (GTC_LOG, ("PM: %d - min: %Id, max: %Id, fr_l: %Id, fr_b: %d%%",
29476             settings.pause_mode,
29477             dd->min_size, dd_max_size, 
29478             dd->fragmentation_limit, (int)(dd->fragmentation_burden_limit * 100)));
29479     }
29480 }
29481
29482 // Initialize the values that are not const.
29483 void gc_heap::init_static_data()
29484 {
29485     size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
29486     size_t gen0_min_size = Align(gen0size / 8 * 5);
29487
29488     size_t gen0_max_size =
29489 #ifdef MULTIPLE_HEAPS
29490         max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024));
29491 #else //MULTIPLE_HEAPS
29492         (gc_can_use_concurrent ?
29493             6*1024*1024 :
29494             max (6*1024*1024,  min ( Align(soh_segment_size/2), 200*1024*1024)));
29495 #endif //MULTIPLE_HEAPS
29496
29497     // TODO: gen0_max_size has a 200mb cap; gen1_max_size should also have a cap.
29498     size_t gen1_max_size = 
29499 #ifdef MULTIPLE_HEAPS
29500         max (6*1024*1024, Align(soh_segment_size/2));
29501 #else //MULTIPLE_HEAPS
29502         (gc_can_use_concurrent ?
29503             6*1024*1024 :
29504             max (6*1024*1024, Align(soh_segment_size/2)));
29505 #endif //MULTIPLE_HEAPS
29506
29507     dprintf (GTC_LOG, ("gen0size: %Id, gen0 min: %Id, max: %Id, gen1 max: %Id",
29508         gen0size, gen0_min_size, gen0_max_size, gen1_max_size));
29509
29510     for (int i = latency_level_first; i <= latency_level_last; i++)
29511     {
29512         static_data_table[i][0].min_size = gen0_min_size;
29513         static_data_table[i][0].max_size = gen0_max_size;
29514         static_data_table[i][1].max_size = gen1_max_size;
29515     }
29516 }
29517
29518 bool gc_heap::init_dynamic_data()
29519 {
29520     qpf = GCToOSInterface::QueryPerformanceFrequency();
29521
29522     uint32_t now = (uint32_t)GetHighPrecisionTimeStamp();
29523
29524     set_static_data();
29525
29526     for (int i = 0; i <= max_generation+1; i++)
29527     {
29528         dynamic_data* dd = dynamic_data_of (i);
29529         dd->gc_clock = 0;
29530         dd->time_clock = now;
29531         dd->current_size = 0;
29532         dd->promoted_size = 0;
29533         dd->collection_count = 0;
29534         dd->new_allocation = dd->min_size;
29535         dd->gc_new_allocation = dd->new_allocation;
29536         dd->desired_allocation = dd->new_allocation;
29537         dd->fragmentation = 0;
29538     }
29539
29540 #ifdef GC_CONFIG_DRIVEN
29541     if (heap_number == 0)
29542         time_init = now;
29543 #endif //GC_CONFIG_DRIVEN
29544
29545     return true;
29546 }
29547
29548 float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
29549 {
29550     if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
29551         return ((limit - limit*cst) / (1.0f - (cst * limit)));
29552     else
29553         return max_limit;
29554 }
29555
29556
29557 //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may 
29558 //not be correct (collection happened too soon). Correct with a linear estimation based on the previous 
29559 //value of the budget 
29560 static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation, 
29561                                        size_t previous_desired_allocation, size_t collection_count)
29562 {
29563     if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
29564     {
29565         dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0)));
29566         new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
29567     }
29568 #if 0 
29569     size_t smoothing = 3; // exponential smoothing factor
29570     if (smoothing  > collection_count)
29571         smoothing  = collection_count;
29572     new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
29573 #else
29574     UNREFERENCED_PARAMETER(collection_count);
29575 #endif //0
29576     return new_allocation;
29577 }
29578
29579 size_t gc_heap::desired_new_allocation (dynamic_data* dd,
29580                                         size_t out, int gen_number,
29581                                         int pass)
29582 {
29583     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29584
29585     if (dd_begin_data_size (dd) == 0)
29586     {
29587         size_t new_allocation = dd_min_size (dd);
29588         current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;        
29589         return new_allocation;
29590     }
29591     else
29592     {
29593         float     cst;
29594         size_t    previous_desired_allocation = dd_desired_allocation (dd);
29595         size_t    current_size = dd_current_size (dd);
29596         float     max_limit = dd_max_limit (dd);
29597         float     limit = dd_limit (dd);
29598         size_t    min_gc_size = dd_min_size (dd);
29599         float     f = 0;
29600         size_t    max_size = dd_max_size (dd);
29601         size_t    new_allocation = 0;
29602         float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
29603         if (gen_number >= max_generation)
29604         {
29605             size_t    new_size = 0;
29606
29607             cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
29608
29609             f = surv_to_growth (cst, limit, max_limit);
29610             size_t max_growth_size = (size_t)(max_size / f);
29611             if (current_size >= max_growth_size)
29612             {
29613                 new_size = max_size;
29614             }
29615             else
29616             {
29617                 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
29618             }
29619
29620             assert ((new_size >= current_size) || (new_size == max_size));
29621
29622             if (gen_number == max_generation)
29623             {
29624                 new_allocation  =  max((new_size - current_size), min_gc_size);
29625
29626                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
29627                                                           dd_desired_allocation (dd), dd_collection_count (dd));
29628
29629                 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
29630                 {
29631                     //reducing allocation in case of fragmentation
29632                     size_t new_allocation1 = max (min_gc_size,
29633                                                   // CAN OVERFLOW
29634                                                   (size_t)((float)new_allocation * current_size /
29635                                                            ((float)current_size + 2*dd_fragmentation (dd))));
29636                     dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
29637                                  new_allocation, new_allocation1));
29638                     new_allocation = new_allocation1;
29639                 }
29640             }
29641             else //large object heap
29642             {
29643                 uint32_t memory_load = 0;
29644                 uint64_t available_physical = 0;
29645                 get_memory_info (&memory_load, &available_physical);
29646                 if (heap_number == 0)
29647                     settings.exit_memory_load = memory_load;
29648                 if (available_physical > 1024*1024)
29649                     available_physical -= 1024*1024;
29650
29651                 uint64_t available_free = available_physical + (uint64_t)generation_free_list_space (generation_of (gen_number));
29652                 if (available_free > (uint64_t)MAX_PTR)
29653                 {
29654                     available_free = (uint64_t)MAX_PTR;
29655                 }
29656
29657                 //try to avoid OOM during large object allocation
29658                 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))), 
29659                                           (size_t)available_free), 
29660                                       max ((current_size/4), min_gc_size));
29661
29662                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29663                                                           dd_desired_allocation (dd), dd_collection_count (dd));
29664
29665             }
29666         }
29667         else
29668         {
29669             size_t survivors = out;
29670             cst = float (survivors) / float (dd_begin_data_size (dd));
29671             f = surv_to_growth (cst, limit, max_limit);
29672             new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
29673
29674             new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
29675                                                       dd_desired_allocation (dd), dd_collection_count (dd));
29676
29677             if (gen_number == 0)
29678             {
29679                 if (pass == 0)
29680                 {
29681
29682                     //printf ("%f, %Id\n", cst, new_allocation);
29683                     size_t free_space = generation_free_list_space (generation_of (gen_number));
29684                     // DTREVIEW - is min_gc_size really a good choice? 
29685                     // on 64-bit this will almost always be true.
29686                     dprintf (GTC_LOG, ("frag: %Id, min: %Id", free_space, min_gc_size));
29687                     if (free_space > min_gc_size)
29688                     {
29689                         settings.gen0_reduction_count = 2;
29690                     }
29691                     else
29692                     {
29693                         if (settings.gen0_reduction_count > 0)
29694                             settings.gen0_reduction_count--;
29695                     }
29696                 }
29697                 if (settings.gen0_reduction_count > 0)
29698                 {
29699                     dprintf (2, ("Reducing new allocation based on fragmentation"));
29700                     new_allocation = min (new_allocation,
29701                                           max (min_gc_size, (max_size/3)));
29702                 }
29703             }
29704         }
29705
29706         size_t new_allocation_ret = 
29707             Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
29708         int gen_data_index = gen_number;
29709         gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
29710         gen_data->new_allocation = new_allocation_ret;
29711
29712         dd_surv (dd) = cst;
29713
29714 #ifdef SIMPLE_DPRINTF
29715         dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
29716                      heap_number, gen_number, out, current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)),
29717                      (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
29718 #else
29719         dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
29720         dprintf (1,("current: %Id alloc: %Id ", current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd))));
29721         dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
29722                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
29723 #endif //SIMPLE_DPRINTF
29724
29725         return new_allocation_ret;
29726     }
29727 }
29728
29729 //returns the planned size of a generation (including free list element)
29730 size_t gc_heap::generation_plan_size (int gen_number)
29731 {
29732     if (0 == gen_number)
29733         return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
29734                     generation_plan_allocation_start (generation_of (gen_number))),
29735                    (int)Align (min_obj_size));
29736     else
29737     {
29738         generation* gen = generation_of (gen_number);
29739         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
29740             return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
29741                     generation_plan_allocation_start (generation_of (gen_number)));
29742         else
29743         {
29744             size_t gensize = 0;
29745             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
29746
29747             PREFIX_ASSUME(seg != NULL);
29748
29749             while (seg && (seg != ephemeral_heap_segment))
29750             {
29751                 gensize += heap_segment_plan_allocated (seg) -
29752                            heap_segment_mem (seg);
29753                 seg = heap_segment_next_rw (seg);
29754             }
29755             if (seg)
29756             {
29757                 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
29758                             heap_segment_mem (ephemeral_heap_segment));
29759             }
29760             return gensize;
29761         }
29762     }
29763
29764 }
29765
29766 //returns the size of a generation (including free list element)
29767 size_t gc_heap::generation_size (int gen_number)
29768 {
29769     if (0 == gen_number)
29770         return max((heap_segment_allocated (ephemeral_heap_segment) -
29771                     generation_allocation_start (generation_of (gen_number))),
29772                    (int)Align (min_obj_size));
29773     else
29774     {
29775         generation* gen = generation_of (gen_number);
29776         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
29777             return (generation_allocation_start (generation_of (gen_number - 1)) -
29778                     generation_allocation_start (generation_of (gen_number)));
29779         else
29780         {
29781             size_t gensize = 0;
29782             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
29783
29784             PREFIX_ASSUME(seg != NULL);
29785
29786             while (seg && (seg != ephemeral_heap_segment))
29787             {
29788                 gensize += heap_segment_allocated (seg) -
29789                            heap_segment_mem (seg);
29790                 seg = heap_segment_next_rw (seg);
29791             }
29792             if (seg)
29793             {
29794                 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
29795                             heap_segment_mem (ephemeral_heap_segment));
29796             }
29797
29798             return gensize;
29799         }
29800     }
29801
29802 }
29803
29804 size_t  gc_heap::compute_in (int gen_number)
29805 {
29806     assert (gen_number != 0);
29807     dynamic_data* dd = dynamic_data_of (gen_number);
29808
29809     size_t in = generation_allocation_size (generation_of (gen_number));
29810
29811     if (gen_number == max_generation && ephemeral_promotion)
29812     {
29813         in = 0;
29814         for (int i = 0; i <= max_generation; i++)
29815         {
29816             dynamic_data* dd = dynamic_data_of (i);
29817             in += dd_survived_size (dd);
29818             if (i != max_generation)
29819             {
29820                 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
29821             }
29822         }
29823     }
29824
29825     dd_gc_new_allocation (dd) -= in;
29826     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
29827
29828     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29829     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
29830     gen_data->in = in;
29831
29832     generation_allocation_size (generation_of (gen_number)) = 0;
29833     return in;
29834 }
29835
29836 void  gc_heap::compute_promoted_allocation (int gen_number)
29837 {
29838     compute_in (gen_number);
29839 }
29840
29841 #ifdef BIT64
29842 inline
29843 size_t gc_heap::trim_youngest_desired (uint32_t memory_load,
29844                                        size_t total_new_allocation,
29845                                        size_t total_min_allocation)
29846 {
29847     if (memory_load < MAX_ALLOWED_MEM_LOAD)
29848     {
29849         // If the total of memory load and gen0 budget exceeds 
29850         // our max memory load limit, trim the gen0 budget so the total 
29851         // is the max memory load limit.
29852         size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
29853         return min (total_new_allocation, remain_memory_load);
29854     }
29855     else
29856     {
29857         return max (mem_one_percent, total_min_allocation);
29858     }
29859 }
29860
29861 size_t gc_heap::joined_youngest_desired (size_t new_allocation)
29862 {
29863     dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
29864
29865     size_t final_new_allocation = new_allocation;
29866     if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
29867     {
29868         uint32_t num_heaps = 1;
29869
29870 #ifdef MULTIPLE_HEAPS
29871         num_heaps = gc_heap::n_heaps;
29872 #endif //MULTIPLE_HEAPS
29873
29874         size_t total_new_allocation = new_allocation * num_heaps;
29875         size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
29876
29877         if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
29878             (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
29879         {
29880             uint32_t memory_load = 0;
29881             get_memory_info (&memory_load);
29882             settings.exit_memory_load = memory_load;
29883             dprintf (2, ("Current emory load: %d", memory_load));
29884
29885             size_t final_total = 
29886                 trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation);
29887             size_t max_new_allocation = 
29888 #ifdef MULTIPLE_HEAPS
29889                                          dd_max_size (g_heaps[0]->dynamic_data_of (0));
29890 #else //MULTIPLE_HEAPS
29891                                          dd_max_size (dynamic_data_of (0));
29892 #endif //MULTIPLE_HEAPS
29893
29894             final_new_allocation  = min (Align ((final_total / num_heaps), get_alignment_constant (TRUE)), max_new_allocation);
29895         }
29896     }
29897
29898     if (final_new_allocation < new_allocation)
29899     {
29900         settings.gen0_reduction_count = 2;
29901     }
29902
29903     return final_new_allocation;
29904 }
29905 #endif // BIT64 
29906
29907 inline
29908 gc_history_per_heap* gc_heap::get_gc_data_per_heap()
29909 {
29910 #ifdef BACKGROUND_GC
29911     return (settings.concurrent ? &bgc_data_per_heap : &gc_data_per_heap);
29912 #else
29913     return &gc_data_per_heap;
29914 #endif //BACKGROUND_GC
29915 }
29916
29917 void gc_heap::compute_new_dynamic_data (int gen_number)
29918 {
29919     PREFIX_ASSUME(gen_number >= 0);
29920     PREFIX_ASSUME(gen_number <= max_generation);
29921
29922     dynamic_data* dd = dynamic_data_of (gen_number);
29923     generation*   gen = generation_of (gen_number);
29924     size_t        in = (gen_number==0) ? 0 : compute_in (gen_number);
29925
29926     size_t total_gen_size = generation_size (gen_number);
29927     //keep track of fragmentation
29928     dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
29929     dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
29930
29931     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29932
29933     size_t out = dd_survived_size (dd);
29934
29935     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
29936     gen_data->size_after = total_gen_size;
29937     gen_data->free_list_space_after = generation_free_list_space (gen);
29938     gen_data->free_obj_space_after = generation_free_obj_space (gen);
29939
29940     if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
29941     {
29942         // When we are in the low latency mode, we can still be
29943         // condemning more than gen1's 'cause of induced GCs.
29944         dd_desired_allocation (dd) = low_latency_alloc;
29945     }
29946     else
29947     {
29948         if (gen_number == 0)
29949         {
29950             //compensate for dead finalizable objects promotion.
29951             //they shoudn't be counted for growth.
29952             size_t final_promoted = 0;
29953             final_promoted = min (promoted_bytes (heap_number), out);
29954             // Prefast: this is clear from above but prefast needs to be told explicitly
29955             PREFIX_ASSUME(final_promoted <= out);
29956
29957             dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
29958             dd_freach_previous_promotion (dd) = final_promoted;
29959             size_t lower_bound = desired_new_allocation  (dd, out-final_promoted, gen_number, 0);
29960
29961             if (settings.condemned_generation == 0)
29962             {
29963                 //there is no noise.
29964                 dd_desired_allocation (dd) = lower_bound;
29965             }
29966             else
29967             {
29968                 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
29969
29970                 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
29971                 //assert ( lower_bound <= higher_bound);
29972
29973                 //discount the noise. Change the desired allocation
29974                 //only if the previous value is outside of the range.
29975                 if (dd_desired_allocation (dd) < lower_bound)
29976                 {
29977                     dd_desired_allocation (dd) = lower_bound;
29978                 }
29979                 else if (dd_desired_allocation (dd) > higher_bound)
29980                 {
29981                     dd_desired_allocation (dd) = higher_bound;
29982                 }
29983 #if defined (BIT64) && !defined (MULTIPLE_HEAPS)
29984                 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
29985 #endif // BIT64 && !MULTIPLE_HEAPS
29986                 trim_youngest_desired_low_memory();
29987                 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
29988             }
29989         }
29990         else
29991         {
29992             dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
29993         }
29994     }
29995
29996     gen_data->pinned_surv = dd_pinned_survived_size (dd);
29997     gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd);
29998
29999     dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
30000     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30001
30002     //update counter
30003     dd_promoted_size (dd) = out;
30004     if (gen_number == max_generation)
30005     {
30006         dd = dynamic_data_of (max_generation+1);
30007         total_gen_size = generation_size (max_generation + 1);
30008         dd_fragmentation (dd) = generation_free_list_space (large_object_generation) + 
30009                                 generation_free_obj_space (large_object_generation);
30010         dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
30011         dd_survived_size (dd) = dd_current_size (dd);
30012         in = 0;
30013         out = dd_current_size (dd);
30014         dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
30015         dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
30016                                            get_alignment_constant (FALSE));
30017         dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30018
30019         gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
30020         gen_data->size_after = total_gen_size;
30021         gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
30022         gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
30023         gen_data->npinned_surv = out;
30024 #ifdef BACKGROUND_GC
30025         end_loh_size = total_gen_size;
30026 #endif //BACKGROUND_GC
30027         //update counter
30028         dd_promoted_size (dd) = out;
30029     }
30030 }
30031
30032 void gc_heap::trim_youngest_desired_low_memory()
30033 {
30034     if (g_low_memory_status)
30035     {
30036         size_t committed_mem = 0;
30037         heap_segment* seg = generation_start_segment (generation_of (max_generation));
30038         while (seg)
30039         {
30040             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30041             seg = heap_segment_next (seg);
30042         }
30043         seg = generation_start_segment (generation_of (max_generation + 1));
30044         while (seg)
30045         {
30046             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30047             seg = heap_segment_next (seg);
30048         }
30049
30050         dynamic_data* dd = dynamic_data_of (0);
30051         size_t current = dd_desired_allocation (dd);
30052         size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_size (dd));
30053
30054         dd_desired_allocation (dd) = min (current, candidate);
30055     }
30056 }
30057
30058 void gc_heap::decommit_ephemeral_segment_pages()
30059 {
30060     if (settings.concurrent)
30061     {
30062         return;
30063     }
30064
30065     size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30066     dynamic_data* dd = dynamic_data_of (0);
30067
30068 #ifndef MULTIPLE_HEAPS
30069     size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
30070     size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
30071     size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
30072
30073     if (dd_desired_allocation (dd) > gc_gen0_desired_high)
30074     {
30075         gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space;
30076     }
30077
30078     if (ephemeral_elapsed >= decommit_timeout)
30079     {
30080         slack_space = min (slack_space, gc_gen0_desired_high);
30081
30082         gc_last_ephemeral_decommit_time = dd_time_clock(dd);
30083         gc_gen0_desired_high = 0;
30084     }
30085 #endif //!MULTIPLE_HEAPS
30086
30087     if (settings.condemned_generation >= (max_generation-1))
30088     {
30089         size_t new_slack_space = 
30090 #ifdef BIT64
30091                     max(min(min(soh_segment_size/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
30092 #else
30093 #ifdef FEATURE_CORECLR
30094                     dd_desired_allocation (dd);
30095 #else
30096                     dd_max_size (dd);
30097 #endif //FEATURE_CORECLR                                    
30098 #endif // BIT64
30099
30100         slack_space = min (slack_space, new_slack_space);
30101     }
30102
30103     decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);    
30104
30105     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30106     current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30107 }
30108
30109 size_t gc_heap::new_allocation_limit (size_t size, size_t free_size, int gen_number)
30110 {
30111     dynamic_data* dd        = dynamic_data_of (gen_number);
30112     ptrdiff_t           new_alloc = dd_new_allocation (dd);
30113     assert (new_alloc == (ptrdiff_t)Align (new_alloc,
30114                                            get_alignment_constant (!(gen_number == (max_generation+1)))));
30115     size_t        limit     = min (max (new_alloc, (ptrdiff_t)size), (ptrdiff_t)free_size);
30116     assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
30117     dd_new_allocation (dd) = (new_alloc - limit );
30118     return limit;
30119 }
30120
30121 //This is meant to be called by decide_on_compacting.
30122
30123 size_t gc_heap::generation_fragmentation (generation* gen,
30124                                           generation* consing_gen,
30125                                           uint8_t* end)
30126 {
30127     size_t frag;
30128     uint8_t* alloc = generation_allocation_pointer (consing_gen);
30129     // If the allocation pointer has reached the ephemeral segment
30130     // fine, otherwise the whole ephemeral segment is considered
30131     // fragmentation
30132     if (in_range_for_segment (alloc, ephemeral_heap_segment))
30133         {
30134             if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
30135                 frag = end - alloc;
30136             else
30137             {
30138                 // case when no survivors, allocated set to beginning
30139                 frag = 0;
30140             }
30141             dprintf (3, ("ephemeral frag: %Id", frag));
30142         }
30143     else
30144         frag = (heap_segment_allocated (ephemeral_heap_segment) -
30145                 heap_segment_mem (ephemeral_heap_segment));
30146     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30147
30148     PREFIX_ASSUME(seg != NULL);
30149
30150     while (seg != ephemeral_heap_segment)
30151     {
30152         frag += (heap_segment_allocated (seg) -
30153                  heap_segment_plan_allocated (seg));
30154         dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
30155                      (heap_segment_allocated (seg) -
30156                       heap_segment_plan_allocated (seg))));
30157
30158         seg = heap_segment_next_rw (seg);
30159         assert (seg);
30160     }
30161     dprintf (3, ("frag: %Id discounting pinned plugs", frag));
30162     //add the length of the dequeued plug free space
30163     size_t bos = 0;
30164     while (bos < mark_stack_bos)
30165     {
30166         frag += (pinned_len (pinned_plug_of (bos)));
30167         bos++;
30168     }
30169
30170     return frag;
30171 }
30172
30173 // for SOH this returns the total sizes of the generation and its 
30174 // younger generation(s).
30175 // for LOH this returns just LOH size.
30176 size_t gc_heap::generation_sizes (generation* gen)
30177 {
30178     size_t result = 0;
30179     if (generation_start_segment (gen ) == ephemeral_heap_segment)
30180         result = (heap_segment_allocated (ephemeral_heap_segment) -
30181                   generation_allocation_start (gen));
30182     else
30183     {
30184         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
30185
30186         PREFIX_ASSUME(seg != NULL);
30187
30188         while (seg)
30189         {
30190             result += (heap_segment_allocated (seg) -
30191                        heap_segment_mem (seg));
30192             seg = heap_segment_next_in_range (seg);
30193         }
30194     }
30195
30196     return result;
30197 }
30198
30199 BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
30200                                     size_t fragmentation,
30201                                     BOOL& should_expand)
30202 {
30203     BOOL should_compact = FALSE;
30204     should_expand = FALSE;
30205     generation*   gen = generation_of (condemned_gen_number);
30206     dynamic_data* dd = dynamic_data_of (condemned_gen_number);
30207     size_t gen_sizes     = generation_sizes(gen);
30208     float  fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
30209                                     (float (fragmentation) / gen_sizes) );
30210
30211     dprintf (GTC_LOG, ("fragmentation: %Id (%d%%)", fragmentation, (int)(fragmentation_burden * 100.0)));
30212
30213 #ifdef STRESS_HEAP
30214     // for pure GC stress runs we need compaction, for GC stress "mix"
30215     // we need to ensure a better mix of compacting and sweeping collections
30216     if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
30217         && !g_pConfig->IsGCStressMix())
30218         should_compact = TRUE;
30219
30220 #ifdef GC_STATS
30221     // in GC stress "mix" mode, for stress induced collections make sure we 
30222     // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
30223     // against the GC's determination, as it may lead to premature OOMs.
30224     if (g_pConfig->IsGCStressMix() && settings.stress_induced)
30225     {
30226         int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
30227         int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
30228         if (compactions < sweeps / 10)
30229         {
30230             should_compact = TRUE;
30231         }
30232     }
30233 #endif // GC_STATS
30234 #endif //STRESS_HEAP
30235
30236     if (GCConfig::GetForceCompact())
30237         should_compact = TRUE;
30238
30239     if ((condemned_gen_number == max_generation) && last_gc_before_oom)
30240     {
30241         should_compact = TRUE;
30242         last_gc_before_oom = FALSE;
30243         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc);
30244     }
30245
30246     if (settings.reason == reason_induced_compacting)
30247     {
30248         dprintf (2, ("induced compacting GC"));
30249         should_compact = TRUE;
30250         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
30251     }
30252
30253     dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
30254                 fragmentation, (int) (100*fragmentation_burden)));
30255
30256     if (!should_compact)
30257     {
30258         if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
30259         {
30260             dprintf(GTC_LOG, ("compacting due to low ephemeral"));
30261             should_compact = TRUE;
30262             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral);
30263         }
30264     }
30265
30266     if (should_compact)
30267     {
30268         if ((condemned_gen_number >= (max_generation - 1)))
30269         {
30270             if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
30271             {
30272                 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
30273                 should_expand = TRUE;
30274             }
30275         }
30276     }
30277
30278 #ifdef BIT64
30279     BOOL high_memory = FALSE;
30280 #endif // BIT64
30281
30282     if (!should_compact)
30283     {
30284         // We are not putting this in dt_high_frag_p because it's not exactly
30285         // high fragmentation - it's just enough planned fragmentation for us to 
30286         // want to compact. Also the "fragmentation" we are talking about here
30287         // is different from anywhere else.
30288         BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
30289                                 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
30290
30291         if (frag_exceeded)
30292         {
30293 #ifdef BACKGROUND_GC
30294             // do not force compaction if this was a stress-induced GC
30295             IN_STRESS_HEAP(if (!settings.stress_induced))
30296             {
30297 #endif // BACKGROUND_GC
30298             assert (settings.concurrent == FALSE);
30299             should_compact = TRUE;
30300             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag);
30301 #ifdef BACKGROUND_GC
30302             }
30303 #endif // BACKGROUND_GC
30304         }
30305
30306 #ifdef BIT64
30307         // check for high memory situation
30308         if(!should_compact)
30309         {
30310             uint32_t num_heaps = 1;
30311 #ifdef MULTIPLE_HEAPS
30312             num_heaps = gc_heap::n_heaps;
30313 #endif // MULTIPLE_HEAPS
30314             
30315             ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
30316             if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th))
30317             {
30318                 if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps)))
30319                 {
30320                     dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
30321                     should_compact = TRUE;
30322                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag);
30323                 }
30324                 high_memory = TRUE;
30325             }
30326             else if(settings.entry_memory_load >= v_high_memory_load_th)
30327             {
30328                 if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps)))
30329                 {
30330                     dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
30331                     should_compact = TRUE;
30332                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag);
30333                 }
30334                 high_memory = TRUE;
30335             }
30336         }
30337 #endif // BIT64
30338     }
30339
30340     // The purpose of calling ensure_gap_allocation here is to make sure
30341     // that we actually are able to commit the memory to allocate generation
30342     // starts.
30343     if ((should_compact == FALSE) &&
30344         (ensure_gap_allocation (condemned_gen_number) == FALSE))
30345     {
30346         should_compact = TRUE;
30347         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps);
30348     }
30349
30350     if (settings.condemned_generation == max_generation)
30351     {
30352         //check the progress
30353         if (
30354 #ifdef BIT64
30355             (high_memory && !should_compact) ||
30356 #endif // BIT64
30357             (generation_plan_allocation_start (generation_of (max_generation - 1)) >= 
30358                 generation_allocation_start (generation_of (max_generation - 1))))
30359         {
30360             dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked",
30361                      generation_size (max_generation),
30362                      generation_plan_size (max_generation)));
30363             //no progress -> lock
30364             settings.should_lock_elevation = TRUE;
30365         }
30366     }
30367
30368     if (settings.pause_mode == pause_no_gc)
30369     {
30370         should_compact = TRUE;
30371         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment))
30372             < soh_allocation_no_gc)
30373         {
30374             should_expand = TRUE;
30375         }
30376     }
30377
30378     dprintf (2, ("will %s", (should_compact ? "compact" : "sweep")));
30379     return should_compact;
30380 }
30381
30382 size_t align_lower_good_size_allocation (size_t size)
30383 {
30384     return (size/64)*64;
30385 }
30386
30387 size_t gc_heap::approximate_new_allocation()
30388 {
30389     dynamic_data* dd0 = dynamic_data_of (0);
30390     return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
30391 }
30392
30393 // After we did a GC we expect to have at least this 
30394 // much space at the end of the segment to satisfy
30395 // a reasonable amount of allocation requests.
30396 size_t gc_heap::end_space_after_gc()
30397 {
30398     return max ((dd_min_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
30399 }
30400
30401 BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
30402 {
30403     uint8_t* start = 0;
30404     
30405     if ((tp == tuning_deciding_condemned_gen) ||
30406         (tp == tuning_deciding_compaction))
30407     {
30408         start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
30409         if (settings.concurrent)
30410         {
30411             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)", 
30412                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30413         }
30414         else
30415         {
30416             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)", 
30417                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
30418         }
30419     }
30420     else if (tp == tuning_deciding_expansion)
30421     {
30422         start = heap_segment_plan_allocated (ephemeral_heap_segment);
30423         dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan", 
30424             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
30425     }
30426     else
30427     {
30428         assert (tp == tuning_deciding_full_gc);
30429         dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)", 
30430             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30431         start = alloc_allocated;
30432     }
30433     
30434     if (start == 0) // empty ephemeral generations
30435     {
30436         assert (tp == tuning_deciding_expansion);
30437         // if there are no survivors in the ephemeral segment, 
30438         // this should be the beginning of ephemeral segment.
30439         start = generation_allocation_pointer (generation_of (max_generation));
30440         assert (start == heap_segment_mem (ephemeral_heap_segment));
30441     }
30442
30443     if (tp == tuning_deciding_expansion)
30444     {
30445         assert (settings.condemned_generation >= (max_generation-1));
30446         size_t gen0size = approximate_new_allocation();
30447         size_t eph_size = gen0size;
30448
30449         for (int j = 1; j <= max_generation-1; j++)
30450         {
30451             eph_size += 2*dd_min_size (dynamic_data_of(j));
30452         }
30453         
30454         // We must find room for one large object and enough room for gen0size
30455         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
30456         {
30457             dprintf (3, ("Enough room before end of segment"));
30458             return TRUE;
30459         }
30460         else
30461         {
30462             size_t room = align_lower_good_size_allocation
30463                 (heap_segment_reserved (ephemeral_heap_segment) - start);
30464             size_t end_seg = room;
30465
30466             //look at the plug free space
30467             size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
30468             bool large_chunk_found = FALSE;
30469             size_t bos = 0;
30470             uint8_t* gen0start = generation_plan_allocation_start (youngest_generation);
30471             dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
30472             if (gen0start == 0)
30473                 return FALSE;
30474             dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
30475                          room, gen0size));
30476             while ((bos < mark_stack_bos) &&
30477                    !((room >= gen0size) && large_chunk_found))
30478             {
30479                 uint8_t* plug = pinned_plug (pinned_plug_of (bos));
30480                 if (in_range_for_segment (plug, ephemeral_heap_segment))
30481                 {
30482                     if (plug >= gen0start)
30483                     {
30484                         size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
30485                         room += chunk;
30486                         if (!large_chunk_found)
30487                         {
30488                             large_chunk_found = (chunk >= largest_alloc);
30489                         }
30490                         dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
30491                                      room, large_chunk_found));
30492                     }
30493                 }
30494                 bos++;
30495             }
30496
30497             if (room >= gen0size)
30498             {
30499                 if (large_chunk_found)
30500                 {
30501                     dprintf (3, ("Enough room"));
30502                     return TRUE;
30503                 }
30504                 else
30505                 {
30506                     // now we need to find largest_alloc at the end of the segment.
30507                     if (end_seg >= end_space_after_gc())
30508                     {
30509                         dprintf (3, ("Enough room (may need end of seg)"));
30510                         return TRUE;
30511                     }
30512                 }
30513             }
30514
30515             dprintf (3, ("Not enough room"));
30516                 return FALSE;
30517         }
30518     }
30519     else
30520     {
30521         size_t end_space = 0;
30522         dynamic_data* dd = dynamic_data_of (0);
30523         if ((tp == tuning_deciding_condemned_gen) ||
30524             (tp == tuning_deciding_full_gc))
30525         {
30526             end_space = 2*dd_min_size (dd);
30527         }
30528         else
30529         {
30530             assert (tp == tuning_deciding_compaction);
30531             end_space = approximate_new_allocation();
30532         }
30533
30534         if (!((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space))
30535         {
30536             dprintf (GTC_LOG, ("ephemeral_gen_fit_p: does not fit without compaction"));
30537         }
30538         return ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space);
30539     }
30540 }
30541
30542 CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_bytes)
30543 {
30544     //create a new alloc context because gen3context is shared.
30545     alloc_context acontext;
30546     acontext.alloc_ptr = 0;
30547     acontext.alloc_limit = 0;
30548     acontext.alloc_bytes = 0;
30549 #ifdef MULTIPLE_HEAPS
30550     acontext.set_alloc_heap(vm_heap);
30551 #endif //MULTIPLE_HEAPS
30552
30553 #ifdef MARK_ARRAY
30554     uint8_t* current_lowest_address = lowest_address;
30555     uint8_t* current_highest_address = highest_address;
30556 #ifdef BACKGROUND_GC
30557     if (recursive_gc_sync::background_running_p())
30558     {
30559         current_lowest_address = background_saved_lowest_address;
30560         current_highest_address = background_saved_highest_address;
30561     }
30562 #endif //BACKGROUND_GC
30563 #endif // MARK_ARRAY
30564
30565     #if BIT64
30566     size_t maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
30567     #else
30568     size_t maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
30569     #endif
30570
30571     if (jsize >= maxObjectSize)
30572     {
30573         if (GCConfig::GetBreakOnOOM())
30574         {
30575             GCToOSInterface::DebugBreak();
30576         }
30577         return NULL;
30578     }
30579
30580     size_t size = AlignQword (jsize);
30581     int align_const = get_alignment_constant (FALSE);
30582 #ifdef FEATURE_LOH_COMPACTION
30583     size_t pad = Align (loh_padding_obj_size, align_const);
30584 #else
30585     size_t pad = 0;
30586 #endif //FEATURE_LOH_COMPACTION
30587
30588     assert (size >= Align (min_obj_size, align_const));
30589 #ifdef _MSC_VER
30590 #pragma inline_depth(0)
30591 #endif //_MSC_VER
30592     if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
30593     {
30594         return 0;
30595     }
30596
30597 #ifdef _MSC_VER
30598 #pragma inline_depth(20)
30599 #endif //_MSC_VER
30600
30601 #ifdef FEATURE_LOH_COMPACTION
30602     // The GC allocator made a free object already in this alloc context and
30603     // adjusted the alloc_ptr accordingly.
30604 #endif //FEATURE_LOH_COMPACTION
30605
30606     uint8_t*  result = acontext.alloc_ptr;
30607
30608     assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
30609     alloc_bytes += size;
30610
30611     CObjectHeader* obj = (CObjectHeader*)result;
30612
30613 #ifdef MARK_ARRAY
30614     if (recursive_gc_sync::background_running_p())
30615     {
30616         if ((result < current_highest_address) && (result >= current_lowest_address))
30617         {
30618             dprintf (3, ("Clearing mark bit at address %Ix",
30619                      (size_t)(&mark_array [mark_word_of (result)])));
30620
30621             mark_array_clear_marked (result);
30622         }
30623 #ifdef BACKGROUND_GC
30624         //the object has to cover one full mark uint32_t
30625         assert (size > mark_word_size);
30626         if (current_c_gc_state == c_gc_state_marking)
30627         {
30628             dprintf (3, ("Concurrent allocation of a large object %Ix",
30629                         (size_t)obj));
30630             //mark the new block specially so we know it is a new object
30631             if ((result < current_highest_address) && (result >= current_lowest_address))
30632             {
30633                 dprintf (3, ("Setting mark bit at address %Ix",
30634                             (size_t)(&mark_array [mark_word_of (result)])));
30635     
30636                 mark_array_set_marked (result);
30637             }
30638         }
30639 #endif //BACKGROUND_GC
30640     }
30641 #endif //MARK_ARRAY
30642
30643     assert (obj != 0);
30644     assert ((size_t)obj == Align ((size_t)obj, align_const));
30645
30646     return obj;
30647 }
30648
30649 void reset_memory (uint8_t* o, size_t sizeo)
30650 {
30651     if (sizeo > 128 * 1024)
30652     {
30653         // We cannot reset the memory for the useful part of a free object.
30654         size_t size_to_skip = min_free_list - plug_skew;
30655
30656         size_t page_start = align_on_page ((size_t)(o + size_to_skip));
30657         size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
30658         // Note we need to compensate for an OS bug here. This bug would cause the MEM_RESET to fail
30659         // on write watched memory.
30660         if (reset_mm_p)
30661         {
30662 #ifdef MULTIPLE_HEAPS
30663             bool unlock_p = true;
30664 #else
30665             // We don't do unlock because there could be many processes using workstation GC and it's
30666             // bad perf to have many threads doing unlock at the same time.
30667             bool unlock_p = false;
30668 #endif // MULTIPLE_HEAPS
30669
30670             reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, unlock_p);
30671         }
30672     }
30673 }
30674
30675 void gc_heap::reset_large_object (uint8_t* o)
30676 {
30677     // If it's a large object, allow the O/S to discard the backing store for these pages.
30678     reset_memory (o, size(o));
30679 }
30680
30681 BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp)
30682 {
30683     BOOL m = FALSE;
30684     // It shouldn't be necessary to do these comparisons because this is only used for blocking
30685     // GCs and LOH segments cannot be out of range.
30686     if ((o >= lowest_address) && (o < highest_address))
30687     {
30688         if (marked (o))
30689         {
30690             if (clearp)
30691             {
30692                 clear_marked (o);
30693                 if (pinned (o))
30694                     clear_pinned(o);
30695             }
30696             m = TRUE;
30697         }
30698         else
30699             m = FALSE;
30700     }
30701     else
30702         m = TRUE;
30703     return m;
30704 }
30705
30706 void gc_heap::walk_survivors_relocation (void* profiling_context, record_surv_fn fn)
30707 {
30708     // Now walk the portion of memory that is actually being relocated.
30709     walk_relocation (profiling_context, fn);
30710
30711 #ifdef FEATURE_LOH_COMPACTION
30712     if (loh_compacted_p)
30713     {
30714         walk_relocation_for_loh (profiling_context, fn);
30715     }
30716 #endif //FEATURE_LOH_COMPACTION
30717 }
30718
30719 void gc_heap::walk_survivors_for_loh (void* profiling_context, record_surv_fn fn)
30720 {
30721     generation* gen        = large_object_generation;
30722     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));;
30723
30724     PREFIX_ASSUME(seg != NULL);
30725
30726     uint8_t* o                = generation_allocation_start (gen);
30727     uint8_t* plug_end         = o;
30728     uint8_t* plug_start       = o;
30729
30730     while (1)
30731     {
30732         if (o >= heap_segment_allocated (seg))
30733         {
30734             seg = heap_segment_next (seg);
30735             if (seg == 0)
30736                 break;
30737             else
30738                 o = heap_segment_mem (seg);
30739         }
30740         if (large_object_marked(o, FALSE))
30741         {
30742             plug_start = o;
30743
30744             BOOL m = TRUE;
30745             while (m)
30746             {
30747                 o = o + AlignQword (size (o));
30748                 if (o >= heap_segment_allocated (seg))
30749                 {
30750                     break;
30751                 }
30752                 m = large_object_marked (o, FALSE);
30753             }
30754
30755             plug_end = o;
30756
30757             fn (plug_start, plug_end, 0, profiling_context, false, false);
30758         }
30759         else
30760         {
30761             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
30762             {
30763                 o = o + AlignQword (size (o));
30764             }
30765         }
30766     }
30767 }
30768
30769 #ifdef BACKGROUND_GC
30770
30771 BOOL gc_heap::background_object_marked (uint8_t* o, BOOL clearp)
30772 {
30773     BOOL m = FALSE;
30774     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
30775     {
30776         if (mark_array_marked (o))
30777         {
30778             if (clearp)
30779             {
30780                 mark_array_clear_marked (o);
30781                 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
30782                 dprintf (3, ("CM: %Ix", o));
30783             }
30784             m = TRUE;
30785         }
30786         else
30787             m = FALSE;
30788     }
30789     else
30790         m = TRUE;
30791
30792     dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
30793     return m;
30794 }
30795
30796 uint8_t* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
30797 {
30798     return
30799         (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
30800 }
30801
30802 void gc_heap::set_mem_verify (uint8_t* start, uint8_t* end, uint8_t b)
30803 {
30804 #ifdef VERIFY_HEAP
30805     if (end > start)
30806     {
30807         if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
30808            !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_MEM_FILL))
30809         {
30810             dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
30811             memset (start, b, (end - start));
30812         }
30813     }
30814 #endif //VERIFY_HEAP
30815 }
30816
30817 void gc_heap::generation_delete_heap_segment (generation* gen, 
30818                                               heap_segment* seg,
30819                                               heap_segment* prev_seg,
30820                                               heap_segment* next_seg)
30821 {
30822     dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
30823     if (gen == large_object_generation)
30824     {
30825         heap_segment_next (prev_seg) = next_seg;
30826
30827         dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
30828
30829         heap_segment_next (seg) = freeable_large_heap_segment;
30830         freeable_large_heap_segment = seg;
30831     }
30832     else
30833     {
30834         if (seg == ephemeral_heap_segment)
30835         {
30836             FATAL_GC_ERROR();
30837         }
30838
30839         heap_segment_next (next_seg) = prev_seg;
30840
30841         dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
30842         heap_segment_next (seg) = freeable_small_heap_segment;
30843         freeable_small_heap_segment = seg;
30844     }
30845
30846     decommit_heap_segment (seg);
30847     seg->flags |= heap_segment_flags_decommitted;
30848
30849     set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
30850 }
30851
30852 void gc_heap::process_background_segment_end (heap_segment* seg, 
30853                                           generation* gen,
30854                                           uint8_t* last_plug_end,
30855                                           heap_segment* start_seg,
30856                                           BOOL* delete_p)
30857 {
30858     *delete_p = FALSE;
30859     uint8_t* allocated = heap_segment_allocated (seg);
30860     uint8_t* background_allocated = heap_segment_background_allocated (seg);
30861
30862     dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)", 
30863                 (size_t)heap_segment_mem (seg), background_allocated, allocated));
30864
30865
30866     if (allocated != background_allocated)
30867     {
30868         if (gen == large_object_generation)
30869         {
30870             FATAL_GC_ERROR();
30871         }
30872
30873         dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[", 
30874                     (size_t)last_plug_end, background_allocated));
30875         thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
30876
30877         fix_brick_to_highest (last_plug_end, background_allocated);
30878
30879         // When we allowed fgc's during going through gaps, we could have erased the brick
30880         // that corresponds to bgc_allocated 'cause we had to update the brick there, 
30881         // recover it here.
30882         fix_brick_to_highest (background_allocated, background_allocated);
30883     }
30884     else
30885     {
30886         // by default, if allocated == background_allocated, it can't
30887         // be the ephemeral segment.
30888         if (seg == ephemeral_heap_segment)
30889         {
30890             FATAL_GC_ERROR();
30891         }
30892
30893         if (allocated == heap_segment_mem (seg))
30894         {
30895             // this can happen with LOH segments when multiple threads
30896             // allocate new segments and not all of them were needed to
30897             // satisfy allocation requests.
30898             assert (gen == large_object_generation);
30899         }
30900
30901         if (last_plug_end == heap_segment_mem (seg))
30902         {
30903             dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
30904                         (size_t)allocated, (*delete_p ? "should" : "should not")));
30905
30906             if (seg != start_seg)
30907             {
30908                 *delete_p = TRUE;
30909             }
30910         }
30911         else
30912         {
30913             dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
30914             heap_segment_allocated (seg) = last_plug_end;
30915             set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
30916
30917             decommit_heap_segment_pages (seg, 0);
30918         }
30919     }
30920
30921     dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
30922     bgc_verify_mark_array_cleared (seg);
30923 }
30924
30925 void gc_heap::process_n_background_segments (heap_segment* seg, 
30926                                              heap_segment* prev_seg,
30927                                              generation* gen)
30928 {
30929     assert (gen != large_object_generation);
30930
30931     while (seg)
30932     {
30933         dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
30934         heap_segment* next_seg = heap_segment_next (seg);
30935
30936         if (heap_segment_read_only_p (seg))
30937         {
30938             prev_seg = seg;
30939         }
30940         else
30941         {
30942             if (heap_segment_allocated (seg) == heap_segment_mem (seg))
30943             {
30944                 // This can happen - if we have a LOH segment where nothing survived
30945                 // or a SOH segment allocated by a gen1 GC when BGC was going where 
30946                 // nothing survived last time we did a gen1 GC.
30947                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
30948             }
30949             else
30950             {
30951                 prev_seg = seg;
30952             }
30953         }
30954
30955         verify_soh_segment_list();
30956         seg = next_seg;
30957     }
30958 }
30959
30960 inline
30961 BOOL gc_heap::fgc_should_consider_object (uint8_t* o,
30962                                           heap_segment* seg,
30963                                           BOOL consider_bgc_mark_p, 
30964                                           BOOL check_current_sweep_p, 
30965                                           BOOL check_saved_sweep_p)
30966 {
30967     // the logic for this function must be kept in sync with the analogous function
30968     // in ToolBox\SOS\Strike\gc.cpp
30969
30970     // TRUE means we don't need to check the bgc mark bit
30971     // FALSE means we do.
30972     BOOL no_bgc_mark_p = FALSE;
30973
30974     if (consider_bgc_mark_p)
30975     {
30976         if (check_current_sweep_p && (o < current_sweep_pos))
30977         {
30978             dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
30979             no_bgc_mark_p = TRUE;
30980         }
30981
30982         if (!no_bgc_mark_p)
30983         {
30984             if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
30985             {
30986                 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
30987                 no_bgc_mark_p = TRUE;
30988             }
30989
30990             if (!check_saved_sweep_p)
30991             {
30992                 uint8_t* background_allocated = heap_segment_background_allocated (seg);
30993                 // if this was the saved ephemeral segment, check_saved_sweep_p 
30994                 // would've been true.
30995                 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
30996                 // background_allocated could be 0 for the new segments acquired during bgc
30997                 // sweep and we still want no_bgc_mark_p to be true.
30998                 if (o >= background_allocated)
30999                 {
31000                     dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
31001                     no_bgc_mark_p = TRUE;
31002                 }
31003             }
31004         }
31005     }
31006     else
31007     {
31008         no_bgc_mark_p = TRUE;
31009     }
31010
31011     dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
31012     return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
31013 }
31014
31015 // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
31016 // if it's TRUE, check_current_sweep_p tells you if you should consider the
31017 // current sweep position or not.
31018 void gc_heap::should_check_bgc_mark (heap_segment* seg, 
31019                                      BOOL* consider_bgc_mark_p, 
31020                                      BOOL* check_current_sweep_p,
31021                                      BOOL* check_saved_sweep_p)
31022 {
31023     // the logic for this function must be kept in sync with the analogous function
31024     // in ToolBox\SOS\Strike\gc.cpp
31025     *consider_bgc_mark_p = FALSE;
31026     *check_current_sweep_p = FALSE;
31027     *check_saved_sweep_p = FALSE;
31028
31029     if (current_c_gc_state == c_gc_state_planning)
31030     {
31031         // We are doing the current_sweep_pos comparison here because we have yet to 
31032         // turn on the swept flag for the segment but in_range_for_segment will return
31033         // FALSE if the address is the same as reserved.
31034         if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
31035         {
31036             dprintf (3, ("seg %Ix is already swept by bgc", seg));
31037         }
31038         else
31039         {
31040             *consider_bgc_mark_p = TRUE;
31041
31042             dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
31043
31044             if (seg == saved_sweep_ephemeral_seg)
31045             {
31046                 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
31047                 *check_saved_sweep_p = TRUE;
31048             }
31049
31050             if (in_range_for_segment (current_sweep_pos, seg))
31051             {
31052                 dprintf (3, ("current sweep pos is %Ix and within seg %Ix", 
31053                               current_sweep_pos, seg));
31054                 *check_current_sweep_p = TRUE;
31055             }
31056         }
31057     }
31058 }
31059
31060 void gc_heap::background_ephemeral_sweep()
31061 {
31062     dprintf (3, ("bgc ephemeral sweep"));
31063
31064     int align_const = get_alignment_constant (TRUE);
31065
31066     saved_sweep_ephemeral_seg = ephemeral_heap_segment;
31067     saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
31068
31069     // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
31070     // we thread onto a list first then publish it when we are done.
31071     allocator youngest_free_list;
31072     size_t youngest_free_list_space = 0;
31073     size_t youngest_free_obj_space = 0;
31074
31075     youngest_free_list.clear();
31076
31077     for (int i = 0; i <= (max_generation - 1); i++)
31078     {
31079         generation* gen_to_reset = generation_of (i);
31080         assert (generation_free_list_space (gen_to_reset) == 0);
31081         // Can only assert free_list_space is 0, not free_obj_space as the allocator could have added 
31082         // something there.
31083     }
31084
31085     for (int i = (max_generation - 1); i >= 0; i--)
31086     {
31087         generation* current_gen = generation_of (i);
31088         uint8_t* o = generation_allocation_start (current_gen);
31089         //Skip the generation gap object
31090         o = o + Align(size (o), align_const);
31091         uint8_t* end = ((i > 0) ?
31092                      generation_allocation_start (generation_of (i - 1)) : 
31093                      heap_segment_allocated (ephemeral_heap_segment));
31094
31095         uint8_t* plug_end = o;
31096         uint8_t* plug_start = o;
31097         BOOL marked_p = FALSE;
31098
31099         while (o < end)
31100         {
31101             marked_p = background_object_marked (o, TRUE);
31102             if (marked_p)
31103             {
31104                 plug_start = o;
31105                 size_t plug_size = plug_start - plug_end;
31106
31107                 if (i >= 1)
31108                 {
31109                     thread_gap (plug_end, plug_size, current_gen);
31110                 }
31111                 else
31112                 {
31113                     if (plug_size > 0)
31114                     {
31115                         make_unused_array (plug_end, plug_size);
31116                         if (plug_size >= min_free_list)
31117                         {
31118                             youngest_free_list_space += plug_size;
31119                             youngest_free_list.thread_item (plug_end, plug_size);
31120                         }
31121                         else
31122                         {
31123                             youngest_free_obj_space += plug_size;
31124                         }
31125                     }
31126                 }
31127
31128                 fix_brick_to_highest (plug_end, plug_start);
31129                 fix_brick_to_highest (plug_start, plug_start);
31130
31131                 BOOL m = TRUE;
31132                 while (m)
31133                 {
31134                     o = o + Align (size (o), align_const);
31135                     if (o >= end)
31136                     {
31137                         break;
31138                     }
31139
31140                     m = background_object_marked (o, TRUE);
31141                 }
31142                 plug_end = o;
31143                 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31144             }
31145             else
31146             {
31147                 while ((o < end) && !background_object_marked (o, FALSE))
31148                 {
31149                     o = o + Align (size (o), align_const);
31150                 }
31151             }
31152         }
31153
31154         if (plug_end != end)
31155         {
31156             if (i >= 1)
31157             {
31158                 thread_gap (plug_end, end - plug_end, current_gen);
31159                 fix_brick_to_highest (plug_end, end);
31160             }
31161             else
31162             {
31163                 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
31164                 // the following line is temporary.
31165                 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
31166 #ifdef VERIFY_HEAP
31167                 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
31168                 {
31169                     make_unused_array (plug_end, (end - plug_end));
31170                 }
31171 #endif //VERIFY_HEAP
31172             }
31173         }
31174
31175         dd_fragmentation (dynamic_data_of (i)) = 
31176             generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
31177     }
31178
31179     generation* youngest_gen = generation_of (0);
31180     generation_free_list_space (youngest_gen) = youngest_free_list_space;
31181     generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
31182     dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
31183     generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
31184 }
31185
31186 void gc_heap::background_sweep()
31187 {
31188     generation* gen         = generation_of (max_generation);
31189     dynamic_data* dd        = dynamic_data_of (max_generation);
31190     // For SOH segments we go backwards.
31191     heap_segment* start_seg = ephemeral_heap_segment;
31192     PREFIX_ASSUME(start_seg != NULL);
31193     heap_segment* fseg      = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31194     heap_segment* seg       = start_seg;
31195     uint8_t* o                 = heap_segment_mem (seg);
31196
31197     heap_segment* prev_seg = heap_segment_next (seg);
31198     int align_const        = get_alignment_constant (TRUE);
31199     if (seg == fseg)
31200     {
31201         assert (o == generation_allocation_start (generation_of (max_generation)));
31202         o = o + Align(size (o), align_const);
31203     }
31204
31205     uint8_t* plug_end      = o;
31206     uint8_t* plug_start    = o;
31207     next_sweep_obj         = o;
31208     current_sweep_pos      = o;
31209
31210     //uint8_t* end              = background_next_end (seg, (gen == large_object_generation));
31211     uint8_t* end              = heap_segment_background_allocated (seg);
31212     BOOL delete_p          = FALSE;
31213
31214     //concurrent_print_time_delta ("finished with mark and start with sweep");
31215     concurrent_print_time_delta ("Sw");
31216     dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31217
31218     //block concurrent allocation for large objects
31219     dprintf (3, ("lh state: planning"));
31220     if (gc_lh_block_event.IsValid())
31221     {
31222         gc_lh_block_event.Reset();
31223     }
31224
31225     for (int i = 0; i <= (max_generation + 1); i++)
31226     {
31227         generation* gen_to_reset = generation_of (i);
31228         generation_allocator (gen_to_reset)->clear();
31229         generation_free_list_space (gen_to_reset) = 0;
31230         generation_free_obj_space (gen_to_reset) = 0;
31231         generation_free_list_allocated (gen_to_reset) = 0;
31232         generation_end_seg_allocated (gen_to_reset) = 0;
31233         generation_condemned_allocated (gen_to_reset) = 0; 
31234         //reset the allocation so foreground gc can allocate into older generation
31235         generation_allocation_pointer (gen_to_reset)= 0;
31236         generation_allocation_limit (gen_to_reset) = 0;
31237         generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
31238     }
31239
31240     FIRE_EVENT(BGC2ndNonConEnd);
31241
31242     current_bgc_state = bgc_sweep_soh;
31243     verify_soh_segment_list();
31244
31245 #ifdef FEATURE_BASICFREEZE
31246     if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
31247         ro_segments_in_range)
31248     {
31249         sweep_ro_segments (generation_start_segment (gen));
31250     }
31251 #endif // FEATURE_BASICFREEZE
31252
31253     //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
31254     if (current_c_gc_state != c_gc_state_planning)
31255     {
31256         current_c_gc_state = c_gc_state_planning;
31257     }
31258
31259     concurrent_print_time_delta ("Swe");
31260
31261     heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
31262     PREFIX_ASSUME(loh_seg  != NULL);
31263     while (loh_seg )
31264     {
31265         loh_seg->flags &= ~heap_segment_flags_swept;
31266         heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
31267         loh_seg = heap_segment_next_rw (loh_seg);
31268     }
31269
31270 #ifdef MULTIPLE_HEAPS
31271     bgc_t_join.join(this, gc_join_restart_ee);
31272     if (bgc_t_join.joined())
31273 #endif //MULTIPLE_HEAPS 
31274     {
31275 #ifdef MULTIPLE_HEAPS
31276         dprintf(2, ("Starting BGC threads for resuming EE"));
31277         bgc_t_join.restart();
31278 #endif //MULTIPLE_HEAPS
31279     }
31280
31281     if (heap_number == 0)
31282     {
31283         restart_EE ();
31284     }
31285
31286     FIRE_EVENT(BGC2ndConBegin);
31287
31288     background_ephemeral_sweep();
31289
31290 #ifdef MULTIPLE_HEAPS
31291     bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
31292     if (bgc_t_join.joined())
31293 #endif //MULTIPLE_HEAPS
31294     {
31295 #ifdef FEATURE_EVENT_TRACE
31296         bgc_heap_walk_for_etw_p = GCEventStatus::IsEnabled(GCEventProvider_Default, 
31297                                                            GCEventKeyword_GCHeapSurvivalAndMovement, 
31298                                                            GCEventLevel_Information);
31299 #endif //FEATURE_EVENT_TRACE
31300
31301         leave_spin_lock (&gc_lock);
31302
31303 #ifdef MULTIPLE_HEAPS
31304         dprintf(2, ("Starting BGC threads for BGC sweeping"));
31305         bgc_t_join.restart();
31306 #endif //MULTIPLE_HEAPS
31307     }
31308
31309     disable_preemptive (true);
31310
31311     dprintf (2, ("bgs: sweeping gen2 objects"));
31312     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31313                     (size_t)heap_segment_mem (seg),
31314                     (size_t)heap_segment_allocated (seg),
31315                     (size_t)heap_segment_background_allocated (seg)));
31316
31317     int num_objs = 256;
31318     int current_num_objs = 0;
31319     heap_segment* next_seg = 0;
31320
31321     while (1)
31322     {
31323         if (o >= end)
31324         {
31325             if (gen == large_object_generation)
31326             {
31327                 next_seg = heap_segment_next (seg);
31328             }
31329             else
31330             {
31331                 next_seg = heap_segment_prev (fseg, seg);
31332             }
31333
31334             delete_p = FALSE;
31335
31336             if (!heap_segment_read_only_p (seg))
31337             {
31338                 if (gen == large_object_generation)
31339                 {
31340                     // we can treat all LOH segments as in the bgc domain
31341                     // regardless of whether we saw in bgc mark or not
31342                     // because we don't allow LOH allocations during bgc
31343                     // sweep anyway - the LOH segments can't change.
31344                     process_background_segment_end (seg, gen, plug_end, 
31345                                                     start_seg, &delete_p);
31346                 }
31347                 else
31348                 {
31349                     assert (heap_segment_background_allocated (seg) != 0);
31350                     process_background_segment_end (seg, gen, plug_end, 
31351                                                     start_seg, &delete_p);
31352
31353                     assert (next_seg || !delete_p);
31354                 }
31355             }
31356
31357             if (delete_p)
31358             {
31359                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
31360             }
31361             else
31362             {
31363                 prev_seg = seg;
31364                 dprintf (2, ("seg %Ix has been swept", seg));
31365                 seg->flags |= heap_segment_flags_swept;
31366             }
31367
31368             verify_soh_segment_list();
31369
31370             seg = next_seg;
31371
31372             dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
31373             
31374             if (seg == 0)
31375             {
31376                 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31377
31378                 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31379
31380                 if (gen != large_object_generation)
31381                 {
31382                     dprintf (2, ("bgs: sweeping gen3 objects"));
31383                     current_bgc_state = bgc_sweep_loh;
31384                     gen = generation_of (max_generation+1);
31385                     start_seg = heap_segment_rw (generation_start_segment (gen));
31386
31387                     PREFIX_ASSUME(start_seg != NULL);
31388
31389                     seg = start_seg;
31390                     prev_seg = 0;
31391                     o = generation_allocation_start (gen);
31392                     assert (method_table (o) == g_gc_pFreeObjectMethodTable);
31393                     align_const = get_alignment_constant (FALSE);
31394                     o = o + Align(size (o), align_const);
31395                     plug_end = o;
31396                     end = heap_segment_allocated (seg);
31397                     dprintf (2, ("sweeping gen3 objects"));
31398                     generation_free_obj_space (gen) = 0;
31399                     generation_allocator (gen)->clear();
31400                     generation_free_list_space (gen) = 0;
31401
31402                     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31403                                     (size_t)heap_segment_mem (seg),
31404                                     (size_t)heap_segment_allocated (seg),
31405                                     (size_t)heap_segment_background_allocated (seg)));
31406                 }
31407                 else
31408                     break;
31409             }
31410             else
31411             {
31412                 o = heap_segment_mem (seg);
31413                 if (seg == fseg)
31414                 {
31415                     assert (gen != large_object_generation);
31416                     assert (o == generation_allocation_start (generation_of (max_generation)));
31417                     align_const = get_alignment_constant (TRUE);
31418                     o = o + Align(size (o), align_const);
31419                 }
31420
31421                 plug_end = o;
31422                 current_sweep_pos = o;
31423                 next_sweep_obj = o;
31424                 
31425                 allow_fgc();
31426                 end = background_next_end (seg, (gen == large_object_generation));
31427                 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31428                                 (size_t)heap_segment_mem (seg),
31429                                 (size_t)heap_segment_allocated (seg),
31430                                 (size_t)heap_segment_background_allocated (seg)));
31431             }
31432         }
31433
31434         if ((o < end) && background_object_marked (o, TRUE))
31435         {
31436             plug_start = o;
31437             if (gen == large_object_generation)
31438             {
31439                 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
31440             }
31441
31442             thread_gap (plug_end, plug_start-plug_end, gen);
31443             if (gen != large_object_generation)
31444             {
31445                 add_gen_free (max_generation, plug_start-plug_end);
31446                 fix_brick_to_highest (plug_end, plug_start);
31447                 // we need to fix the brick for the next plug here 'cause an FGC can
31448                 // happen and can't read a stale brick.
31449                 fix_brick_to_highest (plug_start, plug_start);
31450             }
31451
31452             BOOL m = TRUE;
31453
31454             while (m)
31455             {
31456                 next_sweep_obj = o + Align(size (o), align_const);
31457                 current_num_objs++;
31458                 if (current_num_objs >= num_objs)
31459                 {
31460                     current_sweep_pos = next_sweep_obj;
31461
31462                     allow_fgc();
31463                     current_num_objs = 0;
31464                 }
31465
31466                 o = next_sweep_obj;
31467                 if (o >= end)
31468                 {
31469                     break;
31470                 }
31471
31472                 m = background_object_marked (o, TRUE);
31473             }
31474             plug_end = o;
31475             if (gen != large_object_generation)
31476             {
31477                 add_gen_plug (max_generation, plug_end-plug_start);
31478                 dd_survived_size (dd) += (plug_end - plug_start);
31479             }
31480             dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31481         }
31482         else
31483         {
31484             while ((o < end) && !background_object_marked (o, FALSE))
31485             {
31486                 next_sweep_obj = o + Align(size (o), align_const);;
31487                 current_num_objs++;
31488                 if (current_num_objs >= num_objs)
31489                 {
31490                     current_sweep_pos = plug_end;
31491                     dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
31492                     allow_fgc();
31493                     current_num_objs = 0;
31494                 }
31495
31496                 o = next_sweep_obj;
31497             }
31498         }
31499     }
31500
31501     size_t total_loh_size = generation_size (max_generation + 1);
31502     size_t total_soh_size = generation_sizes (generation_of (max_generation));
31503
31504     dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
31505
31506     dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id", 
31507         generation_free_list_space (generation_of (max_generation)),
31508         generation_free_obj_space (generation_of (max_generation))));
31509     dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id", 
31510         heap_number,
31511         generation_free_list_space (generation_of (max_generation + 1)),
31512         generation_free_obj_space (generation_of (max_generation + 1))));
31513
31514     FIRE_EVENT(BGC2ndConEnd);
31515     concurrent_print_time_delta ("background sweep");
31516     
31517     heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31518     PREFIX_ASSUME(reset_seg != NULL);
31519
31520     while (reset_seg)
31521     {
31522         heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
31523         heap_segment_background_allocated (reset_seg) = 0;
31524         reset_seg = heap_segment_next_rw (reset_seg);
31525     }
31526
31527     // We calculate dynamic data here because if we wait till we signal the lh event, 
31528     // the allocation thread can change the fragmentation and we may read an intermediate
31529     // value (which can be greater than the generation size). Plus by that time it won't 
31530     // be accurate.
31531     compute_new_dynamic_data (max_generation);
31532
31533     enable_preemptive ();
31534
31535 #ifdef MULTIPLE_HEAPS
31536     bgc_t_join.join(this, gc_join_set_state_free);
31537     if (bgc_t_join.joined())
31538 #endif //MULTIPLE_HEAPS
31539     {
31540         // TODO: We are using this join just to set the state. Should
31541         // look into eliminating it - check to make sure things that use 
31542         // this state can live with per heap state like should_check_bgc_mark.
31543         current_c_gc_state = c_gc_state_free;
31544
31545 #ifdef MULTIPLE_HEAPS
31546         dprintf(2, ("Starting BGC threads after background sweep phase"));
31547         bgc_t_join.restart();
31548 #endif //MULTIPLE_HEAPS
31549     }
31550
31551     disable_preemptive (true);
31552
31553     if (gc_lh_block_event.IsValid())
31554     {
31555         gc_lh_block_event.Set();
31556     }
31557
31558     //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31559     dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
31560 }
31561 #endif //BACKGROUND_GC
31562
31563 void gc_heap::sweep_large_objects ()
31564 {
31565     //this min value is for the sake of the dynamic tuning.
31566     //so we know that we are not starting even if we have no
31567     //survivors.
31568     generation* gen        = large_object_generation;
31569     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
31570
31571     PREFIX_ASSUME(start_seg != NULL);
31572
31573     heap_segment* seg      = start_seg;
31574     heap_segment* prev_seg = 0;
31575     uint8_t* o             = generation_allocation_start (gen);
31576     int align_const        = get_alignment_constant (FALSE);
31577
31578     //Skip the generation gap object
31579     o = o + Align(size (o), align_const);
31580
31581     uint8_t* plug_end         = o;
31582     uint8_t* plug_start       = o;
31583
31584     generation_allocator (gen)->clear();
31585     generation_free_list_space (gen) = 0;
31586     generation_free_obj_space (gen) = 0;
31587
31588
31589     dprintf (3, ("sweeping large objects"));
31590     dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix", 
31591                  (size_t)seg,
31592                  (size_t)heap_segment_mem (seg),
31593                  (size_t)heap_segment_allocated (seg),
31594                  o));
31595
31596     while (1)
31597     {
31598         if (o >= heap_segment_allocated (seg))
31599         {
31600             heap_segment* next_seg = heap_segment_next (seg);
31601             //delete the empty segment if not the only one
31602             if ((plug_end == heap_segment_mem (seg)) &&
31603                 (seg != start_seg) && !heap_segment_read_only_p (seg))
31604             {
31605                 //prepare for deletion
31606                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
31607                 assert (prev_seg);
31608                 heap_segment_next (prev_seg) = next_seg;
31609                 heap_segment_next (seg) = freeable_large_heap_segment;
31610                 freeable_large_heap_segment = seg;
31611             }
31612             else
31613             {
31614                 if (!heap_segment_read_only_p (seg))
31615                 {
31616                     dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
31617                     heap_segment_allocated (seg) = plug_end;
31618                     decommit_heap_segment_pages (seg, 0);
31619                 }
31620                 prev_seg = seg;
31621             }
31622             seg = next_seg;
31623             if (seg == 0)
31624                 break;
31625             else
31626             {
31627                 o = heap_segment_mem (seg);
31628                 plug_end = o;
31629                 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
31630                              (size_t)heap_segment_mem (seg),
31631                              (size_t)heap_segment_allocated (seg)));
31632             }
31633         }
31634         if (large_object_marked(o, TRUE))
31635         {
31636             plug_start = o;
31637             //everything between plug_end and plug_start is free
31638             thread_gap (plug_end, plug_start-plug_end, gen);
31639
31640             BOOL m = TRUE;
31641             while (m)
31642             {
31643                 o = o + AlignQword (size (o));
31644                 if (o >= heap_segment_allocated (seg))
31645                 {
31646                     break;
31647                 }
31648                 m = large_object_marked (o, TRUE);
31649             }
31650             plug_end = o;
31651             dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31652         }
31653         else
31654         {
31655             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
31656             {
31657                 o = o + AlignQword (size (o));
31658             }
31659         }
31660     }
31661
31662     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31663
31664     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31665 }
31666
31667 void gc_heap::relocate_in_large_objects ()
31668 {
31669     relocate_args args;
31670     args.low = gc_low;
31671     args.high = gc_high;
31672     args.last_plug = 0;
31673
31674     generation* gen = large_object_generation;
31675
31676     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31677
31678     PREFIX_ASSUME(seg != NULL);
31679
31680     uint8_t* o = generation_allocation_start (gen);
31681
31682     while (1)
31683     {
31684         if (o >= heap_segment_allocated (seg))
31685         {
31686             seg = heap_segment_next_rw (seg);
31687             if (seg == 0)
31688                 break;
31689             else
31690             {
31691                 o = heap_segment_mem (seg);
31692             }
31693         }
31694         while (o < heap_segment_allocated (seg))
31695         {
31696             check_class_object_demotion (o);
31697             if (contain_pointers (o))
31698             {
31699                 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
31700                 go_through_object_nostart (method_table (o), o, size(o), pval,
31701                         {
31702                             reloc_survivor_helper (pval);
31703                         });
31704             }
31705             o = o + AlignQword (size (o));
31706         }
31707     }
31708 }
31709
31710 void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
31711                                                     BOOL relocating)
31712 {
31713     uint8_t*      low               = gc_low;
31714     size_t        end_card          = 0;
31715     generation*   oldest_gen        = generation_of (max_generation+1);
31716     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
31717
31718     PREFIX_ASSUME(seg != NULL);
31719
31720     uint8_t*      beg               = generation_allocation_start (oldest_gen);
31721     uint8_t*      end               = heap_segment_allocated (seg);
31722
31723     size_t  cg_pointers_found = 0;
31724
31725     size_t  card_word_end = (card_of (align_on_card_word (end)) /
31726                              card_word_width);
31727
31728     size_t      n_eph             = 0;
31729     size_t      n_gen             = 0;
31730     size_t      n_card_set        = 0;
31731     uint8_t*    next_boundary = (relocating ?
31732                               generation_plan_allocation_start (generation_of (max_generation -1)) :
31733                               ephemeral_low);
31734
31735     uint8_t*    nhigh         = (relocating ?
31736                               heap_segment_plan_allocated (ephemeral_heap_segment) :
31737                               ephemeral_high);
31738
31739     BOOL          foundp            = FALSE;
31740     uint8_t*      start_address     = 0;
31741     uint8_t*      limit             = 0;
31742     size_t        card              = card_of (beg);
31743     uint8_t*      o                 = beg;
31744 #ifdef BACKGROUND_GC
31745     BOOL consider_bgc_mark_p        = FALSE;
31746     BOOL check_current_sweep_p      = FALSE;
31747     BOOL check_saved_sweep_p        = FALSE;
31748     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31749 #endif //BACKGROUND_GC
31750
31751     size_t total_cards_cleared = 0;
31752
31753     //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
31754     dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
31755     while (1)
31756     {
31757         if ((o < end) && (card_of(o) > card))
31758         {
31759             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
31760             if (cg_pointers_found == 0)
31761             {
31762                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
31763                 clear_cards (card, card_of((uint8_t*)o));
31764                 total_cards_cleared += (card_of((uint8_t*)o) - card);
31765             }
31766             n_eph +=cg_pointers_found;
31767             cg_pointers_found = 0;
31768             card = card_of ((uint8_t*)o);
31769         }
31770         if ((o < end) &&(card >= end_card))
31771         {
31772             foundp = find_card (card_table, card, card_word_end, end_card);
31773             if (foundp)
31774             {
31775                 n_card_set+= end_card - card;
31776                 start_address = max (beg, card_address (card));
31777             }
31778             limit = min (end, card_address (end_card));
31779         }
31780         if ((!foundp) || (o >= end) || (card_address (card) >= end))
31781         {
31782             if ((foundp) && (cg_pointers_found == 0))
31783             {
31784                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
31785                            (size_t)card_address(card+1)));
31786                 clear_cards (card, card+1);
31787                 total_cards_cleared += 1;
31788             }
31789             n_eph +=cg_pointers_found;
31790             cg_pointers_found = 0;
31791             if ((seg = heap_segment_next_rw (seg)) != 0)
31792             {
31793 #ifdef BACKGROUND_GC
31794                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31795 #endif //BACKGROUND_GC
31796                 beg = heap_segment_mem (seg);
31797                 end = compute_next_end (seg, low);
31798                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
31799                 card = card_of (beg);
31800                 o  = beg;
31801                 end_card = 0;
31802                 continue;
31803             }
31804             else
31805             {
31806                 break;
31807             }
31808         }
31809
31810         assert (card_set_p (card));
31811         {
31812             dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
31813                        card, (size_t)o, (size_t)limit));
31814
31815             assert (Align (size (o)) >= Align (min_obj_size));
31816             size_t s = size (o);
31817             uint8_t* next_o =  o + AlignQword (s);
31818             Prefetch (next_o);
31819
31820             while (o < limit)
31821             {
31822                 s = size (o);
31823                 assert (Align (s) >= Align (min_obj_size));
31824                 next_o =  o + AlignQword (s);
31825                 Prefetch (next_o);
31826
31827                 dprintf (4, ("|%Ix|", (size_t)o));
31828                 if (next_o < start_address)
31829                 {
31830                     goto end_object;
31831                 }
31832
31833 #ifdef BACKGROUND_GC
31834                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
31835                 {
31836                     goto end_object;
31837                 }
31838 #endif //BACKGROUND_GC
31839
31840 #ifdef COLLECTIBLE_CLASS
31841                 if (is_collectible(o))
31842                 {
31843                     BOOL passed_end_card_p = FALSE;
31844
31845                     if (card_of (o) > card)
31846                     {
31847                         passed_end_card_p = card_transition (o, end, card_word_end,
31848                             cg_pointers_found, 
31849                             n_eph, n_card_set,
31850                             card, end_card,
31851                             foundp, start_address,
31852                             limit, total_cards_cleared);
31853                     }
31854
31855                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
31856                     {
31857                         // card is valid and it covers the head of the object
31858                         if (fn == &gc_heap::relocate_address)
31859                         {
31860                             keep_card_live (o, n_gen, cg_pointers_found);
31861                         }
31862                         else
31863                         {
31864                             uint8_t* class_obj = get_class_object (o);
31865                             mark_through_cards_helper (&class_obj, n_gen,
31866                                                     cg_pointers_found, fn,
31867                                                     nhigh, next_boundary);
31868                         }
31869                     }
31870
31871                     if (passed_end_card_p)
31872                     {
31873                         if (foundp && (card_address (card) < next_o))
31874                         {
31875                             goto go_through_refs;
31876                         }
31877                         else 
31878                         {
31879                             goto end_object;
31880                         }
31881                     }
31882                 }
31883
31884 go_through_refs:
31885 #endif //COLLECTIBLE_CLASS
31886
31887                 if (contain_pointers (o))
31888                 {
31889                     dprintf(3,("Going through %Ix", (size_t)o));
31890
31891                     go_through_object (method_table(o), o, s, poo,
31892                                        start_address, use_start, (o + s),
31893                        {
31894                            if (card_of ((uint8_t*)poo) > card)
31895                            {
31896                                 BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
31897                                         card_word_end,
31898                                         cg_pointers_found, 
31899                                         n_eph, n_card_set,
31900                                         card, end_card,
31901                                         foundp, start_address,
31902                                         limit, total_cards_cleared);
31903
31904                                 if (passed_end_card_p)
31905                                 {
31906                                     if (foundp && (card_address (card) < next_o))
31907                                     {
31908                                         //new_start();
31909                                         {
31910                                             if (ppstop <= (uint8_t**)start_address)
31911                                             {break;}
31912                                             else if (poo < (uint8_t**)start_address)
31913                                             {poo = (uint8_t**)start_address;}
31914                                         }
31915                                     }
31916                                     else
31917                                     {
31918                                         goto end_object;
31919                                     }
31920                                 }
31921                             }
31922
31923                            mark_through_cards_helper (poo, n_gen,
31924                                                       cg_pointers_found, fn,
31925                                                       nhigh, next_boundary);
31926                        }
31927                         );
31928                 }
31929
31930             end_object:
31931                 o = next_o;
31932             }
31933
31934         }
31935     }
31936
31937     // compute the efficiency ratio of the card table
31938     if (!relocating)
31939     {
31940         generation_skip_ratio = min (((n_eph > 800) ?
31941                                       (int)(((float)n_gen / (float)n_eph) * 100) : 100),
31942                                      generation_skip_ratio);
31943
31944         dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d", 
31945              n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
31946     }
31947     else
31948     {
31949         dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d", 
31950              n_eph, n_gen, n_card_set, generation_skip_ratio));
31951     }
31952 }
31953
31954 void gc_heap::descr_segment (heap_segment* seg )
31955 {
31956 #ifdef TRACE_GC
31957     uint8_t*  x = heap_segment_mem (seg);
31958     while (x < heap_segment_allocated (seg))
31959     {
31960         dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
31961         x = x + Align(size (x));
31962     }
31963 #else // TRACE_GC
31964     UNREFERENCED_PARAMETER(seg);
31965 #endif // TRACE_GC
31966 }
31967
31968 void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
31969 {
31970 #ifdef MULTIPLE_HEAPS
31971     int n_heaps = g_theGCHeap->GetNumberOfHeaps ();
31972     for (int i = 0; i < n_heaps; i++)
31973     {
31974         gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
31975 #else //MULTIPLE_HEAPS
31976     {
31977         gc_heap* hp = NULL;
31978 #ifdef _PREFAST_
31979         // prefix complains about us dereferencing hp in wks build even though we only access static members
31980         // this way. not sure how to shut it up except for this ugly workaround:
31981         PREFIX_ASSUME(hp != NULL);
31982 #endif // _PREFAST_
31983 #endif //MULTIPLE_HEAPS
31984
31985         int curr_gen_number0 = max_generation+1;
31986         while (curr_gen_number0 >= 0)
31987         {
31988             generation* gen = hp->generation_of (curr_gen_number0);
31989             heap_segment* seg = generation_start_segment (gen);
31990             while (seg && (seg != hp->ephemeral_heap_segment))
31991             {
31992                 assert (curr_gen_number0 > 0);
31993
31994                 // report bounds from heap_segment_mem (seg) to
31995                 // heap_segment_allocated (seg);
31996                 // for generation # curr_gen_number0
31997                 // for heap # heap_no
31998
31999                 fn(context, curr_gen_number0, heap_segment_mem (seg),
32000                                               heap_segment_allocated (seg),
32001                                               curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
32002
32003                 seg = heap_segment_next (seg);
32004             }
32005             if (seg)
32006             {
32007                 assert (seg == hp->ephemeral_heap_segment);
32008                 assert (curr_gen_number0 <= max_generation);
32009                 //
32010                 if (curr_gen_number0 == max_generation)
32011                 {
32012                     if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
32013                     {
32014                         // report bounds from heap_segment_mem (seg) to
32015                         // generation_allocation_start (generation_of (max_generation-1))
32016                         // for heap # heap_number
32017
32018                         fn(context, curr_gen_number0, heap_segment_mem (seg),
32019                                                       generation_allocation_start (hp->generation_of (max_generation-1)),
32020                                                       generation_allocation_start (hp->generation_of (max_generation-1)) );
32021                     }
32022                 }
32023                 else if (curr_gen_number0 != 0)
32024                 {
32025                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32026                     // to generation_allocation_start (generation_of (curr_gen_number0-1))
32027                     // for heap # heap_number
32028
32029                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32030                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
32031                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
32032                 }
32033                 else
32034                 {
32035                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32036                     // to heap_segment_allocated (ephemeral_heap_segment);
32037                     // for heap # heap_number
32038
32039                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32040                                                   heap_segment_allocated (hp->ephemeral_heap_segment),
32041                                                   heap_segment_reserved (hp->ephemeral_heap_segment) );
32042                 }
32043             }
32044             curr_gen_number0--;
32045         }
32046     }
32047 }
32048
32049 #ifdef TRACE_GC
32050 // Note that when logging is on it can take a long time to go through the free items.
32051 void gc_heap::print_free_list (int gen, heap_segment* seg)
32052 {
32053     UNREFERENCED_PARAMETER(gen);
32054     UNREFERENCED_PARAMETER(seg);
32055 /*
32056     if (settings.concurrent == FALSE)
32057     {
32058         uint8_t* seg_start = heap_segment_mem (seg);
32059         uint8_t* seg_end = heap_segment_allocated (seg);
32060
32061         dprintf (3, ("Free list in seg %Ix:", seg_start));
32062
32063         size_t total_free_item = 0;
32064
32065         allocator* gen_allocator = generation_allocator (generation_of (gen));
32066         for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
32067         {
32068             uint8_t* fo = gen_allocator->alloc_list_head_of (b);
32069             while (fo)
32070             {
32071                 if (fo >= seg_start && fo < seg_end)
32072                 {
32073                     total_free_item++;
32074
32075                     size_t free_item_len = size(fo);
32076
32077                     dprintf (3, ("[%Ix, %Ix[:%Id",
32078                                  (size_t)fo,
32079                                  (size_t)(fo + free_item_len),
32080                                  free_item_len));
32081                 }
32082
32083                 fo = free_list_slot (fo);
32084             }
32085         }
32086
32087         dprintf (3, ("total %Id free items", total_free_item));
32088     }
32089 */
32090 }
32091 #endif //TRACE_GC
32092
32093 void gc_heap::descr_generations (BOOL begin_gc_p)
32094 {
32095     UNREFERENCED_PARAMETER(begin_gc_p);
32096 #ifdef STRESS_LOG
32097     if (StressLog::StressLogOn(LF_GC, LL_INFO10))
32098     {
32099         gc_heap* hp = 0;
32100 #ifdef MULTIPLE_HEAPS
32101         hp= this;
32102 #endif //MULTIPLE_HEAPS
32103
32104         STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
32105         for (int n = max_generation; n >= 0; --n)
32106         {
32107             STRESS_LOG4(LF_GC, LL_INFO10, "    Generation %d [%p, %p] cur = %p\n",
32108                     n,
32109                     generation_allocation_start(generation_of(n)),
32110                     generation_allocation_limit(generation_of(n)),
32111                     generation_allocation_pointer(generation_of(n)));
32112
32113             heap_segment* seg = generation_start_segment(generation_of(n));
32114             while (seg)
32115             {
32116                 STRESS_LOG4(LF_GC, LL_INFO10, "        Segment mem %p alloc = %p used %p committed %p\n",
32117                         heap_segment_mem(seg),
32118                         heap_segment_allocated(seg),
32119                         heap_segment_used(seg),
32120                         heap_segment_committed(seg));
32121                 seg = heap_segment_next(seg);
32122             }
32123         }
32124     }
32125 #endif  // STRESS_LOG
32126
32127 #ifdef TRACE_GC
32128     dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
32129              (size_t) lowest_address, (size_t) highest_address));
32130 #ifdef BACKGROUND_GC
32131     dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
32132              (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
32133 #endif //BACKGROUND_GC
32134
32135     if (heap_number == 0)
32136     {
32137         dprintf (1, ("total heap size: %Id, commit size: %Id", get_total_heap_size(), get_total_committed_size()));
32138     }
32139
32140     int curr_gen_number = max_generation+1;
32141     while (curr_gen_number >= 0)
32142     {
32143         size_t total_gen_size = generation_size (curr_gen_number);
32144 #ifdef SIMPLE_DPRINTF
32145         dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
32146                       (begin_gc_p ? "BEG" : "END"),
32147                       settings.condemned_generation,
32148                       curr_gen_number,
32149                       total_gen_size,
32150                       dd_fragmentation (dynamic_data_of (curr_gen_number)),
32151                       generation_free_list_space (generation_of (curr_gen_number)),
32152                       generation_free_obj_space (generation_of (curr_gen_number)),
32153                       (total_gen_size ? 
32154                         (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
32155                         0),
32156                       (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
32157                       (settings.heap_expansion ? "(EX)" : " "),
32158                       (settings.promotion ? "Promotion" : "NoPromotion")));
32159 #else
32160         dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
32161                       curr_gen_number,
32162                       size (generation_allocation_start (generation_of (curr_gen_number))),
32163                       total_gen_size,
32164                       dd_fragmentation (dynamic_data_of (curr_gen_number))));
32165 #endif //SIMPLE_DPRINTF
32166
32167         generation* gen = generation_of (curr_gen_number);
32168         heap_segment* seg = generation_start_segment (gen);
32169         while (seg && (seg != ephemeral_heap_segment))
32170         {
32171             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
32172                         curr_gen_number,
32173                         (size_t)heap_segment_mem (seg),
32174                         (size_t)heap_segment_allocated (seg),
32175                         (size_t)heap_segment_committed (seg),
32176                         (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
32177                         (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
32178             print_free_list (curr_gen_number, seg);
32179             seg = heap_segment_next (seg);
32180         }
32181         if (seg && (seg != generation_start_segment (gen)))
32182         {
32183             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32184                          curr_gen_number,
32185                          (size_t)heap_segment_mem (seg),
32186                          (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
32187             print_free_list (curr_gen_number, seg);
32188
32189         }
32190         else if (seg)
32191         {
32192             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32193                          curr_gen_number,
32194                          (size_t)generation_allocation_start (generation_of (curr_gen_number)),
32195                          (size_t)(((curr_gen_number == 0)) ?
32196                                   (heap_segment_allocated
32197                                    (generation_start_segment
32198                                     (generation_of (curr_gen_number)))) :
32199                                   (generation_allocation_start
32200                                    (generation_of (curr_gen_number - 1))))
32201                          ));
32202             print_free_list (curr_gen_number, seg);
32203         }
32204         curr_gen_number--;
32205     }
32206
32207 #endif //TRACE_GC
32208 }
32209
32210 #undef TRACE_GC
32211
32212 //#define TRACE_GC
32213
32214 //-----------------------------------------------------------------------------
32215 //
32216 //                                  VM Specific support
32217 //
32218 //-----------------------------------------------------------------------------
32219
32220
32221 #ifdef TRACE_GC
32222
32223  unsigned int PromotedObjectCount  = 0;
32224  unsigned int CreatedObjectCount       = 0;
32225  unsigned int AllocDuration            = 0;
32226  unsigned int AllocCount               = 0;
32227  unsigned int AllocBigCount            = 0;
32228  unsigned int AllocSmallCount      = 0;
32229  unsigned int AllocStart             = 0;
32230 #endif //TRACE_GC
32231
32232 //Static member variables.
32233 VOLATILE(BOOL)    GCHeap::GcInProgress            = FALSE;
32234 //GCTODO
32235 //CMCSafeLock*      GCHeap::fGcLock;
32236 GCEvent            *GCHeap::WaitForGCEvent         = NULL;
32237 //GCTODO
32238 #ifdef TRACE_GC
32239 unsigned int       GCHeap::GcDuration;
32240 #endif //TRACE_GC
32241 unsigned            GCHeap::GcCondemnedGeneration   = 0;
32242 size_t              GCHeap::totalSurvivedSize       = 0;
32243 #ifdef FEATURE_PREMORTEM_FINALIZATION
32244 CFinalize*          GCHeap::m_Finalize              = 0;
32245 BOOL                GCHeap::GcCollectClasses        = FALSE;
32246 VOLATILE(int32_t)      GCHeap::m_GCFLock               = 0;
32247
32248 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32249 #ifdef STRESS_HEAP
32250 #ifdef BACKGROUND_GC
32251 int                 GCHeap::gc_stress_fgcs_in_bgc   = 0;
32252 #endif // BACKGROUND_GC
32253 #ifndef MULTIPLE_HEAPS
32254 OBJECTHANDLE        GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
32255 int                 GCHeap::m_CurStressObj          = 0;
32256 #endif // !MULTIPLE_HEAPS
32257 #endif // STRESS_HEAP
32258 #endif // FEATURE_REDHAWK
32259
32260 #endif //FEATURE_PREMORTEM_FINALIZATION
32261
32262 class NoGCRegionLockHolder
32263 {
32264 public:
32265     NoGCRegionLockHolder()
32266     {
32267         enter_spin_lock_noinstru(&g_no_gc_lock);
32268     }
32269
32270     ~NoGCRegionLockHolder()
32271     {
32272         leave_spin_lock_noinstru(&g_no_gc_lock);
32273     }
32274 };
32275
32276 // An explanation of locking for finalization:
32277 //
32278 // Multiple threads allocate objects.  During the allocation, they are serialized by
32279 // the AllocLock above.  But they release that lock before they register the object
32280 // for finalization.  That's because there is much contention for the alloc lock, but
32281 // finalization is presumed to be a rare case.
32282 //
32283 // So registering an object for finalization must be protected by the FinalizeLock.
32284 //
32285 // There is another logical queue that involves finalization.  When objects registered
32286 // for finalization become unreachable, they are moved from the "registered" queue to
32287 // the "unreachable" queue.  Note that this only happens inside a GC, so no other
32288 // threads can be manipulating either queue at that time.  Once the GC is over and
32289 // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
32290 // queue and call their finalizers.  This dequeue operation is also protected with
32291 // the finalize lock.
32292 //
32293 // At first, this seems unnecessary.  Only one thread is ever enqueuing or dequeuing
32294 // on the unreachable queue (either the GC thread during a GC or the finalizer thread
32295 // when a GC is not in progress).  The reason we share a lock with threads enqueuing
32296 // on the "registered" queue is that the "registered" and "unreachable" queues are
32297 // interrelated.
32298 //
32299 // They are actually two regions of a longer list, which can only grow at one end.
32300 // So to enqueue an object to the "registered" list, you actually rotate an unreachable
32301 // object at the boundary between the logical queues, out to the other end of the
32302 // unreachable queue -- where all growing takes place.  Then you move the boundary
32303 // pointer so that the gap we created at the boundary is now on the "registered"
32304 // side rather than the "unreachable" side.  Now the object can be placed into the
32305 // "registered" side at that point.  This is much more efficient than doing moves
32306 // of arbitrarily long regions, but it causes the two queues to require a shared lock.
32307 //
32308 // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock.  Instead, it relies
32309 // on the fact that the lock will only be taken for a brief period and that it will
32310 // never provoke or allow a GC while the lock is held.  This is critical.  If the
32311 // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
32312 // allow a GC), then the Alloc client would have to GC protect a finalizable object
32313 // to protect against that eventuality.  That is too slow!
32314
32315
32316
32317 BOOL IsValidObject99(uint8_t *pObject)
32318 {
32319 #ifdef VERIFY_HEAP
32320     if (!((CObjectHeader*)pObject)->IsFree())
32321         ((CObjectHeader *) pObject)->Validate();
32322 #endif //VERIFY_HEAP
32323     return(TRUE);
32324 }
32325
32326 #ifdef BACKGROUND_GC 
32327 BOOL gc_heap::bgc_mark_array_range (heap_segment* seg, 
32328                                     BOOL whole_seg_p,
32329                                     uint8_t** range_beg,
32330                                     uint8_t** range_end)
32331 {
32332     uint8_t* seg_start = heap_segment_mem (seg);
32333     uint8_t* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
32334
32335     if ((seg_start < background_saved_highest_address) &&
32336         (seg_end > background_saved_lowest_address))
32337     {
32338         *range_beg = max (seg_start, background_saved_lowest_address);
32339         *range_end = min (seg_end, background_saved_highest_address);
32340         return TRUE;
32341     }
32342     else
32343     {
32344         return FALSE;
32345     }
32346 }
32347
32348 void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
32349 {
32350 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32351     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32352     {
32353         uint8_t* range_beg = 0;
32354         uint8_t* range_end = 0;
32355
32356         if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
32357         {
32358             size_t  markw = mark_word_of (range_beg);
32359             size_t  markw_end = mark_word_of (range_end);
32360             while (markw < markw_end)
32361             {
32362                 if (mark_array [markw])
32363                 {
32364                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32365                                     markw, mark_array [markw], mark_word_address (markw)));
32366                     FATAL_GC_ERROR();
32367                 }
32368                 markw++;
32369             }
32370             uint8_t* p = mark_word_address (markw_end);
32371             while (p < range_end)
32372             {
32373                 assert (!(mark_array_marked (p)));
32374                 p++;
32375             }
32376         }
32377     }
32378 #endif //VERIFY_HEAP && MARK_ARRAY
32379 }
32380
32381 void gc_heap::verify_mark_bits_cleared (uint8_t* obj, size_t s)
32382 {
32383 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32384     size_t start_mark_bit = mark_bit_of (obj) + 1;
32385     size_t end_mark_bit = mark_bit_of (obj + s);
32386     unsigned int startbit = mark_bit_bit (start_mark_bit);
32387     unsigned int endbit = mark_bit_bit (end_mark_bit);
32388     size_t startwrd = mark_bit_word (start_mark_bit);
32389     size_t endwrd = mark_bit_word (end_mark_bit);
32390     unsigned int result = 0;
32391
32392     unsigned int firstwrd = ~(lowbits (~0, startbit));
32393     unsigned int lastwrd = ~(highbits (~0, endbit));
32394
32395     if (startwrd == endwrd)
32396     {
32397         unsigned int wrd = firstwrd & lastwrd;
32398         result = mark_array[startwrd] & wrd;
32399         if (result)
32400         {
32401             FATAL_GC_ERROR();
32402         }
32403         return;
32404     }
32405
32406     // verify the first mark word is cleared.
32407     if (startbit)
32408     {
32409         result = mark_array[startwrd] & firstwrd;
32410         if (result)
32411         {
32412             FATAL_GC_ERROR();
32413         }
32414         startwrd++;
32415     }
32416
32417     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
32418     {
32419         result = mark_array[wrdtmp];
32420         if (result)
32421         {
32422             FATAL_GC_ERROR();
32423         }
32424     }
32425
32426     // set the last mark word.
32427     if (endbit)
32428     {
32429         result = mark_array[endwrd] & lastwrd;
32430         if (result)
32431         {
32432             FATAL_GC_ERROR();
32433         }
32434     }
32435 #endif //VERIFY_HEAP && MARK_ARRAY
32436 }
32437
32438 void gc_heap::clear_all_mark_array()
32439 {
32440 #ifdef MARK_ARRAY
32441     //size_t num_dwords_written = 0;
32442     //size_t begin_time = GetHighPrecisionTimeStamp();
32443
32444     generation* gen = generation_of (max_generation);
32445     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32446     
32447     while (1)
32448     {
32449         if (seg == 0)
32450         {
32451             if (gen != large_object_generation)
32452             {
32453                 gen = generation_of (max_generation+1);
32454                 seg = heap_segment_rw (generation_start_segment (gen));
32455             }
32456             else
32457             {
32458                 break;
32459             }
32460         }
32461
32462         uint8_t* range_beg = 0;
32463         uint8_t* range_end = 0;
32464
32465         if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
32466         { 
32467             size_t markw = mark_word_of (range_beg);
32468             size_t markw_end = mark_word_of (range_end);
32469             size_t size_total = (markw_end - markw) * sizeof (uint32_t);
32470             //num_dwords_written = markw_end - markw;
32471             size_t size = 0;
32472             size_t size_left = 0;
32473
32474             assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
32475
32476             if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
32477             {
32478                 size = (size_total & ~(sizeof(PTR_PTR) - 1));
32479                 size_left = size_total - size;
32480                 assert ((size_left & (sizeof (uint32_t) - 1)) == 0);
32481             }
32482             else
32483             {
32484                 size = size_total;
32485             }
32486
32487             memclr ((uint8_t*)&mark_array[markw], size);
32488
32489             if (size_left != 0)
32490             {
32491                 uint32_t* markw_to_clear = &mark_array[markw + size / sizeof (uint32_t)];
32492                 for (size_t i = 0; i < (size_left / sizeof (uint32_t)); i++)
32493                 {
32494                     *markw_to_clear = 0;
32495                     markw_to_clear++;
32496                 }
32497             }
32498         }
32499
32500         seg = heap_segment_next_rw (seg);
32501     }
32502
32503     //size_t end_time = GetHighPrecisionTimeStamp() - begin_time; 
32504
32505     //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(uint32_t));
32506
32507 #endif //MARK_ARRAY
32508 }
32509
32510 #endif //BACKGROUND_GC 
32511
32512 void gc_heap::verify_mark_array_cleared (heap_segment* seg)
32513 {
32514 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32515     assert (card_table == g_gc_card_table);
32516     size_t  markw = mark_word_of (heap_segment_mem (seg));
32517     size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
32518
32519     while (markw < markw_end)
32520     {
32521         if (mark_array [markw])
32522         {
32523             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32524                             markw, mark_array [markw], mark_word_address (markw)));
32525             FATAL_GC_ERROR();
32526         }
32527         markw++;
32528     }
32529 #endif //VERIFY_HEAP && MARK_ARRAY
32530 }
32531
32532 void gc_heap::verify_mark_array_cleared ()
32533 {
32534 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32535     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32536     {
32537         generation* gen = generation_of (max_generation);
32538         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32539         
32540         while (1)
32541         {
32542             if (seg == 0)
32543             {
32544                 if (gen != large_object_generation)
32545                 {
32546                     gen = generation_of (max_generation+1);
32547                     seg = heap_segment_rw (generation_start_segment (gen));
32548                 }
32549                 else
32550                 {
32551                     break;
32552                 }
32553             }
32554
32555             bgc_verify_mark_array_cleared (seg);
32556             seg = heap_segment_next_rw (seg);
32557         }
32558     }
32559 #endif //VERIFY_HEAP && MARK_ARRAY
32560 }
32561
32562 void gc_heap::verify_seg_end_mark_array_cleared()
32563 {
32564 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32565     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32566     {
32567         generation* gen = generation_of (max_generation);
32568         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32569         
32570         while (1)
32571         {
32572             if (seg == 0)
32573             {
32574                 if (gen != large_object_generation)
32575                 {
32576                     gen = generation_of (max_generation+1);
32577                     seg = heap_segment_rw (generation_start_segment (gen));
32578                 }
32579                 else
32580                 {
32581                     break;
32582                 }
32583             }
32584
32585             // We already cleared all mark array bits for ephemeral generations
32586             // at the beginning of bgc sweep
32587             uint8_t* from = ((seg == ephemeral_heap_segment) ?
32588                           generation_allocation_start (generation_of (max_generation - 1)) :
32589                           heap_segment_allocated (seg));
32590             size_t  markw = mark_word_of (align_on_mark_word (from));
32591             size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
32592
32593             while (from < mark_word_address (markw))
32594             {
32595                 if (is_mark_bit_set (from))
32596                 {
32597                     dprintf (3, ("mark bit for %Ix was not cleared", from));
32598                     FATAL_GC_ERROR();
32599                 }
32600
32601                 from += mark_bit_pitch;
32602             }
32603
32604             while (markw < markw_end)
32605             {
32606                 if (mark_array [markw])
32607                 {
32608                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32609                                     markw, mark_array [markw], mark_word_address (markw)));
32610                     FATAL_GC_ERROR();
32611                 }
32612                 markw++;
32613             }
32614             seg = heap_segment_next_rw (seg);
32615         }
32616     }
32617 #endif //VERIFY_HEAP && MARK_ARRAY
32618 }
32619
32620 // This function is called to make sure we don't mess up the segment list
32621 // in SOH. It's called by:
32622 // 1) begin and end of ephemeral GCs
32623 // 2) during bgc sweep when we switch segments.
32624 void gc_heap::verify_soh_segment_list()
32625 {
32626 #ifdef VERIFY_HEAP
32627     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32628     {
32629         generation* gen = generation_of (max_generation);
32630         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32631         heap_segment* last_seg = 0;
32632         while (seg)
32633         {
32634             last_seg = seg;
32635             seg = heap_segment_next_rw (seg);
32636         }
32637         if (last_seg != ephemeral_heap_segment)
32638         {
32639             FATAL_GC_ERROR();
32640         }
32641     }
32642 #endif //VERIFY_HEAP
32643 }
32644
32645 // This function can be called at any foreground GCs or blocking GCs. For background GCs,
32646 // it can be called at the end of the final marking; and at any point during background
32647 // sweep.
32648 // NOTE - to be able to call this function during background sweep, we need to temporarily 
32649 // NOT clear the mark array bits as we go.
32650 void gc_heap::verify_partial ()
32651 {
32652 #ifdef BACKGROUND_GC
32653     //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
32654     //generation* gen = large_object_generation;
32655     generation* gen = generation_of (max_generation);
32656     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32657     int align_const = get_alignment_constant (gen != large_object_generation);
32658
32659     uint8_t* o = 0;
32660     uint8_t* end = 0;
32661     size_t s = 0;
32662
32663     // Different ways to fail.
32664     BOOL mark_missed_p = FALSE;
32665     BOOL bad_ref_p = FALSE;
32666     BOOL free_ref_p = FALSE;
32667
32668     while (1)
32669     {
32670         if (seg == 0)
32671         {
32672             if (gen != large_object_generation)
32673             {
32674                 //switch to LOH
32675                 gen = large_object_generation;
32676                 align_const = get_alignment_constant (gen != large_object_generation);
32677                 seg = heap_segment_rw (generation_start_segment (gen));
32678                 continue;
32679             }
32680             else
32681             {
32682                 break;
32683             }
32684         }
32685
32686         o = heap_segment_mem (seg);
32687         end  = heap_segment_allocated (seg);
32688         //printf ("validating [%Ix-[%Ix\n", o, end);
32689         while (o < end)
32690         {
32691             s = size (o);
32692
32693             BOOL marked_p = background_object_marked (o, FALSE);
32694
32695             if (marked_p)
32696             {
32697                 go_through_object_cl (method_table (o), o, s, oo,
32698                     {
32699                         if (*oo)
32700                         {
32701                             //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
32702                             MethodTable *pMT = method_table (*oo);
32703
32704                             if (pMT == g_gc_pFreeObjectMethodTable)
32705                             {
32706                                 free_ref_p = TRUE;
32707                                 FATAL_GC_ERROR();
32708                             }
32709
32710                             if (!pMT->SanityCheck()) 
32711                             {
32712                                 bad_ref_p = TRUE;
32713                                 dprintf (3, ("Bad member of %Ix %Ix",
32714                                             (size_t)oo, (size_t)*oo));
32715                                 FATAL_GC_ERROR();
32716                             }
32717
32718                             if (current_bgc_state == bgc_final_marking)
32719                             {
32720                                 if (marked_p && !background_object_marked (*oo, FALSE))
32721                                 {
32722                                     mark_missed_p = TRUE;
32723                                     FATAL_GC_ERROR();
32724                                 }
32725                             }
32726                         }
32727                     }
32728                                     );
32729             }
32730
32731             o = o + Align(s, align_const);
32732         }
32733         seg = heap_segment_next_rw (seg);
32734     }
32735
32736     //printf ("didn't find any large object large enough...\n");
32737     //printf ("finished verifying loh\n");
32738 #endif //BACKGROUND_GC 
32739 }
32740
32741 #ifdef VERIFY_HEAP
32742
32743 void 
32744 gc_heap::verify_free_lists ()
32745 {
32746     for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
32747     {
32748         dprintf (3, ("Verifying free list for gen:%d", gen_num));
32749         allocator* gen_alloc = generation_allocator (generation_of (gen_num));
32750         size_t sz = gen_alloc->first_bucket_size();
32751         bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
32752
32753         for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
32754         {
32755             uint8_t* free_list = gen_alloc->alloc_list_head_of (a_l_number);
32756             uint8_t* prev = 0;
32757             while (free_list)
32758             {
32759                 if (!((CObjectHeader*)free_list)->IsFree())
32760                 {
32761                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
32762                                  (size_t)free_list));
32763                     FATAL_GC_ERROR();
32764                 }
32765                 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
32766                     || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
32767                 {
32768                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
32769                                  (size_t)free_list));
32770                     FATAL_GC_ERROR();
32771                 }
32772                 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
32773                 {
32774                     dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
32775                                  (size_t)free_list));
32776                     FATAL_GC_ERROR();
32777                 }
32778                 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
32779                 {
32780                     dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
32781                                  (size_t)free_list));
32782                     FATAL_GC_ERROR();
32783                 }
32784                     
32785                 prev = free_list;
32786                 free_list = free_list_slot (free_list);
32787             }
32788             //verify the sanity of the tail 
32789             uint8_t* tail = gen_alloc->alloc_list_tail_of (a_l_number);
32790             if (!((tail == 0) || (tail == prev)))
32791             {
32792                 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
32793                 FATAL_GC_ERROR();
32794             }
32795             if (tail == 0)
32796             {
32797                 uint8_t* head = gen_alloc->alloc_list_head_of (a_l_number);
32798                 if ((head != 0) && (free_list_slot (head) != 0))
32799                 {
32800                     dprintf (3, ("Verifying Heap: tail of free list is not correct"));
32801                     FATAL_GC_ERROR();
32802                 }
32803             }
32804
32805             sz *=2;
32806         }
32807     }
32808 }
32809
32810 void
32811 gc_heap::verify_heap (BOOL begin_gc_p)
32812 {
32813     int             heap_verify_level = static_cast<int>(GCConfig::GetHeapVerifyLevel());
32814     size_t          last_valid_brick = 0;
32815     BOOL            bCurrentBrickInvalid = FALSE;
32816     BOOL            large_brick_p = TRUE;
32817     size_t          curr_brick = 0;
32818     size_t          prev_brick = (size_t)-1;
32819     int             curr_gen_num = max_generation+1;    
32820     heap_segment*   seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
32821
32822     PREFIX_ASSUME(seg != NULL);
32823
32824     uint8_t*        curr_object = heap_segment_mem (seg);
32825     uint8_t*        prev_object = 0;
32826     uint8_t*        begin_youngest = generation_allocation_start(generation_of(0));
32827     uint8_t*        end_youngest = heap_segment_allocated (ephemeral_heap_segment);
32828     uint8_t*        next_boundary = generation_allocation_start (generation_of (max_generation - 1));
32829     int             align_const = get_alignment_constant (FALSE);
32830     size_t          total_objects_verified = 0;
32831     size_t          total_objects_verified_deep = 0;
32832
32833 #ifdef BACKGROUND_GC
32834     BOOL consider_bgc_mark_p    = FALSE;
32835     BOOL check_current_sweep_p  = FALSE;
32836     BOOL check_saved_sweep_p    = FALSE;
32837     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32838 #endif //BACKGROUND_GC
32839
32840 #ifdef MULTIPLE_HEAPS
32841     t_join* current_join = &gc_t_join;
32842 #ifdef BACKGROUND_GC
32843     if (settings.concurrent && (bgc_thread_id.IsCurrentThread()))
32844     {
32845         // We always call verify_heap on entry of GC on the SVR GC threads.
32846         current_join = &bgc_t_join;
32847     }
32848 #endif //BACKGROUND_GC
32849 #endif //MULTIPLE_HEAPS
32850
32851     UNREFERENCED_PARAMETER(begin_gc_p);
32852 #ifdef BACKGROUND_GC 
32853     dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin", 
32854         (begin_gc_p ? "BEG" : "END"),
32855         VolatileLoad(&settings.gc_index), 
32856         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
32857 #else
32858     dprintf (2,("[%s]GC#%d: Verifying heap - begin", 
32859                 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
32860 #endif //BACKGROUND_GC 
32861
32862 #ifndef MULTIPLE_HEAPS
32863     if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
32864         (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
32865     {
32866         FATAL_GC_ERROR();
32867     }
32868 #endif //MULTIPLE_HEAPS
32869
32870 #ifdef BACKGROUND_GC
32871     //don't touch the memory because the program is allocating from it.
32872     if (!settings.concurrent)
32873 #endif //BACKGROUND_GC
32874     {
32875         if (!(heap_verify_level & GCConfig::HEAPVERIFY_NO_MEM_FILL))
32876         {
32877             //uninit the unused portions of segments.
32878             generation* gen1 = large_object_generation;
32879             heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
32880             PREFIX_ASSUME(seg1 != NULL);
32881
32882             while (1)
32883             {
32884                 if (seg1)
32885                 {
32886                     uint8_t* clear_start = heap_segment_allocated (seg1) - plug_skew;
32887                     if (heap_segment_used (seg1) > clear_start)
32888                     {
32889                         dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa", 
32890                                     heap_segment_mem (seg1),
32891                                     clear_start ,
32892                                     heap_segment_used (seg1)));
32893                         memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
32894                             (heap_segment_used (seg1) - clear_start));
32895                     }
32896                     seg1 = heap_segment_next_rw (seg1);
32897                 }
32898                 else
32899                 {
32900                     if (gen1 == large_object_generation)
32901                     {
32902                         gen1 = generation_of (max_generation);
32903                         seg1 = heap_segment_rw (generation_start_segment (gen1));
32904                         PREFIX_ASSUME(seg1 != NULL);
32905                     }
32906                     else
32907                     {
32908                         break;
32909                     }
32910                 }
32911             }
32912         }
32913     }
32914
32915 #ifdef MULTIPLE_HEAPS
32916     current_join->join(this, gc_join_verify_copy_table);
32917     if (current_join->joined())
32918     {
32919         // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
32920         for (int i = 0; i < n_heaps; i++)
32921         {
32922             //copy the card and brick tables
32923             if (g_gc_card_table != g_heaps[i]->card_table)
32924             {
32925                 g_heaps[i]->copy_brick_card_table();
32926             }
32927         }
32928
32929         current_join->restart();
32930     }
32931 #else
32932         if (g_gc_card_table != card_table)
32933             copy_brick_card_table();
32934 #endif //MULTIPLE_HEAPS
32935
32936     //verify that the generation structures makes sense
32937     {
32938         generation* gen = generation_of (max_generation);
32939
32940         assert (generation_allocation_start (gen) ==
32941                 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
32942         int gen_num = max_generation-1;
32943         generation* prev_gen = gen;
32944         while (gen_num >= 0)
32945         {
32946             gen = generation_of (gen_num);
32947             assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
32948             assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
32949             assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
32950
32951             if (generation_start_segment (prev_gen ) ==
32952                 generation_start_segment (gen))
32953             {
32954                 assert (generation_allocation_start (prev_gen) <
32955                         generation_allocation_start (gen));
32956             }
32957             prev_gen = gen;
32958             gen_num--;
32959         }
32960     }
32961
32962     while (1)
32963     {
32964         // Handle segment transitions
32965         if (curr_object >= heap_segment_allocated (seg))
32966         {
32967             if (curr_object > heap_segment_allocated(seg))
32968             {
32969                 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
32970                         (size_t)curr_object, (size_t)seg));
32971                 FATAL_GC_ERROR();
32972             }
32973             seg = heap_segment_next_in_range (seg);
32974             if (seg)
32975             {
32976 #ifdef BACKGROUND_GC
32977                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32978 #endif //BACKGROUND_GC
32979                 curr_object = heap_segment_mem(seg);
32980                 prev_object = 0;
32981                 continue;
32982             }
32983             else
32984             {
32985                 if (curr_gen_num == (max_generation+1))
32986                 {
32987                     curr_gen_num--;
32988                     seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
32989
32990                     PREFIX_ASSUME(seg != NULL);
32991
32992 #ifdef BACKGROUND_GC
32993                     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32994 #endif //BACKGROUND_GC
32995                     curr_object = heap_segment_mem (seg);
32996                     prev_object = 0;
32997                     large_brick_p = FALSE;
32998                     align_const = get_alignment_constant (TRUE);
32999                 }
33000                 else
33001                     break;  // Done Verifying Heap -- no more segments
33002             }
33003         }
33004
33005         // Are we at the end of the youngest_generation?
33006         if (seg == ephemeral_heap_segment)
33007         {
33008             if (curr_object >= end_youngest)
33009             {
33010                 // prev_object length is too long if we hit this int3
33011                 if (curr_object > end_youngest)
33012                 {
33013                     dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
33014                             (size_t)curr_object, (size_t)end_youngest));
33015                     FATAL_GC_ERROR();
33016                 }
33017                 break;
33018             }
33019             
33020             if ((curr_object >= next_boundary) && (curr_gen_num > 0))
33021             {
33022                 curr_gen_num--;
33023                 if (curr_gen_num > 0)
33024                 {
33025                     next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
33026                 }
33027             }
33028         }
33029
33030          //if (is_mark_set (curr_object))
33031          //{
33032          //        printf ("curr_object: %Ix is marked!",(size_t)curr_object);
33033          //        FATAL_GC_ERROR();
33034          //}
33035
33036         size_t s = size (curr_object);
33037         dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
33038         if (s == 0)
33039         {
33040             dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
33041             FATAL_GC_ERROR();
33042         }
33043
33044         // If object is not in the youngest generation, then lets
33045         // verify that the brick table is correct....
33046         if (((seg != ephemeral_heap_segment) ||
33047              (brick_of(curr_object) < brick_of(begin_youngest))))
33048         {
33049             curr_brick = brick_of(curr_object);
33050
33051             // Brick Table Verification...
33052             //
33053             // On brick transition
33054             //     if brick is negative
33055             //          verify that brick indirects to previous valid brick
33056             //     else
33057             //          set current brick invalid flag to be flipped if we
33058             //          encounter an object at the correct place
33059             //
33060             if (curr_brick != prev_brick)
33061             {
33062                 // If the last brick we were examining had positive
33063                 // entry but we never found the matching object, then
33064                 // we have a problem
33065                 // If prev_brick was the last one of the segment
33066                 // it's ok for it to be invalid because it is never looked at
33067                 if (bCurrentBrickInvalid &&
33068                     (curr_brick != brick_of (heap_segment_mem (seg))) &&
33069                     !heap_segment_read_only_p (seg))
33070                 {
33071                     dprintf (3, ("curr brick %Ix invalid", curr_brick));
33072                     FATAL_GC_ERROR();
33073                 }
33074
33075                 if (large_brick_p)
33076                 {
33077                     //large objects verify the table only if they are in
33078                     //range.
33079                     if ((heap_segment_reserved (seg) <= highest_address) &&
33080                         (heap_segment_mem (seg) >= lowest_address) &&
33081                         brick_table [curr_brick] != 0)
33082                     {
33083                         dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
33084                                 curr_brick, (size_t)curr_object));
33085                         FATAL_GC_ERROR();
33086                     }
33087                     else
33088                     {
33089                         bCurrentBrickInvalid = FALSE;
33090                     }
33091                 }
33092                 else
33093                 {
33094                     // If the current brick contains a negative value make sure
33095                     // that the indirection terminates at the last  valid brick
33096                     if (brick_table [curr_brick] <= 0)
33097                     {
33098                         if (brick_table [curr_brick] == 0)
33099                         {
33100                             dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
33101                                     curr_brick, (size_t)curr_object));
33102                             FATAL_GC_ERROR();
33103                         }
33104                         ptrdiff_t i = curr_brick;
33105                         while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
33106                                (brick_table[i] < 0))
33107                         {
33108                             i = i + brick_table[i];
33109                         }
33110                         if (i <  ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
33111                         {
33112                             dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
33113                                     i, brick_of (heap_segment_mem (seg)),
33114                                     curr_brick));
33115                             FATAL_GC_ERROR();
33116                         }
33117                         // if (i != last_valid_brick)
33118                         //  FATAL_GC_ERROR();
33119                         bCurrentBrickInvalid = FALSE;
33120                     }
33121                     else if (!heap_segment_read_only_p (seg))
33122                     {
33123                         bCurrentBrickInvalid = TRUE;
33124                     }
33125                 }
33126             }
33127
33128             if (bCurrentBrickInvalid)
33129             {
33130                 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
33131                 {
33132                     bCurrentBrickInvalid = FALSE;
33133                     last_valid_brick = curr_brick;
33134                 }
33135             }
33136         }
33137
33138         if (*((uint8_t**)curr_object) != (uint8_t *) g_gc_pFreeObjectMethodTable)
33139         {
33140 #ifdef FEATURE_LOH_COMPACTION
33141             if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
33142             {
33143                 assert (method_table (prev_object) == g_gc_pFreeObjectMethodTable);
33144             }
33145 #endif //FEATURE_LOH_COMPACTION
33146
33147             total_objects_verified++;
33148
33149             BOOL can_verify_deep = TRUE;
33150 #ifdef BACKGROUND_GC
33151             can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
33152 #endif //BACKGROUND_GC
33153
33154             BOOL deep_verify_obj = can_verify_deep;
33155             if ((heap_verify_level & GCConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
33156                 deep_verify_obj = FALSE;
33157
33158             ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
33159
33160             if (can_verify_deep)
33161             {
33162                 if (curr_gen_num > 0)
33163                 {
33164                     BOOL need_card_p = FALSE;
33165                     if (contain_pointers_or_collectible (curr_object))
33166                     {
33167                         dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
33168                         size_t crd = card_of (curr_object);
33169                         BOOL found_card_p = card_set_p (crd);
33170
33171 #ifdef COLLECTIBLE_CLASS
33172                         if (is_collectible(curr_object))
33173                         {
33174                             uint8_t* class_obj = get_class_object (curr_object);
33175                             if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
33176                             {
33177                                 if (!found_card_p)
33178                                 {
33179                                     dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
33180                                                 card_of (curr_object), (size_t)curr_object, class_obj));
33181
33182                                     FATAL_GC_ERROR();
33183                                 }
33184                             }
33185                         }
33186 #endif //COLLECTIBLE_CLASS
33187
33188                         if (contain_pointers(curr_object))
33189                         {
33190                             go_through_object_nostart
33191                                 (method_table(curr_object), curr_object, s, oo,
33192                                 {
33193                                     if ((crd != card_of ((uint8_t*)oo)) && !found_card_p)
33194                                     {
33195                                         crd = card_of ((uint8_t*)oo);
33196                                         found_card_p = card_set_p (crd);
33197                                         need_card_p = FALSE;
33198                                     }
33199                                     if ((*oo < ephemeral_high) && (*oo >= next_boundary))
33200                                     {
33201                                         need_card_p = TRUE;
33202                                     }
33203
33204                                 if (need_card_p && !found_card_p)
33205                                 {
33206
33207                                         dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33208                                                     card_of (curr_object), (size_t)curr_object,
33209                                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33210                                         FATAL_GC_ERROR();
33211                                     }
33212                                 }
33213                                     );
33214                         }
33215                         if (need_card_p && !found_card_p)
33216                         {
33217                             dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33218                                     card_of (curr_object), (size_t)curr_object,
33219                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33220                             FATAL_GC_ERROR();
33221                         }
33222                     }
33223                 }
33224                 total_objects_verified_deep++;
33225             }
33226         }
33227
33228         prev_object = curr_object;
33229         prev_brick = curr_brick;
33230         curr_object = curr_object + Align(s, align_const);
33231         if (curr_object < prev_object)
33232         {
33233             dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
33234             FATAL_GC_ERROR();
33235         }
33236     }
33237
33238 #ifdef BACKGROUND_GC
33239     dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id", 
33240                  (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
33241                  (begin_gc_p ? "BEG" : "END"),
33242                  ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
33243                  total_objects_verified, total_objects_verified_deep));
33244     if (current_c_gc_state != c_gc_state_planning)
33245     {
33246         assert (total_objects_verified == total_objects_verified_deep);
33247     }
33248 #endif //BACKGROUND_GC
33249     
33250     verify_free_lists();
33251
33252 #ifdef FEATURE_PREMORTEM_FINALIZATION
33253     finalize_queue->CheckFinalizerObjects();
33254 #endif // FEATURE_PREMORTEM_FINALIZATION
33255
33256     {
33257         // to be consistent with handle table APIs pass a ScanContext*
33258         // to provide the heap number.  the SC isn't complete though so
33259         // limit its scope to handle table verification.
33260         ScanContext sc;
33261         sc.thread_number = heap_number;
33262         GCScan::VerifyHandleTable(max_generation, max_generation, &sc);
33263     }
33264
33265 #ifdef MULTIPLE_HEAPS
33266     current_join->join(this, gc_join_verify_objects_done);
33267     if (current_join->joined())
33268 #endif //MULTIPLE_HEAPS
33269     {
33270         SyncBlockCache::GetSyncBlockCache()->VerifySyncTableEntry();
33271 #ifdef MULTIPLE_HEAPS
33272         current_join->restart();
33273 #endif //MULTIPLE_HEAPS
33274     }
33275
33276 #ifdef BACKGROUND_GC 
33277     if (!settings.concurrent)
33278     {
33279         if (current_c_gc_state == c_gc_state_planning)
33280         {
33281             // temporarily commenting this out 'cause an FGC
33282             // could be triggered before we sweep ephemeral.
33283             //verify_seg_end_mark_array_cleared();
33284         }
33285     }
33286
33287     if (settings.concurrent)
33288     {
33289         verify_mark_array_cleared();
33290     }
33291     dprintf (2,("GC%d(%s): Verifying heap - end", 
33292         VolatileLoad(&settings.gc_index), 
33293         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
33294 #else
33295     dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
33296 #endif //BACKGROUND_GC 
33297 }
33298
33299 #endif  //VERIFY_HEAP
33300
33301
33302 void GCHeap::ValidateObjectMember (Object* obj)
33303 {
33304 #ifdef VERIFY_HEAP
33305     size_t s = size (obj);
33306     uint8_t* o = (uint8_t*)obj;
33307
33308     go_through_object_cl (method_table (obj), o, s, oo,
33309                                 {
33310                                     uint8_t* child_o = *oo;
33311                                     if (child_o)
33312                                     {
33313                                         dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
33314                                         MethodTable *pMT = method_table (child_o);
33315                                         assert(pMT);
33316                                         if (!pMT->SanityCheck()) {
33317                                             dprintf (3, ("Bad member of %Ix %Ix",
33318                                                         (size_t)oo, (size_t)child_o));
33319                                             FATAL_GC_ERROR();
33320                                         }
33321                                     }
33322                                 } );
33323 #endif // VERIFY_HEAP
33324 }
33325
33326 void DestructObject (CObjectHeader* hdr)
33327 {
33328     UNREFERENCED_PARAMETER(hdr); // compiler bug? -- this *is*, indeed, referenced
33329     hdr->~CObjectHeader();
33330 }
33331
33332 HRESULT GCHeap::Shutdown ()
33333 {
33334     deleteGCShadow();
33335
33336     GCScan::GcRuntimeStructuresValid (FALSE);
33337
33338     // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
33339     // threads except the one performing the shutdown.
33340     // ASSERT( !GcInProgress );
33341
33342     // Guard against any more GC occurring and against any threads blocking
33343     // for GC to complete when the GC heap is gone.  This fixes a race condition
33344     // where a thread in GC is destroyed as part of process destruction and
33345     // the remaining threads block for GC complete.
33346
33347     //GCTODO
33348     //EnterAllocLock();
33349     //Enter();
33350     //EnterFinalizeLock();
33351     //SetGCDone();
33352
33353     // during shutdown lot of threads are suspended
33354     // on this even, we don't want to wake them up just yet
33355     //CloseHandle (WaitForGCEvent);
33356
33357     //find out if the global card table hasn't been used yet
33358     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
33359     if (card_table_refcount (ct) == 0)
33360     {
33361         destroy_card_table (ct);
33362         g_gc_card_table = nullptr;
33363
33364 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
33365         g_gc_card_bundle_table = nullptr;
33366 #endif
33367 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33368         SoftwareWriteWatch::StaticClose();
33369 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33370     }
33371
33372     //destroy all segments on the standby list
33373     while(gc_heap::segment_standby_list != 0)
33374     {
33375         heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
33376 #ifdef MULTIPLE_HEAPS
33377         (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33378 #else //MULTIPLE_HEAPS
33379         pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33380 #endif //MULTIPLE_HEAPS
33381         gc_heap::segment_standby_list = next_seg;
33382     }
33383
33384
33385 #ifdef MULTIPLE_HEAPS
33386
33387     for (int i = 0; i < gc_heap::n_heaps; i ++)
33388     {
33389         delete gc_heap::g_heaps[i]->vm_heap;
33390         //destroy pure GC stuff
33391         gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
33392     }
33393 #else
33394     gc_heap::destroy_gc_heap (pGenGCHeap);
33395
33396 #endif //MULTIPLE_HEAPS
33397     gc_heap::shutdown_gc();
33398
33399     return S_OK;
33400 }
33401
33402 // Wait until a garbage collection is complete
33403 // returns NOERROR if wait was OK, other error code if failure.
33404 // WARNING: This will not undo the must complete state. If you are
33405 // in a must complete when you call this, you'd better know what you're
33406 // doing.
33407
33408 #ifdef FEATURE_PREMORTEM_FINALIZATION
33409 static
33410 HRESULT AllocateCFinalize(CFinalize **pCFinalize)
33411 {
33412     *pCFinalize = new (nothrow) CFinalize();
33413     if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
33414         return E_OUTOFMEMORY;
33415
33416     return S_OK;
33417 }
33418 #endif // FEATURE_PREMORTEM_FINALIZATION
33419
33420 // init the instance heap
33421 HRESULT GCHeap::Init(size_t hn)
33422 {
33423     HRESULT hres = S_OK;
33424
33425 #ifdef MULTIPLE_HEAPS
33426     if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
33427         hres = E_OUTOFMEMORY;
33428 #else
33429     UNREFERENCED_PARAMETER(hn);
33430     if (!gc_heap::make_gc_heap())
33431         hres = E_OUTOFMEMORY;
33432 #endif //MULTIPLE_HEAPS
33433
33434     // Failed.
33435     return hres;
33436 }
33437
33438 //System wide initialization
33439 HRESULT GCHeap::Initialize ()
33440 {
33441     HRESULT hr = S_OK;
33442
33443     g_gc_pFreeObjectMethodTable = GCToEEInterface::GetFreeObjectMethodTable();
33444     g_num_processors = GCToOSInterface::GetTotalProcessorCount();
33445     assert(g_num_processors != 0);
33446
33447 //Initialize the static members.
33448 #ifdef TRACE_GC
33449     GcDuration = 0;
33450     CreatedObjectCount = 0;
33451 #endif //TRACE_GC
33452
33453     size_t seg_size = get_valid_segment_size();
33454     gc_heap::soh_segment_size = seg_size;
33455     size_t large_seg_size = get_valid_segment_size(TRUE);
33456     gc_heap::min_loh_segment_size = large_seg_size;
33457     gc_heap::min_segment_size = min (seg_size, large_seg_size);
33458 #ifdef SEG_MAPPING_TABLE
33459     gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_heap::min_segment_size);
33460 #endif //SEG_MAPPING_TABLE
33461
33462 #ifdef MULTIPLE_HEAPS
33463     if (GCConfig::GetNoAffinitize())
33464         gc_heap::gc_thread_no_affinitize_p = true;
33465
33466     uint32_t nhp_from_config = static_cast<uint32_t>(GCConfig::GetHeapCount());
33467     
33468     uint32_t nhp_from_process = GCToOSInterface::GetCurrentProcessCpuCount();
33469
33470     uint32_t nhp = ((nhp_from_config == 0) ? nhp_from_process :
33471                                              (min (nhp_from_config, nhp_from_process)));
33472
33473     nhp = min (nhp, MAX_SUPPORTED_CPUS);
33474
33475     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
33476 #else
33477     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
33478 #endif //MULTIPLE_HEAPS
33479
33480     if (hr != S_OK)
33481         return hr;
33482
33483     gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit();
33484
33485     gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
33486 #ifndef MULTIPLE_HEAPS
33487     gc_heap::mem_one_percent /= g_num_processors;
33488 #endif //!MULTIPLE_HEAPS
33489
33490     // We should only use this if we are in the "many process" mode which really is only applicable
33491     // to very powerful machines - before that's implemented, temporarily I am only enabling this for 80GB+ memory. 
33492     // For now I am using an estimate to calculate these numbers but this should really be obtained 
33493     // programmatically going forward.
33494     // I am assuming 47 processes using WKS GC and 3 using SVR GC.
33495     // I am assuming 3 in part due to the "very high memory load" is 97%.
33496     int available_mem_th = 10;
33497     if (gc_heap::total_physical_mem >= ((uint64_t)80 * 1024 * 1024 * 1024))
33498     {
33499         int adjusted_available_mem_th = 3 + (int)((float)47 / (float)(g_num_processors));
33500         available_mem_th = min (available_mem_th, adjusted_available_mem_th);
33501     }
33502
33503     gc_heap::high_memory_load_th = 100 - available_mem_th;
33504
33505 #if defined(BIT64) 
33506     gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
33507 #endif // BIT64
33508
33509     WaitForGCEvent = new (nothrow) GCEvent;
33510
33511     if (!WaitForGCEvent)
33512     {
33513         return E_OUTOFMEMORY;
33514     }
33515
33516     if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE))
33517     {
33518         return E_FAIL;
33519     }
33520
33521 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33522 #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
33523     if (GCStress<cfg_any>::IsEnabled())  {
33524         for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
33525             m_StressObjs[i] = CreateGlobalHandle(0);
33526         m_CurStressObj = 0;
33527     }
33528 #endif //STRESS_HEAP && !MULTIPLE_HEAPS
33529 #endif // FEATURE_REDHAWK
33530
33531     initGCShadow();         // If we are debugging write barriers, initialize heap shadow
33532
33533 #ifdef MULTIPLE_HEAPS
33534
33535     for (unsigned i = 0; i < nhp; i++)
33536     {
33537         GCHeap* Hp = new (nothrow) GCHeap();
33538         if (!Hp)
33539             return E_OUTOFMEMORY;
33540
33541         if ((hr = Hp->Init (i))!= S_OK)
33542         {
33543             return hr;
33544         }
33545     }
33546     // initialize numa node to heap map
33547     heap_select::init_numa_node_to_heap_map(nhp);
33548 #else
33549     hr = Init (0);
33550 #endif //MULTIPLE_HEAPS
33551
33552     if (hr == S_OK)
33553     {
33554         GCScan::GcRuntimeStructuresValid (TRUE);
33555
33556         GCToEEInterface::DiagUpdateGenerationBounds();
33557     }
33558
33559     return hr;
33560 };
33561
33562 ////
33563 // GC callback functions
33564 bool GCHeap::IsPromoted(Object* object)
33565 {
33566 #ifdef _DEBUG
33567     ((CObjectHeader*)object)->Validate();
33568 #endif //_DEBUG
33569
33570     uint8_t* o = (uint8_t*)object;
33571
33572     if (gc_heap::settings.condemned_generation == max_generation)
33573     {
33574 #ifdef MULTIPLE_HEAPS
33575         gc_heap* hp = gc_heap::g_heaps[0];
33576 #else
33577         gc_heap* hp = pGenGCHeap;
33578 #endif //MULTIPLE_HEAPS
33579
33580 #ifdef BACKGROUND_GC
33581         if (gc_heap::settings.concurrent)
33582         {
33583             bool is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
33584                             hp->background_marked (o));
33585             return is_marked;
33586         }
33587         else
33588 #endif //BACKGROUND_GC
33589         {
33590             return (!((o < hp->highest_address) && (o >= hp->lowest_address))
33591                     || hp->is_mark_set (o));
33592         }
33593     }
33594     else
33595     {
33596         gc_heap* hp = gc_heap::heap_of (o);
33597         return (!((o < hp->gc_high) && (o >= hp->gc_low))
33598                 || hp->is_mark_set (o));
33599     }
33600 }
33601
33602 size_t GCHeap::GetPromotedBytes(int heap_index)
33603 {
33604 #ifdef BACKGROUND_GC
33605     if (gc_heap::settings.concurrent)
33606     {
33607         return gc_heap::bpromoted_bytes (heap_index);
33608     }
33609     else
33610 #endif //BACKGROUND_GC
33611     {
33612         return gc_heap::promoted_bytes (heap_index);
33613     }
33614 }
33615
33616 unsigned int GCHeap::WhichGeneration (Object* object)
33617 {
33618     gc_heap* hp = gc_heap::heap_of ((uint8_t*)object);
33619     unsigned int g = hp->object_gennum ((uint8_t*)object);
33620     dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
33621     return g;
33622 }
33623
33624 bool GCHeap::IsEphemeral (Object* object)
33625 {
33626     uint8_t* o = (uint8_t*)object;
33627     gc_heap* hp = gc_heap::heap_of (o);
33628     return !!hp->ephemeral_pointer_p (o);
33629 }
33630
33631 // Return NULL if can't find next object. When EE is not suspended,
33632 // the result is not accurate: if the input arg is in gen0, the function could 
33633 // return zeroed out memory as next object
33634 Object * GCHeap::NextObj (Object * object)
33635 {
33636 #ifdef VERIFY_HEAP
33637     uint8_t* o = (uint8_t*)object;
33638
33639 #ifndef FEATURE_BASICFREEZE
33640     if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)))
33641     {
33642         return NULL;
33643     }
33644 #endif //!FEATURE_BASICFREEZE
33645
33646     heap_segment * hs = gc_heap::find_segment (o, FALSE);
33647     if (!hs)
33648     {
33649         return NULL;
33650     }
33651
33652     BOOL large_object_p = heap_segment_loh_p (hs);
33653     if (large_object_p)
33654         return NULL; //could be racing with another core allocating. 
33655 #ifdef MULTIPLE_HEAPS
33656     gc_heap* hp = heap_segment_heap (hs);
33657 #else //MULTIPLE_HEAPS
33658     gc_heap* hp = 0;
33659 #endif //MULTIPLE_HEAPS
33660     unsigned int g = hp->object_gennum ((uint8_t*)object);
33661     if ((g == 0) && hp->settings.demotion)
33662         return NULL;//could be racing with another core allocating. 
33663     int align_const = get_alignment_constant (!large_object_p);
33664     uint8_t* nextobj = o + Align (size (o), align_const);
33665     if (nextobj <= o) // either overflow or 0 sized object.
33666     {
33667         return NULL;
33668     }
33669
33670     if ((nextobj < heap_segment_mem(hs)) || 
33671         (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) || 
33672         (nextobj >= hp->alloc_allocated))
33673     {
33674         return NULL;
33675     }
33676
33677     return (Object *)nextobj;
33678 #else
33679     return nullptr;
33680 #endif // VERIFY_HEAP
33681 }
33682
33683 #ifdef VERIFY_HEAP
33684
33685 #ifdef FEATURE_BASICFREEZE
33686 BOOL GCHeap::IsInFrozenSegment (Object * object)
33687 {
33688     uint8_t* o = (uint8_t*)object;
33689     heap_segment * hs = gc_heap::find_segment (o, FALSE);
33690     //We create a frozen object for each frozen segment before the segment is inserted
33691     //to segment list; during ngen, we could also create frozen objects in segments which
33692     //don't belong to current GC heap.
33693     //So we return true if hs is NULL. It might create a hole about detecting invalidate 
33694     //object. But given all other checks present, the hole should be very small
33695     return !hs || heap_segment_read_only_p (hs);
33696 }
33697 #endif //FEATURE_BASICFREEZE
33698
33699 #endif //VERIFY_HEAP
33700
33701 // returns TRUE if the pointer is in one of the GC heaps.
33702 bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only)
33703 {
33704     STATIC_CONTRACT_SO_TOLERANT;
33705
33706     // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment 
33707     // no longer calls GCEvent::Wait which eventually takes a lock.
33708
33709     uint8_t* object = (uint8_t*) vpObject;
33710 #ifndef FEATURE_BASICFREEZE
33711     if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address)))
33712         return FALSE;
33713 #endif //!FEATURE_BASICFREEZE
33714
33715     heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
33716     return !!hs;
33717 }
33718
33719 #ifdef STRESS_PINNING
33720 static n_promote = 0;
33721 #endif //STRESS_PINNING
33722 // promote an object
33723 void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
33724 {
33725     THREAD_NUMBER_FROM_CONTEXT;
33726 #ifndef MULTIPLE_HEAPS
33727     const int thread = 0;
33728 #endif //!MULTIPLE_HEAPS
33729
33730     uint8_t* o = (uint8_t*)*ppObject;
33731
33732     if (o == 0)
33733         return;
33734
33735 #ifdef DEBUG_DestroyedHandleValue
33736     // we can race with destroy handle during concurrent scan
33737     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
33738         return;
33739 #endif //DEBUG_DestroyedHandleValue
33740
33741     HEAP_FROM_THREAD;
33742
33743     gc_heap* hp = gc_heap::heap_of (o);
33744
33745     dprintf (3, ("Promote %Ix", (size_t)o));
33746
33747 #ifdef INTERIOR_POINTERS
33748     if (flags & GC_CALL_INTERIOR)
33749     {
33750         if ((o < hp->gc_low) || (o >= hp->gc_high))
33751         {
33752             return;
33753         }
33754         if ( (o = hp->find_object (o, hp->gc_low)) == 0)
33755         {
33756             return;
33757         }
33758
33759     }
33760 #endif //INTERIOR_POINTERS
33761
33762 #ifdef FEATURE_CONSERVATIVE_GC
33763     // For conservative GC, a value on stack may point to middle of a free object.
33764     // In this case, we don't need to promote the pointer.
33765     if (GCConfig::GetConservativeGC()
33766         && ((CObjectHeader*)o)->IsFree())
33767     {
33768         return;
33769     }
33770 #endif
33771
33772 #ifdef _DEBUG
33773     ((CObjectHeader*)o)->ValidatePromote(sc, flags);
33774 #else 
33775     UNREFERENCED_PARAMETER(sc);
33776 #endif //_DEBUG
33777
33778     if (flags & GC_CALL_PINNED)
33779         hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
33780
33781 #ifdef STRESS_PINNING
33782     if ((++n_promote % 20) == 1)
33783             hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
33784 #endif //STRESS_PINNING
33785
33786 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33787     size_t promoted_size_begin = hp->promoted_bytes (thread);
33788 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33789
33790     if ((o >= hp->gc_low) && (o < hp->gc_high))
33791     {
33792         hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
33793     }
33794
33795 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33796     size_t promoted_size_end = hp->promoted_bytes (thread);
33797     if (g_fEnableARM)
33798     {
33799         if (sc->pCurrentDomain)
33800         {
33801             sc->pCurrentDomain->RecordSurvivedBytes ((promoted_size_end - promoted_size_begin), thread);
33802         }
33803     }
33804 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33805
33806     STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
33807 }
33808
33809 void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
33810                        uint32_t flags)
33811 {
33812     UNREFERENCED_PARAMETER(sc);
33813
33814     uint8_t* object = (uint8_t*)(Object*)(*ppObject);
33815     
33816     THREAD_NUMBER_FROM_CONTEXT;
33817
33818     //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
33819     dprintf (3, ("R: %Ix", (size_t)ppObject));
33820     
33821     if (object == 0)
33822         return;
33823
33824     gc_heap* hp = gc_heap::heap_of (object);
33825
33826 #ifdef _DEBUG
33827     if (!(flags & GC_CALL_INTERIOR))
33828     {
33829         // We cannot validate this object if it's in the condemned gen because it could 
33830         // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
33831         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
33832         {
33833             ((CObjectHeader*)object)->Validate(FALSE);
33834         }
33835     }
33836 #endif //_DEBUG
33837
33838     dprintf (3, ("Relocate %Ix\n", (size_t)object));
33839
33840     uint8_t* pheader;
33841
33842     if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
33843     {
33844         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
33845         {
33846             return;
33847         }
33848
33849         if (gc_heap::loh_object_p (object))
33850         {
33851             pheader = hp->find_object (object, 0);
33852             if (pheader == 0)
33853             {
33854                 return;
33855             }
33856
33857             ptrdiff_t ref_offset = object - pheader;
33858             hp->relocate_address(&pheader THREAD_NUMBER_ARG);
33859             *ppObject = (Object*)(pheader + ref_offset);
33860             return;
33861         }
33862     }
33863
33864     {
33865         pheader = object;
33866         hp->relocate_address(&pheader THREAD_NUMBER_ARG);
33867         *ppObject = (Object*)pheader;
33868     }
33869
33870     STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
33871 }
33872
33873 /*static*/ bool GCHeap::IsObjectInFixedHeap(Object *pObj)
33874 {
33875     // For now we simply look at the size of the object to determine if it in the
33876     // fixed heap or not. If the bit indicating this gets set at some point
33877     // we should key off that instead.
33878     return size( pObj ) >= LARGE_OBJECT_SIZE;
33879 }
33880
33881 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33882 #ifdef STRESS_HEAP
33883
33884 void StressHeapDummy ();
33885
33886 static int32_t GCStressStartCount = -1;
33887 static int32_t GCStressCurCount = 0;
33888 static int32_t GCStressStartAtJit = -1;
33889
33890 // the maximum number of foreground GCs we'll induce during one BGC
33891 // (this number does not include "naturally" occuring GCs).
33892 static int32_t GCStressMaxFGCsPerBGC = -1;
33893
33894 // CLRRandom implementation can produce FPU exceptions if 
33895 // the test/application run by CLR is enabling any FPU exceptions. 
33896 // We want to avoid any unexpected exception coming from stress 
33897 // infrastructure, so CLRRandom is not an option.
33898 // The code below is a replicate of CRT rand() implementation.
33899 // Using CRT rand() is not an option because we will interfere with the user application
33900 // that may also use it. 
33901 int StressRNG(int iMaxValue)
33902 {
33903     static BOOL bisRandInit = FALSE;
33904     static int lHoldrand = 1L;
33905
33906     if (!bisRandInit)
33907     {
33908         lHoldrand = (int)time(NULL);
33909         bisRandInit = TRUE;
33910     }
33911     int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
33912     return randValue % iMaxValue;
33913 }
33914 #endif // STRESS_HEAP
33915 #endif // !FEATURE_REDHAWK
33916
33917 // free up object so that things will move and then do a GC
33918 //return TRUE if GC actually happens, otherwise FALSE
33919 bool GCHeap::StressHeap(gc_alloc_context * context)
33920 {
33921 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
33922     alloc_context* acontext = static_cast<alloc_context*>(context);
33923     assert(context != nullptr);
33924
33925     // if GC stress was dynamically disabled during this run we return FALSE
33926     if (!GCStressPolicy::IsEnabled())
33927         return FALSE;
33928
33929 #ifdef _DEBUG
33930     if (g_pConfig->FastGCStressLevel() && !GCToEEInterface::GetThread()->StressHeapIsEnabled()) {
33931         return FALSE;
33932     }
33933
33934 #endif //_DEBUG
33935
33936     if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
33937 #ifdef _DEBUG
33938         || g_pConfig->FastGCStressLevel() > 1
33939 #endif //_DEBUG
33940         ) {
33941         if (!Thread::UniqueStack(&acontext)) {
33942             return FALSE;
33943         }
33944     }
33945
33946 #ifdef BACKGROUND_GC
33947         // don't trigger a GC from the GC threads but still trigger GCs from user threads.
33948         if (GCToEEInterface::WasCurrentThreadCreatedByGC())
33949         {
33950             return FALSE;
33951         }
33952 #endif //BACKGROUND_GC
33953
33954         if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
33955         {
33956             GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
33957             GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
33958         }
33959
33960         if (GCStressMaxFGCsPerBGC == -1)
33961         {
33962             GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
33963             if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
33964                 GCStressMaxFGCsPerBGC = 6;
33965         }
33966
33967 #ifdef _DEBUG
33968         if (g_JitCount < GCStressStartAtJit)
33969             return FALSE;
33970 #endif //_DEBUG
33971
33972         // Allow programmer to skip the first N Stress GCs so that you can
33973         // get to the interesting ones faster.
33974         Interlocked::Increment(&GCStressCurCount);
33975         if (GCStressCurCount < GCStressStartCount)
33976             return FALSE;
33977
33978         // throttle the number of stress-induced GCs by a factor given by GCStressStep
33979         if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
33980         {
33981             return FALSE;
33982         }
33983
33984 #ifdef BACKGROUND_GC
33985         if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
33986         {
33987             // allow a maximum number of stress induced FGCs during one BGC
33988             if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
33989                 return FALSE;
33990             ++gc_stress_fgcs_in_bgc;
33991         }
33992 #endif // BACKGROUND_GC
33993
33994     if (g_pStringClass == 0)
33995     {
33996         // If the String class has not been loaded, dont do any stressing. This should
33997         // be kept to a minimum to get as complete coverage as possible.
33998         _ASSERTE(g_fEEInit);
33999         return FALSE;
34000     }
34001
34002 #ifndef MULTIPLE_HEAPS
34003     static int32_t OneAtATime = -1;
34004
34005     // Only bother with this if the stress level is big enough and if nobody else is
34006     // doing it right now.  Note that some callers are inside the AllocLock and are
34007     // guaranteed synchronized.  But others are using AllocationContexts and have no
34008     // particular synchronization.
34009     //
34010     // For this latter case, we want a very high-speed way of limiting this to one
34011     // at a time.  A secondary advantage is that we release part of our StressObjs
34012     // buffer sparingly but just as effectively.
34013
34014     if (Interlocked::Increment(&OneAtATime) == 0 &&
34015         !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
34016     {
34017         StringObject* str;
34018
34019         // If the current string is used up
34020         if (HndFetchHandle(m_StressObjs[m_CurStressObj]) == 0)
34021         {
34022             // Populate handles with strings
34023             int i = m_CurStressObj;
34024             while(HndFetchHandle(m_StressObjs[i]) == 0)
34025             {
34026                 _ASSERTE(m_StressObjs[i] != 0);
34027                 unsigned strLen = (LARGE_OBJECT_SIZE - 32) / sizeof(WCHAR);
34028                 unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
34029                 
34030                 // update the cached type handle before allocating
34031                 SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
34032                 str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
34033                 if (str)
34034                 {
34035                     str->SetMethodTable (g_pStringClass);
34036                     str->SetStringLength (strLen);
34037
34038                     HndAssignHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
34039                 }
34040                 i = (i + 1) % NUM_HEAP_STRESS_OBJS;
34041                 if (i == m_CurStressObj) break;
34042             }
34043
34044             // advance the current handle to the next string
34045             m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
34046         }
34047
34048         // Get the current string
34049         str = (StringObject*) OBJECTREFToObject(HndFetchHandle(m_StressObjs[m_CurStressObj]));
34050         if (str)
34051         {
34052             // Chop off the end of the string and form a new object out of it.
34053             // This will 'free' an object at the begining of the heap, which will
34054             // force data movement.  Note that we can only do this so many times.
34055             // before we have to move on to the next string.
34056             unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
34057             if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
34058             {
34059                 unsigned sizeToNextObj = (unsigned)Align(size(str));
34060                 uint8_t* freeObj = ((uint8_t*) str) + sizeToNextObj - sizeOfNewObj;
34061                 pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);                    
34062                 str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
34063             }
34064             else
34065             {
34066                 // Let the string itself become garbage.
34067                 // will be realloced next time around
34068                 HndAssignHandle(m_StressObjs[m_CurStressObj], 0);
34069             }
34070         }
34071     }
34072     Interlocked::Decrement(&OneAtATime);
34073 #endif // !MULTIPLE_HEAPS
34074     if (IsConcurrentGCEnabled())
34075     {
34076         int rgen = StressRNG(10);
34077
34078         // gen0:gen1:gen2 distribution: 40:40:20
34079         if (rgen >= 8)
34080             rgen = 2;
34081         else if (rgen >= 4)
34082             rgen = 1;
34083     else
34084             rgen = 0;
34085
34086         GarbageCollectTry (rgen, FALSE, collection_gcstress);
34087     }
34088     else
34089     {
34090         GarbageCollect(max_generation, FALSE, collection_gcstress);
34091     }
34092
34093     return TRUE;
34094 #else
34095     UNREFERENCED_PARAMETER(context);
34096     return FALSE;
34097 #endif // defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34098 }
34099
34100
34101 #ifdef FEATURE_PREMORTEM_FINALIZATION
34102 #define REGISTER_FOR_FINALIZATION(_object, _size) \
34103     hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
34104 #else // FEATURE_PREMORTEM_FINALIZATION
34105 #define REGISTER_FOR_FINALIZATION(_object, _size) true
34106 #endif // FEATURE_PREMORTEM_FINALIZATION
34107
34108 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {  \
34109     if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size)))   \
34110     {                                                                                       \
34111         STRESS_LOG_OOM_STACK(_size);                                                        \
34112         return NULL;                                                                        \
34113     }                                                                                       \
34114 } while (false)
34115
34116 //
34117 // Small Object Allocator
34118 //
34119 //
34120 // Allocate small object with an alignment requirement of 8-bytes.
34121 Object*
34122 GCHeap::AllocAlign8(gc_alloc_context* ctx, size_t size, uint32_t flags )
34123 {
34124 #ifdef FEATURE_64BIT_ALIGNMENT
34125     CONTRACTL {
34126         NOTHROW;
34127         GC_TRIGGERS;
34128     } CONTRACTL_END;
34129
34130     alloc_context* acontext = static_cast<alloc_context*>(ctx);
34131
34132 #ifdef MULTIPLE_HEAPS
34133     if (acontext->get_alloc_heap() == 0)
34134     {
34135         AssignHeap (acontext);
34136         assert (acontext->get_alloc_heap());
34137     }
34138
34139     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34140 #else
34141     gc_heap* hp = pGenGCHeap;
34142 #endif //MULTIPLE_HEAPS
34143
34144     return AllocAlign8Common(hp, acontext, size, flags);
34145 #else
34146     UNREFERENCED_PARAMETER(ctx);
34147     UNREFERENCED_PARAMETER(size);
34148     UNREFERENCED_PARAMETER(flags);
34149     assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!");
34150     return nullptr;
34151 #endif  //FEATURE_64BIT_ALIGNMENT
34152 }
34153
34154 // Common code used by both variants of AllocAlign8 above.
34155 Object*
34156 GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, uint32_t flags)
34157 {
34158 #ifdef FEATURE_64BIT_ALIGNMENT
34159     CONTRACTL {
34160         NOTHROW;
34161         GC_TRIGGERS;
34162     } CONTRACTL_END;
34163
34164     gc_heap* hp = (gc_heap*)_hp;
34165
34166     TRIGGERSGC();
34167
34168     Object* newAlloc = NULL;
34169
34170 #ifdef TRACE_GC
34171 #ifdef COUNT_CYCLES
34172     AllocStart = GetCycleCount32();
34173     unsigned finish;
34174 #elif defined(ENABLE_INSTRUMENTATION)
34175     unsigned AllocStart = GetInstLogTime();
34176     unsigned finish;
34177 #endif //COUNT_CYCLES
34178 #endif //TRACE_GC
34179
34180     if (size < LARGE_OBJECT_SIZE)
34181     {
34182 #ifdef TRACE_GC
34183         AllocSmallCount++;
34184 #endif //TRACE_GC
34185
34186         // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
34187         // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
34188         // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
34189         size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
34190
34191         // Retrieve the address of the next allocation from the context (note that we're inside the alloc
34192         // lock at this point).
34193         uint8_t*  result = acontext->alloc_ptr;
34194
34195         // Will an allocation at this point yield the correct alignment and fit into the remainder of the
34196         // context?
34197         if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
34198         {
34199             // Yes, we can just go ahead and make the allocation.
34200             newAlloc = (Object*) hp->allocate (size, acontext);
34201             ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34202         }
34203         else
34204         {
34205             // No, either the next available address is not aligned in the way we require it or there's
34206             // not enough space to allocate an object of the required size. In both cases we allocate a
34207             // padding object (marked as a free object). This object's size is such that it will reverse
34208             // the alignment of the next header (asserted below).
34209             //
34210             // We allocate both together then decide based on the result whether we'll format the space as
34211             // free object + real object or real object + free object.
34212             ASSERT((Align(min_obj_size) & 7) == 4);
34213             CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
34214             if (freeobj)
34215             {
34216                 if (((size_t)freeobj & 7) == desiredAlignment)
34217                 {
34218                     // New allocation has desired alignment, return this one and place the free object at the
34219                     // end of the allocated space.
34220                     newAlloc = (Object*)freeobj;
34221                     freeobj = (CObjectHeader*)((uint8_t*)freeobj + Align(size));
34222                 }
34223                 else
34224                 {
34225                     // New allocation is still mis-aligned, format the initial space as a free object and the
34226                     // rest of the space should be correctly aligned for the real object.
34227                     newAlloc = (Object*)((uint8_t*)freeobj + Align(min_obj_size));
34228                     ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34229                 }
34230                 freeobj->SetFree(min_obj_size);
34231             }
34232         }
34233     }
34234     else
34235     {
34236         // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
34237         // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
34238         // we've managed to arrange things so the only case where we see a bias is for boxed value types and
34239         // these can never get large enough to be allocated on the LOH.
34240         ASSERT(65536 < LARGE_OBJECT_SIZE);
34241         ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
34242
34243         alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34244
34245         newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
34246         ASSERT(((size_t)newAlloc & 7) == 0);
34247     }
34248
34249     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34250
34251 #ifdef TRACE_GC
34252 #ifdef COUNT_CYCLES
34253     finish = GetCycleCount32();
34254 #elif defined(ENABLE_INSTRUMENTATION)
34255     finish = GetInstLogTime();
34256 #endif //COUNT_CYCLES
34257     AllocDuration += finish - AllocStart;
34258     AllocCount++;
34259 #endif //TRACE_GC
34260     return newAlloc;
34261 #else
34262     UNREFERENCED_PARAMETER(_hp);
34263     UNREFERENCED_PARAMETER(acontext);
34264     UNREFERENCED_PARAMETER(size);
34265     UNREFERENCED_PARAMETER(flags);
34266     assert(!"Should not call GCHeap::AllocAlign8Common without FEATURE_64BIT_ALIGNMENT defined!");
34267     return nullptr;
34268 #endif // FEATURE_64BIT_ALIGNMENT
34269 }
34270
34271 Object *
34272 GCHeap::AllocLHeap( size_t size, uint32_t flags REQD_ALIGN_DCL)
34273 {
34274     CONTRACTL {
34275         NOTHROW;
34276         GC_TRIGGERS;
34277     } CONTRACTL_END;
34278
34279     TRIGGERSGC();
34280
34281     Object* newAlloc = NULL;
34282
34283 #ifdef TRACE_GC
34284 #ifdef COUNT_CYCLES
34285     AllocStart = GetCycleCount32();
34286     unsigned finish;
34287 #elif defined(ENABLE_INSTRUMENTATION)
34288     unsigned AllocStart = GetInstLogTime();
34289     unsigned finish;
34290 #endif //COUNT_CYCLES
34291 #endif //TRACE_GC
34292
34293 #ifdef MULTIPLE_HEAPS
34294     //take the first heap....
34295     gc_heap* hp = gc_heap::g_heaps[0];
34296 #else
34297     gc_heap* hp = pGenGCHeap;
34298 #ifdef _PREFAST_
34299     // prefix complains about us dereferencing hp in wks build even though we only access static members
34300     // this way. not sure how to shut it up except for this ugly workaround:
34301     PREFIX_ASSUME(hp != NULL);
34302 #endif //_PREFAST_
34303 #endif //MULTIPLE_HEAPS
34304
34305     alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34306
34307     newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34308 #ifdef FEATURE_STRUCTALIGN
34309     newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34310 #endif // FEATURE_STRUCTALIGN
34311     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34312
34313 #ifdef TRACE_GC
34314 #ifdef COUNT_CYCLES
34315     finish = GetCycleCount32();
34316 #elif defined(ENABLE_INSTRUMENTATION)
34317     finish = GetInstLogTime();
34318 #endif //COUNT_CYCLES
34319     AllocDuration += finish - AllocStart;
34320     AllocCount++;
34321 #endif //TRACE_GC
34322     return newAlloc;
34323 }
34324
34325 Object*
34326 GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_DCL)
34327 {
34328     CONTRACTL {
34329         NOTHROW;
34330         GC_TRIGGERS;
34331     } CONTRACTL_END;
34332
34333     TRIGGERSGC();
34334
34335     Object* newAlloc = NULL;
34336     alloc_context* acontext = static_cast<alloc_context*>(context);
34337
34338 #ifdef TRACE_GC
34339 #ifdef COUNT_CYCLES
34340     AllocStart = GetCycleCount32();
34341     unsigned finish;
34342 #elif defined(ENABLE_INSTRUMENTATION)
34343     unsigned AllocStart = GetInstLogTime();
34344     unsigned finish;
34345 #endif //COUNT_CYCLES
34346 #endif //TRACE_GC
34347
34348 #ifdef MULTIPLE_HEAPS
34349     if (acontext->get_alloc_heap() == 0)
34350     {
34351         AssignHeap (acontext);
34352         assert (acontext->get_alloc_heap());
34353     }
34354 #endif //MULTIPLE_HEAPS
34355
34356 #ifdef MULTIPLE_HEAPS
34357     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34358 #else
34359     gc_heap* hp = pGenGCHeap;
34360 #ifdef _PREFAST_
34361     // prefix complains about us dereferencing hp in wks build even though we only access static members
34362     // this way. not sure how to shut it up except for this ugly workaround:
34363     PREFIX_ASSUME(hp != NULL);
34364 #endif //_PREFAST_
34365 #endif //MULTIPLE_HEAPS
34366
34367     if (size < LARGE_OBJECT_SIZE)
34368     {
34369
34370 #ifdef TRACE_GC
34371         AllocSmallCount++;
34372 #endif //TRACE_GC
34373         newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
34374 #ifdef FEATURE_STRUCTALIGN
34375         newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
34376 #endif // FEATURE_STRUCTALIGN
34377 //        ASSERT (newAlloc);
34378     }
34379     else 
34380     {
34381         newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34382 #ifdef FEATURE_STRUCTALIGN
34383         newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34384 #endif // FEATURE_STRUCTALIGN
34385     }
34386
34387     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34388
34389 #ifdef TRACE_GC
34390 #ifdef COUNT_CYCLES
34391     finish = GetCycleCount32();
34392 #elif defined(ENABLE_INSTRUMENTATION)
34393     finish = GetInstLogTime();
34394 #endif //COUNT_CYCLES
34395     AllocDuration += finish - AllocStart;
34396     AllocCount++;
34397 #endif //TRACE_GC
34398     return newAlloc;
34399 }
34400
34401 void
34402 GCHeap::FixAllocContext (gc_alloc_context* context, bool lockp, void* arg, void *heap)
34403 {
34404     alloc_context* acontext = static_cast<alloc_context*>(context);
34405 #ifdef MULTIPLE_HEAPS
34406
34407     if (arg != 0)
34408         acontext->alloc_count = 0;
34409
34410     uint8_t * alloc_ptr = acontext->alloc_ptr;
34411
34412     if (!alloc_ptr)
34413         return;
34414
34415     // The acontext->alloc_heap can be out of sync with the ptrs because
34416     // of heap re-assignment in allocate
34417     gc_heap* hp = gc_heap::heap_of (alloc_ptr);
34418 #else
34419     gc_heap* hp = pGenGCHeap;
34420 #endif //MULTIPLE_HEAPS
34421
34422     if (heap == NULL || heap == hp)
34423     {
34424         if (lockp)
34425         {
34426             enter_spin_lock (&hp->more_space_lock);
34427         }
34428         hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
34429                                 get_alignment_constant(TRUE));
34430         if (lockp)
34431         {
34432             leave_spin_lock (&hp->more_space_lock);
34433         }
34434     }
34435 }
34436
34437 Object*
34438 GCHeap::GetContainingObject (void *pInteriorPtr, bool fCollectedGenOnly)
34439 {
34440     uint8_t *o = (uint8_t*)pInteriorPtr;
34441
34442     gc_heap* hp = gc_heap::heap_of (o);
34443
34444     uint8_t* lowest = (fCollectedGenOnly ? hp->gc_low : hp->lowest_address);
34445     uint8_t* highest = (fCollectedGenOnly ? hp->gc_high : hp->highest_address);
34446
34447     if (o >= lowest && o < highest)
34448     {
34449         o = hp->find_object (o, lowest);
34450     }
34451     else
34452     {
34453         o = NULL;
34454     }
34455     
34456     return (Object *)o;
34457 }
34458
34459 BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
34460 {
34461     if (dd_new_allocation (dd) < 0)
34462     {
34463         return TRUE;
34464     }
34465
34466     if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
34467     {
34468         return TRUE;
34469     }
34470
34471     return FALSE;
34472 }
34473
34474 //----------------------------------------------------------------------------
34475 // #GarbageCollector
34476 //
34477 //  API to ensure that a complete new garbage collection takes place
34478 //
34479 HRESULT
34480 GCHeap::GarbageCollect (int generation, bool low_memory_p, int mode)
34481 {
34482 #if defined(BIT64) 
34483     if (low_memory_p)
34484     {
34485         size_t total_allocated = 0;
34486         size_t total_desired = 0;
34487 #ifdef MULTIPLE_HEAPS
34488         int hn = 0;
34489         for (hn = 0; hn < gc_heap::n_heaps; hn++)
34490         {
34491             gc_heap* hp = gc_heap::g_heaps [hn];
34492             total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
34493             total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
34494                 dd_new_allocation (hp->dynamic_data_of (0));
34495         }
34496 #else
34497         gc_heap* hp = pGenGCHeap;
34498         total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
34499         total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
34500             dd_new_allocation (hp->dynamic_data_of (0));
34501 #endif //MULTIPLE_HEAPS
34502
34503         if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
34504         {
34505             dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
34506                          total_allocated, total_desired));
34507
34508             return S_OK;
34509         }
34510     }
34511 #endif // BIT64 
34512
34513 #ifdef MULTIPLE_HEAPS
34514     gc_heap* hpt = gc_heap::g_heaps[0];
34515 #else
34516     gc_heap* hpt = 0;
34517 #endif //MULTIPLE_HEAPS
34518
34519     generation = (generation < 0) ? max_generation : min (generation, max_generation);
34520     dynamic_data* dd = hpt->dynamic_data_of (generation);
34521
34522 #ifdef BACKGROUND_GC
34523     if (recursive_gc_sync::background_running_p())
34524     {
34525         if ((mode == collection_optimized) || (mode & collection_non_blocking))
34526         {
34527             return S_OK;
34528         }
34529         if (mode & collection_blocking)
34530         {
34531             pGenGCHeap->background_gc_wait();
34532             if (mode & collection_optimized)
34533             {
34534                 return S_OK;
34535             }
34536         }
34537     }
34538 #endif //BACKGROUND_GC
34539
34540     if (mode & collection_optimized)
34541     {
34542         if (pGenGCHeap->gc_started)
34543         {
34544             return S_OK;
34545         }
34546         else 
34547         {
34548             BOOL should_collect = FALSE;
34549             BOOL should_check_loh = (generation == max_generation);
34550 #ifdef MULTIPLE_HEAPS
34551             for (int i = 0; i < gc_heap::n_heaps; i++)
34552             {
34553                 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
34554                 dynamic_data* dd2 = (should_check_loh ? 
34555                                      (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
34556                                      0);
34557
34558                 if (should_collect_optimized (dd1, low_memory_p))
34559                 {
34560                     should_collect = TRUE;
34561                     break;
34562                 }
34563                 if (dd2 && should_collect_optimized (dd2, low_memory_p))
34564                 {
34565                     should_collect = TRUE;
34566                     break;
34567                 }
34568             }
34569 #else
34570             should_collect = should_collect_optimized (dd, low_memory_p);
34571             if (!should_collect && should_check_loh)
34572             {
34573                 should_collect = 
34574                     should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
34575             }
34576 #endif //MULTIPLE_HEAPS
34577             if (!should_collect)
34578             {
34579                 return S_OK;
34580             }
34581         }
34582     }
34583
34584     size_t CollectionCountAtEntry = dd_collection_count (dd);
34585     size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
34586     size_t CurrentCollectionCount = 0;
34587
34588 retry:
34589
34590     CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
34591     
34592     if ((mode & collection_blocking) && 
34593         (generation == max_generation) && 
34594         (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
34595     {
34596 #ifdef BACKGROUND_GC
34597         if (recursive_gc_sync::background_running_p())
34598         {
34599             pGenGCHeap->background_gc_wait();
34600         }
34601 #endif //BACKGROUND_GC
34602
34603         goto retry;
34604     }
34605
34606     if (CollectionCountAtEntry == CurrentCollectionCount)
34607     {
34608         goto retry;
34609     }
34610
34611     return S_OK;
34612 }
34613
34614 size_t
34615 GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
34616 {
34617     int gen = (generation < 0) ? 
34618                max_generation : min (generation, max_generation);
34619
34620     gc_reason reason = reason_empty;
34621     
34622     if (low_memory_p) 
34623     {
34624         if (mode & collection_blocking)
34625             reason = reason_lowmemory_blocking;
34626         else
34627             reason = reason_lowmemory;
34628     }
34629     else
34630         reason = reason_induced;
34631
34632     if (reason == reason_induced)
34633     {
34634         if (mode & collection_compacting)
34635         {
34636             reason = reason_induced_compacting;
34637         }
34638         else if (mode & collection_non_blocking)
34639         {
34640             reason = reason_induced_noforce;
34641         }
34642 #ifdef STRESS_HEAP
34643         else if (mode & collection_gcstress)
34644         {
34645             reason = reason_gcstress;
34646         }
34647 #endif
34648     }
34649
34650     return GarbageCollectGeneration (gen, reason);
34651 }
34652
34653 void gc_heap::do_pre_gc()
34654 {
34655     STRESS_LOG_GC_STACK;
34656
34657 #ifdef STRESS_LOG
34658     STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
34659                         (uint32_t)settings.condemned_generation,
34660                         (uint32_t)settings.reason);
34661 #endif // STRESS_LOG
34662
34663 #ifdef MULTIPLE_HEAPS
34664     gc_heap* hp = g_heaps[0];
34665 #else
34666     gc_heap* hp = 0;
34667 #endif //MULTIPLE_HEAPS
34668
34669 #ifdef BACKGROUND_GC
34670     settings.b_state = hp->current_bgc_state;
34671 #endif //BACKGROUND_GC
34672
34673 #ifdef BACKGROUND_GC
34674     dprintf (1, ("*GC* %d(gen0:%d)(%d)(%s)(%d)", 
34675         VolatileLoad(&settings.gc_index), 
34676         dd_collection_count (hp->dynamic_data_of (0)),
34677         settings.condemned_generation,
34678         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
34679         settings.b_state));
34680 #else
34681     dprintf (1, ("*GC* %d(gen0:%d)(%d)", 
34682         VolatileLoad(&settings.gc_index), 
34683         dd_collection_count(hp->dynamic_data_of(0)),
34684         settings.condemned_generation));
34685 #endif //BACKGROUND_GC
34686
34687     // TODO: this can happen...it's because of the way we are calling
34688     // do_pre_gc, will fix later.
34689     //if (last_gc_index > VolatileLoad(&settings.gc_index))
34690     //{
34691     //    FATAL_GC_ERROR();
34692     //}
34693
34694     last_gc_index = VolatileLoad(&settings.gc_index);
34695     GCHeap::UpdatePreGCCounters();
34696
34697     if (settings.concurrent)
34698     {
34699 #ifdef BACKGROUND_GC
34700         full_gc_counts[gc_type_background]++;
34701 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34702         GCHeap::gc_stress_fgcs_in_bgc = 0;
34703 #endif // STRESS_HEAP && !FEATURE_REDHAWK
34704 #endif // BACKGROUND_GC
34705     }
34706     else
34707     {
34708         if (settings.condemned_generation == max_generation)
34709         {
34710             full_gc_counts[gc_type_blocking]++;
34711         }
34712         else
34713         {
34714 #ifdef BACKGROUND_GC
34715             if (settings.background_p)
34716             {
34717                 ephemeral_fgc_counts[settings.condemned_generation]++;
34718             }
34719 #endif //BACKGROUND_GC
34720         }
34721     }
34722
34723 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34724     if (g_fEnableARM)
34725     {
34726         SystemDomain::ResetADSurvivedBytes();
34727     }
34728 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34729 }
34730
34731 #ifdef GC_CONFIG_DRIVEN
34732 void gc_heap::record_interesting_info_per_heap()
34733 {
34734     // datapoints are always from the last blocking GC so don't record again
34735     // for BGCs.
34736     if (!(settings.concurrent))
34737     {
34738         for (int i = 0; i < max_idp_count; i++)
34739         {
34740             interesting_data_per_heap[i] += interesting_data_per_gc[i];
34741         }
34742     }
34743
34744     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
34745     if (compact_reason >= 0)
34746         (compact_reasons_per_heap[compact_reason])++;
34747     int expand_mechanism = get_gc_data_per_heap()->get_mechanism (gc_heap_expand);
34748     if (expand_mechanism >= 0)
34749         (expand_mechanisms_per_heap[expand_mechanism])++;
34750
34751     for (int i = 0; i < max_gc_mechanism_bits_count; i++)
34752     {
34753         if (get_gc_data_per_heap()->is_mechanism_bit_set ((gc_mechanism_bit_per_heap)i))
34754             (interesting_mechanism_bits_per_heap[i])++;
34755     }
34756
34757     //         h#  | GC  | gen | C   | EX  | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
34758     cprintf (("%2d | %6d | %1d | %1s | %2s | %2s | %2s | %2s | %2s || %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id |",
34759             heap_number,
34760             (size_t)settings.gc_index,
34761             settings.condemned_generation,
34762             // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs.
34763             (settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W") : ""), // compaction
34764             ((expand_mechanism >= 0)? "X" : ""), // EX
34765             ((expand_mechanism == expand_reuse_normal) ? "X" : ""), // NF
34766             ((expand_mechanism == expand_reuse_bestfit) ? "X" : ""), // BF
34767             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_mark_list_bit) ? "X" : ""), // ML
34768             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_demotion_bit) ? "X" : ""), // DM
34769             interesting_data_per_gc[idp_pre_short],
34770             interesting_data_per_gc[idp_post_short],
34771             interesting_data_per_gc[idp_merged_pin],
34772             interesting_data_per_gc[idp_converted_pin],
34773             interesting_data_per_gc[idp_pre_pin],
34774             interesting_data_per_gc[idp_post_pin],
34775             interesting_data_per_gc[idp_pre_and_post_pin],
34776             interesting_data_per_gc[idp_pre_short_padded],
34777             interesting_data_per_gc[idp_post_short_padded]));
34778 }
34779
34780 void gc_heap::record_global_mechanisms()
34781 {
34782     for (int i = 0; i < max_global_mechanisms_count; i++)
34783     {
34784         if (gc_data_global.get_mechanism_p ((gc_global_mechanism_p)i))
34785         {
34786             ::record_global_mechanism (i);
34787         }
34788     }
34789 }
34790
34791 BOOL gc_heap::should_do_sweeping_gc (BOOL compact_p)
34792 {
34793     if (!compact_ratio)
34794         return (!compact_p);
34795
34796     size_t compact_count = compact_or_sweep_gcs[0];
34797     size_t sweep_count = compact_or_sweep_gcs[1];
34798
34799     size_t total_count = compact_count + sweep_count;
34800     BOOL should_compact = compact_p;
34801     if (total_count > 3)
34802     {
34803         if (compact_p)
34804         {
34805             int temp_ratio = (int)((compact_count + 1) * 100 / (total_count + 1));
34806             if (temp_ratio > compact_ratio)
34807             {
34808                 // cprintf (("compact would be: %d, total_count: %d, ratio would be %d%% > target\n",
34809                 //     (compact_count + 1), (total_count + 1), temp_ratio));
34810                 should_compact = FALSE;
34811             }
34812         }
34813         else
34814         {
34815             int temp_ratio = (int)((sweep_count + 1) * 100 / (total_count + 1));
34816             if (temp_ratio > (100 - compact_ratio))
34817             {
34818                 // cprintf (("sweep would be: %d, total_count: %d, ratio would be %d%% > target\n",
34819                 //     (sweep_count + 1), (total_count + 1), temp_ratio));
34820                 should_compact = TRUE;
34821             }
34822         }
34823     }
34824
34825     return !should_compact;
34826 }
34827 #endif //GC_CONFIG_DRIVEN
34828
34829 void gc_heap::do_post_gc()
34830 {
34831     if (!settings.concurrent)
34832     {
34833         initGCShadow();
34834     }
34835
34836 #ifdef TRACE_GC
34837 #ifdef COUNT_CYCLES
34838     AllocStart = GetCycleCount32();
34839 #else
34840     AllocStart = clock();
34841 #endif //COUNT_CYCLES
34842 #endif //TRACE_GC
34843
34844 #ifdef MULTIPLE_HEAPS
34845     gc_heap* hp = g_heaps[0];
34846 #else
34847     gc_heap* hp = 0;
34848 #endif //MULTIPLE_HEAPS
34849     
34850     GCToEEInterface::GcDone(settings.condemned_generation);
34851
34852     GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index),
34853                          (uint32_t)settings.condemned_generation,
34854                          (uint32_t)settings.reason,
34855                          !!settings.concurrent);
34856
34857     //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)", 
34858     dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)", 
34859         VolatileLoad(&settings.gc_index), 
34860         dd_collection_count(hp->dynamic_data_of(0)),
34861         settings.condemned_generation,
34862         (settings.concurrent ? "BGC" : "GC")));
34863
34864     if (settings.exit_memory_load != 0)
34865         last_gc_memory_load = settings.exit_memory_load;
34866     else if (settings.entry_memory_load != 0)
34867         last_gc_memory_load = settings.entry_memory_load;
34868
34869     last_gc_heap_size = get_total_heap_size();
34870     last_gc_fragmentation = get_total_fragmentation();
34871
34872     GCHeap::UpdatePostGCCounters();
34873 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34874     //if (g_fEnableARM)
34875     //{
34876     //    SystemDomain::GetADSurvivedBytes();
34877     //}
34878 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34879
34880 #ifdef STRESS_LOG
34881     STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
34882                       (uint32_t)settings.condemned_generation,
34883                       (uint32_t)settings.reason);
34884 #endif // STRESS_LOG
34885
34886 #ifdef GC_CONFIG_DRIVEN
34887     if (!settings.concurrent)
34888     {
34889         if (settings.compaction)
34890             (compact_or_sweep_gcs[0])++;
34891         else
34892             (compact_or_sweep_gcs[1])++;
34893     }
34894
34895 #ifdef MULTIPLE_HEAPS
34896     for (int i = 0; i < n_heaps; i++)
34897         g_heaps[i]->record_interesting_info_per_heap();
34898 #else
34899     record_interesting_info_per_heap();
34900 #endif //MULTIPLE_HEAPS
34901     record_global_mechanisms();
34902 #endif //GC_CONFIG_DRIVEN
34903 }
34904
34905 unsigned GCHeap::GetGcCount()
34906 {
34907     return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
34908 }
34909
34910 size_t
34911 GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
34912 {
34913     dprintf (2, ("triggered a GC!"));
34914
34915 #ifdef MULTIPLE_HEAPS
34916     gc_heap* hpt = gc_heap::g_heaps[0];
34917 #else
34918     gc_heap* hpt = 0;
34919 #endif //MULTIPLE_HEAPS
34920     bool cooperative_mode = true;
34921     dynamic_data* dd = hpt->dynamic_data_of (gen);
34922     size_t localCount = dd_collection_count (dd);
34923
34924     enter_spin_lock (&gc_heap::gc_lock);
34925     dprintf (SPINLOCK_LOG, ("GC Egc"));
34926     ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
34927
34928     //don't trigger another GC if one was already in progress
34929     //while waiting for the lock
34930     {
34931         size_t col_count = dd_collection_count (dd);
34932
34933         if (localCount != col_count)
34934         {
34935 #ifdef SYNCHRONIZATION_STATS
34936             gc_lock_contended++;
34937 #endif //SYNCHRONIZATION_STATS
34938             dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
34939             leave_spin_lock (&gc_heap::gc_lock);
34940
34941             // We don't need to release msl here 'cause this means a GC
34942             // has happened and would have release all msl's.
34943             return col_count;
34944          }
34945     }
34946
34947 #ifdef COUNT_CYCLES
34948     int gc_start = GetCycleCount32();
34949 #endif //COUNT_CYCLES
34950
34951 #ifdef TRACE_GC
34952 #ifdef COUNT_CYCLES
34953     AllocDuration += GetCycleCount32() - AllocStart;
34954 #else
34955     AllocDuration += clock() - AllocStart;
34956 #endif //COUNT_CYCLES
34957 #endif //TRACE_GC
34958
34959     gc_heap::g_low_memory_status = (reason == reason_lowmemory) || 
34960                                    (reason == reason_lowmemory_blocking) ||
34961                                    (gc_heap::latency_level == latency_level_memory_footprint);
34962
34963     gc_trigger_reason = reason;
34964
34965 #ifdef MULTIPLE_HEAPS
34966     for (int i = 0; i < gc_heap::n_heaps; i++)
34967     {
34968         gc_heap::g_heaps[i]->reset_gc_done();
34969     }
34970 #else
34971     gc_heap::reset_gc_done();
34972 #endif //MULTIPLE_HEAPS
34973
34974     gc_heap::gc_started = TRUE;
34975
34976     {
34977         init_sync_log_stats();
34978
34979 #ifndef MULTIPLE_HEAPS
34980         cooperative_mode = gc_heap::enable_preemptive ();
34981
34982         dprintf (2, ("Suspending EE"));
34983         BEGIN_TIMING(suspend_ee_during_log);
34984         GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
34985         END_TIMING(suspend_ee_during_log);
34986         gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();
34987         gc_heap::disable_preemptive (cooperative_mode);
34988         if (gc_heap::proceed_with_gc_p)
34989             pGenGCHeap->settings.init_mechanisms();
34990         else
34991             gc_heap::update_collection_counts_for_no_gc();
34992
34993 #endif //!MULTIPLE_HEAPS
34994     }
34995
34996 // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
34997
34998 #ifdef TRACE_GC
34999 #ifdef COUNT_CYCLES
35000     unsigned start;
35001     unsigned finish;
35002     start = GetCycleCount32();
35003 #else
35004     clock_t start;
35005     clock_t finish;
35006     start = clock();
35007 #endif //COUNT_CYCLES
35008     PromotedObjectCount = 0;
35009 #endif //TRACE_GC
35010
35011     unsigned int condemned_generation_number = gen;
35012
35013     // We want to get a stack from the user thread that triggered the GC
35014     // instead of on the GC thread which is the case for Server GC.
35015     // But we are doing it for Workstation GC as well to be uniform.
35016     FIRE_EVENT(GCTriggered, static_cast<uint32_t>(reason));
35017
35018 #ifdef MULTIPLE_HEAPS
35019     GcCondemnedGeneration = condemned_generation_number;
35020
35021     cooperative_mode = gc_heap::enable_preemptive ();
35022
35023     BEGIN_TIMING(gc_during_log);
35024     gc_heap::ee_suspend_event.Set();
35025     gc_heap::wait_for_gc_done();
35026     END_TIMING(gc_during_log);
35027
35028     gc_heap::disable_preemptive (cooperative_mode);
35029
35030     condemned_generation_number = GcCondemnedGeneration;
35031 #else
35032     if (gc_heap::proceed_with_gc_p)
35033     {
35034         BEGIN_TIMING(gc_during_log);
35035         pGenGCHeap->garbage_collect (condemned_generation_number);
35036         END_TIMING(gc_during_log);
35037     }
35038 #endif //MULTIPLE_HEAPS
35039
35040 #ifdef TRACE_GC
35041 #ifdef COUNT_CYCLES
35042     finish = GetCycleCount32();
35043 #else
35044     finish = clock();
35045 #endif //COUNT_CYCLES
35046     GcDuration += finish - start;
35047     dprintf (3,
35048              ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
35049               VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
35050               finish - start, GcDuration,
35051               AllocCount ? (AllocDuration / AllocCount) : 0,
35052               AllocSmallCount, AllocBigCount));
35053     AllocCount = 0;
35054     AllocDuration = 0;
35055 #endif // TRACE_GC
35056
35057 #ifdef BACKGROUND_GC
35058     // We are deciding whether we should fire the alloc wait end event here
35059     // because in begin_foreground we could be calling end_foreground 
35060     // if we need to retry.
35061     if (gc_heap::alloc_wait_event_p)
35062     {
35063         hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
35064         gc_heap::alloc_wait_event_p = FALSE;
35065     }
35066 #endif //BACKGROUND_GC
35067
35068 #ifndef MULTIPLE_HEAPS
35069 #ifdef BACKGROUND_GC
35070     if (!gc_heap::dont_restart_ee_p)
35071     {
35072 #endif //BACKGROUND_GC
35073         BEGIN_TIMING(restart_ee_during_log);
35074         GCToEEInterface::RestartEE(TRUE);
35075         END_TIMING(restart_ee_during_log);
35076 #ifdef BACKGROUND_GC
35077     }
35078 #endif //BACKGROUND_GC
35079 #endif //!MULTIPLE_HEAPS
35080
35081 #ifdef COUNT_CYCLES
35082     printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
35083             GetCycleCount32() - gc_start);
35084 #endif //COUNT_CYCLES
35085
35086 #ifndef MULTIPLE_HEAPS
35087     process_sync_log_stats();
35088     gc_heap::gc_started = FALSE;
35089     gc_heap::set_gc_done();
35090     dprintf (SPINLOCK_LOG, ("GC Lgc"));
35091     leave_spin_lock (&gc_heap::gc_lock);    
35092 #endif //!MULTIPLE_HEAPS
35093
35094 #ifdef FEATURE_PREMORTEM_FINALIZATION
35095     GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers);
35096 #endif // FEATURE_PREMORTEM_FINALIZATION
35097
35098     return dd_collection_count (dd);
35099 }
35100
35101 size_t      GCHeap::GetTotalBytesInUse ()
35102 {
35103 #ifdef MULTIPLE_HEAPS
35104     //enumarate all the heaps and get their size.
35105     size_t tot_size = 0;
35106     for (int i = 0; i < gc_heap::n_heaps; i++)
35107     {
35108         GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
35109         tot_size += Hp->ApproxTotalBytesInUse (FALSE);
35110     }
35111     return tot_size;
35112 #else
35113     return ApproxTotalBytesInUse ();
35114 #endif //MULTIPLE_HEAPS
35115 }
35116
35117 int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
35118 {
35119     if (get_bgc_fgc_count != 0)
35120     {
35121 #ifdef BACKGROUND_GC
35122         if (generation == max_generation)
35123         {
35124             return (int)(gc_heap::full_gc_counts[gc_type_background]);
35125         }
35126         else
35127         {
35128             return (int)(gc_heap::ephemeral_fgc_counts[generation]);
35129         }
35130 #else
35131         return 0;
35132 #endif //BACKGROUND_GC
35133     }
35134
35135 #ifdef MULTIPLE_HEAPS
35136     gc_heap* hp = gc_heap::g_heaps [0];
35137 #else  //MULTIPLE_HEAPS
35138     gc_heap* hp = pGenGCHeap;
35139 #endif //MULTIPLE_HEAPS
35140     if (generation > max_generation)
35141         return 0;
35142     else
35143         return (int)dd_collection_count (hp->dynamic_data_of (generation));
35144 }
35145
35146 size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
35147 {
35148     size_t totsize = 0;
35149     //GCTODO
35150     //ASSERT(InMustComplete());
35151     enter_spin_lock (&pGenGCHeap->gc_lock);
35152
35153     heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
35154     // Get small block heap size info
35155     totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
35156     heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
35157     while (seg1 != eph_seg)
35158     {
35159         totsize += heap_segment_allocated (seg1) -
35160             heap_segment_mem (seg1);
35161         seg1 = heap_segment_next (seg1);
35162     }
35163
35164     //discount the fragmentation
35165     for (int i = 0; i <= max_generation; i++)
35166     {
35167         generation* gen = pGenGCHeap->generation_of (i);
35168         totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
35169     }
35170
35171     if (!small_heap_only)
35172     {
35173         heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
35174
35175         while (seg2 != 0)
35176         {
35177             totsize += heap_segment_allocated (seg2) -
35178                 heap_segment_mem (seg2);
35179             seg2 = heap_segment_next (seg2);
35180         }
35181
35182         //discount the fragmentation
35183         generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
35184         size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
35185         totsize -= frag;
35186     }
35187     leave_spin_lock (&pGenGCHeap->gc_lock);
35188     return totsize;
35189 }
35190
35191 #ifdef MULTIPLE_HEAPS
35192 void GCHeap::AssignHeap (alloc_context* acontext)
35193 {
35194     // Assign heap based on processor
35195     acontext->set_alloc_heap(GetHeap(heap_select::select_heap(acontext, 0)));
35196     acontext->set_home_heap(acontext->get_alloc_heap());
35197 }
35198 GCHeap* GCHeap::GetHeap (int n)
35199 {
35200     assert (n < gc_heap::n_heaps);
35201     return gc_heap::g_heaps [n]->vm_heap;
35202 }
35203 #endif //MULTIPLE_HEAPS
35204
35205 bool GCHeap::IsThreadUsingAllocationContextHeap(gc_alloc_context* context, int thread_number)
35206 {
35207     alloc_context* acontext = static_cast<alloc_context*>(context);
35208 #ifdef MULTIPLE_HEAPS
35209     return ((acontext->get_home_heap() == GetHeap(thread_number)) ||
35210             ((acontext->get_home_heap() == 0) && (thread_number == 0)));
35211 #else
35212     UNREFERENCED_PARAMETER(acontext);
35213     UNREFERENCED_PARAMETER(thread_number);
35214     return true;
35215 #endif //MULTIPLE_HEAPS
35216 }
35217
35218 // Returns the number of processors required to trigger the use of thread based allocation contexts
35219 int GCHeap::GetNumberOfHeaps ()
35220 {
35221 #ifdef MULTIPLE_HEAPS
35222     return gc_heap::n_heaps;
35223 #else
35224     return 1;
35225 #endif //MULTIPLE_HEAPS
35226 }
35227
35228 /*
35229   in this way we spend extra time cycling through all the heaps while create the handle
35230   it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
35231 */
35232 int GCHeap::GetHomeHeapNumber ()
35233 {
35234 #ifdef MULTIPLE_HEAPS
35235     Thread *pThread = GCToEEInterface::GetThread();
35236     for (int i = 0; i < gc_heap::n_heaps; i++)
35237     {
35238         if (pThread)
35239         {
35240             gc_alloc_context* ctx = GCToEEInterface::GetAllocContext();
35241             GCHeap *hp = static_cast<alloc_context*>(ctx)->get_home_heap();
35242             if (hp == gc_heap::g_heaps[i]->vm_heap) return i;
35243         }
35244     }
35245     return 0;
35246 #else
35247     return 0;
35248 #endif //MULTIPLE_HEAPS
35249 }
35250
35251 unsigned int GCHeap::GetCondemnedGeneration()
35252
35253     return gc_heap::settings.condemned_generation;
35254 }
35255
35256 void GCHeap::GetMemoryInfo(uint32_t* highMemLoadThreshold, 
35257                            uint64_t* totalPhysicalMem, 
35258                            uint32_t* lastRecordedMemLoad,
35259                            size_t* lastRecordedHeapSize,
35260                            size_t* lastRecordedFragmentation)
35261 {
35262     *highMemLoadThreshold = gc_heap::high_memory_load_th;
35263     *totalPhysicalMem = gc_heap::total_physical_mem;
35264     *lastRecordedMemLoad = gc_heap::last_gc_memory_load;
35265     *lastRecordedHeapSize = gc_heap::last_gc_heap_size;
35266     *lastRecordedFragmentation = gc_heap::last_gc_fragmentation;
35267 }
35268
35269 int GCHeap::GetGcLatencyMode()
35270 {
35271     return (int)(pGenGCHeap->settings.pause_mode);
35272 }
35273
35274 int GCHeap::SetGcLatencyMode (int newLatencyMode)
35275 {
35276     if (gc_heap::settings.pause_mode == pause_no_gc)
35277         return (int)set_pause_mode_no_gc;
35278
35279     gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
35280
35281     if (new_mode == pause_low_latency)
35282     {
35283 #ifndef MULTIPLE_HEAPS
35284         pGenGCHeap->settings.pause_mode = new_mode;
35285 #endif //!MULTIPLE_HEAPS
35286     }
35287     else if (new_mode == pause_sustained_low_latency)
35288     {
35289 #ifdef BACKGROUND_GC
35290         if (gc_heap::gc_can_use_concurrent)
35291         {
35292             pGenGCHeap->settings.pause_mode = new_mode;
35293         }
35294 #endif //BACKGROUND_GC
35295     }
35296     else
35297     {
35298         pGenGCHeap->settings.pause_mode = new_mode;
35299     }
35300
35301 #ifdef BACKGROUND_GC
35302     if (recursive_gc_sync::background_running_p())
35303     {
35304         // If we get here, it means we are doing an FGC. If the pause
35305         // mode was altered we will need to save it in the BGC settings.
35306         if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
35307         {
35308             gc_heap::saved_bgc_settings.pause_mode = new_mode;
35309         }
35310     }
35311 #endif //BACKGROUND_GC
35312
35313     return (int)set_pause_mode_success;
35314 }
35315
35316 int GCHeap::GetLOHCompactionMode()
35317 {
35318     return pGenGCHeap->loh_compaction_mode;
35319 }
35320
35321 void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
35322 {
35323 #ifdef FEATURE_LOH_COMPACTION
35324     pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
35325 #endif //FEATURE_LOH_COMPACTION
35326 }
35327
35328 bool GCHeap::RegisterForFullGCNotification(uint32_t gen2Percentage,
35329                                            uint32_t lohPercentage)
35330 {
35331 #ifdef MULTIPLE_HEAPS
35332     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35333     {
35334         gc_heap* hp = gc_heap::g_heaps [hn];
35335         hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
35336     }
35337 #else //MULTIPLE_HEAPS
35338     pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
35339 #endif //MULTIPLE_HEAPS
35340
35341     pGenGCHeap->full_gc_approach_event.Reset();
35342     pGenGCHeap->full_gc_end_event.Reset();
35343     pGenGCHeap->full_gc_approach_event_set = false;
35344
35345     pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
35346     pGenGCHeap->fgn_loh_percent = lohPercentage;
35347
35348     return TRUE;
35349 }
35350
35351 bool GCHeap::CancelFullGCNotification()
35352 {
35353     pGenGCHeap->fgn_maxgen_percent = 0;
35354     pGenGCHeap->fgn_loh_percent = 0;
35355
35356     pGenGCHeap->full_gc_approach_event.Set();
35357     pGenGCHeap->full_gc_end_event.Set();
35358     
35359     return TRUE;
35360 }
35361
35362 int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
35363 {
35364     dprintf (2, ("WFGA: Begin wait"));
35365     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
35366     dprintf (2, ("WFGA: End wait"));
35367     return result;
35368 }
35369
35370 int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
35371 {
35372     dprintf (2, ("WFGE: Begin wait"));
35373     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
35374     dprintf (2, ("WFGE: End wait"));
35375     return result;
35376 }
35377
35378 int GCHeap::StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC)
35379 {
35380     NoGCRegionLockHolder lh;
35381
35382     dprintf (1, ("begin no gc called"));
35383     start_no_gc_region_status status = gc_heap::prepare_for_no_gc_region (totalSize, lohSizeKnown, lohSize, disallowFullBlockingGC);
35384     if (status == start_no_gc_success)
35385     {
35386         GarbageCollect (max_generation);
35387         status = gc_heap::get_start_no_gc_region_status();
35388     }
35389
35390     if (status != start_no_gc_success)
35391         gc_heap::handle_failure_for_no_gc();
35392
35393     return (int)status;
35394 }
35395
35396 int GCHeap::EndNoGCRegion()
35397 {
35398     NoGCRegionLockHolder lh;
35399     return (int)gc_heap::end_no_gc_region();
35400 }
35401
35402 void GCHeap::PublishObject (uint8_t* Obj)
35403 {
35404 #ifdef BACKGROUND_GC
35405     gc_heap* hp = gc_heap::heap_of (Obj);
35406     hp->bgc_alloc_lock->loh_alloc_done (Obj);
35407 #endif //BACKGROUND_GC
35408 }
35409
35410 // The spec for this one isn't clear. This function
35411 // returns the size that can be allocated without
35412 // triggering a GC of any kind.
35413 size_t GCHeap::ApproxFreeBytes()
35414 {
35415     //GCTODO
35416     //ASSERT(InMustComplete());
35417     enter_spin_lock (&pGenGCHeap->gc_lock);
35418
35419     generation* gen = pGenGCHeap->generation_of (0);
35420     size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
35421
35422     leave_spin_lock (&pGenGCHeap->gc_lock);
35423
35424     return res;
35425 }
35426
35427 HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
35428 {
35429     if ((gen < 0) || (gen > max_generation))
35430         return E_FAIL;
35431 #ifdef MULTIPLE_HEAPS
35432     counters->current_size = 0;
35433     counters->promoted_size = 0;
35434     counters->collection_count = 0;
35435
35436     //enumarate all the heaps and get their counters.
35437     for (int i = 0; i < gc_heap::n_heaps; i++)
35438     {
35439         dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
35440
35441         counters->current_size += dd_current_size (dd);
35442         counters->promoted_size += dd_promoted_size (dd);
35443         if (i == 0)
35444         counters->collection_count += dd_collection_count (dd);
35445     }
35446 #else
35447     dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
35448     counters->current_size = dd_current_size (dd);
35449     counters->promoted_size = dd_promoted_size (dd);
35450     counters->collection_count = dd_collection_count (dd);
35451 #endif //MULTIPLE_HEAPS
35452     return S_OK;
35453 }
35454
35455 // Get the segment size to use, making sure it conforms.
35456 size_t GCHeap::GetValidSegmentSize(bool large_seg)
35457 {
35458     return get_valid_segment_size (large_seg);
35459 }
35460
35461 // Get the max gen0 heap size, making sure it conforms.
35462 size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
35463 {
35464     size_t gen0size = static_cast<size_t>(GCConfig::GetGen0Size());
35465
35466     if ((gen0size == 0) || !g_theGCHeap->IsValidGen0MaxSize(gen0size))
35467     {
35468 #ifdef SERVER_GC
35469         // performance data seems to indicate halving the size results
35470         // in optimal perf.  Ask for adjusted gen0 size.
35471         gen0size = max(GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),(256*1024));
35472
35473         // if gen0 size is too large given the available memory, reduce it.
35474         // Get true cache size, as we don't want to reduce below this.
35475         size_t trueSize = max(GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE),(256*1024));
35476         dprintf (2, ("cache: %Id-%Id, cpu: %Id", 
35477             GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),
35478             GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)));
35479
35480         int n_heaps = gc_heap::n_heaps;
35481 #else //SERVER_GC
35482         size_t trueSize = GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE);
35483         gen0size = max((4*trueSize/5),(256*1024));
35484         trueSize = max(trueSize, (256*1024));
35485         int n_heaps = 1;
35486 #endif //SERVER_GC
35487
35488         // if the total min GC across heaps will exceed 1/6th of available memory,
35489         // then reduce the min GC size until it either fits or has been reduced to cache size.
35490         while ((gen0size * n_heaps) > GCToOSInterface::GetPhysicalMemoryLimit() / 6)
35491         {
35492             gen0size = gen0size / 2;
35493             if (gen0size <= trueSize)
35494             {
35495                 gen0size = trueSize;
35496                 break;
35497             }
35498         }
35499     }
35500
35501     // Generation 0 must never be more than 1/2 the segment size.
35502     if (gen0size >= (seg_size / 2))
35503         gen0size = seg_size / 2;
35504
35505     return (gen0size);
35506 }
35507
35508 void GCHeap::SetReservedVMLimit (size_t vmlimit)
35509 {
35510     gc_heap::reserved_memory_limit = vmlimit;
35511 }
35512
35513
35514 //versions of same method on each heap
35515
35516 #ifdef FEATURE_PREMORTEM_FINALIZATION
35517
35518 Object* GCHeap::GetNextFinalizableObject()
35519 {
35520
35521 #ifdef MULTIPLE_HEAPS
35522
35523     //return the first non critical one in the first queue.
35524     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35525     {
35526         gc_heap* hp = gc_heap::g_heaps [hn];
35527         Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
35528         if (O)
35529             return O;
35530     }
35531     //return the first non crtitical/critical one in the first queue.
35532     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35533     {
35534         gc_heap* hp = gc_heap::g_heaps [hn];
35535         Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
35536         if (O)
35537             return O;
35538     }
35539     return 0;
35540
35541
35542 #else //MULTIPLE_HEAPS
35543     return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
35544 #endif //MULTIPLE_HEAPS
35545
35546 }
35547
35548 size_t GCHeap::GetNumberFinalizableObjects()
35549 {
35550 #ifdef MULTIPLE_HEAPS
35551     size_t cnt = 0;
35552     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35553     {
35554         gc_heap* hp = gc_heap::g_heaps [hn];
35555         cnt += hp->finalize_queue->GetNumberFinalizableObjects();
35556     }
35557     return cnt;
35558
35559
35560 #else //MULTIPLE_HEAPS
35561     return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
35562 #endif //MULTIPLE_HEAPS
35563 }
35564
35565 size_t GCHeap::GetFinalizablePromotedCount()
35566 {
35567 #ifdef MULTIPLE_HEAPS
35568     size_t cnt = 0;
35569
35570     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35571     {
35572         gc_heap* hp = gc_heap::g_heaps [hn];
35573         cnt += hp->finalize_queue->GetPromotedCount();
35574     }
35575     return cnt;
35576
35577 #else //MULTIPLE_HEAPS
35578     return pGenGCHeap->finalize_queue->GetPromotedCount();
35579 #endif //MULTIPLE_HEAPS
35580 }
35581
35582 bool GCHeap::FinalizeAppDomain(void *pDomain, bool fRunFinalizers)
35583 {
35584 #ifdef MULTIPLE_HEAPS
35585     bool foundp = false;
35586     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35587     {
35588         gc_heap* hp = gc_heap::g_heaps [hn];
35589         if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
35590             foundp = true;
35591     }
35592     return foundp;
35593
35594 #else //MULTIPLE_HEAPS
35595     return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
35596 #endif //MULTIPLE_HEAPS
35597 }
35598
35599 bool GCHeap::ShouldRestartFinalizerWatchDog()
35600 {
35601     // This condition was historically used as part of the condition to detect finalizer thread timeouts
35602     return gc_heap::gc_lock.lock != -1;
35603 }
35604
35605 void GCHeap::SetFinalizeQueueForShutdown(bool fHasLock)
35606 {
35607 #ifdef MULTIPLE_HEAPS
35608     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35609     {
35610         gc_heap* hp = gc_heap::g_heaps [hn];
35611         hp->finalize_queue->SetSegForShutDown(fHasLock);
35612     }
35613
35614 #else //MULTIPLE_HEAPS
35615     pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
35616 #endif //MULTIPLE_HEAPS
35617 }
35618
35619 //---------------------------------------------------------------------------
35620 // Finalized class tracking
35621 //---------------------------------------------------------------------------
35622
35623 bool GCHeap::RegisterForFinalization (int gen, Object* obj)
35624 {
35625     if (gen == -1)
35626         gen = 0;
35627     if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
35628     {
35629         //just reset the bit
35630         ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
35631         return true;
35632     }
35633     else
35634     {
35635         gc_heap* hp = gc_heap::heap_of ((uint8_t*)obj);
35636         return hp->finalize_queue->RegisterForFinalization (gen, obj);
35637     }
35638 }
35639
35640 void GCHeap::SetFinalizationRun (Object* obj)
35641 {
35642     ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
35643 }
35644
35645
35646 //--------------------------------------------------------------------
35647 //
35648 //          Support for finalization
35649 //
35650 //--------------------------------------------------------------------
35651
35652 inline
35653 unsigned int gen_segment (int gen)
35654 {
35655     assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
35656     return (NUMBERGENERATIONS - gen - 1);
35657 }
35658
35659 bool CFinalize::Initialize()
35660 {
35661     CONTRACTL {
35662         NOTHROW;
35663         GC_NOTRIGGER;
35664     } CONTRACTL_END;
35665
35666     m_Array = new (nothrow)(Object*[100]);
35667
35668     if (!m_Array)
35669     {
35670         ASSERT (m_Array);
35671         STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
35672         if (GCConfig::GetBreakOnOOM())
35673         {
35674             GCToOSInterface::DebugBreak();
35675         }
35676         return false;
35677     }
35678     m_EndArray = &m_Array[100];
35679
35680     for (int i =0; i < FreeList; i++)
35681     {
35682         SegQueueLimit (i) = m_Array;
35683     }
35684     m_PromotedCount = 0;
35685     lock = -1;
35686 #ifdef _DEBUG
35687     lockowner_threadid.Clear();
35688 #endif // _DEBUG
35689
35690     return true;
35691 }
35692
35693 CFinalize::~CFinalize()
35694 {
35695     delete m_Array;
35696 }
35697
35698 size_t CFinalize::GetPromotedCount ()
35699 {
35700     return m_PromotedCount;
35701 }
35702
35703 inline
35704 void CFinalize::EnterFinalizeLock()
35705 {
35706     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
35707              GCToEEInterface::GetThread() == 0 ||
35708              GCToEEInterface::IsPreemptiveGCDisabled());
35709
35710 retry:
35711     if (Interlocked::CompareExchange(&lock, 0, -1) >= 0)
35712     {
35713         unsigned int i = 0;
35714         while (lock >= 0)
35715         {
35716             YieldProcessor();           // indicate to the processor that we are spining
35717             if (++i & 7)
35718                 GCToOSInterface::YieldThread (0);
35719             else
35720                 GCToOSInterface::Sleep (5);
35721         }
35722         goto retry;
35723     }
35724
35725 #ifdef _DEBUG
35726     lockowner_threadid.SetToCurrentThread();
35727 #endif // _DEBUG
35728 }
35729
35730 inline
35731 void CFinalize::LeaveFinalizeLock()
35732 {
35733     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
35734              GCToEEInterface::GetThread() == 0 ||
35735              GCToEEInterface::IsPreemptiveGCDisabled());
35736
35737 #ifdef _DEBUG
35738     lockowner_threadid.Clear();
35739 #endif // _DEBUG
35740     lock = -1;
35741 }
35742
35743 bool
35744 CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
35745 {
35746     CONTRACTL {
35747         NOTHROW;
35748         GC_NOTRIGGER;
35749     } CONTRACTL_END;
35750
35751     EnterFinalizeLock();
35752     // Adjust gen
35753     unsigned int dest = 0;
35754
35755     if (g_fFinalizerRunOnShutDown)
35756     {
35757         //no method table available yet,
35758         //put it in the finalizer queue and sort out when
35759         //dequeueing
35760         dest = FinalizerListSeg;
35761     }
35762
35763     else
35764         dest = gen_segment (gen);
35765
35766     // Adjust boundary for segments so that GC will keep objects alive.
35767     Object*** s_i = &SegQueue (FreeList);
35768     if ((*s_i) == m_EndArray)
35769     {
35770         if (!GrowArray())
35771         {
35772             LeaveFinalizeLock();
35773             if (method_table(obj) == NULL)
35774             {
35775                 // If the object is uninitialized, a valid size should have been passed.
35776                 assert (size >= Align (min_obj_size));
35777                 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
35778                 ((CObjectHeader*)obj)->SetFree(size);
35779             }
35780             STRESS_LOG_OOM_STACK(0);
35781             if (GCConfig::GetBreakOnOOM())
35782             {
35783                 GCToOSInterface::DebugBreak();
35784             }
35785             return false;
35786         }
35787     }
35788     Object*** end_si = &SegQueueLimit (dest);
35789     do
35790     {
35791         //is the segment empty?
35792         if (!(*s_i == *(s_i-1)))
35793         {
35794             //no, swap the end elements.
35795             *(*s_i) = *(*(s_i-1));
35796         }
35797         //increment the fill pointer
35798         (*s_i)++;
35799         //go to the next segment.
35800         s_i--;
35801     } while (s_i > end_si);
35802
35803     // We have reached the destination segment
35804     // store the object
35805     **s_i = obj;
35806     // increment the fill pointer
35807     (*s_i)++;
35808
35809     LeaveFinalizeLock();
35810
35811     return true;
35812 }
35813
35814 Object*
35815 CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
35816 {
35817     Object* obj = 0;
35818     //serialize
35819     EnterFinalizeLock();
35820
35821 retry:
35822     if (!IsSegEmpty(FinalizerListSeg))
35823     {
35824         if (g_fFinalizerRunOnShutDown)
35825         {
35826             obj = *(SegQueueLimit (FinalizerListSeg)-1);
35827             if (method_table(obj)->HasCriticalFinalizer())
35828             {
35829                 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
35830                           FinalizerListSeg, CriticalFinalizerListSeg);
35831                 goto retry;
35832             }
35833             else
35834                 --SegQueueLimit (FinalizerListSeg);
35835         }
35836         else
35837             obj =  *(--SegQueueLimit (FinalizerListSeg));
35838
35839     }
35840     else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
35841     {
35842         //the FinalizerList is empty, we can adjust both
35843         // limit instead of moving the object to the free list
35844         obj =  *(--SegQueueLimit (CriticalFinalizerListSeg));
35845         --SegQueueLimit (FinalizerListSeg);
35846     }
35847     if (obj)
35848     {
35849         dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
35850     }
35851     LeaveFinalizeLock();
35852     return obj;
35853 }
35854
35855 void
35856 CFinalize::SetSegForShutDown(BOOL fHasLock)
35857 {
35858     int i;
35859
35860     if (!fHasLock)
35861         EnterFinalizeLock();
35862     for (i = 0; i <= max_generation; i++)
35863     {
35864         unsigned int seg = gen_segment (i);
35865         Object** startIndex = SegQueueLimit (seg)-1;
35866         Object** stopIndex  = SegQueue (seg);
35867         for (Object** po = startIndex; po >= stopIndex; po--)
35868         {
35869             Object* obj = *po;
35870             if (method_table(obj)->HasCriticalFinalizer())
35871             {
35872                 MoveItem (po, seg, CriticalFinalizerListSeg);
35873             }
35874             else
35875             {
35876                 MoveItem (po, seg, FinalizerListSeg);
35877             }
35878         }
35879     }
35880     if (!fHasLock)
35881         LeaveFinalizeLock();
35882 }
35883
35884 void
35885 CFinalize::DiscardNonCriticalObjects()
35886 {
35887     //empty the finalization queue
35888     Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
35889     Object** stopIndex  = SegQueue (FinalizerListSeg);
35890     for (Object** po = startIndex; po >= stopIndex; po--)
35891     {
35892         MoveItem (po, FinalizerListSeg, FreeList);
35893     }
35894 }
35895
35896 size_t
35897 CFinalize::GetNumberFinalizableObjects()
35898 {
35899     return SegQueueLimit (FinalizerListSeg) -
35900         (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
35901 }
35902
35903 BOOL
35904 CFinalize::FinalizeSegForAppDomain (void *pDomain, 
35905                                     BOOL fRunFinalizers, 
35906                                     unsigned int Seg)
35907 {
35908     BOOL finalizedFound = FALSE;
35909     Object** endIndex = SegQueue (Seg);
35910     for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
35911     {
35912         CObjectHeader* obj = (CObjectHeader*)*i;
35913
35914         // Objects are put into the finalization queue before they are complete (ie their methodtable
35915         // may be null) so we must check that the object we found has a method table before checking
35916         // if it has the index we are looking for. If the methodtable is null, it can't be from the
35917         // unloading domain, so skip it.
35918         if (method_table(obj) == NULL)
35919         {
35920             continue;
35921         }
35922
35923         // does the EE actually want us to finalize this object?
35924         if (!GCToEEInterface::ShouldFinalizeObjectForUnload(pDomain, obj))
35925         {
35926             continue;
35927         }
35928
35929         if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
35930         {
35931             //remove the object because we don't want to
35932             //run the finalizer
35933             MoveItem (i, Seg, FreeList);
35934             //Reset the bit so it will be put back on the queue
35935             //if resurrected and re-registered.
35936             obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
35937         }
35938         else
35939         {
35940             if (method_table(obj)->HasCriticalFinalizer())
35941             {
35942                 finalizedFound = TRUE;
35943                 MoveItem (i, Seg, CriticalFinalizerListSeg);
35944             }
35945             else
35946             {
35947                 if (GCToEEInterface::AppDomainIsRudeUnload(pDomain))
35948                 {
35949                     MoveItem (i, Seg, FreeList);
35950                 }
35951                 else
35952                 {
35953                     finalizedFound = TRUE;
35954                     MoveItem (i, Seg, FinalizerListSeg);
35955                 }
35956             }
35957         }
35958     }
35959
35960     return finalizedFound;
35961 }
35962
35963 bool
35964 CFinalize::FinalizeAppDomain (void *pDomain, bool fRunFinalizers)
35965 {
35966     bool finalizedFound = false;
35967
35968     unsigned int startSeg = gen_segment (max_generation);
35969
35970     EnterFinalizeLock();
35971
35972     for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
35973     {
35974         if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
35975         {
35976             finalizedFound = true;
35977         }
35978     }
35979
35980     LeaveFinalizeLock();
35981
35982     return finalizedFound;
35983 }
35984
35985 void
35986 CFinalize::MoveItem (Object** fromIndex,
35987                      unsigned int fromSeg,
35988                      unsigned int toSeg)
35989 {
35990
35991     int step;
35992     ASSERT (fromSeg != toSeg);
35993     if (fromSeg > toSeg)
35994         step = -1;
35995     else
35996         step = +1;
35997     // Place the element at the boundary closest to dest
35998     Object** srcIndex = fromIndex;
35999     for (unsigned int i = fromSeg; i != toSeg; i+= step)
36000     {
36001         Object**& destFill = m_FillPointers[i+(step - 1 )/2];
36002         Object** destIndex = destFill - (step + 1)/2;
36003         if (srcIndex != destIndex)
36004         {
36005             Object* tmp = *srcIndex;
36006             *srcIndex = *destIndex;
36007             *destIndex = tmp;
36008         }
36009         destFill -= step;
36010         srcIndex = destIndex;
36011     }
36012 }
36013
36014 void
36015 CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
36016 {
36017     ScanContext sc;
36018     if (pSC == 0)
36019         pSC = &sc;
36020
36021     pSC->thread_number = hn;
36022
36023     //scan the finalization queue
36024     Object** startIndex  = SegQueue (CriticalFinalizerListSeg);
36025     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
36026
36027     for (Object** po = startIndex; po < stopIndex; po++)
36028     {
36029         Object* o = *po;
36030         //dprintf (3, ("scan freacheable %Ix", (size_t)o));
36031         dprintf (3, ("scan f %Ix", (size_t)o));
36032 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
36033         if (g_fEnableARM)
36034         {
36035             pSC->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(o->GetAppDomainIndex());
36036         }
36037 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
36038
36039         (*fn)(po, pSC, 0);
36040     }
36041 }
36042
36043 void CFinalize::WalkFReachableObjects (fq_walk_fn fn)
36044 {
36045     Object** startIndex = SegQueue (CriticalFinalizerListSeg);
36046     Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
36047     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
36048     for (Object** po = startIndex; po < stopIndex; po++)
36049     {
36050         //report *po
36051         fn(po < stopCriticalIndex, *po);
36052     }
36053 }
36054
36055 BOOL
36056 CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
36057                                 gc_heap* hp)
36058 {
36059     ScanContext sc;
36060     sc.promotion = TRUE;
36061 #ifdef MULTIPLE_HEAPS
36062     sc.thread_number = hp->heap_number;
36063 #else
36064     UNREFERENCED_PARAMETER(hp);
36065 #endif //MULTIPLE_HEAPS
36066
36067     BOOL finalizedFound = FALSE;
36068
36069     //start with gen and explore all the younger generations.
36070     unsigned int startSeg = gen_segment (gen);
36071     {
36072         m_PromotedCount = 0;
36073         for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
36074         {
36075             Object** endIndex = SegQueue (Seg);
36076             for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36077             {
36078                 CObjectHeader* obj = (CObjectHeader*)*i;
36079                 dprintf (3, ("scanning: %Ix", (size_t)obj));
36080                 if (!g_theGCHeap->IsPromoted (obj))
36081                 {
36082                     dprintf (3, ("freacheable: %Ix", (size_t)obj));
36083
36084                     assert (method_table(obj)->HasFinalizer());
36085
36086                     if (GCToEEInterface::EagerFinalized(obj))
36087                     {
36088                         MoveItem (i, Seg, FreeList);
36089                     }
36090                     else if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36091                     {
36092                         //remove the object because we don't want to
36093                         //run the finalizer
36094
36095                         MoveItem (i, Seg, FreeList);
36096
36097                         //Reset the bit so it will be put back on the queue
36098                         //if resurrected and re-registered.
36099                         obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36100
36101                     }
36102                     else
36103                     {
36104                         m_PromotedCount++;
36105
36106                         if (method_table(obj)->HasCriticalFinalizer())
36107                         {
36108                             MoveItem (i, Seg, CriticalFinalizerListSeg);
36109                         }
36110                         else
36111                         {
36112                             MoveItem (i, Seg, FinalizerListSeg);
36113                         }
36114                     }
36115                 }
36116 #ifdef BACKGROUND_GC
36117                 else
36118                 {
36119                     if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
36120                     {
36121                         // TODO - fix the following line.
36122                         //assert (gc_heap::background_object_marked ((uint8_t*)obj, FALSE));
36123                         dprintf (3, ("%Ix is marked", (size_t)obj));
36124                     }
36125                 }
36126 #endif //BACKGROUND_GC
36127             }
36128         }
36129     }
36130     finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
36131                      !IsSegEmpty(CriticalFinalizerListSeg);
36132                     
36133     if (finalizedFound)
36134     {
36135         //Promote the f-reachable objects
36136         GcScanRoots (pfn,
36137 #ifdef MULTIPLE_HEAPS
36138                      hp->heap_number
36139 #else
36140                      0
36141 #endif //MULTIPLE_HEAPS
36142                      , 0);
36143
36144         hp->settings.found_finalizers = TRUE;
36145
36146 #ifdef BACKGROUND_GC
36147         if (hp->settings.concurrent)
36148         {
36149             hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
36150         }
36151 #endif //BACKGROUND_GC
36152         if (hp->settings.concurrent && hp->settings.found_finalizers)
36153         {
36154             if (!mark_only_p)
36155                 GCToEEInterface::EnableFinalization(true);
36156         }
36157     }
36158
36159     return finalizedFound;
36160 }
36161
36162 //Relocates all of the objects in the finalization array
36163 void
36164 CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
36165 {
36166     ScanContext sc;
36167     sc.promotion = FALSE;
36168 #ifdef MULTIPLE_HEAPS
36169     sc.thread_number = hp->heap_number;
36170 #else
36171     UNREFERENCED_PARAMETER(hp);
36172 #endif //MULTIPLE_HEAPS
36173
36174     unsigned int Seg = gen_segment (gen);
36175
36176     Object** startIndex = SegQueue (Seg);
36177     for (Object** po = startIndex; po < SegQueue (FreeList);po++)
36178     {
36179         GCHeap::Relocate (po, &sc);
36180     }
36181 }
36182
36183 void
36184 CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
36185 {
36186     // update the generation fill pointers.
36187     // if gen_0_empty is FALSE, test each object to find out if
36188     // it was promoted or not
36189     if (gen_0_empty_p)
36190     {
36191         for (int i = min (gen+1, max_generation); i > 0; i--)
36192         {
36193             m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
36194         }
36195     }
36196     else
36197     {
36198         //Look for demoted or promoted plugs
36199
36200         for (int i = gen; i >= 0; i--)
36201         {
36202             unsigned int Seg = gen_segment (i);
36203             Object** startIndex = SegQueue (Seg);
36204
36205             for (Object** po = startIndex;
36206                  po < SegQueueLimit (gen_segment(i)); po++)
36207             {
36208                 int new_gen = g_theGCHeap->WhichGeneration (*po);
36209                 if (new_gen != i)
36210                 {
36211                     if (new_gen > i)
36212                     {
36213                         //promotion
36214                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
36215                     }
36216                     else
36217                     {
36218                         //demotion
36219                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
36220                         //back down in order to see all objects.
36221                         po--;
36222                     }
36223                 }
36224
36225             }
36226         }
36227     }
36228 }
36229
36230 BOOL
36231 CFinalize::GrowArray()
36232 {
36233     size_t oldArraySize = (m_EndArray - m_Array);
36234     size_t newArraySize =  (size_t)(((float)oldArraySize / 10) * 12);
36235
36236     Object** newArray = new (nothrow) Object*[newArraySize];
36237     if (!newArray)
36238     {
36239         // It's not safe to throw here, because of the FinalizeLock.  Tell our caller
36240         // to throw for us.
36241 //        ASSERT (newArray);
36242         return FALSE;
36243     }
36244     memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
36245
36246     //adjust the fill pointers
36247     for (int i = 0; i < FreeList; i++)
36248     {
36249         m_FillPointers [i] += (newArray - m_Array);
36250     }
36251     delete m_Array;
36252     m_Array = newArray;
36253     m_EndArray = &m_Array [newArraySize];
36254
36255     return TRUE;
36256 }
36257
36258 #ifdef VERIFY_HEAP
36259 void CFinalize::CheckFinalizerObjects()
36260 {
36261     for (int i = 0; i <= max_generation; i++)
36262     {
36263         Object **startIndex = SegQueue (gen_segment (i));
36264         Object **stopIndex  = SegQueueLimit (gen_segment (i));
36265
36266         for (Object **po = startIndex; po < stopIndex; po++)
36267         {
36268             if ((int)g_theGCHeap->WhichGeneration (*po) < i)
36269                 FATAL_GC_ERROR ();
36270             ((CObjectHeader*)*po)->Validate();
36271         }
36272     }
36273 }
36274 #endif //VERIFY_HEAP
36275
36276 #endif // FEATURE_PREMORTEM_FINALIZATION
36277
36278
36279 //------------------------------------------------------------------------------
36280 //
36281 //                      End of VM specific support
36282 //
36283 //------------------------------------------------------------------------------
36284 void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36285 {
36286     generation* gen = gc_heap::generation_of (gen_number);
36287     heap_segment*    seg = generation_start_segment (gen);
36288     uint8_t*       x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
36289                      generation_allocation_start (gen));
36290
36291     uint8_t*       end = heap_segment_allocated (seg);
36292     BOOL small_object_segments = TRUE;
36293     int align_const = get_alignment_constant (small_object_segments);
36294
36295     while (1)
36296
36297     {
36298         if (x >= end)
36299         {
36300             if ((seg = heap_segment_next (seg)) != 0)
36301             {
36302                 x = heap_segment_mem (seg);
36303                 end = heap_segment_allocated (seg);
36304                 continue;
36305             }
36306             else
36307             {
36308                 if (small_object_segments && walk_large_object_heap_p)
36309
36310                 {
36311                     small_object_segments = FALSE;
36312                     align_const = get_alignment_constant (small_object_segments);
36313                     seg = generation_start_segment (large_object_generation);
36314                     x = heap_segment_mem (seg);
36315                     end = heap_segment_allocated (seg);
36316                     continue;
36317                 }
36318                 else
36319                 {
36320                     break;
36321                 }
36322             }
36323         }
36324
36325         size_t s = size (x);
36326         CObjectHeader* o = (CObjectHeader*)x;
36327
36328         if (!o->IsFree())
36329
36330         {
36331             _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
36332
36333             if (!fn (o->GetObjectBase(), context))
36334                 return;
36335         }
36336         x = x + Align (s, align_const);
36337     }
36338 }
36339
36340 void gc_heap::walk_finalize_queue (fq_walk_fn fn)
36341 {
36342 #ifdef FEATURE_PREMORTEM_FINALIZATION
36343     finalize_queue->WalkFReachableObjects (fn);
36344 #endif //FEATURE_PREMORTEM_FINALIZATION
36345 }
36346
36347 void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36348 {
36349 #ifdef MULTIPLE_HEAPS
36350     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36351     {
36352         gc_heap* hp = gc_heap::g_heaps [hn];
36353
36354         hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p);
36355     }
36356 #else
36357     walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p);
36358 #endif //MULTIPLE_HEAPS
36359 }
36360
36361 void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context)
36362 {
36363     uint8_t* o = (uint8_t*)obj;
36364     if (o)
36365     {
36366         go_through_object_cl (method_table (o), o, size(o), oo,
36367                                     {
36368                                         if (*oo)
36369                                         {
36370                                             Object *oh = (Object*)*oo;
36371                                             if (!fn (oh, context))
36372                                                 return;
36373                                         }
36374                                     }
36375             );
36376     }
36377 }
36378
36379 void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type)
36380 {
36381     gc_heap* hp = (gc_heap*)gc_context;
36382     hp->walk_survivors (fn, diag_context, type);
36383 }
36384
36385 void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p)
36386 {
36387     gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
36388 }
36389
36390 void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
36391 {
36392     gc_heap* hp = (gc_heap*)gc_context;
36393     hp->walk_finalize_queue (fn);
36394 }
36395
36396 void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc)
36397 {
36398 #ifdef MULTIPLE_HEAPS
36399     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36400     {
36401         gc_heap* hp = gc_heap::g_heaps [hn];
36402         hp->finalize_queue->GcScanRoots(fn, hn, sc);
36403     }
36404 #else
36405         pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc);
36406 #endif //MULTIPLE_HEAPS
36407 }
36408
36409 void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36410 {
36411     UNREFERENCED_PARAMETER(gen_number);
36412     GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn);
36413 }
36414
36415 void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36416 {
36417     UNREFERENCED_PARAMETER(gen_number);
36418     GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn);
36419 }
36420
36421 // Go through and touch (read) each page straddled by a memory block.
36422 void TouchPages(void * pStart, size_t cb)
36423 {
36424     const uint32_t pagesize = OS_PAGE_SIZE;
36425     _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
36426     if (cb)
36427     {
36428         VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
36429         VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) -  (((size_t)pStart) & (pagesize-1)));
36430         while (p < pEnd)
36431         {
36432             char a;
36433             a = VolatileLoad(p);
36434             //printf("Touching page %lxh\n", (uint32_t)p);
36435             p += pagesize;
36436         }
36437     }
36438 }
36439
36440 #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
36441     // This code is designed to catch the failure to update the write barrier
36442     // The way it works is to copy the whole heap right after every GC.  The write
36443     // barrier code has been modified so that it updates the shadow as well as the
36444     // real GC heap.  Before doing the next GC, we walk the heap, looking for pointers
36445     // that were updated in the real heap, but not the shadow.  A mismatch indicates
36446     // an error.  The offending code can be found by breaking after the correct GC,
36447     // and then placing a data breakpoint on the Heap location that was updated without
36448     // going through the write barrier.
36449
36450     // Called at process shutdown
36451 void deleteGCShadow()
36452 {
36453     if (g_GCShadow != 0)
36454         GCToOSInterface::VirtualRelease (g_GCShadow, g_GCShadowEnd - g_GCShadow);
36455     g_GCShadow = 0;
36456     g_GCShadowEnd = 0;
36457 }
36458
36459     // Called at startup and right after a GC, get a snapshot of the GC Heap
36460 void initGCShadow()
36461 {
36462     if (!(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK))
36463         return;
36464
36465     size_t len = g_gc_highest_address - g_gc_lowest_address;
36466     if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) 
36467     {
36468         deleteGCShadow();
36469         g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None);
36470         if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len))
36471         {
36472             _ASSERTE(!"Not enough memory to run HeapVerify level 2");
36473             // If after the assert we decide to allow the program to continue 
36474             // running we need to be in a state that will not trigger any 
36475             // additional AVs while we fail to allocate a shadow segment, i.e. 
36476             // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
36477             deleteGCShadow();
36478             return;
36479         }
36480
36481         g_GCShadowEnd += len;
36482     }
36483
36484     // save the value of g_gc_lowest_address at this time.  If this value changes before
36485     // the next call to checkGCWriteBarrier() it means we extended the heap (with a
36486     // large object segment most probably), and the whole shadow segment is inconsistent.
36487     g_shadow_lowest_address = g_gc_lowest_address;
36488
36489         //****** Copy the whole GC heap ******
36490     //
36491     // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
36492     // can produce a NULL result.  This is because the initialization has not completed.
36493     //
36494     generation* gen = gc_heap::generation_of (max_generation);
36495     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36496
36497     ptrdiff_t delta = g_GCShadow - g_gc_lowest_address;
36498     BOOL small_object_segments = TRUE;
36499     while(1)
36500     {
36501         if (!seg)
36502         {
36503             if (small_object_segments)
36504             {
36505                 small_object_segments = FALSE;
36506                 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
36507                 continue;
36508             }
36509             else
36510                 break;
36511         }
36512             // Copy the segment
36513         uint8_t* start = heap_segment_mem(seg);
36514         uint8_t* end = heap_segment_allocated (seg);
36515         memcpy(start + delta, start, end - start);
36516         seg = heap_segment_next_rw (seg);
36517     }
36518 }
36519
36520 #define INVALIDGCVALUE (void*)((size_t)0xcccccccd)
36521
36522     // test to see if 'ptr' was only updated via the write barrier.
36523 inline void testGCShadow(Object** ptr)
36524 {
36525     Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)];
36526     if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow)
36527     {
36528
36529         // If you get this assertion, someone updated a GC pointer in the heap without
36530         // using the write barrier.  To find out who, check the value of 
36531         // dd_collection_count (dynamic_data_of (0)). Also
36532         // note the value of 'ptr'.  Rerun the App that the previous GC just occurred.
36533         // Then put a data breakpoint for the value of 'ptr'  Then check every write
36534         // to pointer between the two GCs.  The last one is not using the write barrier.
36535
36536         // If the memory of interest does not exist at system startup,
36537         // you need to set the data breakpoint right after the memory gets committed
36538         // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
36539         // in the memory window.  run until the memory gets mapped. Then you can set
36540         // your breakpoint
36541
36542         // Note a recent change, we've identified race conditions when updating the gc shadow.
36543         // Throughout the runtime, code will update an address in the gc heap, then erect the
36544         // write barrier, which calls updateGCShadow. With an app that pounds one heap location
36545         // from multiple threads, you can hit this assert even though all involved are using the
36546         // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
36547         // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
36548         // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
36549         // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
36550         // TODO: erroneous asserts in here.
36551
36552         if(*shadow!=INVALIDGCVALUE)
36553         {
36554 #ifdef FEATURE_BASICFREEZE
36555             // Write barriers for stores of references to frozen objects may be optimized away.
36556             if (!gc_heap::frozen_object_p(*ptr))
36557 #endif // FEATURE_BASICFREEZE
36558             {
36559                 _ASSERTE(!"Pointer updated without using write barrier");
36560             }
36561         }
36562         /*
36563         else
36564         {
36565              printf("saw a INVALIDGCVALUE. (just to let you know)\n");
36566         }
36567         */
36568     }
36569 }
36570
36571 void testGCShadowHelper (uint8_t* x)
36572 {
36573     size_t s = size (x);
36574     if (contain_pointers (x))
36575     {
36576         go_through_object_nostart (method_table(x), x, s, oo,
36577                            { testGCShadow((Object**) oo); });
36578     }
36579 }
36580
36581     // Walk the whole heap, looking for pointers that were not updated with the write barrier.
36582 void checkGCWriteBarrier()
36583 {
36584     // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment
36585     // and the GC shadow segment did not track that change!
36586     if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address)
36587     {
36588         // No shadow stack, nothing to check.
36589         return;
36590     }
36591
36592     {
36593         generation* gen = gc_heap::generation_of (max_generation);
36594         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36595
36596         PREFIX_ASSUME(seg != NULL);
36597
36598         while(seg)
36599         {
36600             uint8_t* x = heap_segment_mem(seg);
36601             while (x < heap_segment_allocated (seg))
36602             {
36603                 size_t s = size (x);
36604                 testGCShadowHelper (x);
36605                 x = x + Align (s);
36606             }
36607             seg = heap_segment_next_rw (seg);
36608         }
36609     }
36610
36611     {
36612         // go through large object heap
36613         int alignment = get_alignment_constant(FALSE);
36614         generation* gen = gc_heap::generation_of (max_generation+1);
36615         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36616
36617         PREFIX_ASSUME(seg != NULL);
36618
36619         while(seg)
36620         {
36621             uint8_t* x = heap_segment_mem(seg);
36622             while (x < heap_segment_allocated (seg))
36623             {
36624                 size_t s = size (x);
36625                 testGCShadowHelper (x);
36626                 x = x + Align (s, alignment);
36627             }
36628             seg = heap_segment_next_rw (seg);
36629         }
36630     }
36631 }
36632 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
36633
36634 #endif // !DACCESS_COMPILE
36635
36636 #ifdef FEATURE_BASICFREEZE
36637 void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
36638 {
36639 #ifdef DACCESS_COMPILE
36640     UNREFERENCED_PARAMETER(seg);
36641     UNREFERENCED_PARAMETER(pvContext);
36642     UNREFERENCED_PARAMETER(pfnMethodTable);
36643     UNREFERENCED_PARAMETER(pfnObjRef);
36644 #else
36645     uint8_t *o = heap_segment_mem(seg);
36646
36647     // small heap alignment constant
36648     int alignment = get_alignment_constant(TRUE);
36649
36650     while (o < heap_segment_allocated(seg))
36651     {
36652         pfnMethodTable(pvContext, o);
36653
36654         if (contain_pointers (o))
36655         {
36656             go_through_object_nostart (method_table (o), o, size(o), oo,
36657                    {
36658                        if (*oo)
36659                            pfnObjRef(pvContext, oo);
36660                    }
36661             );
36662         }
36663
36664         o += Align(size(o), alignment);
36665     }
36666 #endif //!DACCESS_COMPILE
36667 }
36668 #endif // FEATURE_BASICFREEZE
36669
36670 #ifndef DACCESS_COMPILE
36671 HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
36672 {
36673 #ifdef BACKGROUND_GC
36674     if (recursive_gc_sync::background_running_p())
36675     {
36676         uint32_t dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
36677         if (dwRet == WAIT_OBJECT_0)
36678             return S_OK;
36679         else if (dwRet == WAIT_TIMEOUT)
36680             return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
36681         else
36682             return E_FAIL;      // It is not clear if what the last error would be if the wait failed,
36683                                 // as there are too many layers in between. The best we can do is to return E_FAIL;
36684     }
36685 #endif
36686
36687     return S_OK;
36688 }
36689 #endif // !DACCESS_COMPILE
36690
36691 void GCHeap::TemporaryEnableConcurrentGC()
36692 {
36693 #ifdef BACKGROUND_GC
36694     gc_heap::temp_disable_concurrent_p = false;
36695 #endif //BACKGROUND_GC
36696 }
36697
36698 void GCHeap::TemporaryDisableConcurrentGC()
36699 {
36700 #ifdef BACKGROUND_GC
36701     gc_heap::temp_disable_concurrent_p = true;
36702 #endif //BACKGROUND_GC
36703 }
36704
36705 bool GCHeap::IsConcurrentGCEnabled()
36706 {
36707 #ifdef BACKGROUND_GC
36708     return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
36709 #else
36710     return FALSE;
36711 #endif //BACKGROUND_GC
36712 }
36713
36714 void GCHeap::SetFinalizeRunOnShutdown(bool value)
36715 {
36716     g_fFinalizerRunOnShutDown = value;
36717 }
36718
36719 void PopulateDacVars(GcDacVars *gcDacVars)
36720 {
36721 #ifndef DACCESS_COMPILE
36722     assert(gcDacVars != nullptr);
36723     *gcDacVars = {};
36724     gcDacVars->major_version_number = 1;
36725     gcDacVars->minor_version_number = 0;
36726     gcDacVars->built_with_svr = &g_built_with_svr_gc;
36727     gcDacVars->build_variant = &g_build_variant;
36728     gcDacVars->gc_structures_invalid_cnt = const_cast<int32_t*>(&GCScan::m_GcStructuresInvalidCnt);
36729     gcDacVars->generation_size = sizeof(generation);
36730     gcDacVars->max_gen = &g_max_generation;
36731 #ifndef MULTIPLE_HEAPS
36732     gcDacVars->mark_array = &gc_heap::mark_array;
36733     gcDacVars->ephemeral_heap_segment = reinterpret_cast<dac_heap_segment**>(&gc_heap::ephemeral_heap_segment);
36734     gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state);
36735     gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast<dac_heap_segment**>(&gc_heap::saved_sweep_ephemeral_seg);
36736     gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start;
36737     gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address;
36738     gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address;
36739     gcDacVars->alloc_allocated = &gc_heap::alloc_allocated;
36740     gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj;
36741     gcDacVars->oom_info = &gc_heap::oom_info;
36742     gcDacVars->finalize_queue = reinterpret_cast<dac_finalize_queue**>(&gc_heap::finalize_queue);
36743     gcDacVars->generation_table = reinterpret_cast<dac_generation**>(&gc_heap::generation_table);
36744 #ifdef GC_CONFIG_DRIVEN
36745     gcDacVars->gc_global_mechanisms = reinterpret_cast<size_t**>(&gc_global_mechanisms);
36746     gcDacVars->interesting_data_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_data_per_heap);
36747     gcDacVars->compact_reasons_per_heap = reinterpret_cast<size_t**>(&gc_heap::compact_reasons_per_heap);
36748     gcDacVars->expand_mechanisms_per_heap = reinterpret_cast<size_t**>(&gc_heap::expand_mechanisms_per_heap);
36749     gcDacVars->interesting_mechanism_bits_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_mechanism_bits_per_heap);
36750 #endif // GC_CONFIG_DRIVEN
36751 #ifdef HEAP_ANALYZE
36752     gcDacVars->internal_root_array = &gc_heap::internal_root_array;
36753     gcDacVars->internal_root_array_index = &gc_heap::internal_root_array_index;
36754     gcDacVars->heap_analyze_success = &gc_heap::heap_analyze_success;
36755 #endif // HEAP_ANALYZE
36756 #else
36757     gcDacVars->n_heaps = &gc_heap::n_heaps;
36758     gcDacVars->g_heaps = reinterpret_cast<dac_gc_heap***>(&gc_heap::g_heaps);
36759 #endif // MULTIPLE_HEAPS
36760 #endif // DACCESS_COMPILE
36761 }