Merge pull request #21400 from BruceForstall/FixArm32FloatRange
[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     n_heaps = number_of_heaps;
9932 #else //MULTIPLE_HEAPS
9933     reserved_memory_limit = segment_size + heap_size;
9934     block_count = 1;
9935 #endif //MULTIPLE_HEAPS
9936
9937     if (!reserve_initial_memory(segment_size,heap_size,block_count))
9938         return E_OUTOFMEMORY;
9939
9940 #ifdef CARD_BUNDLE
9941     //check if we need to turn on card_bundles.
9942 #ifdef MULTIPLE_HEAPS
9943     // use INT64 arithmetic here because of possible overflow on 32p
9944     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*number_of_heaps;
9945 #else
9946     // use INT64 arithmetic here because of possible overflow on 32p
9947     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
9948 #endif //MULTIPLE_HEAPS
9949
9950     if (can_use_write_watch_for_card_table() && reserved_memory >= th)
9951     {
9952         settings.card_bundles = TRUE;
9953     }
9954     else
9955     {
9956         settings.card_bundles = FALSE;
9957     }
9958 #endif //CARD_BUNDLE
9959
9960     settings.first_init();
9961
9962     int latency_level_from_config = static_cast<int>(GCConfig::GetLatencyLevel());
9963     if (latency_level_from_config >= latency_level_first && latency_level_from_config <= latency_level_last)
9964     {
9965         gc_heap::latency_level = static_cast<gc_latency_level>(latency_level_from_config);
9966     }
9967
9968     init_static_data();
9969
9970     g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address);
9971
9972     if (!g_gc_card_table)
9973         return E_OUTOFMEMORY;
9974
9975     gc_started = FALSE;
9976
9977 #ifdef MULTIPLE_HEAPS
9978     g_heaps = new (nothrow) gc_heap* [number_of_heaps];
9979     if (!g_heaps)
9980         return E_OUTOFMEMORY;
9981
9982 #ifdef _PREFAST_ 
9983 #pragma warning(push)
9984 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
9985 #endif // _PREFAST_
9986     g_promoted = new (nothrow) size_t [number_of_heaps*16];
9987     g_bpromoted = new (nothrow) size_t [number_of_heaps*16];
9988 #ifdef MH_SC_MARK
9989     g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
9990 #endif //MH_SC_MARK
9991 #ifdef _PREFAST_ 
9992 #pragma warning(pop)
9993 #endif // _PREFAST_
9994     if (!g_promoted || !g_bpromoted)
9995         return E_OUTOFMEMORY;
9996
9997 #ifdef MH_SC_MARK
9998     if (!g_mark_stack_busy)
9999         return E_OUTOFMEMORY;
10000 #endif //MH_SC_MARK
10001
10002     if (!create_thread_support (number_of_heaps))
10003         return E_OUTOFMEMORY;
10004
10005     if (!heap_select::init (number_of_heaps))
10006         return E_OUTOFMEMORY;
10007
10008 #endif //MULTIPLE_HEAPS
10009
10010     if (!init_semi_shared())
10011     {
10012         hres = E_FAIL;
10013     }
10014
10015     return hres;
10016 }
10017
10018 //Initializes PER_HEAP_ISOLATED data members.
10019 int
10020 gc_heap::init_semi_shared()
10021 {
10022     int ret = 0;
10023
10024     // This is used for heap expansion - it's to fix exactly the start for gen 0
10025     // through (max_generation-1). When we expand the heap we allocate all these
10026     // gen starts at the beginning of the new ephemeral seg. 
10027     eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
10028
10029 #ifdef MARK_LIST
10030 #ifdef MULTIPLE_HEAPS
10031     mark_list_size = min (150*1024, max (8192, soh_segment_size/(2*10*32)));
10032     g_mark_list = make_mark_list (mark_list_size*n_heaps);
10033
10034     min_balance_threshold = alloc_quantum_balance_units * CLR_SIZE * 2;
10035 #ifdef PARALLEL_MARK_LIST_SORT
10036     g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
10037     if (!g_mark_list_copy)
10038     {
10039         goto cleanup;
10040     }
10041 #endif //PARALLEL_MARK_LIST_SORT
10042
10043 #else //MULTIPLE_HEAPS
10044
10045     mark_list_size = max (8192, soh_segment_size/(64*32));
10046     g_mark_list = make_mark_list (mark_list_size);
10047
10048 #endif //MULTIPLE_HEAPS
10049
10050     dprintf (3, ("mark_list_size: %d", mark_list_size));
10051
10052     if (!g_mark_list)
10053     {
10054         goto cleanup;
10055     }
10056 #endif //MARK_LIST
10057
10058 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10059     if (!seg_mapping_table_init())
10060         goto cleanup;
10061 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10062
10063 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10064     seg_table = sorted_table::make_sorted_table();
10065
10066     if (!seg_table)
10067         goto cleanup;
10068 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10069
10070     segment_standby_list = 0;
10071
10072     if (!full_gc_approach_event.CreateManualEventNoThrow(FALSE))
10073     {
10074         goto cleanup;
10075     }
10076     if (!full_gc_end_event.CreateManualEventNoThrow(FALSE))
10077     {
10078         goto cleanup;
10079     }
10080
10081     fgn_maxgen_percent = 0;
10082     fgn_loh_percent = 0;
10083     full_gc_approach_event_set = false;
10084
10085     memset (full_gc_counts, 0, sizeof (full_gc_counts));
10086
10087     last_gc_index = 0;
10088     should_expand_in_full_gc = FALSE;
10089
10090 #ifdef FEATURE_LOH_COMPACTION
10091     loh_compaction_always_p = GCConfig::GetLOHCompactionMode() != 0;
10092     loh_compaction_mode = loh_compaction_default;
10093 #endif //FEATURE_LOH_COMPACTION
10094
10095 #ifdef BACKGROUND_GC
10096     memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
10097     bgc_alloc_spin_count = static_cast<uint32_t>(GCConfig::GetBGCSpinCount());
10098     bgc_alloc_spin = static_cast<uint32_t>(GCConfig::GetBGCSpin());
10099
10100     {   
10101         int number_bgc_threads = 1;
10102 #ifdef MULTIPLE_HEAPS
10103         number_bgc_threads = n_heaps;
10104 #endif //MULTIPLE_HEAPS
10105         if (!create_bgc_threads_support (number_bgc_threads))
10106         {
10107             goto cleanup;
10108         }
10109     }
10110 #endif //BACKGROUND_GC
10111
10112     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
10113
10114 #ifdef GC_CONFIG_DRIVEN
10115     compact_or_sweep_gcs[0] = 0;
10116     compact_or_sweep_gcs[1] = 0;
10117 #endif //GC_CONFIG_DRIVEN
10118
10119 #ifdef SHORT_PLUGS
10120     short_plugs_pad_ratio = (double)DESIRED_PLUG_LENGTH / (double)(DESIRED_PLUG_LENGTH - Align (min_obj_size));
10121 #endif //SHORT_PLUGS
10122
10123     ret = 1;
10124
10125 cleanup:
10126
10127     if (!ret)
10128     {
10129         if (full_gc_approach_event.IsValid())
10130         {
10131             full_gc_approach_event.CloseEvent();
10132         }
10133         if (full_gc_end_event.IsValid())
10134         {
10135             full_gc_end_event.CloseEvent();
10136         }
10137     }
10138
10139     return ret;
10140 }
10141
10142 gc_heap* gc_heap::make_gc_heap (
10143 #ifdef MULTIPLE_HEAPS
10144                                 GCHeap* vm_hp,
10145                                 int heap_number
10146 #endif //MULTIPLE_HEAPS
10147                                 )
10148 {
10149     gc_heap* res = 0;
10150
10151 #ifdef MULTIPLE_HEAPS
10152     res = new (nothrow) gc_heap;
10153     if (!res)
10154         return 0;
10155
10156     res->vm_heap = vm_hp;
10157     res->alloc_context_count = 0;
10158
10159 #ifdef MARK_LIST
10160 #ifdef PARALLEL_MARK_LIST_SORT
10161     res->mark_list_piece_start = new (nothrow) uint8_t**[n_heaps];
10162     if (!res->mark_list_piece_start)
10163         return 0;
10164
10165 #ifdef _PREFAST_ 
10166 #pragma warning(push)
10167 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10168 #endif // _PREFAST_
10169     res->mark_list_piece_end = new (nothrow) uint8_t**[n_heaps + 32]; // +32 is padding to reduce false sharing
10170 #ifdef _PREFAST_ 
10171 #pragma warning(pop)
10172 #endif // _PREFAST_
10173
10174     if (!res->mark_list_piece_end)
10175         return 0;
10176 #endif //PARALLEL_MARK_LIST_SORT
10177 #endif //MARK_LIST
10178
10179
10180 #endif //MULTIPLE_HEAPS
10181
10182     if (res->init_gc_heap (
10183 #ifdef MULTIPLE_HEAPS
10184         heap_number
10185 #else  //MULTIPLE_HEAPS
10186         0
10187 #endif //MULTIPLE_HEAPS
10188         )==0)
10189     {
10190         return 0;
10191     }
10192
10193 #ifdef MULTIPLE_HEAPS
10194     return res;
10195 #else
10196     return (gc_heap*)1;
10197 #endif //MULTIPLE_HEAPS
10198 }
10199
10200 uint32_t
10201 gc_heap::wait_for_gc_done(int32_t timeOut)
10202 {
10203     bool cooperative_mode = enable_preemptive ();
10204
10205     uint32_t dwWaitResult = NOERROR;
10206
10207     gc_heap* wait_heap = NULL;
10208     while (gc_heap::gc_started)
10209     {       
10210 #ifdef MULTIPLE_HEAPS
10211         wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
10212         dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
10213 #endif // MULTIPLE_HEAPS
10214
10215 #ifdef _PREFAST_
10216         PREFIX_ASSUME(wait_heap != NULL);
10217 #endif // _PREFAST_
10218
10219         dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE); 
10220     }
10221     disable_preemptive (cooperative_mode);
10222
10223     return dwWaitResult;
10224 }
10225
10226 void 
10227 gc_heap::set_gc_done()
10228 {
10229     enter_gc_done_event_lock();
10230     if (!gc_done_event_set)
10231     {
10232         gc_done_event_set = true;
10233         dprintf (2, ("heap %d: setting gc_done_event", heap_number));
10234         gc_done_event.Set();
10235     }
10236     exit_gc_done_event_lock();
10237 }
10238
10239 void 
10240 gc_heap::reset_gc_done()
10241 {
10242     enter_gc_done_event_lock();
10243     if (gc_done_event_set)
10244     {
10245         gc_done_event_set = false;
10246         dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
10247         gc_done_event.Reset();
10248     }
10249     exit_gc_done_event_lock();
10250 }
10251
10252 void 
10253 gc_heap::enter_gc_done_event_lock()
10254 {
10255     uint32_t dwSwitchCount = 0;
10256 retry:
10257
10258     if (Interlocked::CompareExchange(&gc_done_event_lock, 0, -1) >= 0)
10259     {
10260         while (gc_done_event_lock >= 0)
10261         {
10262             if  (g_num_processors > 1)
10263             {
10264                 int spin_count = 32 * g_num_processors;
10265                 for (int j = 0; j < spin_count; j++)
10266                 {
10267                     if  (gc_done_event_lock < 0)
10268                         break;
10269                     YieldProcessor();           // indicate to the processor that we are spining
10270                 }
10271                 if  (gc_done_event_lock >= 0)
10272                     GCToOSInterface::YieldThread(++dwSwitchCount);
10273             }
10274             else
10275                 GCToOSInterface::YieldThread(++dwSwitchCount);
10276         }
10277         goto retry;
10278     }
10279 }
10280
10281 void 
10282 gc_heap::exit_gc_done_event_lock()
10283 {
10284     gc_done_event_lock = -1;
10285 }
10286
10287 #ifndef MULTIPLE_HEAPS
10288
10289 #ifdef RECORD_LOH_STATE
10290 int gc_heap::loh_state_index = 0;
10291 gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
10292 #endif //RECORD_LOH_STATE
10293
10294 VOLATILE(int32_t) gc_heap::gc_done_event_lock;
10295 VOLATILE(bool) gc_heap::gc_done_event_set;
10296 GCEvent gc_heap::gc_done_event;
10297 #endif //!MULTIPLE_HEAPS
10298 VOLATILE(bool) gc_heap::internal_gc_done;
10299
10300 void gc_heap::add_saved_spinlock_info (
10301             msl_enter_state enter_state, 
10302             msl_take_state take_state)
10303
10304 {
10305 #ifdef SPINLOCK_HISTORY
10306     spinlock_info* current = &last_spinlock_info[spinlock_info_index];
10307
10308     current->enter_state = enter_state;
10309     current->take_state = take_state;
10310     current->thread_id.SetToCurrentThread();
10311
10312     spinlock_info_index++;
10313
10314     assert (spinlock_info_index <= max_saved_spinlock_info);
10315
10316     if (spinlock_info_index >= max_saved_spinlock_info)
10317     {
10318         spinlock_info_index = 0;
10319     }
10320 #else
10321     MAYBE_UNUSED_VAR(enter_state);
10322     MAYBE_UNUSED_VAR(take_state);
10323 #endif //SPINLOCK_HISTORY
10324 }
10325
10326 int
10327 gc_heap::init_gc_heap (int  h_number)
10328 {
10329 #ifdef MULTIPLE_HEAPS
10330
10331     time_bgc_last = 0;
10332
10333 #ifdef SPINLOCK_HISTORY
10334     spinlock_info_index = 0;
10335     memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
10336 #endif //SPINLOCK_HISTORY
10337
10338     // initialize per heap members.
10339     ephemeral_low = (uint8_t*)1;
10340
10341     ephemeral_high = MAX_PTR;
10342
10343     ephemeral_heap_segment = 0;
10344
10345     freeable_large_heap_segment = 0;
10346
10347     condemned_generation_num = 0;
10348
10349     blocking_collection = FALSE;
10350
10351     generation_skip_ratio = 100;
10352
10353     mark_stack_tos = 0;
10354
10355     mark_stack_bos = 0;
10356
10357     mark_stack_array_length = 0;
10358
10359     mark_stack_array = 0;
10360
10361     verify_pinned_queue_p = FALSE;
10362
10363     loh_pinned_queue_tos = 0;
10364
10365     loh_pinned_queue_bos = 0;
10366
10367     loh_pinned_queue_length = 0;
10368
10369     loh_pinned_queue_decay = LOH_PIN_DECAY;
10370
10371     loh_pinned_queue = 0;
10372
10373     min_overflow_address = MAX_PTR;
10374
10375     max_overflow_address = 0;
10376
10377     gen0_bricks_cleared = FALSE;
10378
10379     gen0_must_clear_bricks = 0;
10380
10381     allocation_quantum = CLR_SIZE;
10382
10383     more_space_lock = gc_lock;
10384
10385     ro_segments_in_range = FALSE;
10386
10387     loh_alloc_since_cg = 0;
10388
10389     new_heap_segment = NULL;
10390
10391 #ifdef RECORD_LOH_STATE
10392     loh_state_index = 0;
10393 #endif //RECORD_LOH_STATE
10394 #endif //MULTIPLE_HEAPS
10395
10396 #ifdef MULTIPLE_HEAPS
10397     if (h_number > n_heaps)
10398     {
10399         assert (!"Number of heaps exceeded");
10400         return 0;
10401     }
10402
10403     heap_number = h_number;
10404 #endif //MULTIPLE_HEAPS
10405
10406     memset (&oom_info, 0, sizeof (oom_info));
10407     memset (&fgm_result, 0, sizeof (fgm_result));
10408     if (!gc_done_event.CreateManualEventNoThrow(FALSE))
10409     {
10410         return 0;
10411     }
10412     gc_done_event_lock = -1;
10413     gc_done_event_set = false;
10414
10415 #ifndef SEG_MAPPING_TABLE
10416     if (!gc_heap::seg_table->ensure_space_for_insert ())
10417     {
10418         return 0;
10419     }
10420 #endif //!SEG_MAPPING_TABLE
10421
10422     heap_segment* seg = get_initial_segment (soh_segment_size, h_number);
10423     if (!seg)
10424         return 0;
10425
10426     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg),
10427                               (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)),
10428                               gc_etw_segment_small_object_heap);
10429     
10430 #ifdef SEG_MAPPING_TABLE
10431     seg_mapping_table_add_segment (seg, __this);
10432 #else //SEG_MAPPING_TABLE
10433     seg_table->insert ((uint8_t*)seg, sdelta);
10434 #endif //SEG_MAPPING_TABLE
10435
10436 #ifdef MULTIPLE_HEAPS
10437     heap_segment_heap (seg) = this;
10438 #endif //MULTIPLE_HEAPS
10439
10440     /* todo: Need a global lock for this */
10441     uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))];
10442     own_card_table (ct);
10443     card_table = translate_card_table (ct);
10444     /* End of global lock */
10445
10446     brick_table = card_table_brick_table (ct);
10447     highest_address = card_table_highest_address (ct);
10448     lowest_address = card_table_lowest_address (ct);
10449
10450 #ifdef CARD_BUNDLE
10451     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
10452     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
10453             card_table_card_bundle_table (ct));
10454 #endif //CARD_BUNDLE
10455
10456 #ifdef MARK_ARRAY
10457     if (gc_can_use_concurrent)
10458         mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))]));
10459     else
10460         mark_array = NULL;
10461 #endif //MARK_ARRAY
10462
10463     uint8_t*  start = heap_segment_mem (seg);
10464
10465     for (int i = 0; i < 1 + max_generation; i++)
10466     {
10467         make_generation (generation_table [ (max_generation - i) ],
10468                          seg, start, 0);
10469         generation_table [(max_generation - i)].gen_num = max_generation - i;
10470         start += Align (min_obj_size);
10471     }
10472
10473     heap_segment_allocated (seg) = start;
10474     alloc_allocated = start;
10475     heap_segment_used (seg) = start - plug_skew;
10476
10477     ephemeral_heap_segment = seg;
10478
10479 #ifndef SEG_MAPPING_TABLE
10480     if (!gc_heap::seg_table->ensure_space_for_insert ())
10481     {
10482         return 0;
10483     }
10484 #endif //!SEG_MAPPING_TABLE
10485     //Create the large segment generation
10486     heap_segment* lseg = get_initial_segment(min_loh_segment_size, h_number);
10487     if (!lseg)
10488         return 0;
10489     lseg->flags |= heap_segment_flags_loh;
10490
10491     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(lseg),
10492                               (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)),
10493                               gc_etw_segment_large_object_heap);
10494
10495 #ifdef SEG_MAPPING_TABLE
10496     seg_mapping_table_add_segment (lseg, __this);
10497 #else //SEG_MAPPING_TABLE
10498     seg_table->insert ((uint8_t*)lseg, sdelta);
10499 #endif //SEG_MAPPING_TABLE
10500
10501     generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
10502     //assign the alloc_list for the large generation 
10503     generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
10504     generation_table [max_generation+1].gen_num = max_generation+1;
10505     make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
10506     heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
10507     heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
10508
10509     for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
10510     {
10511         generation*  gen = generation_of (gen_num);
10512         make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
10513     }
10514
10515 #ifdef MULTIPLE_HEAPS
10516     heap_segment_heap (lseg) = this;
10517
10518     //initialize the alloc context heap
10519     generation_alloc_context (generation_of (0))->set_alloc_heap(vm_heap);
10520
10521     //initialize the alloc context heap
10522     generation_alloc_context (generation_of (max_generation+1))->set_alloc_heap(vm_heap);
10523
10524 #endif //MULTIPLE_HEAPS
10525
10526     //Do this only once
10527 #ifdef MULTIPLE_HEAPS
10528     if (h_number == 0)
10529 #endif //MULTIPLE_HEAPS
10530     {
10531 #ifndef INTERIOR_POINTERS
10532         //set the brick_table for large objects
10533         //but default value is clearded
10534         //clear_brick_table ((uint8_t*)heap_segment_mem (lseg),
10535         //                   (uint8_t*)heap_segment_reserved (lseg));
10536
10537 #else //INTERIOR_POINTERS
10538
10539         //Because of the interior pointer business, we have to clear
10540         //the whole brick table
10541         //but the default value is cleared
10542         // clear_brick_table (lowest_address, highest_address);
10543 #endif //INTERIOR_POINTERS
10544     }
10545
10546     if (!init_dynamic_data())
10547     {
10548         return 0;
10549     }
10550
10551     etw_allocation_running_amount[0] = 0;
10552     etw_allocation_running_amount[1] = 0;
10553
10554     //needs to be done after the dynamic data has been initialized
10555 #ifndef MULTIPLE_HEAPS
10556     allocation_running_amount = dd_min_size (dynamic_data_of (0));
10557 #endif //!MULTIPLE_HEAPS
10558
10559     fgn_last_alloc = dd_min_size (dynamic_data_of (0));
10560
10561     mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
10562     if (!arr)
10563         return 0;
10564
10565     make_mark_stack(arr);
10566
10567 #ifdef BACKGROUND_GC
10568     freeable_small_heap_segment = 0;
10569     gchist_index_per_heap = 0;
10570     uint8_t** b_arr = new (nothrow) (uint8_t* [MARK_STACK_INITIAL_LENGTH]);
10571     if (!b_arr)
10572         return 0;
10573
10574     make_background_mark_stack (b_arr);
10575 #endif //BACKGROUND_GC
10576
10577     ephemeral_low = generation_allocation_start(generation_of(max_generation - 1));
10578     ephemeral_high = heap_segment_reserved(ephemeral_heap_segment);
10579     if (heap_number == 0)
10580     {
10581         stomp_write_barrier_initialize(
10582 #ifdef MULTIPLE_HEAPS
10583             reinterpret_cast<uint8_t*>(1), reinterpret_cast<uint8_t*>(~0)
10584 #else
10585             ephemeral_low, ephemeral_high
10586 #endif //!MULTIPLE_HEAPS
10587         );
10588     }
10589
10590 #ifdef MARK_ARRAY
10591     // why would we clear the mark array for this page? it should be cleared..
10592     // clear the first committed page
10593     //if(gc_can_use_concurrent)
10594     //{
10595     //    clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10596     //}
10597 #endif //MARK_ARRAY
10598
10599 #ifdef MULTIPLE_HEAPS
10600     //register the heap in the heaps array
10601
10602     if (!create_gc_thread ())
10603         return 0;
10604
10605     g_heaps [heap_number] = this;
10606
10607 #endif //MULTIPLE_HEAPS
10608
10609 #ifdef FEATURE_PREMORTEM_FINALIZATION
10610     HRESULT hr = AllocateCFinalize(&finalize_queue);
10611     if (FAILED(hr))
10612         return 0;
10613 #endif // FEATURE_PREMORTEM_FINALIZATION
10614
10615     max_free_space_items = MAX_NUM_FREE_SPACES;
10616
10617     bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10618
10619     if (!bestfit_seg)
10620     {
10621         return 0;
10622     }
10623
10624     if (!bestfit_seg->alloc())
10625     {
10626         return 0;
10627     }
10628
10629     last_gc_before_oom = FALSE;
10630
10631 #ifdef MULTIPLE_HEAPS
10632
10633 #ifdef HEAP_ANALYZE
10634
10635     heap_analyze_success = TRUE;
10636
10637     internal_root_array  = 0;
10638
10639     internal_root_array_index = 0;
10640
10641     internal_root_array_length = initial_internal_roots;
10642
10643     current_obj          = 0;
10644
10645     current_obj_size     = 0;
10646
10647 #endif //HEAP_ANALYZE
10648
10649 #endif // MULTIPLE_HEAPS
10650
10651 #ifdef BACKGROUND_GC
10652     bgc_thread_id.Clear();
10653
10654     if (!create_bgc_thread_support())
10655     {
10656         return 0;
10657     }
10658
10659     bgc_alloc_lock = new (nothrow) exclusive_sync;
10660     if (!bgc_alloc_lock)
10661     {
10662         return 0;
10663     }
10664
10665     bgc_alloc_lock->init();
10666
10667     if (h_number == 0)
10668     {
10669         if (!recursive_gc_sync::init())
10670             return 0;
10671     }
10672
10673     bgc_thread_running = 0;
10674     bgc_thread = 0;
10675     bgc_threads_timeout_cs.Initialize();
10676     expanded_in_fgc = 0;
10677     current_bgc_state = bgc_not_in_process;
10678     background_soh_alloc_count = 0;
10679     background_loh_alloc_count = 0;
10680     bgc_overflow_count = 0;
10681     end_loh_size = dd_min_size (dynamic_data_of (max_generation + 1));
10682 #endif //BACKGROUND_GC
10683
10684 #ifdef GC_CONFIG_DRIVEN
10685     memset (interesting_data_per_heap, 0, sizeof (interesting_data_per_heap));
10686     memset(compact_reasons_per_heap, 0, sizeof (compact_reasons_per_heap));
10687     memset(expand_mechanisms_per_heap, 0, sizeof (expand_mechanisms_per_heap));
10688     memset(interesting_mechanism_bits_per_heap, 0, sizeof (interesting_mechanism_bits_per_heap));
10689 #endif //GC_CONFIG_DRIVEN
10690
10691     return 1;
10692 }
10693
10694 void
10695 gc_heap::destroy_semi_shared()
10696 {
10697 //TODO: will need to move this to per heap
10698 //#ifdef BACKGROUND_GC
10699 //    if (c_mark_list)
10700 //        delete c_mark_list;
10701 //#endif //BACKGROUND_GC
10702
10703 #ifdef MARK_LIST
10704     if (g_mark_list)
10705         delete g_mark_list;
10706 #endif //MARK_LIST
10707
10708 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10709     if (seg_mapping_table)
10710         delete seg_mapping_table;
10711 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10712
10713 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10714     //destroy the segment map
10715     seg_table->delete_sorted_table();
10716 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10717 }
10718
10719 void
10720 gc_heap::self_destroy()
10721 {
10722 #ifdef BACKGROUND_GC
10723     kill_gc_thread();
10724 #endif //BACKGROUND_GC
10725
10726     if (gc_done_event.IsValid())
10727     {
10728         gc_done_event.CloseEvent();
10729     }
10730
10731     // destroy every segment.
10732     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10733
10734     PREFIX_ASSUME(seg != NULL);
10735
10736     heap_segment* next_seg;
10737     while (seg)
10738     {
10739         next_seg = heap_segment_next_rw (seg);
10740         delete_heap_segment (seg);
10741         seg = next_seg;
10742     }
10743
10744     seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10745
10746     PREFIX_ASSUME(seg != NULL);
10747
10748     while (seg)
10749     {
10750         next_seg = heap_segment_next_rw (seg);
10751         delete_heap_segment (seg);
10752         seg = next_seg;
10753     }
10754
10755     // get rid of the card table
10756     release_card_table (card_table);
10757
10758     // destroy the mark stack
10759     delete mark_stack_array;
10760
10761 #ifdef FEATURE_PREMORTEM_FINALIZATION
10762     if (finalize_queue)
10763         delete finalize_queue;
10764 #endif // FEATURE_PREMORTEM_FINALIZATION
10765 }
10766
10767 void
10768 gc_heap::destroy_gc_heap(gc_heap* heap)
10769 {
10770     heap->self_destroy();
10771     delete heap;
10772 }
10773
10774 // Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10775 // the finalizer queue has been drained.
10776 void gc_heap::shutdown_gc()
10777 {
10778     destroy_semi_shared();
10779
10780 #ifdef MULTIPLE_HEAPS
10781     //delete the heaps array
10782     delete g_heaps;
10783     destroy_thread_support();
10784     n_heaps = 0;
10785 #endif //MULTIPLE_HEAPS
10786     //destroy seg_manager
10787
10788     destroy_initial_memory();
10789
10790     GCToOSInterface::Shutdown();
10791 }
10792
10793 inline
10794 BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10795                           uint8_t* old_loc, int use_padding)
10796 {
10797     BOOL already_padded = FALSE;
10798 #ifdef SHORT_PLUGS
10799     if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
10800     {
10801         alloc_pointer = alloc_pointer + Align (min_obj_size);
10802         already_padded = TRUE;
10803     }
10804 #endif //SHORT_PLUGS
10805
10806     if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
10807         size = size + switch_alignment_size (already_padded);
10808
10809 #ifdef FEATURE_STRUCTALIGN
10810     alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
10811 #endif // FEATURE_STRUCTALIGN
10812
10813     // in allocate_in_condemned_generation we can have this when we
10814     // set the alloc_limit to plan_allocated which could be less than 
10815     // alloc_ptr
10816     if (alloc_limit < alloc_pointer)
10817     {
10818         return FALSE;
10819     }
10820
10821     if (old_loc != 0)
10822     {
10823         return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0))) 
10824 #ifdef SHORT_PLUGS
10825                 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
10826 #else //SHORT_PLUGS
10827                 ||((alloc_pointer + size) == alloc_limit)
10828 #endif //SHORT_PLUGS
10829             );
10830     }
10831     else
10832     {
10833         assert (size == Align (min_obj_size));
10834         return ((size_t)(alloc_limit - alloc_pointer) >= size);
10835     }
10836 }
10837
10838 inline
10839 BOOL gc_heap::a_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10840                             int align_const)
10841 {
10842     // We could have run into cases where this is true when alloc_allocated is the 
10843     // the same as the seg committed.
10844     if (alloc_limit < alloc_pointer)
10845     {
10846         return FALSE;
10847     }
10848
10849     return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
10850 }
10851
10852 // Grow by committing more pages
10853 BOOL gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* high_address)
10854 {
10855     assert (high_address <= heap_segment_reserved (seg));
10856
10857     //return 0 if we are at the end of the segment.
10858     if (align_on_page (high_address) > heap_segment_reserved (seg))
10859         return FALSE;
10860
10861     if (high_address <= heap_segment_committed (seg))
10862         return TRUE;
10863
10864     size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
10865     c_size = max (c_size, 16*OS_PAGE_SIZE);
10866     c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
10867
10868     if (c_size == 0)
10869         return FALSE;
10870
10871     STRESS_LOG2(LF_GC, LL_INFO10000,
10872                 "Growing heap_segment: %Ix high address: %Ix\n",
10873                 (size_t)seg, (size_t)high_address);
10874
10875     dprintf(3, ("Growing segment allocation %Ix %Ix", (size_t)heap_segment_committed(seg),c_size));
10876     
10877     if (!virtual_alloc_commit_for_heap(heap_segment_committed (seg), c_size, heap_number))
10878     {
10879         dprintf(3, ("Cannot grow heap segment"));
10880         return FALSE;
10881     }
10882 #ifdef MARK_ARRAY
10883 #ifndef BACKGROUND_GC
10884     clear_mark_array (heap_segment_committed (seg),
10885                       heap_segment_committed (seg)+c_size, TRUE);
10886 #endif //BACKGROUND_GC
10887 #endif //MARK_ARRAY
10888     heap_segment_committed (seg) += c_size;
10889     STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
10890                 (size_t)heap_segment_committed (seg));
10891
10892     assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
10893
10894     assert (high_address <= heap_segment_committed (seg));
10895
10896     return TRUE;
10897 }
10898
10899 inline
10900 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)
10901 {
10902 #ifdef SHORT_PLUGS
10903     if ((old_loc != 0) && pad_front_p)
10904     {
10905         allocated = allocated + Align (min_obj_size);
10906     }
10907 #endif //SHORT_PLUGS
10908
10909     if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
10910         size = size + switch_alignment_size (FALSE);
10911 #ifdef FEATURE_STRUCTALIGN
10912     size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
10913     return grow_heap_segment (seg, allocated + pad + size);
10914 #else // FEATURE_STRUCTALIGN
10915     return grow_heap_segment (seg, allocated + size);
10916 #endif // FEATURE_STRUCTALIGN
10917 }
10918
10919 //used only in older generation allocation (i.e during gc).
10920 void gc_heap::adjust_limit (uint8_t* start, size_t limit_size, generation* gen,
10921                             int gennum)
10922 {
10923     UNREFERENCED_PARAMETER(gennum);
10924     dprintf (3, ("gc Expanding segment allocation"));
10925     heap_segment* seg = generation_allocation_segment (gen);
10926     if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
10927     {
10928         if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
10929         {
10930             assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
10931             assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
10932             heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
10933         }
10934         else
10935         {
10936             uint8_t*  hole = generation_allocation_pointer (gen);
10937             size_t  size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
10938
10939             if (size != 0)
10940             {
10941                 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
10942                 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
10943                 if (size >= Align (min_free_list))
10944                 {
10945                     if (allocated_size < min_free_list)
10946                     {
10947                         if (size >= (Align (min_free_list) + Align (min_obj_size)))
10948                         {
10949                             //split hole into min obj + threadable free item
10950                             make_unused_array (hole, min_obj_size);
10951                             generation_free_obj_space (gen) += Align (min_obj_size);
10952                             make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
10953                             generation_free_list_space (gen) += size - Align (min_obj_size);
10954                             generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size), 
10955                                                                           size - Align (min_obj_size));
10956                             add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
10957                         }
10958                         else
10959                         {
10960                             dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
10961                             make_unused_array (hole, size);
10962                             generation_free_obj_space (gen) += size;
10963                         }
10964                     }
10965                     else 
10966                     {
10967                         dprintf (3, ("threading hole in front of free list"));
10968                         make_unused_array (hole, size);
10969                         generation_free_list_space (gen) += size;
10970                         generation_allocator(gen)->thread_item_front (hole, size);
10971                         add_gen_free (gen->gen_num, size);
10972                     }
10973                 }
10974                 else
10975                 {
10976                     make_unused_array (hole, size);
10977                     generation_free_obj_space (gen) += size;
10978                 }
10979             }
10980         }
10981         generation_allocation_pointer (gen) = start;
10982         generation_allocation_context_start_region (gen) = start;
10983     }
10984     generation_allocation_limit (gen) = (start + limit_size);
10985 }
10986
10987 void verify_mem_cleared (uint8_t* start, size_t size)
10988 {
10989     if (!Aligned (size))
10990     {
10991         FATAL_GC_ERROR();
10992     }
10993
10994     PTR_PTR curr_ptr = (PTR_PTR) start;
10995     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
10996     {
10997         if (*(curr_ptr++) != 0)
10998         {
10999             FATAL_GC_ERROR();
11000         }
11001     }
11002 }
11003
11004 #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
11005 void gc_heap::set_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11006 {
11007     size_t start_mark_bit = mark_bit_of (start);
11008     size_t end_mark_bit = mark_bit_of (end);
11009     unsigned int startbit = mark_bit_bit (start_mark_bit);
11010     unsigned int endbit = mark_bit_bit (end_mark_bit);
11011     size_t startwrd = mark_bit_word (start_mark_bit);
11012     size_t endwrd = mark_bit_word (end_mark_bit);
11013
11014     dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11015         (size_t)start, (size_t)start_mark_bit, 
11016         (size_t)end, (size_t)end_mark_bit));
11017
11018     unsigned int firstwrd = ~(lowbits (~0, startbit));
11019     unsigned int lastwrd = ~(highbits (~0, endbit));
11020
11021     if (startwrd == endwrd)
11022     {
11023         unsigned int wrd = firstwrd & lastwrd;
11024         mark_array[startwrd] |= wrd;
11025         return;
11026     }
11027
11028     // set the first mark word.
11029     if (startbit)
11030     {
11031         mark_array[startwrd] |= firstwrd;
11032         startwrd++;
11033     }
11034
11035     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11036     {
11037         mark_array[wrdtmp] = ~(unsigned int)0;
11038     }
11039
11040     // set the last mark word.
11041     if (endbit)
11042     {
11043         mark_array[endwrd] |= lastwrd;
11044     }
11045 }
11046
11047 // makes sure that the mark array bits between start and end are 0.
11048 void gc_heap::check_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11049 {
11050     size_t start_mark_bit = mark_bit_of (start);
11051     size_t end_mark_bit = mark_bit_of (end);
11052     unsigned int startbit = mark_bit_bit (start_mark_bit);
11053     unsigned int endbit = mark_bit_bit (end_mark_bit);
11054     size_t startwrd = mark_bit_word (start_mark_bit);
11055     size_t endwrd = mark_bit_word (end_mark_bit);
11056
11057     //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11058     //    (size_t)start, (size_t)start_mark_bit, 
11059     //    (size_t)end, (size_t)end_mark_bit));
11060
11061     unsigned int firstwrd = ~(lowbits (~0, startbit));
11062     unsigned int lastwrd = ~(highbits (~0, endbit));
11063
11064     if (startwrd == endwrd)
11065     {
11066         unsigned int wrd = firstwrd & lastwrd;
11067         if (mark_array[startwrd] & wrd)
11068         {
11069             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11070                             wrd, startwrd, 
11071                             mark_array [startwrd], mark_word_address (startwrd)));
11072             FATAL_GC_ERROR();
11073         }
11074         return;
11075     }
11076
11077     // set the first mark word.
11078     if (startbit)
11079     {
11080         if (mark_array[startwrd] & firstwrd)
11081         {
11082             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11083                             firstwrd, startwrd, 
11084                             mark_array [startwrd], mark_word_address (startwrd)));
11085             FATAL_GC_ERROR();
11086         }
11087
11088         startwrd++;
11089     }
11090
11091     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11092     {
11093         if (mark_array[wrdtmp])
11094         {
11095             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11096                             wrdtmp, 
11097                             mark_array [wrdtmp], mark_word_address (wrdtmp)));
11098             FATAL_GC_ERROR();
11099         }
11100     }
11101
11102     // set the last mark word.
11103     if (endbit)
11104     {
11105         if (mark_array[endwrd] & lastwrd)
11106         {
11107             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11108                             lastwrd, lastwrd, 
11109                             mark_array [lastwrd], mark_word_address (lastwrd)));
11110             FATAL_GC_ERROR();
11111         }
11112     }
11113 }
11114 #endif //VERIFY_HEAP && BACKGROUND_GC
11115
11116 allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
11117 {
11118     assert (num_b < MAX_BUCKET_COUNT);
11119     num_buckets = num_b;
11120     frst_bucket_size = fbs;
11121     buckets = b;
11122 }
11123
11124 alloc_list& allocator::alloc_list_of (unsigned int bn)
11125 {
11126     assert (bn < num_buckets);
11127     if (bn == 0)
11128         return first_bucket;
11129     else
11130         return buckets [bn-1];
11131 }
11132
11133 size_t& allocator::alloc_list_damage_count_of (unsigned int bn)
11134 {
11135     assert (bn < num_buckets);
11136     if (bn == 0)
11137         return first_bucket.alloc_list_damage_count();
11138     else
11139         return buckets [bn-1].alloc_list_damage_count();
11140 }
11141
11142 void allocator::unlink_item (unsigned int bn, uint8_t* item, uint8_t* prev_item, BOOL use_undo_p)
11143 {
11144     //unlink the free_item
11145     alloc_list* al = &alloc_list_of (bn);
11146     if (prev_item)
11147     {
11148         if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
11149         {
11150             assert (item == free_list_slot (prev_item));
11151             free_list_undo (prev_item) = item;
11152             alloc_list_damage_count_of (bn)++;
11153         }
11154         free_list_slot (prev_item) = free_list_slot(item);
11155     }
11156     else
11157     {
11158         al->alloc_list_head() = (uint8_t*)free_list_slot(item);
11159     }
11160     if (al->alloc_list_tail() == item)
11161     {
11162         al->alloc_list_tail() = prev_item;
11163     }
11164 }
11165
11166 void allocator::clear()
11167 {
11168     for (unsigned int i = 0; i < num_buckets; i++)
11169     {
11170         alloc_list_head_of (i) = 0;
11171         alloc_list_tail_of (i) = 0;
11172     }
11173 }
11174
11175 //always thread to the end.
11176 void allocator::thread_free_item (uint8_t* item, uint8_t*& head, uint8_t*& tail)
11177 {
11178     free_list_slot (item) = 0;
11179     free_list_undo (item) = UNDO_EMPTY;
11180     assert (item != head);
11181
11182     if (head == 0)
11183     {
11184        head = item;
11185     }
11186     //TODO: This shouldn't happen anymore - verify that's the case.
11187     //the following is necessary because the last free element
11188     //may have been truncated, and tail isn't updated.
11189     else if (free_list_slot (head) == 0)
11190     {
11191         free_list_slot (head) = item;
11192     }
11193     else
11194     {
11195         assert (item != tail);
11196         assert (free_list_slot(tail) == 0);
11197         free_list_slot (tail) = item;
11198     }
11199     tail = item;
11200 }
11201
11202 void allocator::thread_item (uint8_t* item, size_t size)
11203 {
11204     size_t sz = frst_bucket_size;
11205     unsigned int a_l_number = 0; 
11206
11207     for (; a_l_number < (num_buckets-1); a_l_number++)
11208     {
11209         if (size < sz)
11210         {
11211             break;
11212         }
11213         sz = sz * 2;
11214     }
11215     alloc_list* al = &alloc_list_of (a_l_number);
11216     thread_free_item (item, 
11217                       al->alloc_list_head(),
11218                       al->alloc_list_tail());
11219 }
11220
11221 void allocator::thread_item_front (uint8_t* item, size_t size)
11222 {
11223     //find right free list
11224     size_t sz = frst_bucket_size;
11225     unsigned int a_l_number = 0; 
11226     for (; a_l_number < (num_buckets-1); a_l_number++)
11227     {
11228         if (size < sz)
11229         {
11230             break;
11231         }
11232         sz = sz * 2;
11233     }
11234     alloc_list* al = &alloc_list_of (a_l_number);
11235     free_list_slot (item) = al->alloc_list_head();
11236     free_list_undo (item) = UNDO_EMPTY;
11237
11238     if (al->alloc_list_tail() == 0)
11239     {
11240         al->alloc_list_tail() = al->alloc_list_head();
11241     }
11242     al->alloc_list_head() = item;
11243     if (al->alloc_list_tail() == 0)
11244     {
11245         al->alloc_list_tail() = item;
11246     }
11247 }
11248
11249 void allocator::copy_to_alloc_list (alloc_list* toalist)
11250 {
11251     for (unsigned int i = 0; i < num_buckets; i++)
11252     {
11253         toalist [i] = alloc_list_of (i);
11254 #ifdef FL_VERIFICATION
11255         uint8_t* free_item = alloc_list_head_of (i);
11256         size_t count = 0;
11257         while (free_item)
11258         {
11259             count++;
11260             free_item = free_list_slot (free_item);
11261         }
11262
11263         toalist[i].item_count = count;
11264 #endif //FL_VERIFICATION
11265     }
11266 }
11267
11268 void allocator::copy_from_alloc_list (alloc_list* fromalist)
11269 {
11270     BOOL repair_list = !discard_if_no_fit_p ();
11271     for (unsigned int i = 0; i < num_buckets; i++)
11272     {
11273         size_t count = alloc_list_damage_count_of (i);
11274         alloc_list_of (i) = fromalist [i];
11275         assert (alloc_list_damage_count_of (i) == 0);
11276
11277         if (repair_list)
11278         {
11279             //repair the the list
11280             //new items may have been added during the plan phase 
11281             //items may have been unlinked. 
11282             uint8_t* free_item = alloc_list_head_of (i);
11283             while (free_item && count)
11284             {
11285                 assert (((CObjectHeader*)free_item)->IsFree());
11286                 if ((free_list_undo (free_item) != UNDO_EMPTY))
11287                 {
11288                     count--;
11289                     free_list_slot (free_item) = free_list_undo (free_item);
11290                     free_list_undo (free_item) = UNDO_EMPTY;
11291                 }
11292
11293                 free_item = free_list_slot (free_item);
11294             }
11295
11296 #ifdef FL_VERIFICATION
11297             free_item = alloc_list_head_of (i);
11298             size_t item_count = 0;
11299             while (free_item)
11300             {
11301                 item_count++;
11302                 free_item = free_list_slot (free_item);
11303             }
11304
11305             assert (item_count == alloc_list_of (i).item_count);
11306 #endif //FL_VERIFICATION
11307         }
11308 #ifdef DEBUG
11309         uint8_t* tail_item = alloc_list_tail_of (i);
11310         assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
11311 #endif
11312     }
11313 }
11314
11315 void allocator::commit_alloc_list_changes()
11316 {
11317     BOOL repair_list = !discard_if_no_fit_p ();
11318     if (repair_list)
11319     {
11320         for (unsigned int i = 0; i < num_buckets; i++)
11321         {
11322             //remove the undo info from list. 
11323             uint8_t* free_item = alloc_list_head_of (i);
11324             size_t count = alloc_list_damage_count_of (i);
11325             while (free_item && count)
11326             {
11327                 assert (((CObjectHeader*)free_item)->IsFree());
11328
11329                 if (free_list_undo (free_item) != UNDO_EMPTY)
11330                 {
11331                     free_list_undo (free_item) = UNDO_EMPTY;
11332                     count--;
11333                 }
11334
11335                 free_item = free_list_slot (free_item);
11336             }
11337
11338             alloc_list_damage_count_of (i) = 0; 
11339         }
11340     }
11341 }
11342
11343 void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size,
11344                                 alloc_context* acontext, heap_segment* seg,
11345                                 int align_const, int gen_number)
11346 {
11347     size_t aligned_min_obj_size = Align(min_obj_size, align_const);
11348
11349     //probably should pass seg==0 for free lists.
11350     if (seg)
11351     {
11352         assert (heap_segment_used (seg) <= heap_segment_committed (seg));
11353     }
11354
11355     dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
11356                (size_t)start + limit_size - aligned_min_obj_size));
11357
11358     if ((acontext->alloc_limit != start) &&
11359         (acontext->alloc_limit + aligned_min_obj_size)!= start)
11360     {
11361         uint8_t*  hole = acontext->alloc_ptr;
11362         if (hole != 0)
11363         {
11364             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
11365             dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
11366             // when we are finishing an allocation from a free list
11367             // we know that the free area was Align(min_obj_size) larger
11368             acontext->alloc_bytes -= size;
11369             size_t free_obj_size = size + aligned_min_obj_size;
11370             make_unused_array (hole, free_obj_size);
11371             generation_free_obj_space (generation_of (gen_number)) += free_obj_size;
11372         }
11373         acontext->alloc_ptr = start;
11374     }
11375     else  
11376     {  
11377         // If the next alloc context is right up against the current one it means we are absorbing the min  
11378         // object, so need to account for that.  
11379         acontext->alloc_bytes += (start - acontext->alloc_limit);  
11380     }  
11381
11382     acontext->alloc_limit = (start + limit_size - aligned_min_obj_size);
11383     acontext->alloc_bytes += limit_size - ((gen_number < max_generation + 1) ? aligned_min_obj_size : 0);
11384
11385 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11386     if (g_fEnableARM)
11387     {
11388         AppDomain* alloc_appdomain = GetAppDomain();
11389         alloc_appdomain->RecordAllocBytes (limit_size, heap_number);
11390     }
11391 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11392
11393     uint8_t* saved_used = 0;
11394
11395     if (seg)
11396     {
11397         saved_used = heap_segment_used (seg);
11398     }
11399
11400     if (seg == ephemeral_heap_segment)
11401     {
11402         //Sometimes the allocated size is advanced without clearing the
11403         //memory. Let's catch up here
11404         if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
11405         {
11406 #ifdef MARK_ARRAY
11407 #ifndef BACKGROUND_GC
11408             clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
11409 #endif //BACKGROUND_GC
11410 #endif //MARK_ARRAY
11411             heap_segment_used (seg) = alloc_allocated - plug_skew;
11412         }
11413     }
11414 #ifdef BACKGROUND_GC
11415     else if (seg)
11416     {
11417         uint8_t* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
11418 #ifdef FEATURE_LOH_COMPACTION
11419         old_allocated -= Align (loh_padding_obj_size, align_const);
11420 #endif //FEATURE_LOH_COMPACTION
11421
11422         assert (heap_segment_used (seg) >= old_allocated);
11423     }
11424 #endif //BACKGROUND_GC
11425     if ((seg == 0) ||
11426         (start - plug_skew + limit_size) <= heap_segment_used (seg))
11427     {
11428         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory(1)", heap_number));
11429         add_saved_spinlock_info (me_release, mt_clr_mem);
11430         leave_spin_lock (&more_space_lock);
11431         dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
11432         memclr (start - plug_skew, limit_size);
11433     }
11434     else
11435     {
11436         uint8_t* used = heap_segment_used (seg);
11437         heap_segment_used (seg) = start + limit_size - plug_skew;
11438
11439         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory", heap_number));
11440         add_saved_spinlock_info (me_release, mt_clr_mem);
11441         leave_spin_lock (&more_space_lock);
11442         if ((start - plug_skew) < used)
11443         {
11444             if (used != saved_used)
11445             {
11446                 FATAL_GC_ERROR ();
11447             }
11448
11449             dprintf (2, ("clearing memory before used at %Ix for %Id bytes", 
11450                 (start - plug_skew), (plug_skew + used - start)));
11451             memclr (start - plug_skew, used - (start - plug_skew));
11452         }
11453     }
11454
11455     //this portion can be done after we release the lock
11456     if (seg == ephemeral_heap_segment)
11457     {
11458 #ifdef FFIND_OBJECT
11459         if (gen0_must_clear_bricks > 0)
11460         {
11461             //set the brick table to speed up find_object
11462             size_t b = brick_of (acontext->alloc_ptr);
11463             set_brick (b, acontext->alloc_ptr - brick_address (b));
11464             b++;
11465             dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
11466                          b, brick_of (align_on_brick (start + limit_size))));
11467             volatile short* x = &brick_table [b];
11468             short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
11469
11470             for (;x < end_x;x++)
11471                 *x = -1;
11472         }
11473         else
11474 #endif //FFIND_OBJECT
11475         {
11476             gen0_bricks_cleared = FALSE;
11477         }
11478     }
11479
11480     // verifying the memory is completely cleared.
11481     //verify_mem_cleared (start - plug_skew, limit_size);
11482 }
11483
11484 /* in order to make the allocator faster, allocate returns a
11485  * 0 filled object. Care must be taken to set the allocation limit to the
11486  * allocation pointer after gc
11487  */
11488
11489 size_t gc_heap::limit_from_size (size_t size, size_t room, int gen_number,
11490                                  int align_const)
11491 {
11492     size_t new_limit = new_allocation_limit ((size + Align (min_obj_size, align_const)),
11493                                              min (room,max (size + Align (min_obj_size, align_const),
11494                                                             ((gen_number < max_generation+1) ?
11495                                                              allocation_quantum :
11496                                                              0))),
11497                                              gen_number);
11498     assert (new_limit >= (size + Align (min_obj_size, align_const)));
11499     dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
11500     return new_limit;
11501 }
11502
11503 void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size, 
11504                           uint8_t* allocated, uint8_t* reserved)
11505 {
11506     dprintf (1, ("total committed on the heap is %Id", get_total_committed_size()));
11507
11508     UNREFERENCED_PARAMETER(heap_num);
11509
11510     if (reason == oom_budget)
11511     {
11512         alloc_size = dd_min_size (dynamic_data_of (0)) / 2;
11513     }
11514
11515     if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
11516     {
11517         // This means during the last GC we needed to reserve and/or commit more memory
11518         // but we couldn't. We proceeded with the GC and ended up not having enough
11519         // memory at the end. This is a legitimate OOM situtation. Otherwise we 
11520         // probably made a mistake and didn't expand the heap when we should have.
11521         reason = oom_low_mem;
11522     }
11523
11524     oom_info.reason = reason;
11525     oom_info.allocated = allocated;
11526     oom_info.reserved = reserved;
11527     oom_info.alloc_size = alloc_size;
11528     oom_info.gc_index = settings.gc_index;
11529     oom_info.fgm = fgm_result.fgm;
11530     oom_info.size = fgm_result.size;
11531     oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
11532     oom_info.loh_p = fgm_result.loh_p;
11533
11534     fgm_result.fgm = fgm_no_failure;
11535
11536     // Break early - before the more_space_lock is release so no other threads
11537     // could have allocated on the same heap when OOM happened.
11538     if (GCConfig::GetBreakOnOOM())
11539     {
11540         GCToOSInterface::DebugBreak();
11541     }
11542 }
11543
11544 #ifdef BACKGROUND_GC
11545 BOOL gc_heap::background_allowed_p()
11546 {
11547     return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
11548 }
11549 #endif //BACKGROUND_GC
11550
11551 void gc_heap::check_for_full_gc (int gen_num, size_t size)
11552 {
11553     BOOL should_notify = FALSE;
11554     // if we detect full gc because of the allocation budget specified this is TRUE;
11555     // it's FALSE if it's due to other factors.
11556     BOOL alloc_factor = TRUE; 
11557     int i = 0;
11558     int n = 0;
11559     int n_initial = gen_num;
11560     BOOL local_blocking_collection = FALSE;
11561     BOOL local_elevation_requested = FALSE;
11562     int new_alloc_remain_percent = 0;
11563
11564     if (full_gc_approach_event_set)
11565     {
11566         return;
11567     }
11568     
11569     if (gen_num != (max_generation + 1))
11570     {
11571         gen_num = max_generation;
11572     }
11573
11574     dynamic_data* dd_full = dynamic_data_of (gen_num);
11575     ptrdiff_t new_alloc_remain = 0;
11576     uint32_t pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
11577
11578     for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
11579     {
11580         dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)", 
11581                      heap_number, gen_index,
11582                      dd_new_allocation (dynamic_data_of (gen_index)),
11583                      dd_desired_allocation (dynamic_data_of (gen_index))));
11584     }
11585
11586     // For small object allocations we only check every fgn_check_quantum bytes.
11587     if (n_initial == 0)
11588     {
11589         dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
11590         dynamic_data* dd_0 = dynamic_data_of (n_initial);
11591         if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
11592             (dd_new_allocation (dd_0) >= 0))
11593         {
11594             return;
11595         }
11596         else
11597         {
11598             fgn_last_alloc = dd_new_allocation (dd_0);
11599             dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
11600         }
11601
11602         // We don't consider the size that came from soh 'cause it doesn't contribute to the
11603         // gen2 budget.
11604         size = 0;
11605     }
11606
11607     for (i = n+1; i <= max_generation; i++)
11608     {
11609         if (get_new_allocation (i) <= 0)
11610         {
11611             n = min (i, max_generation);
11612         }
11613         else
11614             break;
11615     }
11616
11617     dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
11618     if (gen_num == max_generation)
11619     {
11620         // If it's small object heap we should first see if we will even be looking at gen2 budget
11621         // in the next GC or not. If not we should go directly to checking other factors.
11622         if (n < (max_generation - 1))
11623         {
11624             goto check_other_factors;
11625         }
11626     }
11627
11628     new_alloc_remain = dd_new_allocation (dd_full) - size;
11629
11630     new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
11631
11632     dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%", 
11633                  gen_num, pct, new_alloc_remain_percent));
11634
11635     if (new_alloc_remain_percent <= (int)pct)
11636     {
11637 #ifdef BACKGROUND_GC
11638         // If background GC is enabled, we still want to check whether this will
11639         // be a blocking GC or not because we only want to notify when it's a 
11640         // blocking full GC.
11641         if (background_allowed_p())
11642         {
11643             goto check_other_factors;
11644         }
11645 #endif //BACKGROUND_GC
11646
11647         should_notify = TRUE;
11648         goto done;
11649     }
11650
11651 check_other_factors:
11652
11653     dprintf (2, ("FGC: checking other factors"));
11654     n = generation_to_condemn (n, 
11655                                &local_blocking_collection, 
11656                                &local_elevation_requested, 
11657                                TRUE);
11658
11659     if (local_elevation_requested && (n == max_generation))
11660     {
11661         if (settings.should_lock_elevation)
11662         {
11663             int local_elevation_locked_count = settings.elevation_locked_count + 1;
11664             if (local_elevation_locked_count != 6)
11665             {
11666                 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1", 
11667                     local_elevation_locked_count));
11668                 n = max_generation - 1;
11669             }
11670         }
11671     }
11672
11673     dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11674
11675 #ifdef BACKGROUND_GC
11676     // When background GC is enabled it decreases the accuracy of our predictability -
11677     // by the time the GC happens, we may not be under BGC anymore. If we try to 
11678     // predict often enough it should be ok.
11679     if ((n == max_generation) &&
11680         (recursive_gc_sync::background_running_p()))
11681     {
11682         n = max_generation - 1;
11683         dprintf (2, ("FGN: bgc - 1 instead of 2"));
11684     }
11685
11686     if ((n == max_generation) && !local_blocking_collection)
11687     {
11688         if (!background_allowed_p())
11689         {
11690             local_blocking_collection = TRUE;
11691         }
11692     }
11693 #endif //BACKGROUND_GC
11694
11695     dprintf (2, ("FGN: we estimate gen%d will be collected: %s", 
11696                        n, 
11697                        (local_blocking_collection ? "blocking" : "background")));
11698
11699     if ((n == max_generation) && local_blocking_collection)
11700     {
11701         alloc_factor = FALSE;
11702         should_notify = TRUE;
11703         goto done;
11704     }
11705
11706 done:
11707
11708     if (should_notify)
11709     {
11710         dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)", 
11711                      n_initial,
11712                      (alloc_factor ? "alloc" : "other"),
11713                      dd_collection_count (dynamic_data_of (0)),
11714                      new_alloc_remain_percent, 
11715                      gen_num));
11716
11717         send_full_gc_notification (n_initial, alloc_factor);
11718     }
11719 }
11720
11721 void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11722 {
11723     if (!full_gc_approach_event_set)
11724     {
11725         assert (full_gc_approach_event.IsValid());
11726         FIRE_EVENT(GCFullNotify_V1, gen_num, due_to_alloc_p);
11727
11728         full_gc_end_event.Reset();
11729         full_gc_approach_event.Set();
11730         full_gc_approach_event_set = true;
11731     }
11732 }
11733
11734 wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms)
11735 {
11736     if (fgn_maxgen_percent == 0)
11737     {
11738         return wait_full_gc_na;
11739     }
11740
11741     uint32_t wait_result = user_thread_wait(event, FALSE, time_out_ms);
11742
11743     if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11744     {
11745         if (fgn_maxgen_percent == 0)
11746         {
11747             return wait_full_gc_cancelled;
11748         }
11749         
11750         if (wait_result == WAIT_OBJECT_0)
11751         {
11752 #ifdef BACKGROUND_GC
11753             if (fgn_last_gc_was_concurrent)
11754             {
11755                 fgn_last_gc_was_concurrent = FALSE;
11756                 return wait_full_gc_na;
11757             }
11758             else
11759 #endif //BACKGROUND_GC
11760             {
11761                 return wait_full_gc_success;
11762             }
11763         }
11764         else
11765         {
11766             return wait_full_gc_timeout;
11767         }
11768     }
11769     else
11770     {
11771         return wait_full_gc_failed;
11772     }
11773 }
11774
11775 size_t gc_heap::get_full_compact_gc_count()
11776 {
11777     return full_gc_counts[gc_type_compacting];
11778 }
11779
11780 // DTREVIEW - we should check this in dt_low_ephemeral_space_p
11781 // as well.
11782 inline
11783 BOOL gc_heap::short_on_end_of_seg (int gen_number,
11784                                    heap_segment* seg,
11785                                    int align_const)
11786 {
11787     UNREFERENCED_PARAMETER(gen_number);
11788     uint8_t* allocated = heap_segment_allocated(seg);
11789
11790     return (!a_size_fit_p (end_space_after_gc(),
11791                           allocated,
11792                           heap_segment_reserved (seg), 
11793                           align_const));
11794 }
11795
11796 #ifdef _MSC_VER
11797 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
11798 #endif // _MSC_VER
11799
11800 inline
11801 BOOL gc_heap::a_fit_free_list_p (int gen_number, 
11802                                  size_t size, 
11803                                  alloc_context* acontext,
11804                                  int align_const)
11805 {
11806     BOOL can_fit = FALSE;
11807     generation* gen = generation_of (gen_number);
11808     allocator* gen_allocator = generation_allocator (gen);
11809     size_t sz_list = gen_allocator->first_bucket_size();
11810     for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
11811     {
11812         if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
11813         {
11814             uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
11815             uint8_t* prev_free_item = 0;
11816
11817             while (free_list != 0)
11818             {
11819                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11820                 size_t free_list_size = unused_array_size (free_list);
11821                 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
11822                 {
11823                     dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
11824                                  (size_t)free_list, free_list_size));
11825
11826                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11827                     // We ask for more Align (min_obj_size)
11828                     // to make sure that we can insert a free object
11829                     // in adjust_limit will set the limit lower
11830                     size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
11831
11832                     uint8_t*  remain = (free_list + limit);
11833                     size_t remain_size = (free_list_size - limit);
11834                     if (remain_size >= Align(min_free_list, align_const))
11835                     {
11836                         make_unused_array (remain, remain_size);
11837                         gen_allocator->thread_item_front (remain, remain_size);
11838                         assert (remain_size >= Align (min_obj_size, align_const));
11839                     }
11840                     else
11841                     {
11842                         //absorb the entire free list
11843                         limit += remain_size;
11844                     }
11845                     generation_free_list_space (gen) -= limit;
11846
11847                     adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
11848
11849                     can_fit = TRUE;
11850                     goto end;
11851                 }
11852                 else if (gen_allocator->discard_if_no_fit_p())
11853                 {
11854                     assert (prev_free_item == 0);
11855                     dprintf (3, ("couldn't use this free area, discarding"));
11856                     generation_free_obj_space (gen) += free_list_size;
11857
11858                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11859                     generation_free_list_space (gen) -= free_list_size;
11860                 }
11861                 else
11862                 {
11863                     prev_free_item = free_list;
11864                 }
11865                 free_list = free_list_slot (free_list); 
11866             }
11867         }
11868         sz_list = sz_list * 2;
11869     }
11870 end:
11871     return can_fit;
11872 }
11873
11874
11875 #ifdef BACKGROUND_GC
11876 void gc_heap::bgc_loh_alloc_clr (uint8_t* alloc_start,
11877                                  size_t size, 
11878                                  alloc_context* acontext,
11879                                  int align_const, 
11880                                  int lock_index,
11881                                  BOOL check_used_p,
11882                                  heap_segment* seg)
11883 {
11884     make_unused_array (alloc_start, size);
11885
11886 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11887     if (g_fEnableARM)
11888     {
11889         AppDomain* alloc_appdomain = GetAppDomain();
11890         alloc_appdomain->RecordAllocBytes (size, heap_number);
11891     }
11892 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11893
11894     size_t size_of_array_base = sizeof(ArrayBase);
11895
11896     bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
11897
11898     // clear memory while not holding the lock. 
11899     size_t size_to_skip = size_of_array_base;
11900     size_t size_to_clear = size - size_to_skip - plug_skew;
11901     size_t saved_size_to_clear = size_to_clear;
11902     if (check_used_p)
11903     {
11904         uint8_t* end = alloc_start + size - plug_skew;
11905         uint8_t* used = heap_segment_used (seg);
11906         if (used < end)
11907         {
11908             if ((alloc_start + size_to_skip) < used)
11909             {
11910                 size_to_clear = used - (alloc_start + size_to_skip);
11911             }
11912             else
11913             {
11914                 size_to_clear = 0;
11915             }
11916             dprintf (2, ("bgc loh: setting used to %Ix", end));
11917             heap_segment_used (seg) = end;
11918         }
11919
11920         dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
11921                      used, alloc_start, end, size_to_clear));
11922     }
11923     else
11924     {
11925         dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
11926     }
11927
11928 #ifdef VERIFY_HEAP
11929     // since we filled in 0xcc for free object when we verify heap,
11930     // we need to make sure we clear those bytes.
11931     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
11932     {
11933         if (size_to_clear < saved_size_to_clear)
11934         {
11935             size_to_clear = saved_size_to_clear;
11936         }
11937     }
11938 #endif //VERIFY_HEAP
11939     
11940     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
11941     add_saved_spinlock_info (me_release, mt_clr_large_mem);
11942     leave_spin_lock (&more_space_lock);
11943     memclr (alloc_start + size_to_skip, size_to_clear);
11944
11945     bgc_alloc_lock->loh_alloc_set (alloc_start);
11946
11947     acontext->alloc_ptr = alloc_start;
11948     acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
11949
11950     // need to clear the rest of the object before we hand it out.
11951     clear_unused_array(alloc_start, size);
11952 }
11953 #endif //BACKGROUND_GC
11954
11955 BOOL gc_heap::a_fit_free_list_large_p (size_t size, 
11956                                        alloc_context* acontext,
11957                                        int align_const)
11958 {
11959 #ifdef BACKGROUND_GC
11960     wait_for_background_planning (awr_loh_alloc_during_plan);
11961 #endif //BACKGROUND_GC
11962
11963     BOOL can_fit = FALSE;
11964     int gen_number = max_generation + 1;
11965     generation* gen = generation_of (gen_number);
11966     allocator* loh_allocator = generation_allocator (gen); 
11967
11968 #ifdef FEATURE_LOH_COMPACTION
11969     size_t loh_pad = Align (loh_padding_obj_size, align_const);
11970 #endif //FEATURE_LOH_COMPACTION
11971
11972 #ifdef BACKGROUND_GC
11973     int cookie = -1;
11974 #endif //BACKGROUND_GC
11975     size_t sz_list = loh_allocator->first_bucket_size();
11976     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
11977     {
11978         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
11979         {
11980             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
11981             uint8_t* prev_free_item = 0;
11982             while (free_list != 0)
11983             {
11984                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11985
11986                 size_t free_list_size = unused_array_size(free_list);
11987
11988 #ifdef FEATURE_LOH_COMPACTION
11989                 if ((size + loh_pad) <= free_list_size)
11990 #else
11991                 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
11992                     (size == free_list_size))
11993 #endif //FEATURE_LOH_COMPACTION
11994                 {
11995 #ifdef BACKGROUND_GC
11996                     cookie = bgc_alloc_lock->loh_alloc_set (free_list);
11997 #endif //BACKGROUND_GC
11998
11999                     //unlink the free_item
12000                     loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12001
12002                     // Substract min obj size because limit_from_size adds it. Not needed for LOH
12003                     size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size, 
12004                                                     gen_number, align_const);
12005
12006 #ifdef FEATURE_LOH_COMPACTION
12007                     make_unused_array (free_list, loh_pad);
12008                     limit -= loh_pad;
12009                     free_list += loh_pad;
12010                     free_list_size -= loh_pad;
12011 #endif //FEATURE_LOH_COMPACTION
12012
12013                     uint8_t*  remain = (free_list + limit);
12014                     size_t remain_size = (free_list_size - limit);
12015                     if (remain_size != 0)
12016                     {
12017                         assert (remain_size >= Align (min_obj_size, align_const));
12018                         make_unused_array (remain, remain_size);
12019                     }
12020                     if (remain_size >= Align(min_free_list, align_const))
12021                     {
12022                         loh_thread_gap_front (remain, remain_size, gen);
12023                         assert (remain_size >= Align (min_obj_size, align_const));
12024                     }
12025                     else
12026                     {
12027                         generation_free_obj_space (gen) += remain_size;
12028                     }
12029                     generation_free_list_space (gen) -= free_list_size;
12030                     dprintf (3, ("found fit on loh at %Ix", free_list));
12031 #ifdef BACKGROUND_GC
12032                     if (cookie != -1)
12033                     {
12034                         bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
12035                     }
12036                     else
12037 #endif //BACKGROUND_GC
12038                     {
12039                         adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
12040                     }
12041
12042                     //fix the limit to compensate for adjust_limit_clr making it too short 
12043                     acontext->alloc_limit += Align (min_obj_size, align_const);
12044                     can_fit = TRUE;
12045                     goto exit;
12046                 }
12047                 prev_free_item = free_list;
12048                 free_list = free_list_slot (free_list); 
12049             }
12050         }
12051         sz_list = sz_list * 2;
12052     }
12053 exit:
12054     return can_fit;
12055 }
12056
12057 #ifdef _MSC_VER
12058 #pragma warning(default:4706)
12059 #endif // _MSC_VER
12060
12061 BOOL gc_heap::a_fit_segment_end_p (int gen_number,
12062                                    heap_segment* seg,
12063                                    size_t size, 
12064                                    alloc_context* acontext,
12065                                    int align_const,
12066                                    BOOL* commit_failed_p)
12067 {
12068     *commit_failed_p = FALSE;
12069     size_t limit = 0;
12070 #ifdef BACKGROUND_GC
12071     int cookie = -1;
12072 #endif //BACKGROUND_GC
12073
12074     uint8_t*& allocated = ((gen_number == 0) ?
12075                         alloc_allocated : 
12076                         heap_segment_allocated(seg));
12077
12078     size_t pad = Align (min_obj_size, align_const);
12079
12080 #ifdef FEATURE_LOH_COMPACTION
12081     if (gen_number == (max_generation + 1))
12082     {
12083         pad += Align (loh_padding_obj_size, align_const);
12084     }
12085 #endif //FEATURE_LOH_COMPACTION
12086
12087     uint8_t* end = heap_segment_committed (seg) - pad;
12088
12089     if (a_size_fit_p (size, allocated, end, align_const))
12090     {
12091         limit = limit_from_size (size, 
12092                                  (end - allocated), 
12093                                  gen_number, align_const);
12094         goto found_fit;
12095     }
12096
12097     end = heap_segment_reserved (seg) - pad;
12098
12099     if (a_size_fit_p (size, allocated, end, align_const))
12100     {
12101         limit = limit_from_size (size, 
12102                                  (end - allocated), 
12103                                  gen_number, align_const);
12104         if (grow_heap_segment (seg, allocated + limit))
12105         {
12106             goto found_fit;
12107         }
12108         else
12109         {
12110             dprintf (2, ("can't grow segment, doing a full gc"));
12111             *commit_failed_p = TRUE;
12112         }
12113     }
12114     goto found_no_fit;
12115
12116 found_fit:
12117
12118 #ifdef BACKGROUND_GC
12119     if (gen_number != 0)
12120     {
12121         cookie = bgc_alloc_lock->loh_alloc_set (allocated);
12122     }
12123 #endif //BACKGROUND_GC
12124
12125     uint8_t* old_alloc;
12126     old_alloc = allocated;
12127 #ifdef FEATURE_LOH_COMPACTION
12128     if (gen_number == (max_generation + 1))
12129     {
12130         size_t loh_pad = Align (loh_padding_obj_size, align_const);
12131         make_unused_array (old_alloc, loh_pad);
12132         old_alloc += loh_pad;
12133         allocated += loh_pad;
12134         limit -= loh_pad;
12135     }
12136 #endif //FEATURE_LOH_COMPACTION
12137
12138 #if defined (VERIFY_HEAP) && defined (_DEBUG)
12139         ((void**) allocated)[-1] = 0;     //clear the sync block
12140 #endif //VERIFY_HEAP && _DEBUG
12141     allocated += limit;
12142
12143     dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
12144
12145 #ifdef BACKGROUND_GC
12146     if (cookie != -1)
12147     {
12148         bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
12149     }
12150     else
12151 #endif //BACKGROUND_GC
12152     {
12153         adjust_limit_clr (old_alloc, limit, acontext, seg, align_const, gen_number);
12154     }
12155
12156     return TRUE;
12157
12158 found_no_fit:
12159
12160     return FALSE;
12161 }
12162
12163 BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
12164                                        size_t size, 
12165                                        alloc_context* acontext,
12166                                        int align_const,
12167                                        BOOL* commit_failed_p,
12168                                        oom_reason* oom_r)
12169 {
12170     *commit_failed_p = FALSE;
12171     heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
12172     BOOL can_allocate_p = FALSE;
12173
12174     while (seg)
12175     {
12176         if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)), 
12177                                  acontext, align_const, commit_failed_p))
12178         {
12179             acontext->alloc_limit += Align (min_obj_size, align_const);
12180             can_allocate_p = TRUE;
12181             break;
12182         }
12183         else
12184         {
12185             if (*commit_failed_p)
12186             {
12187                 *oom_r = oom_cant_commit;
12188                 break;
12189             }
12190             else
12191             {
12192                 seg = heap_segment_next_rw (seg);
12193             }
12194         }
12195     }
12196
12197     return can_allocate_p;
12198 }
12199
12200 #ifdef BACKGROUND_GC
12201 inline
12202 void gc_heap::wait_for_background (alloc_wait_reason awr)
12203 {
12204     dprintf (2, ("BGC is already in progress, waiting for it to finish"));
12205     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc done", heap_number));
12206     add_saved_spinlock_info (me_release, mt_wait_bgc);
12207     leave_spin_lock (&more_space_lock);
12208     background_gc_wait (awr);
12209     enter_spin_lock (&more_space_lock);
12210     add_saved_spinlock_info (me_acquire, mt_wait_bgc);
12211     dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc done", heap_number));
12212 }
12213
12214 void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr)
12215 {
12216     if (recursive_gc_sync::background_running_p())
12217     {
12218         uint32_t memory_load;
12219         get_memory_info (&memory_load);
12220         if (memory_load >= 95)
12221         {
12222             dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
12223             wait_for_background (awr);
12224         }
12225     }
12226 }
12227
12228 #endif //BACKGROUND_GC
12229
12230 // We request to trigger an ephemeral GC but we may get a full compacting GC.
12231 // return TRUE if that's the case.
12232 BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
12233 {
12234 #ifdef BACKGROUND_GC
12235     wait_for_bgc_high_memory (awr_loh_oos_bgc);
12236 #endif //BACKGROUND_GC
12237
12238     BOOL did_full_compact_gc = FALSE;
12239
12240     dprintf (2, ("triggering a gen1 GC"));
12241     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12242     vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
12243
12244 #ifdef MULTIPLE_HEAPS
12245     enter_spin_lock (&more_space_lock);
12246     add_saved_spinlock_info (me_acquire, mt_t_eph_gc);
12247     dprintf (SPINLOCK_LOG, ("[%d]Emsl after a GC", heap_number));
12248 #endif //MULTIPLE_HEAPS
12249
12250     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12251
12252     if (current_full_compact_gc_count > last_full_compact_gc_count)
12253     {
12254         dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
12255         did_full_compact_gc = TRUE;
12256     }
12257
12258     return did_full_compact_gc;
12259 }
12260
12261 BOOL gc_heap::soh_try_fit (int gen_number,
12262                            size_t size, 
12263                            alloc_context* acontext,
12264                            int align_const,
12265                            BOOL* commit_failed_p,
12266                            BOOL* short_seg_end_p)
12267 {
12268     BOOL can_allocate = TRUE;
12269     if (short_seg_end_p)
12270     {
12271         *short_seg_end_p = FALSE;
12272     }
12273
12274     can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
12275     if (!can_allocate)
12276     {
12277         if (short_seg_end_p)
12278         {
12279             *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
12280         }
12281         // If the caller doesn't care, we always try to fit at the end of seg;
12282         // otherwise we would only try if we are actually not short at end of seg.
12283         if (!short_seg_end_p || !(*short_seg_end_p))
12284         {
12285             can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size, 
12286                                                 acontext, align_const, commit_failed_p);
12287         }
12288     }
12289
12290     return can_allocate;
12291 }
12292
12293 BOOL gc_heap::allocate_small (int gen_number,
12294                               size_t size, 
12295                               alloc_context* acontext,
12296                               int align_const)
12297 {
12298 #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
12299     if (recursive_gc_sync::background_running_p())
12300     {
12301         background_soh_alloc_count++;
12302         if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
12303         {
12304             add_saved_spinlock_info (me_release, mt_alloc_small);
12305             dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl", heap_number));
12306             leave_spin_lock (&more_space_lock);
12307             bool cooperative_mode = enable_preemptive ();
12308             GCToOSInterface::Sleep (bgc_alloc_spin);
12309             disable_preemptive (cooperative_mode);
12310             enter_spin_lock (&more_space_lock);
12311             add_saved_spinlock_info (me_acquire, mt_alloc_small);
12312             dprintf (SPINLOCK_LOG, ("[%d]spin Emsl", heap_number));
12313         }
12314         else
12315         {
12316             //GCToOSInterface::YieldThread (0);
12317         }
12318     }
12319 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
12320
12321     gc_reason gr = reason_oos_soh;
12322     oom_reason oom_r = oom_no_failure;
12323
12324     // No variable values should be "carried over" from one state to the other. 
12325     // That's why there are local variable for each state
12326
12327     allocation_state soh_alloc_state = a_state_start;
12328
12329     // If we can get a new seg it means allocation will succeed.
12330     while (1)
12331     {
12332         dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
12333         switch (soh_alloc_state)
12334         {
12335             case a_state_can_allocate:
12336             case a_state_cant_allocate:
12337             {
12338                 goto exit;
12339             }
12340             case a_state_start:
12341             {
12342                 soh_alloc_state = a_state_try_fit;
12343                 break;
12344             }
12345             case a_state_try_fit:
12346             {
12347                 BOOL commit_failed_p = FALSE;
12348                 BOOL can_use_existing_p = FALSE;
12349
12350                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12351                                                   align_const, &commit_failed_p,
12352                                                   NULL);
12353                 soh_alloc_state = (can_use_existing_p ?
12354                                         a_state_can_allocate : 
12355                                         (commit_failed_p ? 
12356                                             a_state_trigger_full_compact_gc :
12357                                             a_state_trigger_ephemeral_gc));
12358                 break;
12359             }
12360             case a_state_try_fit_after_bgc:
12361             {
12362                 BOOL commit_failed_p = FALSE;
12363                 BOOL can_use_existing_p = FALSE;
12364                 BOOL short_seg_end_p = FALSE;
12365
12366                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12367                                                   align_const, &commit_failed_p,
12368                                                   &short_seg_end_p);
12369                 soh_alloc_state = (can_use_existing_p ? 
12370                                         a_state_can_allocate : 
12371                                         (short_seg_end_p ? 
12372                                             a_state_trigger_2nd_ephemeral_gc : 
12373                                             a_state_trigger_full_compact_gc));
12374                 break;
12375             }
12376             case a_state_try_fit_after_cg:
12377             {
12378                 BOOL commit_failed_p = FALSE;
12379                 BOOL can_use_existing_p = FALSE;
12380                 BOOL short_seg_end_p = FALSE;
12381
12382                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12383                                                   align_const, &commit_failed_p,
12384                                                   &short_seg_end_p);
12385                 if (short_seg_end_p)
12386                 {
12387                     soh_alloc_state = a_state_cant_allocate;
12388                     oom_r = oom_budget;
12389                 }
12390                 else
12391                 {
12392                     if (can_use_existing_p)
12393                     {
12394                         soh_alloc_state = a_state_can_allocate;
12395                     }
12396                     else
12397                     {
12398 #ifdef MULTIPLE_HEAPS
12399                         if (!commit_failed_p)
12400                         {
12401                             // some other threads already grabbed the more space lock and allocated
12402                             // so we should attempt an ephemeral GC again.
12403                             assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
12404                             soh_alloc_state = a_state_trigger_ephemeral_gc; 
12405                         }
12406                         else
12407 #endif //MULTIPLE_HEAPS
12408                         {
12409                             assert (commit_failed_p);
12410                             soh_alloc_state = a_state_cant_allocate;
12411                             oom_r = oom_cant_commit;
12412                         }
12413                     }
12414                 }
12415                 break;
12416             }
12417             case a_state_check_and_wait_for_bgc:
12418             {
12419                 BOOL bgc_in_progress_p = FALSE;
12420                 BOOL did_full_compacting_gc = FALSE;
12421
12422                 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc);
12423                 soh_alloc_state = (did_full_compacting_gc ? 
12424                                         a_state_try_fit_after_cg : 
12425                                         a_state_try_fit_after_bgc);
12426                 break;
12427             }
12428             case a_state_trigger_ephemeral_gc:
12429             {
12430                 BOOL commit_failed_p = FALSE;
12431                 BOOL can_use_existing_p = FALSE;
12432                 BOOL short_seg_end_p = FALSE;
12433                 BOOL bgc_in_progress_p = FALSE;
12434                 BOOL did_full_compacting_gc = FALSE;
12435
12436                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12437                 if (did_full_compacting_gc)
12438                 {
12439                     soh_alloc_state = a_state_try_fit_after_cg;
12440                 }
12441                 else
12442                 {
12443                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12444                                                       align_const, &commit_failed_p,
12445                                                       &short_seg_end_p);
12446 #ifdef BACKGROUND_GC
12447                     bgc_in_progress_p = recursive_gc_sync::background_running_p();
12448 #endif //BACKGROUND_GC
12449
12450                     if (short_seg_end_p)
12451                     {
12452                         soh_alloc_state = (bgc_in_progress_p ? 
12453                                                 a_state_check_and_wait_for_bgc : 
12454                                                 a_state_trigger_full_compact_gc);
12455
12456                         if (fgn_maxgen_percent)
12457                         {
12458                             dprintf (2, ("FGN: doing last GC before we throw OOM"));
12459                             send_full_gc_notification (max_generation, FALSE);
12460                         }
12461                     }
12462                     else
12463                     {
12464                         if (can_use_existing_p)
12465                         {
12466                             soh_alloc_state = a_state_can_allocate;
12467                         }
12468                         else
12469                         {
12470 #ifdef MULTIPLE_HEAPS
12471                             if (!commit_failed_p)
12472                             {
12473                                 // some other threads already grabbed the more space lock and allocated
12474                                 // so we should attempt an ephemeral GC again.
12475                                 assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
12476                                 soh_alloc_state = a_state_trigger_ephemeral_gc;
12477                             }
12478                             else
12479 #endif //MULTIPLE_HEAPS
12480                             {
12481                                 soh_alloc_state = a_state_trigger_full_compact_gc;
12482                                 if (fgn_maxgen_percent)
12483                                 {
12484                                     dprintf (2, ("FGN: failed to commit, doing full compacting GC"));
12485                                     send_full_gc_notification (max_generation, FALSE);
12486                                 }
12487                             }
12488                         }
12489                     }
12490                 }
12491                 break;
12492             }
12493             case a_state_trigger_2nd_ephemeral_gc:
12494             {
12495                 BOOL commit_failed_p = FALSE;
12496                 BOOL can_use_existing_p = FALSE;
12497                 BOOL short_seg_end_p = FALSE;
12498                 BOOL did_full_compacting_gc = FALSE;
12499
12500
12501                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12502                 
12503                 if (did_full_compacting_gc)
12504                 {
12505                     soh_alloc_state = a_state_try_fit_after_cg;
12506                 }
12507                 else
12508                 {
12509                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12510                                                       align_const, &commit_failed_p,
12511                                                       &short_seg_end_p);
12512                     if (short_seg_end_p || commit_failed_p)
12513                     {
12514                         soh_alloc_state = a_state_trigger_full_compact_gc;
12515                     }
12516                     else
12517                     {
12518                         assert (can_use_existing_p);
12519                         soh_alloc_state = a_state_can_allocate;
12520                     }
12521                 }
12522                 break;
12523             }
12524             case a_state_trigger_full_compact_gc:
12525             {
12526                 BOOL got_full_compacting_gc = FALSE;
12527
12528                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
12529                 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12530                 break;
12531             }
12532             default:
12533             {
12534                 assert (!"Invalid state!");
12535                 break;
12536             }
12537         }
12538     }
12539
12540 exit:
12541     if (soh_alloc_state == a_state_cant_allocate)
12542     {
12543         assert (oom_r != oom_no_failure);
12544         handle_oom (heap_number, 
12545                     oom_r, 
12546                     size,
12547                     heap_segment_allocated (ephemeral_heap_segment),
12548                     heap_segment_reserved (ephemeral_heap_segment));
12549
12550         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for oom", heap_number));
12551         add_saved_spinlock_info (me_release, mt_alloc_small_cant);
12552         leave_spin_lock (&more_space_lock);
12553     }
12554
12555     return (soh_alloc_state == a_state_can_allocate);
12556 }
12557
12558 #ifdef BACKGROUND_GC
12559 inline
12560 void gc_heap::wait_for_background_planning (alloc_wait_reason awr)
12561 {
12562     while (current_c_gc_state == c_gc_state_planning)
12563     {
12564         dprintf (3, ("lh state planning, cannot allocate"));
12565
12566         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc plan", heap_number));
12567         add_saved_spinlock_info (me_release, mt_wait_bgc_plan);
12568         leave_spin_lock (&more_space_lock);
12569         background_gc_wait_lh (awr);
12570         enter_spin_lock (&more_space_lock);
12571         add_saved_spinlock_info (me_acquire, mt_wait_bgc_plan);
12572         dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc plan", heap_number));
12573     }
12574     assert ((current_c_gc_state == c_gc_state_free) ||
12575             (current_c_gc_state == c_gc_state_marking));
12576 }
12577
12578 BOOL gc_heap::bgc_loh_should_allocate()
12579 {
12580     size_t min_gc_size = dd_min_size(dynamic_data_of (max_generation + 1));
12581
12582     if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
12583     {
12584         return TRUE;
12585     }
12586
12587     if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
12588     {
12589         if ((bgc_begin_loh_size / end_loh_size) > 2)
12590         {
12591             dprintf (3, ("alloc-ed too much before bgc started"));
12592         }
12593         else
12594         {
12595             dprintf (3, ("alloc-ed too much after bgc started"));
12596         }
12597         return FALSE;
12598     }
12599     else
12600     {
12601         bgc_alloc_spin_loh = (uint32_t)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
12602         return TRUE;
12603     }
12604 }
12605 #endif //BACKGROUND_GC
12606
12607 size_t gc_heap::get_large_seg_size (size_t size)
12608 {
12609     size_t default_seg_size = min_loh_segment_size;
12610 #ifdef SEG_MAPPING_TABLE
12611     size_t align_size =  default_seg_size;
12612 #else //SEG_MAPPING_TABLE
12613     size_t align_size =  default_seg_size / 2;
12614 #endif //SEG_MAPPING_TABLE
12615     int align_const = get_alignment_constant (FALSE);
12616     size_t large_seg_size = align_on_page (
12617         max (default_seg_size,
12618             ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE +
12619             align_size) / align_size * align_size)));
12620     return large_seg_size;
12621 }
12622
12623 BOOL gc_heap::loh_get_new_seg (generation* gen,
12624                                size_t size,
12625                                int align_const,
12626                                BOOL* did_full_compact_gc,
12627                                oom_reason* oom_r)
12628 {
12629     UNREFERENCED_PARAMETER(gen);
12630     UNREFERENCED_PARAMETER(align_const);
12631
12632     *did_full_compact_gc = FALSE;
12633
12634     size_t seg_size = get_large_seg_size (size);
12635
12636     heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
12637
12638     if (new_seg)
12639     {
12640         loh_alloc_since_cg += seg_size;
12641     }
12642     else
12643     {
12644         *oom_r = oom_loh;
12645     }
12646
12647     return (new_seg != 0);
12648 }
12649
12650 BOOL gc_heap::retry_full_compact_gc (size_t size)
12651 {
12652     size_t seg_size = get_large_seg_size (size);
12653
12654     if (loh_alloc_since_cg >= (2 * (uint64_t)seg_size))
12655     {
12656         return TRUE;
12657     }
12658
12659 #ifdef MULTIPLE_HEAPS
12660     uint64_t total_alloc_size = 0;
12661     for (int i = 0; i < n_heaps; i++)
12662     {
12663         total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12664     }
12665
12666     if (total_alloc_size >= (2 * (uint64_t)seg_size))
12667     {
12668         return TRUE;
12669     }
12670 #endif //MULTIPLE_HEAPS
12671
12672     return FALSE;
12673 }
12674
12675 BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12676                                       BOOL* did_full_compact_gc)
12677 {
12678     BOOL bgc_in_progress = FALSE;
12679     *did_full_compact_gc = FALSE;
12680 #ifdef BACKGROUND_GC
12681     if (recursive_gc_sync::background_running_p())
12682     {
12683         bgc_in_progress = TRUE;
12684         size_t last_full_compact_gc_count = get_full_compact_gc_count();
12685         wait_for_background (awr);
12686         size_t current_full_compact_gc_count = get_full_compact_gc_count();
12687         if (current_full_compact_gc_count > last_full_compact_gc_count)
12688         {
12689             *did_full_compact_gc = TRUE;
12690         }
12691     }
12692 #endif //BACKGROUND_GC
12693
12694     return bgc_in_progress;
12695 }
12696
12697 BOOL gc_heap::loh_try_fit (int gen_number,
12698                            size_t size, 
12699                            alloc_context* acontext,
12700                            int align_const,
12701                            BOOL* commit_failed_p,
12702                            oom_reason* oom_r)
12703 {
12704     BOOL can_allocate = TRUE;
12705
12706     if (!a_fit_free_list_large_p (size, acontext, align_const))
12707     {
12708         can_allocate = loh_a_fit_segment_end_p (gen_number, size, 
12709                                                 acontext, align_const, 
12710                                                 commit_failed_p, oom_r);
12711
12712 #ifdef BACKGROUND_GC
12713         if (can_allocate && recursive_gc_sync::background_running_p())
12714         {
12715             bgc_loh_size_increased += size;
12716         }
12717 #endif //BACKGROUND_GC
12718     }
12719 #ifdef BACKGROUND_GC
12720     else
12721     {
12722         if (recursive_gc_sync::background_running_p())
12723         {
12724             bgc_loh_allocated_in_free += size;
12725         }
12726     }
12727 #endif //BACKGROUND_GC
12728
12729     return can_allocate;
12730 }
12731
12732 BOOL gc_heap::trigger_full_compact_gc (gc_reason gr, 
12733                                        oom_reason* oom_r)
12734 {
12735     BOOL did_full_compact_gc = FALSE;
12736
12737     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12738
12739     // Set this so the next GC will be a full compacting GC.
12740     if (!last_gc_before_oom)
12741     {
12742         last_gc_before_oom = TRUE;
12743     }
12744
12745 #ifdef BACKGROUND_GC
12746     if (recursive_gc_sync::background_running_p())
12747     {
12748         wait_for_background ((gr == reason_oos_soh) ? awr_gen0_oos_bgc : awr_loh_oos_bgc);
12749         dprintf (2, ("waited for BGC - done"));
12750     }
12751 #endif //BACKGROUND_GC
12752
12753     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12754     if (current_full_compact_gc_count > last_full_compact_gc_count)
12755     {
12756         dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
12757         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12758         did_full_compact_gc = TRUE;
12759         goto exit;
12760     }
12761
12762     dprintf (3, ("h%d full GC", heap_number));
12763     vm_heap->GarbageCollectGeneration(max_generation, gr);
12764
12765 #ifdef MULTIPLE_HEAPS
12766     enter_spin_lock (&more_space_lock);
12767     dprintf (SPINLOCK_LOG, ("[%d]Emsl after full gc", heap_number));
12768     add_saved_spinlock_info (me_acquire, mt_t_full_gc);
12769 #endif //MULTIPLE_HEAPS
12770
12771     current_full_compact_gc_count = get_full_compact_gc_count();
12772
12773     if (current_full_compact_gc_count == last_full_compact_gc_count)
12774     {
12775         dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
12776         // We requested a full GC but didn't get because of the elevation logic
12777         // which means we should fail.
12778         *oom_r = oom_unproductive_full_gc;
12779     }
12780     else
12781     {
12782         dprintf (3, ("h%d: T full compacting GC (%d->%d)", 
12783             heap_number, 
12784             last_full_compact_gc_count, 
12785             current_full_compact_gc_count));
12786
12787         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12788         did_full_compact_gc = TRUE;
12789     }
12790
12791 exit:
12792     return did_full_compact_gc;
12793 }
12794
12795 #ifdef RECORD_LOH_STATE
12796 void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, EEThreadId thread_id)
12797 {
12798     // When the state is can_allocate we already have released the more
12799     // space lock. So we are not logging states here since this code
12800     // is not thread safe.
12801     if (loh_state_to_save != a_state_can_allocate)
12802     {
12803         last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
12804         last_loh_states[loh_state_index].thread_id = thread_id;
12805         loh_state_index++;
12806
12807         if (loh_state_index == max_saved_loh_states)
12808         {
12809             loh_state_index = 0;
12810         }
12811
12812         assert (loh_state_index < max_saved_loh_states);
12813     }
12814 }
12815 #endif //RECORD_LOH_STATE
12816
12817 BOOL gc_heap::allocate_large (int gen_number,
12818                               size_t size, 
12819                               alloc_context* acontext,
12820                               int align_const)
12821 {
12822 #ifdef BACKGROUND_GC
12823     if (recursive_gc_sync::background_running_p() && (current_c_gc_state != c_gc_state_planning))
12824     {
12825         background_loh_alloc_count++;
12826         //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
12827         {
12828             if (bgc_loh_should_allocate())
12829             {
12830                 if (!bgc_alloc_spin_loh)
12831                 {
12832                     add_saved_spinlock_info (me_release, mt_alloc_large);
12833                     dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl loh", heap_number));
12834                     leave_spin_lock (&more_space_lock);
12835                     bool cooperative_mode = enable_preemptive ();
12836                     GCToOSInterface::YieldThread (bgc_alloc_spin_loh);
12837                     disable_preemptive (cooperative_mode);
12838                     enter_spin_lock (&more_space_lock);
12839                     add_saved_spinlock_info (me_acquire, mt_alloc_large);
12840                     dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
12841                 }
12842             }
12843             else
12844             {
12845                 wait_for_background (awr_loh_alloc_during_bgc);
12846             }
12847         }
12848     }
12849 #endif //BACKGROUND_GC
12850
12851     gc_reason gr = reason_oos_loh;
12852     generation* gen = generation_of (gen_number);
12853     oom_reason oom_r = oom_no_failure;
12854     size_t current_full_compact_gc_count = 0;
12855
12856     // No variable values should be "carried over" from one state to the other. 
12857     // That's why there are local variable for each state
12858     allocation_state loh_alloc_state = a_state_start;
12859 #ifdef RECORD_LOH_STATE
12860     EEThreadId current_thread_id;
12861     current_thread_id.SetToCurrentThread();
12862 #endif //RECORD_LOH_STATE
12863
12864     // If we can get a new seg it means allocation will succeed.
12865     while (1)
12866     {
12867         dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
12868
12869 #ifdef RECORD_LOH_STATE
12870         add_saved_loh_state (loh_alloc_state, current_thread_id);
12871 #endif //RECORD_LOH_STATE
12872         switch (loh_alloc_state)
12873         {
12874             case a_state_can_allocate:
12875             case a_state_cant_allocate:
12876             {
12877                 goto exit;
12878             }
12879             case a_state_start:
12880             {
12881                 loh_alloc_state = a_state_try_fit;
12882                 break;
12883             }
12884             case a_state_try_fit:
12885             {
12886                 BOOL commit_failed_p = FALSE;
12887                 BOOL can_use_existing_p = FALSE;
12888
12889                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12890                                                   align_const, &commit_failed_p, &oom_r);
12891                 loh_alloc_state = (can_use_existing_p ?
12892                                         a_state_can_allocate : 
12893                                         (commit_failed_p ? 
12894                                             a_state_trigger_full_compact_gc :
12895                                             a_state_acquire_seg));
12896                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12897                 break;
12898             }
12899             case a_state_try_fit_new_seg:
12900             {
12901                 BOOL commit_failed_p = FALSE;
12902                 BOOL can_use_existing_p = FALSE;
12903
12904                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12905                                                   align_const, &commit_failed_p, &oom_r);
12906                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12907                 // another LOH allocating thread could have beat us to acquire the msl so 
12908                 // we need to try again.
12909                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
12910                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12911                 break;
12912             }
12913             case a_state_try_fit_new_seg_after_cg:
12914             {
12915                 BOOL commit_failed_p = FALSE;
12916                 BOOL can_use_existing_p = FALSE;
12917
12918                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12919                                                   align_const, &commit_failed_p, &oom_r);
12920                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12921                 // another LOH allocating thread could have beat us to acquire the msl so 
12922                 // we need to try again. However, if we failed to commit, which means we 
12923                 // did have space on the seg, we bail right away 'cause we already did a 
12924                 // full compacting GC.
12925                 loh_alloc_state = (can_use_existing_p ? 
12926                                         a_state_can_allocate : 
12927                                         (commit_failed_p ? 
12928                                             a_state_cant_allocate :
12929                                             a_state_acquire_seg_after_cg));
12930                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12931                 break;
12932             }
12933             case a_state_try_fit_no_seg:
12934             {
12935                 BOOL commit_failed_p = FALSE;
12936                 BOOL can_use_existing_p = FALSE;
12937
12938                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12939                                                   align_const, &commit_failed_p, &oom_r);
12940                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_cant_allocate);
12941                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12942                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12943                 break;
12944             }
12945             case a_state_try_fit_after_cg:
12946             {
12947                 BOOL commit_failed_p = FALSE;
12948                 BOOL can_use_existing_p = FALSE;
12949
12950                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12951                                                   align_const, &commit_failed_p, &oom_r);
12952                 loh_alloc_state = (can_use_existing_p ?
12953                                         a_state_can_allocate : 
12954                                         (commit_failed_p ? 
12955                                             a_state_cant_allocate :
12956                                             a_state_acquire_seg_after_cg));
12957                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12958                 break;
12959             }
12960             case a_state_try_fit_after_bgc:
12961             {
12962                 BOOL commit_failed_p = FALSE;
12963                 BOOL can_use_existing_p = FALSE;
12964
12965                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12966                                                   align_const, &commit_failed_p, &oom_r);
12967                 loh_alloc_state = (can_use_existing_p ?
12968                                         a_state_can_allocate : 
12969                                         (commit_failed_p ? 
12970                                             a_state_trigger_full_compact_gc :
12971                                             a_state_acquire_seg_after_bgc));
12972                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12973                 break;
12974             }
12975             case a_state_acquire_seg:
12976             {
12977                 BOOL can_get_new_seg_p = FALSE;
12978                 BOOL did_full_compacting_gc = FALSE;
12979
12980                 current_full_compact_gc_count = get_full_compact_gc_count();
12981
12982                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12983                 loh_alloc_state = (can_get_new_seg_p ? 
12984                                         a_state_try_fit_new_seg : 
12985                                         (did_full_compacting_gc ? 
12986                                             a_state_check_retry_seg :
12987                                             a_state_check_and_wait_for_bgc));
12988                 break;
12989             }
12990             case a_state_acquire_seg_after_cg:
12991             {
12992                 BOOL can_get_new_seg_p = FALSE;
12993                 BOOL did_full_compacting_gc = FALSE;
12994
12995                 current_full_compact_gc_count = get_full_compact_gc_count();
12996
12997                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12998                 // Since we release the msl before we try to allocate a seg, other
12999                 // threads could have allocated a bunch of segments before us so
13000                 // we might need to retry.
13001                 loh_alloc_state = (can_get_new_seg_p ? 
13002                                         a_state_try_fit_new_seg_after_cg : 
13003                                         a_state_check_retry_seg);
13004                 break;
13005             }
13006             case a_state_acquire_seg_after_bgc:
13007             {
13008                 BOOL can_get_new_seg_p = FALSE;
13009                 BOOL did_full_compacting_gc = FALSE;
13010              
13011                 current_full_compact_gc_count = get_full_compact_gc_count();
13012
13013                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); 
13014                 loh_alloc_state = (can_get_new_seg_p ? 
13015                                         a_state_try_fit_new_seg : 
13016                                         (did_full_compacting_gc ? 
13017                                             a_state_check_retry_seg :
13018                                             a_state_trigger_full_compact_gc));
13019                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13020                 break;
13021             }
13022             case a_state_check_and_wait_for_bgc:
13023             {
13024                 BOOL bgc_in_progress_p = FALSE;
13025                 BOOL did_full_compacting_gc = FALSE;
13026
13027                 if (fgn_maxgen_percent)
13028                 {
13029                     dprintf (2, ("FGN: failed to acquire seg, may need to do a full blocking GC"));
13030                     send_full_gc_notification (max_generation, FALSE);
13031                 }
13032
13033                 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc);
13034                 loh_alloc_state = (!bgc_in_progress_p ?
13035                                         a_state_trigger_full_compact_gc : 
13036                                         (did_full_compacting_gc ? 
13037                                             a_state_try_fit_after_cg :
13038                                             a_state_try_fit_after_bgc));
13039                 break;
13040             }
13041             case a_state_trigger_full_compact_gc:
13042             {
13043                 BOOL got_full_compacting_gc = FALSE;
13044
13045                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
13046                 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
13047                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13048                 break;
13049             }
13050             case a_state_check_retry_seg:
13051             {
13052                 BOOL should_retry_gc = retry_full_compact_gc (size);
13053                 BOOL should_retry_get_seg = FALSE;
13054                 if (!should_retry_gc)
13055                 {
13056                     size_t last_full_compact_gc_count = current_full_compact_gc_count;
13057                     current_full_compact_gc_count = get_full_compact_gc_count();
13058
13059                     if (current_full_compact_gc_count > (last_full_compact_gc_count + 1))
13060                     {
13061                         should_retry_get_seg = TRUE;
13062                     }
13063                 }
13064     
13065                 loh_alloc_state = (should_retry_gc ? 
13066                                         a_state_trigger_full_compact_gc : 
13067                                         (should_retry_get_seg ?
13068                                             a_state_acquire_seg_after_cg :
13069                                             a_state_cant_allocate));
13070                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13071                 break;
13072             }
13073             default:
13074             {
13075                 assert (!"Invalid state!");
13076                 break;
13077             }
13078         }
13079     }
13080
13081 exit:
13082     if (loh_alloc_state == a_state_cant_allocate)
13083     {
13084         assert (oom_r != oom_no_failure);
13085         handle_oom (heap_number, 
13086                     oom_r, 
13087                     size,
13088                     0,
13089                     0);
13090
13091         add_saved_spinlock_info (me_release, mt_alloc_large_cant);
13092         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for loh oom", heap_number));
13093         leave_spin_lock (&more_space_lock);
13094     }
13095
13096     return (loh_alloc_state == a_state_can_allocate);
13097 }
13098
13099 int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
13100                                    int gen_number)
13101 {
13102     if (gc_heap::gc_started)
13103     {
13104         wait_for_gc_done();
13105         return -1;
13106     }
13107
13108 #ifdef SYNCHRONIZATION_STATS
13109     int64_t msl_acquire_start = GCToOSInterface::QueryPerformanceCounter();
13110 #endif //SYNCHRONIZATION_STATS
13111     enter_spin_lock (&more_space_lock);
13112     add_saved_spinlock_info (me_acquire, mt_try_alloc);
13113     dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
13114 #ifdef SYNCHRONIZATION_STATS
13115     int64_t msl_acquire = GCToOSInterface::QueryPerformanceCounter() - msl_acquire_start;
13116     total_msl_acquire += msl_acquire;
13117     num_msl_acquired++;
13118     if (msl_acquire > 200)
13119     {
13120         num_high_msl_acquire++;
13121     }
13122     else
13123     {
13124         num_low_msl_acquire++;
13125     }
13126 #endif //SYNCHRONIZATION_STATS
13127
13128     /*
13129     // We are commenting this out 'cause we don't see the point - we already
13130     // have checked gc_started when we were acquiring the msl - no need to check
13131     // again. This complicates the logic in bgc_suspend_EE 'cause that one would
13132     // need to release msl which causes all sorts of trouble.
13133     if (gc_heap::gc_started)
13134     {
13135 #ifdef SYNCHRONIZATION_STATS
13136         good_suspension++;
13137 #endif //SYNCHRONIZATION_STATS
13138         BOOL fStress = (g_pConfig->GetGCStressLevel() & GCConfig::GCSTRESS_TRANSITION) != 0;
13139         if (!fStress)
13140         {
13141             //Rendez vous early (MP scaling issue)
13142             //dprintf (1, ("[%d]waiting for gc", heap_number));
13143             wait_for_gc_done();
13144 #ifdef MULTIPLE_HEAPS
13145             return -1;
13146 #endif //MULTIPLE_HEAPS
13147         }
13148     }
13149     */
13150
13151     dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
13152
13153     int align_const = get_alignment_constant (gen_number != (max_generation+1));
13154
13155     if (fgn_maxgen_percent)
13156     {
13157         check_for_full_gc (gen_number, size);
13158     }
13159
13160     if (!(new_allocation_allowed (gen_number)))
13161     {
13162         if (fgn_maxgen_percent && (gen_number == 0))
13163         {
13164             // We only check gen0 every so often, so take this opportunity to check again.
13165             check_for_full_gc (gen_number, size);
13166         }
13167
13168 #ifdef BACKGROUND_GC
13169         wait_for_bgc_high_memory (awr_gen0_alloc);
13170 #endif //BACKGROUND_GC
13171
13172 #ifdef SYNCHRONIZATION_STATS
13173         bad_suspension++;
13174 #endif //SYNCHRONIZATION_STATS
13175         dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
13176
13177         if (!settings.concurrent || (gen_number == 0))
13178         {
13179             vm_heap->GarbageCollectGeneration (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh));
13180 #ifdef MULTIPLE_HEAPS
13181             enter_spin_lock (&more_space_lock);
13182             add_saved_spinlock_info (me_acquire, mt_try_budget);
13183             dprintf (SPINLOCK_LOG, ("[%d]Emsl out budget", heap_number));
13184 #endif //MULTIPLE_HEAPS
13185         }
13186     }
13187
13188     BOOL can_allocate = ((gen_number == 0) ?
13189         allocate_small (gen_number, size, acontext, align_const) :
13190         allocate_large (gen_number, size, acontext, align_const));
13191    
13192     if (can_allocate)
13193     {
13194         size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
13195         int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
13196
13197         etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
13198
13199
13200         if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
13201         {
13202 #ifdef FEATURE_REDHAWK
13203             FIRE_EVENT(GCAllocationTick_V1, (uint32_t)etw_allocation_running_amount[etw_allocation_index],
13204                                             (gen_number == 0) ? gc_etw_alloc_soh : gc_etw_alloc_loh);
13205 #else
13206             // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
13207             // The ones that do are much less efficient.
13208 #if defined(FEATURE_EVENT_TRACE)
13209             if (EVENT_ENABLED(GCAllocationTick_V3))
13210             {
13211                 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
13212             }
13213 #endif //FEATURE_EVENT_TRACE
13214 #endif //FEATURE_REDHAWK
13215             etw_allocation_running_amount[etw_allocation_index] = 0;
13216         }
13217     }
13218
13219     return (int)can_allocate;
13220 }
13221
13222 #ifdef MULTIPLE_HEAPS
13223 void gc_heap::balance_heaps (alloc_context* acontext)
13224 {
13225
13226     if (acontext->alloc_count < 4)
13227     {
13228         if (acontext->alloc_count == 0)
13229         {
13230             acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, 0) ));
13231             gc_heap* hp = acontext->get_home_heap()->pGenGCHeap;
13232             dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
13233             acontext->set_alloc_heap(acontext->get_home_heap());
13234             hp->alloc_context_count++;
13235         }
13236     }
13237     else
13238     {
13239         BOOL set_home_heap = FALSE;
13240         int hint = 0;
13241
13242         if (heap_select::can_find_heap_fast())
13243         {
13244             if (acontext->get_home_heap() != NULL)
13245                 hint = acontext->get_home_heap()->pGenGCHeap->heap_number;
13246             if (acontext->get_home_heap() != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
13247             {
13248                 set_home_heap = TRUE;
13249             }
13250         }
13251         else
13252         {
13253             // can't use gdt
13254             if ((acontext->alloc_count & 3) == 0)
13255                 set_home_heap = TRUE;
13256         }
13257
13258         if (set_home_heap)
13259         {
13260 /*
13261             // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
13262             if (n_heaps > MAX_SUPPORTED_CPUS)
13263             {
13264                 // on machines with many processors cache affinity is really king, so don't even try
13265                 // to balance on these.
13266                 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
13267                 acontext->alloc_heap = acontext->home_heap;
13268             }
13269             else
13270 */
13271             {
13272                 gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13273
13274                 dynamic_data* dd = org_hp->dynamic_data_of (0);
13275                 ptrdiff_t org_size = dd_new_allocation (dd);
13276                 int org_alloc_context_count;
13277                 int max_alloc_context_count;
13278                 gc_heap* max_hp;
13279                 ptrdiff_t max_size;
13280                 size_t delta = dd_min_size (dd)/4;
13281
13282                 int start, end, finish;
13283                 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13284                 finish = start + n_heaps;
13285
13286 try_again:
13287                 do
13288                 {
13289                     max_hp = org_hp;
13290                     max_size = org_size + delta;
13291                     acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, hint) ));
13292
13293                     if (org_hp == acontext->get_home_heap()->pGenGCHeap)
13294                         max_size = max_size + delta;
13295
13296                     org_alloc_context_count = org_hp->alloc_context_count;
13297                     max_alloc_context_count = org_alloc_context_count;
13298                     if (max_alloc_context_count > 1)
13299                         max_size /= max_alloc_context_count;
13300
13301                     for (int i = start; i < end; i++)
13302                     {
13303                         gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13304                         dd = hp->dynamic_data_of (0);
13305                         ptrdiff_t size = dd_new_allocation (dd);
13306                         if (hp == acontext->get_home_heap()->pGenGCHeap)
13307                             size = size + delta;
13308                         int hp_alloc_context_count = hp->alloc_context_count;
13309                         if (hp_alloc_context_count > 0)
13310                             size /= (hp_alloc_context_count + 1);
13311                         if (size > max_size)
13312                         {
13313                             max_hp = hp;
13314                             max_size = size;
13315                             max_alloc_context_count = hp_alloc_context_count;
13316                         }
13317                     }
13318                 }
13319                 while (org_alloc_context_count != org_hp->alloc_context_count ||
13320                        max_alloc_context_count != max_hp->alloc_context_count);
13321
13322                 if ((max_hp == org_hp) && (end < finish))
13323                 {   
13324                     start = end; end = finish; 
13325                     delta = dd_min_size(dd)/2; // Make it twice as hard to balance to remote nodes on NUMA.
13326                     goto try_again;
13327                 }
13328
13329                 if (max_hp != org_hp)
13330                 {
13331                     org_hp->alloc_context_count--;
13332                     max_hp->alloc_context_count++;
13333                     acontext->set_alloc_heap(GCHeap::GetHeap(max_hp->heap_number));
13334                     if (GCToOSInterface::CanEnableGCCPUGroups())
13335                     {   //only set ideal processor when max_hp and org_hp are in the same cpu
13336                         //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
13337                         uint16_t org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
13338                         uint16_t max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
13339                         if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
13340                         {   
13341                             uint16_t group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
13342
13343                             GCThreadAffinity affinity;
13344                             affinity.Processor = group_proc_no;
13345                             affinity.Group = org_gn;
13346                             if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13347                             {
13348                                 dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
13349                                             org_hp->heap_number));
13350                             }
13351                         }
13352                     }
13353                     else 
13354                     {
13355                         uint16_t proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
13356
13357                         GCThreadAffinity affinity;
13358                         affinity.Processor = proc_no;
13359                         affinity.Group = GCThreadAffinity::None;
13360
13361                         if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13362                         {
13363                             dprintf (3, ("Failed to set the ideal processor for heap %d.",
13364                                         org_hp->heap_number));
13365                         }
13366                     }
13367                     dprintf (3, ("Switching context %p (home heap %d) ", 
13368                                  acontext,
13369                         acontext->get_home_heap()->pGenGCHeap->heap_number));
13370                     dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ", 
13371                                  org_hp->heap_number,
13372                                  org_size,
13373                                  org_alloc_context_count));
13374                     dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n", 
13375                                  max_hp->heap_number,
13376                                  dd_new_allocation(max_hp->dynamic_data_of(0)),
13377                                                    max_alloc_context_count));
13378                 }
13379             }
13380         }
13381     }
13382     acontext->alloc_count++;
13383 }
13384
13385 gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t /*size*/)
13386 {
13387     gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13388     //dprintf (1, ("LA: %Id", size));
13389
13390     //if (size > 128*1024)
13391     if (1)
13392     {
13393         dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
13394
13395         ptrdiff_t org_size = dd_new_allocation (dd);
13396         gc_heap* max_hp;
13397         ptrdiff_t max_size;
13398         size_t delta = dd_min_size (dd) * 4;
13399
13400         int start, end, finish;
13401         heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13402         finish = start + n_heaps;
13403
13404 try_again:
13405         {
13406             max_hp = org_hp;
13407             max_size = org_size + delta;
13408             dprintf (3, ("orig hp: %d, max size: %d",
13409                 org_hp->heap_number,
13410                 max_size));
13411
13412             for (int i = start; i < end; i++)
13413             {
13414                 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13415                 dd = hp->dynamic_data_of (max_generation + 1);
13416                 ptrdiff_t size = dd_new_allocation (dd);
13417                 dprintf (3, ("hp: %d, size: %d",
13418                     hp->heap_number,
13419                     size));
13420                 if (size > max_size)
13421                 {
13422                     max_hp = hp;
13423                     max_size = size;
13424                     dprintf (3, ("max hp: %d, max size: %d",
13425                         max_hp->heap_number,
13426                         max_size));
13427                 }
13428             }
13429         }
13430
13431         if ((max_hp == org_hp) && (end < finish))
13432         {
13433             start = end; end = finish;
13434             delta = dd_min_size(dd) * 4;   // Need to tuning delta
13435             goto try_again;
13436         }
13437
13438         if (max_hp != org_hp)
13439         {
13440             dprintf (3, ("loh: %d(%Id)->%d(%Id)", 
13441                 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
13442                 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
13443         }
13444
13445         return max_hp;
13446     }
13447     else
13448     {
13449         return org_hp;
13450     }
13451 }
13452 #endif //MULTIPLE_HEAPS
13453
13454 BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
13455                                   int alloc_generation_number)
13456 {
13457     int status;
13458     do
13459     { 
13460 #ifdef MULTIPLE_HEAPS
13461         if (alloc_generation_number == 0)
13462         {
13463             balance_heaps (acontext);
13464             status = acontext->get_alloc_heap()->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
13465         }
13466         else
13467         {
13468             gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
13469             status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
13470         }
13471 #else
13472         status = try_allocate_more_space (acontext, size, alloc_generation_number);
13473 #endif //MULTIPLE_HEAPS
13474     }
13475     while (status == -1);
13476     
13477     return (status != 0);
13478 }
13479
13480 inline
13481 CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
13482 {
13483     size_t size = Align (jsize);
13484     assert (size >= Align (min_obj_size));
13485     {
13486     retry:
13487         uint8_t*  result = acontext->alloc_ptr;
13488         acontext->alloc_ptr+=size;
13489         if (acontext->alloc_ptr <= acontext->alloc_limit)
13490         {
13491             CObjectHeader* obj = (CObjectHeader*)result;
13492             assert (obj != 0);
13493             return obj;
13494         }
13495         else
13496         {
13497             acontext->alloc_ptr -= size;
13498
13499 #ifdef _MSC_VER
13500 #pragma inline_depth(0)
13501 #endif //_MSC_VER
13502
13503             if (! allocate_more_space (acontext, size, 0))
13504                 return 0;
13505
13506 #ifdef _MSC_VER
13507 #pragma inline_depth(20)
13508 #endif //_MSC_VER
13509
13510             goto retry;
13511         }
13512     }
13513 }
13514
13515 inline
13516 CObjectHeader* gc_heap::try_fast_alloc (size_t jsize)
13517 {
13518     size_t size = Align (jsize);
13519     assert (size >= Align (min_obj_size));
13520     generation* gen = generation_of (0);
13521     uint8_t*  result = generation_allocation_pointer (gen);
13522     generation_allocation_pointer (gen) += size;
13523     if (generation_allocation_pointer (gen) <=
13524         generation_allocation_limit (gen))
13525     {
13526         return (CObjectHeader*)result;
13527     }
13528     else
13529     {
13530         generation_allocation_pointer (gen) -= size;
13531         return 0;
13532     }
13533 }
13534 void  gc_heap::leave_allocation_segment (generation* gen)
13535 {
13536     adjust_limit (0, 0, gen, max_generation);
13537 }
13538
13539 void gc_heap::init_free_and_plug()
13540 {
13541 #ifdef FREE_USAGE_STATS
13542     for (int i = 0; i <= settings.condemned_generation; i++)
13543     {
13544         generation* gen = generation_of (i);
13545         memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
13546         memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13547         memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
13548     }
13549
13550     if (settings.condemned_generation != max_generation)
13551     {
13552         for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
13553         {
13554             generation* gen = generation_of (i);
13555             memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13556         }
13557     }
13558 #endif //FREE_USAGE_STATS
13559 }
13560
13561 void gc_heap::print_free_and_plug (const char* msg)
13562 {
13563 #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
13564     int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
13565     for (int i = 0; i <= older_gen; i++)
13566     {
13567         generation* gen = generation_of (i);
13568         for (int j = 0; j < NUM_GEN_POWER2; j++)
13569         {
13570             if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
13571             {
13572                 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id", 
13573                     msg, 
13574                     heap_number, 
13575                     (settings.concurrent ? "BGC" : "GC"),
13576                     settings.gc_index,
13577                     i,
13578                     (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
13579             }
13580         }
13581     }
13582 #else
13583     UNREFERENCED_PARAMETER(msg);
13584 #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
13585 }
13586
13587 void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
13588 {
13589 #ifdef FREE_USAGE_STATS
13590     dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
13591     generation* gen = generation_of (gen_number);
13592     size_t sz = BASE_GEN_SIZE;
13593     int i = 0;
13594
13595     for (; i < NUM_GEN_POWER2; i++)
13596     {
13597         if (plug_size < sz)
13598         {
13599             break;
13600         }
13601         sz = sz * 2;
13602     }
13603     
13604     (gen->gen_plugs[i])++;
13605 #else
13606     UNREFERENCED_PARAMETER(gen_number);
13607     UNREFERENCED_PARAMETER(plug_size);
13608 #endif //FREE_USAGE_STATS
13609 }
13610
13611 void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
13612 {
13613 #ifdef FREE_USAGE_STATS
13614     generation* gen = generation_of (gen_number);
13615     size_t sz = BASE_GEN_SIZE;
13616     int i = 0;
13617
13618     for (; i < NUM_GEN_POWER2; i++)
13619     {
13620         if (free_size < sz)
13621         {
13622             break;
13623         }
13624         sz = sz * 2;
13625     }
13626     
13627     (gen->gen_current_pinned_free_spaces[i])++;
13628     generation_pinned_free_obj_space (gen) += free_size;
13629     dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)", 
13630         free_size, (i + 10), gen_number, 
13631         generation_pinned_free_obj_space (gen),
13632         gen->gen_current_pinned_free_spaces[i]));
13633 #else
13634     UNREFERENCED_PARAMETER(gen_number);
13635     UNREFERENCED_PARAMETER(free_size);
13636 #endif //FREE_USAGE_STATS
13637 }
13638
13639 void gc_heap::add_gen_free (int gen_number, size_t free_size)
13640 {
13641 #ifdef FREE_USAGE_STATS
13642     dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13643     generation* gen = generation_of (gen_number);
13644     size_t sz = BASE_GEN_SIZE;
13645     int i = 0;
13646
13647     for (; i < NUM_GEN_POWER2; i++)
13648     {
13649         if (free_size < sz)
13650         {
13651             break;
13652         }
13653         sz = sz * 2;
13654     }
13655     
13656     (gen->gen_free_spaces[i])++;
13657 #else
13658     UNREFERENCED_PARAMETER(gen_number);
13659     UNREFERENCED_PARAMETER(free_size);
13660 #endif //FREE_USAGE_STATS
13661 }
13662
13663 void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13664 {
13665 #ifdef FREE_USAGE_STATS
13666     dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13667     generation* gen = generation_of (gen_number);
13668     size_t sz = BASE_GEN_SIZE;
13669     int i = 0;
13670
13671     for (; i < NUM_GEN_POWER2; i++)
13672     {
13673         if (free_size < sz)
13674         {
13675             break;
13676         }
13677         sz = sz * 2;
13678     }
13679     
13680     (gen->gen_free_spaces[i])--;
13681 #else
13682     UNREFERENCED_PARAMETER(gen_number);
13683     UNREFERENCED_PARAMETER(free_size);
13684 #endif //FREE_USAGE_STATS
13685 }
13686
13687 uint8_t* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13688                                              int from_gen_number,
13689                                              uint8_t* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13690 {
13691     size = Align (size);
13692     assert (size >= Align (min_obj_size));
13693     assert (from_gen_number < max_generation);
13694     assert (from_gen_number >= 0);
13695     assert (generation_of (from_gen_number + 1) == gen);
13696
13697     allocator* gen_allocator = generation_allocator (gen);
13698     BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13699     int pad_in_front = (old_loc != 0)? USE_PADDING_FRONT : 0;
13700
13701     size_t real_size = size + Align (min_obj_size);
13702     if (pad_in_front)
13703         real_size += Align (min_obj_size);
13704
13705     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13706                        generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13707     {
13708         size_t sz_list = gen_allocator->first_bucket_size();
13709         for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13710         {
13711             if ((real_size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13712             {
13713                 uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13714                 uint8_t* prev_free_item = 0;
13715                 while (free_list != 0)
13716                 {
13717                     dprintf (3, ("considering free list %Ix", (size_t)free_list));
13718
13719                     size_t free_list_size = unused_array_size (free_list);
13720
13721                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
13722                                     old_loc, USE_PADDING_TAIL | pad_in_front))
13723                     {
13724                         dprintf (4, ("F:%Ix-%Id",
13725                                      (size_t)free_list, free_list_size));
13726
13727                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
13728                         generation_free_list_space (gen) -= free_list_size;
13729                         remove_gen_free (gen->gen_num, free_list_size);
13730
13731                         adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
13732                         goto finished;
13733                     }
13734                     // We do first fit on bucket 0 because we are not guaranteed to find a fit there.
13735                     else if (discard_p || (a_l_idx == 0))
13736                     {
13737                         dprintf (3, ("couldn't use this free area, discarding"));
13738                         generation_free_obj_space (gen) += free_list_size;
13739
13740                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
13741                         generation_free_list_space (gen) -= free_list_size;
13742                         remove_gen_free (gen->gen_num, free_list_size);
13743                     }
13744                     else
13745                     {
13746                         prev_free_item = free_list;
13747                     }
13748                     free_list = free_list_slot (free_list); 
13749                 }
13750             }
13751             sz_list = sz_list * 2;
13752         }
13753         //go back to the beginning of the segment list 
13754         generation_allocate_end_seg_p (gen) = TRUE;
13755         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
13756         if (seg != generation_allocation_segment (gen))
13757         {
13758             leave_allocation_segment (gen);
13759             generation_allocation_segment (gen) = seg;
13760         }
13761         while (seg != ephemeral_heap_segment)
13762         {
13763             if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13764                            heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
13765             {
13766                 dprintf (3, ("using what's left in committed"));
13767                 adjust_limit (heap_segment_plan_allocated (seg),
13768                               heap_segment_committed (seg) -
13769                               heap_segment_plan_allocated (seg),
13770                               gen, from_gen_number+1);
13771                 // dformat (t, 3, "Expanding segment allocation");
13772                 heap_segment_plan_allocated (seg) =
13773                     heap_segment_committed (seg);
13774                 goto finished;
13775             }
13776             else
13777             {
13778                 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13779                                 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13780                     grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
13781                 {
13782                     dprintf (3, ("using what's left in reserved"));
13783                     adjust_limit (heap_segment_plan_allocated (seg),
13784                                   heap_segment_committed (seg) -
13785                                   heap_segment_plan_allocated (seg),
13786                                   gen, from_gen_number+1);
13787                     heap_segment_plan_allocated (seg) =
13788                         heap_segment_committed (seg);
13789
13790                     goto finished;
13791                 }
13792                 else
13793                 {
13794                     leave_allocation_segment (gen);
13795                     heap_segment*   next_seg = heap_segment_next_rw (seg);
13796                     if (next_seg)
13797                     {
13798                         dprintf (3, ("getting next segment"));
13799                         generation_allocation_segment (gen) = next_seg;
13800                         generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13801                         generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13802                     }
13803                     else
13804                     {
13805                         size = 0;
13806                         goto finished;
13807                     }
13808                 }
13809             }
13810             seg = generation_allocation_segment (gen);
13811         }
13812         //No need to fix the last region. Will be done later
13813         size = 0;
13814         goto finished;
13815     }
13816     finished:
13817     if (0 == size)
13818     {
13819         return 0;
13820     }
13821     else
13822     {
13823         uint8_t*  result = generation_allocation_pointer (gen);
13824         size_t pad = 0;
13825
13826 #ifdef SHORT_PLUGS
13827         if ((pad_in_front & USE_PADDING_FRONT) &&
13828             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13829              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13830         {
13831             pad = Align (min_obj_size);
13832             set_plug_padded (old_loc);
13833         }
13834 #endif //SHORT_PLUGS
13835
13836 #ifdef FEATURE_STRUCTALIGN
13837         _ASSERTE(!old_loc || alignmentOffset != 0);
13838         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13839         if (old_loc != 0)
13840         {
13841             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13842             set_node_aligninfo (old_loc, requiredAlignment, pad1);
13843             pad += pad1;
13844         }
13845 #else // FEATURE_STRUCTALIGN
13846         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13847         {
13848             pad += switch_alignment_size (is_plug_padded (old_loc));
13849             set_node_realigned (old_loc);
13850             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13851                          (size_t)old_loc, (size_t)(result+pad)));
13852             assert (same_large_alignment_p (result + pad, old_loc));
13853         }
13854 #endif // FEATURE_STRUCTALIGN
13855         dprintf (3, ("Allocate %Id bytes", size));
13856
13857         if ((old_loc == 0) || (pad != 0))
13858         {
13859             //allocating a non plug or a gap, so reset the start region
13860             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13861         }
13862
13863         generation_allocation_pointer (gen) += size + pad;
13864         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13865         if (generation_allocate_end_seg_p (gen))
13866         {
13867             generation_end_seg_allocated (gen) += size;
13868         }
13869         else
13870         {
13871             generation_free_list_allocated (gen) += size;
13872         }
13873         generation_allocation_size (gen) += size;
13874
13875         dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix", 
13876             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13877             generation_allocation_context_start_region (gen)));
13878
13879         return result + pad;;
13880     }
13881 }
13882
13883 void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
13884 {
13885     //make sure that every generation has a planned allocation start
13886     int  gen_number = max_generation - 1;
13887     while (gen_number>= 0)
13888     {
13889         generation* gen = generation_of (gen_number);
13890         if (0 == generation_plan_allocation_start (gen))
13891         {
13892             realloc_plan_generation_start (gen, consing_gen);
13893
13894             assert (generation_plan_allocation_start (gen));
13895         }
13896         gen_number--;
13897     }
13898
13899     // now we know the planned allocation size
13900     size_t  size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
13901     heap_segment* seg = generation_allocation_segment (consing_gen);
13902     if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
13903     {
13904         if (size != 0)
13905         {
13906             heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
13907         }
13908     }
13909     else
13910     {
13911         assert (settings.condemned_generation == max_generation);
13912         uint8_t* first_address = generation_allocation_limit (consing_gen);
13913         //look through the pinned plugs for relevant ones.
13914         //Look for the right pinned plug to start from.
13915         size_t mi = 0;
13916         mark* m = 0;
13917         while (mi != mark_stack_tos)
13918         {
13919             m = pinned_plug_of (mi);
13920             if ((pinned_plug (m) == first_address))
13921                 break;
13922             else
13923                 mi++;
13924         }
13925         assert (mi != mark_stack_tos);
13926         pinned_len (m) = size;
13927     }
13928 }
13929
13930 //tododefrag optimize for new segment (plan_allocated == mem)
13931 uint8_t* gc_heap::allocate_in_expanded_heap (generation* gen,
13932                                           size_t size,
13933                                           BOOL& adjacentp,
13934                                           uint8_t* old_loc,
13935 #ifdef SHORT_PLUGS
13936                                           BOOL set_padding_on_saved_p,
13937                                           mark* pinned_plug_entry,
13938 #endif //SHORT_PLUGS
13939                                           BOOL consider_bestfit,
13940                                           int active_new_gen_number
13941                                           REQD_ALIGN_AND_OFFSET_DCL)
13942 {
13943     UNREFERENCED_PARAMETER(active_new_gen_number);
13944     dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
13945
13946     size = Align (size);
13947     assert (size >= Align (min_obj_size));
13948     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
13949
13950     if (consider_bestfit && use_bestfit)
13951     {
13952         assert (bestfit_seg);
13953         dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id", 
13954                     old_loc, size));
13955         return bestfit_seg->fit (old_loc, 
13956 #ifdef SHORT_PLUGS
13957                                  set_padding_on_saved_p,
13958                                  pinned_plug_entry,
13959 #endif //SHORT_PLUGS
13960                                  size REQD_ALIGN_AND_OFFSET_ARG);
13961     }
13962
13963     heap_segment* seg = generation_allocation_segment (gen);
13964
13965     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13966                        generation_allocation_limit (gen), old_loc,
13967                        ((generation_allocation_limit (gen) !=
13968                           heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
13969     {
13970         dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
13971             generation_allocation_limit (gen)));
13972
13973         adjacentp = FALSE;
13974         uint8_t* first_address = (generation_allocation_limit (gen) ?
13975                                generation_allocation_limit (gen) :
13976                                heap_segment_mem (seg));
13977         assert (in_range_for_segment (first_address, seg));
13978
13979         uint8_t* end_address   = heap_segment_reserved (seg);
13980
13981         dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
13982             first_address, generation_allocation_limit (gen), end_address));
13983
13984         size_t mi = 0;
13985         mark* m = 0;
13986
13987         if (heap_segment_allocated (seg) != heap_segment_mem (seg))
13988         {
13989             assert (settings.condemned_generation == max_generation);
13990             //look through the pinned plugs for relevant ones.
13991             //Look for the right pinned plug to start from.
13992             while (mi != mark_stack_tos)
13993             {
13994                 m = pinned_plug_of (mi);
13995                 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
13996                 {
13997                     dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
13998                     break;
13999                 }
14000                 else
14001                     mi++;
14002             }
14003             if (mi != mark_stack_tos)
14004             {
14005                 //fix old free list.
14006                 size_t  hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
14007                 {
14008                     dprintf(3,("gc filling up hole"));
14009                     ptrdiff_t mi1 = (ptrdiff_t)mi;
14010                     while ((mi1 >= 0) &&
14011                            (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
14012                     {
14013                         dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
14014                         mi1--;
14015                     }
14016                     if (mi1 >= 0)
14017                     {
14018                         size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
14019                         pinned_len (pinned_plug_of(mi1)) = hsize;
14020                         dprintf (3, ("changing %Ix len %Ix->%Ix", 
14021                             pinned_plug (pinned_plug_of(mi1)), 
14022                             saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
14023                     }
14024                 }
14025             }
14026         }
14027         else
14028         {
14029             assert (generation_allocation_limit (gen) ==
14030                     generation_allocation_pointer (gen));
14031             mi = mark_stack_tos;
14032         }
14033
14034         while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
14035         {
14036             size_t len = pinned_len (m);
14037             uint8_t*  free_list = (pinned_plug (m) - len);
14038             dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)", 
14039                 free_list, (free_list + len), len));
14040             if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
14041             {
14042                 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
14043                             (size_t)free_list, len));
14044                 {
14045                     generation_allocation_pointer (gen) = free_list;
14046                     generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14047                     generation_allocation_limit (gen) = (free_list + len);
14048                 }
14049                 goto allocate_in_free;
14050             }
14051             mi++;
14052             m = pinned_plug_of (mi);
14053         }
14054
14055         //switch to the end of the segment.
14056         generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
14057         generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14058         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14059         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14060         dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)", 
14061             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14062             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
14063
14064         if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14065                          generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
14066         {
14067             dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
14068                 generation_allocation_limit (gen)));
14069             assert (!"Can't allocate if no free space");
14070             return 0;
14071         }
14072     }
14073     else
14074     {
14075         adjacentp = TRUE;
14076     }
14077
14078 allocate_in_free:
14079     {
14080         uint8_t*  result = generation_allocation_pointer (gen);
14081         size_t pad = 0;
14082
14083 #ifdef SHORT_PLUGS
14084         if ((pad_in_front & USE_PADDING_FRONT) &&
14085             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14086              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14087
14088         {
14089             pad = Align (min_obj_size);
14090             set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
14091         }
14092 #endif //SHORT_PLUGS
14093
14094 #ifdef FEATURE_STRUCTALIGN
14095         _ASSERTE(!old_loc || alignmentOffset != 0);
14096         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14097         if (old_loc != 0)
14098         {
14099             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14100             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14101             pad += pad1;
14102             adjacentp = FALSE;
14103         }
14104 #else // FEATURE_STRUCTALIGN
14105         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14106         {
14107             pad += switch_alignment_size (is_plug_padded (old_loc));
14108             set_node_realigned (old_loc);
14109             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14110                          (size_t)old_loc, (size_t)(result+pad)));
14111             assert (same_large_alignment_p (result + pad, old_loc));
14112             adjacentp = FALSE;
14113         }
14114 #endif // FEATURE_STRUCTALIGN
14115
14116         if ((old_loc == 0) || (pad != 0))
14117         {
14118             //allocating a non plug or a gap, so reset the start region
14119             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14120         }
14121
14122         generation_allocation_pointer (gen) += size + pad;
14123         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14124         dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
14125
14126         dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix", 
14127             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14128             generation_allocation_context_start_region (gen)));
14129
14130         return result + pad;
14131     }
14132 }
14133
14134 generation*  gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
14135 {
14136     heap_segment* seg = generation_allocation_segment (consing_gen);
14137     if (seg != ephemeral_heap_segment)
14138     {
14139         assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
14140         assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
14141
14142         //fix the allocated size of the segment.
14143         heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14144
14145         generation* new_consing_gen = generation_of (max_generation - 1);
14146         generation_allocation_pointer (new_consing_gen) =
14147                 heap_segment_mem (ephemeral_heap_segment);
14148         generation_allocation_limit (new_consing_gen) =
14149             generation_allocation_pointer (new_consing_gen);
14150         generation_allocation_context_start_region (new_consing_gen) = 
14151             generation_allocation_pointer (new_consing_gen);
14152         generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
14153
14154         return new_consing_gen;
14155     }
14156     else
14157         return consing_gen;
14158 }
14159
14160 uint8_t* gc_heap::allocate_in_condemned_generations (generation* gen,
14161                                                   size_t size,
14162                                                   int from_gen_number,
14163 #ifdef SHORT_PLUGS
14164                                                   BOOL* convert_to_pinned_p,
14165                                                   uint8_t* next_pinned_plug,
14166                                                   heap_segment* current_seg,
14167 #endif //SHORT_PLUGS
14168                                                   uint8_t* old_loc
14169                                                   REQD_ALIGN_AND_OFFSET_DCL)
14170 {
14171     // Make sure that the youngest generation gap hasn't been allocated
14172     if (settings.promotion)
14173     {
14174         assert (generation_plan_allocation_start (youngest_generation) == 0);
14175     }
14176
14177     size = Align (size);
14178     assert (size >= Align (min_obj_size));
14179     int to_gen_number = from_gen_number;
14180     if (from_gen_number != (int)max_generation)
14181     {
14182         to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
14183     }
14184
14185     dprintf (3, ("aic gen%d: s: %Id, %d->%d, %Ix->%Ix", gen->gen_num, size, from_gen_number, 
14186           to_gen_number, generation_allocation_pointer(gen), generation_allocation_limit(gen)));
14187
14188     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
14189
14190     if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
14191     {
14192         generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14193         generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14194     }
14195 retry:
14196     {
14197         heap_segment* seg = generation_allocation_segment (gen);
14198         if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14199                            generation_allocation_limit (gen), old_loc,
14200                            ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
14201         {
14202             if ((! (pinned_plug_que_empty_p()) &&
14203                  (generation_allocation_limit (gen) ==
14204                   pinned_plug (oldest_pin()))))
14205             {
14206                 size_t entry = deque_pinned_plug();
14207                 mark* pinned_plug_entry = pinned_plug_of (entry);
14208                 size_t len = pinned_len (pinned_plug_entry);
14209                 uint8_t* plug = pinned_plug (pinned_plug_entry);
14210                 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
14211
14212 #ifdef FREE_USAGE_STATS
14213                 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
14214                 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id", 
14215                     generation_allocated_since_last_pin (gen), 
14216                     plug,
14217                     generation_allocated_in_pinned_free (gen)));
14218                 generation_allocated_since_last_pin (gen) = 0;
14219
14220                 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
14221 #endif //FREE_USAGE_STATS
14222
14223                 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix", 
14224                     mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
14225
14226                 assert(mark_stack_array[entry].len == 0 ||
14227                        mark_stack_array[entry].len >= Align(min_obj_size));
14228                 generation_allocation_pointer (gen) = plug + len;
14229                 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14230                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14231                 set_allocator_next_pin (gen);
14232
14233                 //Add the size of the pinned plug to the right pinned allocations
14234                 //find out which gen this pinned plug came from 
14235                 int frgn = object_gennum (plug);
14236                 if ((frgn != (int)max_generation) && settings.promotion)
14237                 {
14238                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
14239                     int togn = object_gennum_plan (plug);
14240                     if (frgn < togn)
14241                     {
14242                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
14243                     }
14244                 }
14245                 goto retry;
14246             }
14247             
14248             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
14249             {
14250                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14251                 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
14252             }
14253             else
14254             {
14255                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
14256                 {
14257                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14258                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14259                     dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
14260                 }
14261                 else
14262                 {
14263 #ifndef RESPECT_LARGE_ALIGNMENT
14264                     assert (gen != youngest_generation);
14265 #endif //RESPECT_LARGE_ALIGNMENT
14266
14267                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14268                                     heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14269                         (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
14270                                             size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
14271                     {
14272                         dprintf (3, ("Expanded segment allocation by committing more memory"));
14273                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14274                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14275                     }
14276                     else
14277                     {
14278                         heap_segment*   next_seg = heap_segment_next (seg);
14279                         assert (generation_allocation_pointer (gen)>=
14280                                 heap_segment_mem (seg));
14281                         // Verify that all pinned plugs for this segment are consumed
14282                         if (!pinned_plug_que_empty_p() &&
14283                             ((pinned_plug (oldest_pin()) <
14284                               heap_segment_allocated (seg)) &&
14285                              (pinned_plug (oldest_pin()) >=
14286                               generation_allocation_pointer (gen))))
14287                         {
14288                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
14289                                          pinned_plug (oldest_pin())));
14290                             FATAL_GC_ERROR();
14291                         }
14292                         assert (generation_allocation_pointer (gen)>=
14293                                 heap_segment_mem (seg));
14294                         assert (generation_allocation_pointer (gen)<=
14295                                 heap_segment_committed (seg));
14296                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
14297
14298                         if (next_seg)
14299                         {
14300                             generation_allocation_segment (gen) = next_seg;
14301                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14302                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14303                             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14304                         }
14305                         else
14306                         {
14307                             return 0; //should only happen during allocation of generation 0 gap
14308                             // in that case we are going to grow the heap anyway
14309                         }
14310                     }
14311                 }
14312             }
14313             set_allocator_next_pin (gen);
14314
14315             goto retry;
14316         }
14317     }
14318
14319     {
14320         assert (generation_allocation_pointer (gen)>=
14321                 heap_segment_mem (generation_allocation_segment (gen)));
14322         uint8_t* result = generation_allocation_pointer (gen);
14323         size_t pad = 0;
14324 #ifdef SHORT_PLUGS
14325         if ((pad_in_front & USE_PADDING_FRONT) &&
14326             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14327              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14328         {
14329             ptrdiff_t dist = old_loc - result;
14330             if (dist == 0)
14331             {
14332                 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
14333                 pad = 0;
14334             }
14335             else
14336             {
14337                 if ((dist > 0) && (dist < (ptrdiff_t)Align (min_obj_size)))
14338                 {
14339                     dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
14340                     FATAL_GC_ERROR();
14341                 }
14342
14343                 pad = Align (min_obj_size);
14344                 set_plug_padded (old_loc);
14345             }
14346         }
14347 #endif //SHORT_PLUGS
14348 #ifdef FEATURE_STRUCTALIGN
14349         _ASSERTE(!old_loc || alignmentOffset != 0);
14350         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14351         if ((old_loc != 0))
14352         {
14353             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14354             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14355             pad += pad1;
14356         }
14357 #else // FEATURE_STRUCTALIGN
14358         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14359         {
14360             pad += switch_alignment_size (is_plug_padded (old_loc));
14361             set_node_realigned(old_loc);
14362             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14363                          (size_t)old_loc, (size_t)(result+pad)));
14364             assert (same_large_alignment_p (result + pad, old_loc));
14365         }
14366 #endif // FEATURE_STRUCTALIGN
14367
14368 #ifdef SHORT_PLUGS
14369         if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
14370         {
14371             assert (old_loc != 0);
14372             ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
14373             assert (dist_to_next_pin >= 0);
14374
14375             if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
14376             {
14377                 dprintf (3, ("%Ix->(%Ix,%Ix),%Ix(%Ix)(%Ix),NP->PP", 
14378                     old_loc, 
14379                     generation_allocation_pointer (gen),
14380                     generation_allocation_limit (gen),
14381                     next_pinned_plug,
14382                     size, 
14383                     dist_to_next_pin));
14384                 clear_plug_padded (old_loc);
14385                 pad = 0;
14386                 *convert_to_pinned_p = TRUE;
14387                 record_interesting_data_point (idp_converted_pin);
14388
14389                 return 0;
14390             }
14391         }
14392 #endif //SHORT_PLUGS
14393
14394         if ((old_loc == 0) || (pad != 0))
14395         {
14396             //allocating a non plug or a gap, so reset the start region
14397             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14398         }
14399
14400         generation_allocation_pointer (gen) += size + pad;
14401         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14402
14403 #ifdef FREE_USAGE_STATS
14404         generation_allocated_since_last_pin (gen) += size;
14405 #endif //FREE_USAGE_STATS
14406
14407         dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix", 
14408             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14409             generation_allocation_context_start_region (gen)));
14410
14411         assert (result + pad);
14412         return result + pad;
14413     }
14414 }
14415
14416 inline int power (int x, int y)
14417 {
14418     int z = 1;
14419     for (int i = 0; i < y; i++)
14420     {
14421         z = z*x;
14422     }
14423     return z;
14424 }
14425
14426 int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation, 
14427                                            int n_initial,
14428                                            BOOL* blocking_collection_p
14429                                            STRESS_HEAP_ARG(int n_original))
14430 {
14431     int n = n_initial;
14432 #ifdef MULTIPLE_HEAPS
14433     BOOL blocking_p = *blocking_collection_p;
14434     if (!blocking_p)
14435     {
14436         for (int i = 0; i < n_heaps; i++)
14437         {
14438             if (g_heaps[i]->last_gc_before_oom)
14439             {
14440                 dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
14441                 *blocking_collection_p = TRUE;
14442                 break;
14443             }
14444         }
14445     }
14446 #endif //MULTIPLE_HEAPS
14447
14448     if (should_evaluate_elevation && (n == max_generation))
14449     {
14450         dprintf (GTC_LOG, ("lock: %d(%d)", 
14451             (settings.should_lock_elevation ? 1 : 0), 
14452             settings.elevation_locked_count));
14453
14454         if (settings.should_lock_elevation)
14455         {
14456             settings.elevation_locked_count++;
14457             if (settings.elevation_locked_count == 6)
14458             {
14459                 settings.elevation_locked_count = 0;
14460             }
14461             else
14462             {
14463                 n = max_generation - 1;
14464                 settings.elevation_reduced = TRUE;
14465             }
14466         }
14467         else
14468         {
14469             settings.elevation_locked_count = 0;
14470         }
14471     }
14472     else
14473     {
14474         settings.should_lock_elevation = FALSE;
14475         settings.elevation_locked_count = 0;
14476     }
14477
14478 #ifdef STRESS_HEAP
14479 #ifdef BACKGROUND_GC
14480     // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14481     // generations to be collected,
14482
14483     // [LOCALGC TODO] STRESS_HEAP is not defined for a standalone GC so there are multiple
14484     // things that need to be fixed in this code block.
14485     if (n_original != max_generation &&
14486         g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
14487     {
14488 #ifndef FEATURE_REDHAWK
14489         // for the GC stress mix mode throttle down gen2 collections
14490         if (g_pConfig->IsGCStressMix())
14491         {
14492             size_t current_gc_count = 0;
14493
14494 #ifdef MULTIPLE_HEAPS
14495             current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
14496 #else
14497             current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
14498 #endif //MULTIPLE_HEAPS
14499             // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
14500             if ((current_gc_count % 10) == 0)
14501             {
14502                 n = max_generation;
14503             }
14504         }
14505         // for traditional GC stress
14506         else
14507 #endif // !FEATURE_REDHAWK
14508         if (*blocking_collection_p)
14509         {
14510             // We call StressHeap() a lot for Concurrent GC Stress. However,
14511             // if we can not do a concurrent collection, no need to stress anymore.
14512             // @TODO: Enable stress when the memory pressure goes down again
14513             GCStressPolicy::GlobalDisable();
14514         }
14515         else
14516         {
14517             n = max_generation;
14518         }
14519     }
14520 #endif //BACKGROUND_GC
14521 #endif //STRESS_HEAP
14522
14523     return n;
14524 }
14525
14526 inline
14527 size_t get_survived_size (gc_history_per_heap* hist)
14528 {
14529     size_t surv_size = 0;
14530     gc_generation_data* gen_data;
14531
14532     for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14533     {
14534         gen_data = &(hist->gen_data[gen_number]); 
14535         surv_size += (gen_data->size_after - 
14536                       gen_data->free_list_space_after - 
14537                       gen_data->free_obj_space_after);
14538     }
14539
14540     return surv_size;
14541 }
14542
14543 size_t gc_heap::get_total_survived_size()
14544 {
14545     size_t total_surv_size = 0;
14546 #ifdef MULTIPLE_HEAPS
14547     for (int i = 0; i < gc_heap::n_heaps; i++)
14548     {
14549         gc_heap* hp = gc_heap::g_heaps[i];
14550         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
14551         total_surv_size += get_survived_size (current_gc_data_per_heap);
14552     }
14553 #else
14554     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14555     total_surv_size = get_survived_size (current_gc_data_per_heap);
14556 #endif //MULTIPLE_HEAPS
14557     return total_surv_size;
14558 }
14559
14560 // Gets what's allocated on both SOH and LOH that hasn't been collected.
14561 size_t gc_heap::get_current_allocated()
14562 {
14563     dynamic_data* dd = dynamic_data_of (0);
14564     size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
14565     dd = dynamic_data_of (max_generation + 1);
14566     current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
14567
14568     return current_alloc;
14569 }
14570
14571 size_t gc_heap::get_total_allocated()
14572 {
14573     size_t total_current_allocated = 0;
14574 #ifdef MULTIPLE_HEAPS
14575     for (int i = 0; i < gc_heap::n_heaps; i++)
14576     {
14577         gc_heap* hp = gc_heap::g_heaps[i];
14578         total_current_allocated += hp->get_current_allocated();
14579     }
14580 #else
14581     total_current_allocated = get_current_allocated();
14582 #endif //MULTIPLE_HEAPS
14583     return total_current_allocated;
14584 }
14585
14586 size_t gc_heap::current_generation_size (int gen_number)
14587 {
14588     dynamic_data* dd = dynamic_data_of (gen_number);
14589     size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
14590                         - dd_new_allocation (dd));
14591
14592     return gen_size;
14593 }
14594
14595 #ifdef _PREFAST_
14596 #pragma warning(push)
14597 #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
14598 #endif //_PREFAST_
14599
14600 /*
14601     This is called by when we are actually doing a GC, or when we are just checking whether
14602     we would do a full blocking GC, in which case check_only_p is TRUE.
14603
14604     The difference between calling this with check_only_p TRUE and FALSE is that when it's
14605     TRUE: 
14606             settings.reason is ignored
14607             budgets are not checked (since they are checked before this is called)
14608             it doesn't change anything non local like generation_skip_ratio
14609 */
14610 int gc_heap::generation_to_condemn (int n_initial, 
14611                                     BOOL* blocking_collection_p, 
14612                                     BOOL* elevation_requested_p,
14613                                     BOOL check_only_p)
14614 {
14615     gc_mechanisms temp_settings = settings;
14616     gen_to_condemn_tuning temp_condemn_reasons;
14617     gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
14618     gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
14619     if (!check_only_p)
14620     {
14621         if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
14622         {
14623             assert (n_initial >= 1);
14624         }
14625
14626         assert (settings.reason != reason_empty);
14627     }
14628
14629     local_condemn_reasons->init();
14630
14631     int n = n_initial;
14632     int n_alloc = n;
14633     if (heap_number == 0)
14634     {
14635         dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
14636     }
14637     int i = 0;
14638     int temp_gen = 0;
14639     BOOL low_memory_detected = g_low_memory_status;
14640     uint32_t memory_load = 0;
14641     uint64_t available_physical = 0;
14642     uint64_t available_page_file = 0;
14643     BOOL check_memory = FALSE;
14644     BOOL high_fragmentation  = FALSE;
14645     BOOL v_high_memory_load  = FALSE;
14646     BOOL high_memory_load    = FALSE;
14647     BOOL low_ephemeral_space = FALSE;
14648     BOOL evaluate_elevation  = TRUE;
14649     *elevation_requested_p   = FALSE;
14650     *blocking_collection_p   = FALSE;
14651
14652     BOOL check_max_gen_alloc = TRUE;
14653
14654 #ifdef STRESS_HEAP
14655     int orig_gen = n;
14656 #endif //STRESS_HEAP
14657
14658     if (!check_only_p)
14659     {
14660         dd_fragmentation (dynamic_data_of (0)) = 
14661             generation_free_list_space (youngest_generation) + 
14662             generation_free_obj_space (youngest_generation);
14663
14664         dd_fragmentation (dynamic_data_of (max_generation + 1)) = 
14665             generation_free_list_space (large_object_generation) + 
14666             generation_free_obj_space (large_object_generation);
14667
14668         //save new_allocation
14669         for (i = 0; i <= max_generation+1; i++)
14670         {
14671             dynamic_data* dd = dynamic_data_of (i);
14672             dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)", 
14673                             heap_number, i,
14674                             dd_new_allocation (dd),
14675                             dd_desired_allocation (dd)));
14676             dd_gc_new_allocation (dd) = dd_new_allocation (dd);
14677         }
14678
14679         local_condemn_reasons->set_gen (gen_initial, n);
14680         temp_gen = n;
14681
14682 #ifdef BACKGROUND_GC
14683         if (recursive_gc_sync::background_running_p())
14684         {
14685             dprintf (GTC_LOG, ("bgc in prog, 1"));
14686             check_max_gen_alloc = FALSE;
14687         }
14688 #endif //BACKGROUND_GC
14689
14690         if (check_max_gen_alloc)
14691         {
14692             //figure out if large objects need to be collected.
14693             if (get_new_allocation (max_generation+1) <= 0)
14694             {
14695                 n = max_generation;
14696                 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14697             }
14698         }
14699
14700         //figure out which generation ran out of allocation
14701         for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
14702         {
14703             if (get_new_allocation (i) <= 0)
14704             {
14705                 n = i;
14706             }
14707             else
14708                 break;
14709         }
14710     }
14711
14712     if (n > temp_gen)
14713     {
14714         local_condemn_reasons->set_gen (gen_alloc_budget, n);
14715     }
14716
14717     dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
14718
14719     n_alloc = n;
14720
14721 #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
14722     //time based tuning
14723     // if enough time has elapsed since the last gc
14724     // and the number of gc is too low (1/10 of lower gen) then collect
14725     // This should also be enabled if we have memory concerns
14726     int n_time_max = max_generation;
14727
14728     if (!check_only_p)
14729     {
14730         if (recursive_gc_sync::background_running_p())
14731         {
14732             n_time_max = max_generation - 1;
14733         }
14734     }
14735
14736     if ((local_settings->pause_mode == pause_interactive) ||
14737         (local_settings->pause_mode == pause_sustained_low_latency))
14738     {
14739         dynamic_data* dd0 = dynamic_data_of (0);
14740         size_t now = GetHighPrecisionTimeStamp();
14741         temp_gen = n;
14742         for (i = (temp_gen+1); i <= n_time_max; i++)
14743         {
14744             dynamic_data* dd = dynamic_data_of (i);
14745             if ((now > dd_time_clock(dd) + dd_time_clock_interval(dd)) &&
14746                 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + dd_gc_clock_interval(dd))) &&
14747                 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
14748             {
14749                 n = min (i, n_time_max);
14750                 dprintf (GTC_LOG, ("time %d", n));
14751             }
14752         }
14753         if (n > temp_gen)
14754         {
14755             local_condemn_reasons->set_gen (gen_time_tuning, n);
14756         }
14757     }
14758
14759     if (n != n_alloc)
14760     {
14761         dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
14762     }
14763 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
14764
14765     if (n < (max_generation - 1))
14766     {
14767         if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
14768         {
14769             n = max (n, max_generation - 1);
14770             local_settings->promotion = TRUE;
14771             dprintf (GTC_LOG, ("h%d: skip %d, c %d",
14772                         heap_number, generation_skip_ratio, n));
14773             local_condemn_reasons->set_condition (gen_low_card_p);
14774         }
14775     }
14776
14777     if (!check_only_p)
14778     {
14779         generation_skip_ratio = 100;
14780     }
14781
14782     if (dt_low_ephemeral_space_p (check_only_p ? 
14783                                   tuning_deciding_full_gc : 
14784                                   tuning_deciding_condemned_gen))
14785     {
14786         low_ephemeral_space = TRUE;
14787
14788         n = max (n, max_generation - 1);
14789         local_condemn_reasons->set_condition (gen_low_ephemeral_p);
14790         dprintf (GTC_LOG, ("h%d: low eph", heap_number));
14791
14792 #ifdef BACKGROUND_GC
14793         if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
14794 #endif //BACKGROUND_GC
14795         {
14796             //It is better to defragment first if we are running out of space for
14797             //the ephemeral generation but we have enough fragmentation to make up for it
14798             //in the non ephemeral generation. Essentially we are trading a gen2 for 
14799             // having to expand heap in ephemeral collections.
14800             if (dt_high_frag_p (tuning_deciding_condemned_gen, 
14801                                 max_generation - 1, 
14802                                 TRUE))
14803             {
14804                 high_fragmentation = TRUE;
14805                 local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
14806                 dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
14807             }
14808         }
14809     }
14810
14811     //figure out which ephemeral generation is too fragramented
14812     temp_gen = n;
14813     for (i = n+1; i < max_generation; i++)
14814     {
14815         if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
14816         {
14817             dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
14818             n = i;
14819         }
14820         else
14821             break;
14822     }
14823
14824     if (low_ephemeral_space)
14825     {
14826         //enable promotion
14827         local_settings->promotion = TRUE;
14828     }
14829
14830     if (n > temp_gen)
14831     {
14832         local_condemn_reasons->set_condition (gen_eph_high_frag_p);
14833     }
14834
14835     if (!check_only_p)
14836     {
14837         if (settings.pause_mode == pause_low_latency)
14838         {
14839             if (!is_induced (settings.reason))
14840             {
14841                 n = min (n, max_generation - 1);
14842                 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
14843                 evaluate_elevation = FALSE;
14844                 goto exit;
14845             }
14846         }
14847     }
14848
14849     // It's hard to catch when we get to the point that the memory load is so high
14850     // we get an induced GC from the finalizer thread so we are checking the memory load
14851     // for every gen0 GC.
14852     check_memory = (check_only_p ? 
14853                     (n >= 0) : 
14854                     ((n >= 1) || low_memory_detected));
14855
14856     if (check_memory)
14857     {
14858         //find out if we are short on memory
14859         get_memory_info (&memory_load, &available_physical, &available_page_file);
14860         if (heap_number == 0)
14861         {
14862             dprintf (GTC_LOG, ("ml: %d", memory_load));
14863         }
14864         
14865         // Need to get it early enough for all heaps to use.
14866         entry_available_physical_mem = available_physical;
14867         local_settings->entry_memory_load = memory_load;
14868
14869         // @TODO: Force compaction more often under GCSTRESS
14870         if (memory_load >= high_memory_load_th || low_memory_detected)
14871         {
14872 #ifdef SIMPLE_DPRINTF
14873             // stress log can't handle any parameter that's bigger than a void*.
14874             if (heap_number == 0)
14875             {
14876                 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d", total_physical_mem, available_physical));
14877             }
14878 #endif //SIMPLE_DPRINTF
14879
14880             high_memory_load = TRUE;
14881
14882             if (memory_load >= v_high_memory_load_th || low_memory_detected)
14883             {
14884                 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
14885                 // gen1/gen0 may take a lot more memory than gen2.
14886                 if (!high_fragmentation)
14887                 {
14888                     high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation);
14889                 }
14890                 v_high_memory_load = TRUE;
14891             }
14892             else
14893             {
14894                 if (!high_fragmentation)
14895                 {
14896                     high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, available_physical);
14897                 }
14898             }
14899
14900             if (high_fragmentation)
14901             {
14902                 if (high_memory_load)
14903                 {
14904                     local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
14905                 }
14906                 else if (v_high_memory_load)
14907                 {
14908                     local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
14909                 }
14910             }
14911         }
14912     }
14913
14914     dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
14915                  heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
14916                  high_fragmentation));
14917
14918     if (should_expand_in_full_gc)
14919     {
14920         dprintf (GTC_LOG, ("h%d: expand_in_full - BLOCK", heap_number));
14921         *blocking_collection_p = TRUE;
14922         if (!check_only_p)
14923         {
14924             should_expand_in_full_gc = FALSE;
14925         }
14926         evaluate_elevation = FALSE;
14927         n = max_generation;
14928         local_condemn_reasons->set_condition (gen_expand_fullgc_p);
14929     }
14930
14931     if (last_gc_before_oom)
14932     {
14933         dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
14934         n = max_generation;
14935         *blocking_collection_p = TRUE;
14936         if ((local_settings->reason == reason_oos_loh) ||
14937             (local_settings->reason == reason_alloc_loh))
14938             evaluate_elevation = FALSE;
14939
14940         local_condemn_reasons->set_condition (gen_before_oom);
14941     }
14942
14943     if (!check_only_p)
14944     {
14945         if (is_induced_blocking (settings.reason) && 
14946             n_initial == max_generation
14947             IN_STRESS_HEAP( && !settings.stress_induced ))
14948         {
14949             if (heap_number == 0)
14950             {
14951                 dprintf (GTC_LOG, ("induced - BLOCK"));
14952             }
14953
14954             *blocking_collection_p = TRUE;
14955             local_condemn_reasons->set_condition (gen_induced_fullgc_p);
14956             evaluate_elevation = FALSE;
14957         }
14958
14959         if (settings.reason == reason_induced_noforce)
14960         {
14961             local_condemn_reasons->set_condition (gen_induced_noforce_p);
14962             evaluate_elevation = FALSE;
14963         }
14964     }
14965
14966     if (evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
14967     {
14968         *elevation_requested_p = TRUE;
14969 #ifdef BIT64
14970         // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
14971         if (high_memory_load || v_high_memory_load)
14972         {
14973             dynamic_data* dd_max = dynamic_data_of (max_generation);
14974             if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
14975             {
14976                 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)", 
14977                     dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
14978                 n = max_generation;
14979                 local_condemn_reasons->set_condition (gen_almost_max_alloc);
14980             }
14981         }
14982
14983         if (n <= max_generation)
14984         {
14985 #endif // BIT64
14986             if (high_fragmentation)
14987             {
14988                 //elevate to max_generation
14989                 n = max_generation;
14990                 dprintf (GTC_LOG, ("h%d: f full", heap_number));
14991
14992 #ifdef BACKGROUND_GC
14993                 if (high_memory_load || v_high_memory_load)
14994                 {
14995                     // For background GC we want to do blocking collections more eagerly because we don't
14996                     // want to get into the situation where the memory load becomes high while we are in
14997                     // a background GC and we'd have to wait for the background GC to finish to start
14998                     // a blocking collection (right now the implemenation doesn't handle converting 
14999                     // a background GC to a blocking collection midway.
15000                     dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
15001                     *blocking_collection_p = TRUE;
15002                 }
15003 #else
15004                 if (v_high_memory_load)
15005                 {
15006                     dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
15007                     *blocking_collection_p = TRUE;
15008                 }
15009 #endif //BACKGROUND_GC
15010             }
15011             else
15012             {
15013                 n = max (n, max_generation - 1);
15014                 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
15015             }
15016 #ifdef BIT64
15017         }
15018 #endif // BIT64
15019     }
15020
15021     if ((n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
15022     {
15023         dprintf (GTC_LOG, ("h%d: budget %d, check 2",
15024                       heap_number, n_alloc));
15025         if (get_new_allocation (max_generation) <= 0)
15026         {
15027             dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
15028             n = max_generation;
15029             local_condemn_reasons->set_condition (gen_max_gen1);
15030         }
15031     }
15032
15033     //figure out if max_generation is too fragmented -> blocking collection
15034     if (n == max_generation)
15035     {
15036         if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
15037         {
15038             dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
15039             local_condemn_reasons->set_condition (gen_max_high_frag_p);
15040             if (local_settings->pause_mode != pause_sustained_low_latency)
15041             {
15042                 *blocking_collection_p = TRUE;
15043             }
15044         }
15045     }
15046
15047 #ifdef BACKGROUND_GC
15048     if (n == max_generation)
15049     {
15050         if (heap_number == 0)
15051         {
15052             BOOL bgc_heap_too_small = TRUE;
15053             size_t gen2size = 0;
15054             size_t gen3size = 0;
15055 #ifdef MULTIPLE_HEAPS
15056             for (int i = 0; i < n_heaps; i++)
15057             {
15058                 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) || 
15059                     ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
15060                 {
15061                     bgc_heap_too_small = FALSE;
15062                     break;
15063                 }
15064             }
15065 #else //MULTIPLE_HEAPS
15066             if ((current_generation_size (max_generation) > bgc_min_per_heap) || 
15067                 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
15068             {
15069                 bgc_heap_too_small = FALSE;
15070             }            
15071 #endif //MULTIPLE_HEAPS
15072
15073             if (bgc_heap_too_small)
15074             {
15075                 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
15076
15077 #ifdef STRESS_HEAP
15078                 // do not turn stress-induced collections into blocking GCs
15079                 if (!settings.stress_induced)
15080 #endif //STRESS_HEAP
15081                 {
15082                     *blocking_collection_p = TRUE;
15083                 }
15084
15085                 local_condemn_reasons->set_condition (gen_gen2_too_small);
15086             }
15087         }
15088     }
15089 #endif //BACKGROUND_GC
15090
15091 exit:
15092     if (!check_only_p)
15093     {
15094 #ifdef STRESS_HEAP
15095 #ifdef BACKGROUND_GC
15096         // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
15097         // generations to be collected,
15098
15099         if (orig_gen != max_generation &&
15100             g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
15101         {
15102             *elevation_requested_p = FALSE;
15103         }
15104 #endif //BACKGROUND_GC
15105 #endif //STRESS_HEAP
15106
15107         if (check_memory)
15108         {
15109             fgm_result.available_pagefile_mb = (size_t)(available_page_file / (1024 * 1024));
15110         }
15111
15112         local_condemn_reasons->set_gen (gen_final_per_heap, n);
15113         get_gc_data_per_heap()->gen_to_condemn_reasons.init (local_condemn_reasons);
15114
15115 #ifdef DT_LOG
15116         local_condemn_reasons->print (heap_number);
15117 #endif //DT_LOG
15118
15119         if ((local_settings->reason == reason_oos_soh) || 
15120             (local_settings->reason == reason_oos_loh))
15121         {
15122             assert (n >= 1);
15123         }
15124     }
15125
15126     return n;
15127 }
15128
15129 #ifdef _PREFAST_
15130 #pragma warning(pop)
15131 #endif //_PREFAST_
15132
15133 inline
15134 size_t gc_heap::min_reclaim_fragmentation_threshold (uint32_t num_heaps)
15135 {
15136     // if the memory load is higher, the threshold we'd want to collect gets lower.
15137     size_t min_mem_based_on_available = 
15138         (500 - (settings.entry_memory_load - high_memory_load_th) * 40) * 1024 * 1024 / num_heaps;
15139     size_t ten_percent_size = (size_t)((float)generation_size (max_generation) * 0.10);
15140     uint64_t three_percent_mem = mem_one_percent * 3 / num_heaps;
15141
15142 #ifdef SIMPLE_DPRINTF
15143     dprintf (GTC_LOG, ("min av: %Id, 10%% gen2: %Id, 3%% mem: %I64d", 
15144         min_mem_based_on_available, ten_percent_size, three_percent_mem));
15145 #endif //SIMPLE_DPRINTF
15146     return (size_t)(min (min_mem_based_on_available, min (ten_percent_size, three_percent_mem)));
15147 }
15148
15149 inline
15150 uint64_t gc_heap::min_high_fragmentation_threshold(uint64_t available_mem, uint32_t num_heaps)
15151 {
15152     return min (available_mem, (256*1024*1024)) / num_heaps;
15153 }
15154
15155 enum {
15156 CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
15157 };
15158
15159
15160 #ifdef BACKGROUND_GC
15161 void gc_heap::init_background_gc ()
15162 {
15163     //reset the allocation so foreground gc can allocate into older (max_generation) generation
15164     generation* gen = generation_of (max_generation);
15165     generation_allocation_pointer (gen)= 0;
15166     generation_allocation_limit (gen) = 0;
15167     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
15168
15169     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
15170
15171     //reset the plan allocation for each segment
15172     for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
15173         seg = heap_segment_next_rw (seg))
15174     {
15175         heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
15176     }
15177
15178     if (heap_number == 0)
15179     {
15180         dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix", 
15181             heap_number,
15182             background_saved_lowest_address, 
15183             background_saved_highest_address));
15184     }
15185
15186     gc_lh_block_event.Reset();
15187 }
15188
15189 #endif //BACKGROUND_GC
15190
15191 inline
15192 void fire_drain_mark_list_event (size_t mark_list_objects)
15193 {
15194     FIRE_EVENT(BGCDrainMark, mark_list_objects);
15195 }
15196
15197 inline
15198 void fire_revisit_event (size_t dirtied_pages, 
15199                          size_t marked_objects,
15200                          BOOL large_objects_p)
15201 {
15202     FIRE_EVENT(BGCRevisit, dirtied_pages, marked_objects, large_objects_p);
15203 }
15204
15205 inline
15206 void fire_overflow_event (uint8_t* overflow_min,
15207                           uint8_t* overflow_max,
15208                           size_t marked_objects, 
15209                           int large_objects_p)
15210 {
15211     FIRE_EVENT(BGCOverflow, (uint64_t)overflow_min, (uint64_t)overflow_max, marked_objects, large_objects_p);
15212 }
15213
15214 void gc_heap::concurrent_print_time_delta (const char* msg)
15215 {
15216 #ifdef TRACE_GC
15217     size_t current_time = GetHighPrecisionTimeStamp();
15218     size_t elapsed_time = current_time - time_bgc_last;
15219     time_bgc_last = current_time;
15220
15221     dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
15222 #else
15223     UNREFERENCED_PARAMETER(msg);
15224 #endif //TRACE_GC
15225 }
15226
15227 void gc_heap::free_list_info (int gen_num, const char* msg)
15228 {
15229     UNREFERENCED_PARAMETER(gen_num);
15230 #if defined (BACKGROUND_GC) && defined (TRACE_GC)
15231     dprintf (3, ("h%d: %s", heap_number, msg));
15232     for (int i = 0; i <= (max_generation + 1); i++)
15233     {
15234         generation* gen = generation_of (i);
15235         if ((generation_allocation_size (gen) == 0) && 
15236             (generation_free_list_space (gen) == 0) && 
15237             (generation_free_obj_space (gen) == 0))
15238         {
15239             // don't print if everything is 0.
15240         }
15241         else
15242         {
15243             dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
15244                 heap_number, i, 
15245                 generation_allocation_size (gen), 
15246                 generation_free_list_space (gen), 
15247                 generation_free_obj_space (gen)));
15248         }
15249     }
15250 #else
15251     UNREFERENCED_PARAMETER(msg);
15252 #endif // BACKGROUND_GC && TRACE_GC
15253 }
15254
15255 void gc_heap::update_collection_counts_for_no_gc()
15256 {
15257     assert (settings.pause_mode == pause_no_gc);
15258
15259     settings.condemned_generation = max_generation;
15260 #ifdef MULTIPLE_HEAPS
15261     for (int i = 0; i < n_heaps; i++)
15262         g_heaps[i]->update_collection_counts();
15263 #else //MULTIPLE_HEAPS
15264     update_collection_counts();
15265 #endif //MULTIPLE_HEAPS
15266
15267     full_gc_counts[gc_type_blocking]++;
15268 }
15269
15270 BOOL gc_heap::should_proceed_with_gc()
15271 {
15272     if (gc_heap::settings.pause_mode == pause_no_gc)
15273     {
15274         if (current_no_gc_region_info.started)
15275         {
15276             // The no_gc mode was already in progress yet we triggered another GC,
15277             // this effectively exits the no_gc mode.
15278             restore_data_for_no_gc();
15279         }
15280         else
15281             return should_proceed_for_no_gc();
15282     }
15283
15284     return TRUE;
15285 }
15286
15287 //internal part of gc used by the serial and concurrent version
15288 void gc_heap::gc1()
15289 {
15290 #ifdef BACKGROUND_GC
15291     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15292 #endif //BACKGROUND_GC
15293
15294 #ifdef TIME_GC
15295     mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
15296 #endif //TIME_GC
15297
15298     verify_soh_segment_list();
15299
15300     int n = settings.condemned_generation;
15301
15302     update_collection_counts ();
15303
15304 #ifdef BACKGROUND_GC
15305     bgc_alloc_lock->check();
15306 #endif //BACKGROUND_GC
15307
15308     free_list_info (max_generation, "beginning");
15309
15310     vm_heap->GcCondemnedGeneration = settings.condemned_generation;
15311
15312     assert (g_gc_card_table == card_table);
15313
15314 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
15315     assert (g_gc_card_bundle_table == card_bundle_table);
15316 #endif    
15317
15318     {
15319         if (n == max_generation)
15320         {
15321             gc_low = lowest_address;
15322             gc_high = highest_address;
15323         }
15324         else
15325         {
15326             gc_low = generation_allocation_start (generation_of (n));
15327             gc_high = heap_segment_reserved (ephemeral_heap_segment);
15328         }   
15329 #ifdef BACKGROUND_GC
15330         if (settings.concurrent)
15331         {
15332 #ifdef TRACE_GC
15333             time_bgc_last = GetHighPrecisionTimeStamp();
15334 #endif //TRACE_GC
15335
15336             FIRE_EVENT(BGCBegin);
15337
15338             concurrent_print_time_delta ("BGC");
15339
15340 //#ifdef WRITE_WATCH
15341             //reset_write_watch (FALSE);
15342 //#endif //WRITE_WATCH
15343
15344             concurrent_print_time_delta ("RW");
15345             background_mark_phase();
15346             free_list_info (max_generation, "after mark phase");
15347             
15348             background_sweep();
15349             free_list_info (max_generation, "after sweep phase");
15350         }
15351         else
15352 #endif //BACKGROUND_GC
15353         {
15354             mark_phase (n, FALSE);
15355
15356             GCScan::GcRuntimeStructuresValid (FALSE);
15357             plan_phase (n);
15358             GCScan::GcRuntimeStructuresValid (TRUE);
15359         }
15360     }
15361
15362     size_t end_gc_time = GetHighPrecisionTimeStamp();
15363 //    printf ("generation: %d, elapsed time: %Id\n", n,  end_gc_time - dd_time_clock (dynamic_data_of (0)));
15364
15365     //adjust the allocation size from the pinned quantities. 
15366     for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
15367     {
15368         generation* gn = generation_of (gen_number);
15369         if (settings.compaction)
15370         {
15371             generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
15372             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
15373         }
15374         else
15375         {
15376             generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
15377             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
15378         }
15379         generation_pinned_allocation_sweep_size (gn) = 0;
15380         generation_pinned_allocation_compact_size (gn) = 0;
15381     }
15382
15383 #ifdef BACKGROUND_GC
15384     if (settings.concurrent)
15385     {
15386         dynamic_data* dd = dynamic_data_of (n);
15387         dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15388
15389         free_list_info (max_generation, "after computing new dynamic data");
15390
15391         gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
15392
15393         for (int gen_number = 0; gen_number < max_generation; gen_number++)
15394         {
15395             dprintf (2, ("end of BGC: gen%d new_alloc: %Id", 
15396                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15397             current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
15398             current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15399             current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15400         }
15401     }
15402     else
15403 #endif //BACKGROUND_GC
15404     {
15405         free_list_info (max_generation, "end");
15406         for (int gen_number = 0; gen_number <= n; gen_number++)
15407         {
15408             dynamic_data* dd = dynamic_data_of (gen_number);
15409             dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15410             compute_new_dynamic_data (gen_number);
15411         }
15412
15413         if (n != max_generation)
15414         {
15415             int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1));
15416             for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++)
15417             {
15418                 get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number);
15419                 get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15420                 get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15421             }
15422         }
15423
15424         get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100);
15425
15426         free_list_info (max_generation, "after computing new dynamic data");
15427         
15428         if (heap_number == 0)
15429         {
15430             dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms", 
15431                 dd_collection_count (dynamic_data_of (0)), 
15432                 settings.condemned_generation,
15433                 dd_gc_elapsed_time (dynamic_data_of (0))));
15434         }
15435
15436         for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
15437         {
15438             dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id", 
15439                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15440         }
15441     }
15442
15443     if (n < max_generation)
15444     {
15445         compute_promoted_allocation (1 + n);
15446
15447         dynamic_data* dd = dynamic_data_of (1 + n);
15448         size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) + 
15449                                    generation_free_obj_space (generation_of (1 + n));
15450
15451 #ifdef BACKGROUND_GC
15452         if (current_c_gc_state != c_gc_state_planning)
15453 #endif //BACKGROUND_GC
15454         {
15455             if (settings.promotion)
15456             {
15457                 dd_fragmentation (dd) = new_fragmentation;
15458             }
15459             else
15460             {
15461                 //assert (dd_fragmentation (dd) == new_fragmentation);
15462             }
15463         }
15464     }
15465
15466 #ifdef BACKGROUND_GC
15467     if (!settings.concurrent)
15468 #endif //BACKGROUND_GC
15469     {
15470 #ifndef FEATURE_REDHAWK
15471         // GCToEEInterface::IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
15472         assert(GCToEEInterface::IsGCThread());
15473 #endif // FEATURE_REDHAWK
15474         adjust_ephemeral_limits();
15475     }
15476
15477 #ifdef BACKGROUND_GC
15478     assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
15479     assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
15480 #endif //BACKGROUND_GC
15481
15482     if (fgn_maxgen_percent)
15483     {
15484         if (settings.condemned_generation == (max_generation - 1))
15485         {
15486             check_for_full_gc (max_generation - 1, 0);
15487         }
15488         else if (settings.condemned_generation == max_generation)
15489         {
15490             if (full_gc_approach_event_set 
15491 #ifdef MULTIPLE_HEAPS
15492                 && (heap_number == 0)
15493 #endif //MULTIPLE_HEAPS
15494                 )
15495             {
15496                 dprintf (2, ("FGN-GC: setting gen2 end event"));
15497
15498                 full_gc_approach_event.Reset();
15499 #ifdef BACKGROUND_GC
15500                 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
15501                 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
15502 #endif //BACKGROUND_GC
15503                 full_gc_end_event.Set();
15504                 full_gc_approach_event_set = false;            
15505             }
15506         }
15507     }
15508
15509 #ifdef BACKGROUND_GC
15510     if (!settings.concurrent)
15511 #endif //BACKGROUND_GC
15512     {
15513         //decide on the next allocation quantum
15514         if (alloc_contexts_used >= 1)
15515         {
15516             allocation_quantum = Align (min ((size_t)CLR_SIZE,
15517                                             (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
15518                                             get_alignment_constant(FALSE));
15519             dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
15520         }
15521     }
15522
15523     descr_generations (FALSE);
15524
15525     verify_soh_segment_list();
15526
15527 #ifdef BACKGROUND_GC
15528     add_to_history_per_heap();
15529     if (heap_number == 0)
15530     {
15531         add_to_history();
15532     }
15533 #endif // BACKGROUND_GC
15534
15535 #ifdef GC_STATS
15536     if (GCStatistics::Enabled() && heap_number == 0)
15537         g_GCStatistics.AddGCStats(settings, 
15538             dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
15539 #endif // GC_STATS
15540
15541 #ifdef TIME_GC
15542     fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
15543              n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
15544 #endif //TIME_GC
15545
15546 #ifdef BACKGROUND_GC
15547     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15548 #endif //BACKGROUND_GC
15549
15550 #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15551     if (FALSE 
15552 #ifdef VERIFY_HEAP
15553         // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same
15554         // value. If we ever allow randomly adjusting this as the process runs,
15555         // we cannot call it this way as joins need to match - we must have the same
15556         // value for all heaps like we do with bgc_heap_walk_for_etw_p.
15557         || (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15558 #endif
15559 #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
15560         || (bgc_heap_walk_for_etw_p && settings.concurrent)
15561 #endif
15562         )
15563     {
15564 #ifdef BACKGROUND_GC
15565         bool cooperative_mode = true;
15566
15567         if (settings.concurrent)
15568         {
15569             cooperative_mode = enable_preemptive ();
15570
15571 #ifdef MULTIPLE_HEAPS
15572             bgc_t_join.join(this, gc_join_suspend_ee_verify);
15573             if (bgc_t_join.joined())
15574             {
15575                 bgc_threads_sync_event.Reset();
15576
15577                 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
15578                 bgc_t_join.restart();
15579             }
15580             if (heap_number == 0)
15581             {
15582                 suspend_EE();
15583                 bgc_threads_sync_event.Set();
15584             }
15585             else
15586             {
15587                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15588                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15589             }
15590 #else
15591             suspend_EE();
15592 #endif //MULTIPLE_HEAPS
15593
15594             //fix the allocation area so verify_heap can proceed.
15595             fix_allocation_contexts (FALSE);
15596         }
15597 #endif //BACKGROUND_GC
15598
15599 #ifdef BACKGROUND_GC
15600         assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15601 #ifdef FEATURE_EVENT_TRACE
15602         if (bgc_heap_walk_for_etw_p && settings.concurrent)
15603         {
15604             GCToEEInterface::DiagWalkBGCSurvivors(__this);
15605
15606 #ifdef MULTIPLE_HEAPS
15607             bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
15608             if (bgc_t_join.joined())
15609             {
15610                 bgc_t_join.restart();
15611             }
15612 #endif // MULTIPLE_HEAPS
15613         }
15614 #endif // FEATURE_EVENT_TRACE
15615 #endif //BACKGROUND_GC
15616
15617 #ifdef VERIFY_HEAP
15618         if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15619             verify_heap (FALSE);
15620 #endif // VERIFY_HEAP
15621
15622 #ifdef BACKGROUND_GC
15623         if (settings.concurrent)
15624         {
15625             repair_allocation_contexts (TRUE);
15626
15627 #ifdef MULTIPLE_HEAPS
15628             bgc_t_join.join(this, gc_join_restart_ee_verify);
15629             if (bgc_t_join.joined())
15630             {
15631                 bgc_threads_sync_event.Reset();
15632
15633                 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
15634                 bgc_t_join.restart();
15635             }
15636             if (heap_number == 0)
15637             {
15638                 restart_EE();
15639                 bgc_threads_sync_event.Set();
15640             }
15641             else
15642             {
15643                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15644                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15645             }
15646 #else
15647             restart_EE();
15648 #endif //MULTIPLE_HEAPS
15649
15650             disable_preemptive (cooperative_mode);
15651         }
15652 #endif //BACKGROUND_GC
15653     }
15654 #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15655
15656 #ifdef MULTIPLE_HEAPS
15657     if (!settings.concurrent)
15658     {
15659         gc_t_join.join(this, gc_join_done);
15660         if (gc_t_join.joined ())
15661         {
15662             gc_heap::internal_gc_done = false;
15663
15664             //equalize the new desired size of the generations
15665             int limit = settings.condemned_generation;
15666             if (limit == max_generation)
15667             {
15668                 limit = max_generation+1;
15669             }
15670             for (int gen = 0; gen <= limit; gen++)
15671             {
15672                 size_t total_desired = 0;
15673
15674                 for (int i = 0; i < gc_heap::n_heaps; i++)
15675                 {
15676                     gc_heap* hp = gc_heap::g_heaps[i];
15677                     dynamic_data* dd = hp->dynamic_data_of (gen);
15678                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
15679                     if (temp_total_desired < total_desired)
15680                     {
15681                         // we overflowed.
15682                         total_desired = (size_t)MAX_PTR;
15683                         break;
15684                     }
15685                     total_desired = temp_total_desired;
15686                 }
15687
15688                 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
15689                                                     get_alignment_constant ((gen != (max_generation+1))));
15690
15691                 if (gen == 0)
15692                 {
15693 #if 1 //subsumed by the linear allocation model 
15694                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15695                     // apply some smoothing.
15696                     static size_t smoothed_desired_per_heap = 0;
15697                     size_t smoothing = 3; // exponential smoothing factor
15698                     if (smoothing  > VolatileLoad(&settings.gc_index))
15699                         smoothing  = VolatileLoad(&settings.gc_index);
15700                     smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
15701                     dprintf (1, ("sn = %Id  n = %Id", smoothed_desired_per_heap, desired_per_heap));
15702                     desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
15703 #endif //0
15704
15705                     // if desired_per_heap is close to min_gc_size, trim it
15706                     // down to min_gc_size to stay in the cache
15707                     gc_heap* hp = gc_heap::g_heaps[0];
15708                     dynamic_data* dd = hp->dynamic_data_of (gen);
15709                     size_t min_gc_size = dd_min_size(dd);
15710                     // if min GC size larger than true on die cache, then don't bother
15711                     // limiting the desired size
15712                     if ((min_gc_size <= GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)) &&
15713                         desired_per_heap <= 2*min_gc_size)
15714                     {
15715                         desired_per_heap = min_gc_size;
15716                     }
15717 #ifdef BIT64
15718                     desired_per_heap = joined_youngest_desired (desired_per_heap);
15719                     dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
15720 #endif // BIT64
15721
15722                     gc_data_global.final_youngest_desired = desired_per_heap;
15723                 }
15724 #if 1 //subsumed by the linear allocation model 
15725                 if (gen == (max_generation + 1))
15726                 {
15727                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15728                     // apply some smoothing.
15729                     static size_t smoothed_desired_per_heap_loh = 0;
15730                     size_t smoothing = 3; // exponential smoothing factor
15731                     size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
15732                     if (smoothing  > loh_count)
15733                         smoothing  = loh_count;
15734                     smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
15735                     dprintf( 2, ("smoothed_desired_per_heap_loh  = %Id  desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
15736                     desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
15737                 }
15738 #endif //0
15739                 for (int i = 0; i < gc_heap::n_heaps; i++)
15740                 {
15741                     gc_heap* hp = gc_heap::g_heaps[i];
15742                     dynamic_data* dd = hp->dynamic_data_of (gen);
15743                     dd_desired_allocation (dd) = desired_per_heap;
15744                     dd_gc_new_allocation (dd) = desired_per_heap;
15745                     dd_new_allocation (dd) = desired_per_heap;
15746
15747                     if (gen == 0)
15748                     {
15749                         hp->fgn_last_alloc = desired_per_heap;
15750                     }
15751                 }
15752             }
15753
15754 #ifdef FEATURE_LOH_COMPACTION
15755             BOOL all_heaps_compacted_p = TRUE;
15756 #endif //FEATURE_LOH_COMPACTION
15757             for (int i = 0; i < gc_heap::n_heaps; i++)
15758             {
15759                 gc_heap* hp = gc_heap::g_heaps[i];
15760                 hp->decommit_ephemeral_segment_pages();
15761                 hp->rearrange_large_heap_segments();
15762 #ifdef FEATURE_LOH_COMPACTION
15763                 all_heaps_compacted_p &= hp->loh_compacted_p;
15764 #endif //FEATURE_LOH_COMPACTION
15765             }
15766
15767 #ifdef FEATURE_LOH_COMPACTION
15768             check_loh_compact_mode (all_heaps_compacted_p);
15769 #endif //FEATURE_LOH_COMPACTION
15770
15771             fire_pevents();
15772
15773             gc_t_join.restart();
15774         }
15775         alloc_context_count = 0;
15776         heap_select::mark_heap (heap_number);
15777     }
15778
15779 #else
15780     gc_data_global.final_youngest_desired = 
15781         dd_desired_allocation (dynamic_data_of (0));
15782
15783     check_loh_compact_mode (loh_compacted_p);
15784
15785     decommit_ephemeral_segment_pages();
15786     fire_pevents();
15787
15788     if (!(settings.concurrent))
15789     {
15790         rearrange_large_heap_segments();
15791         do_post_gc();
15792     }
15793
15794 #ifdef BACKGROUND_GC
15795     recover_bgc_settings();
15796 #endif //BACKGROUND_GC
15797 #endif //MULTIPLE_HEAPS
15798 }
15799
15800 void gc_heap::save_data_for_no_gc()
15801 {
15802     current_no_gc_region_info.saved_pause_mode = settings.pause_mode;
15803 #ifdef MULTIPLE_HEAPS
15804     // This is to affect heap balancing. 
15805     for (int i = 0; i < n_heaps; i++)
15806     {
15807         current_no_gc_region_info.saved_gen0_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (0));
15808         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = min_balance_threshold;
15809         current_no_gc_region_info.saved_gen3_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1));
15810         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = 0;
15811     }
15812 #endif //MULTIPLE_HEAPS
15813 }
15814
15815 void gc_heap::restore_data_for_no_gc()
15816 {
15817     gc_heap::settings.pause_mode = current_no_gc_region_info.saved_pause_mode;
15818 #ifdef MULTIPLE_HEAPS
15819     for (int i = 0; i < n_heaps; i++)
15820     {
15821         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = current_no_gc_region_info.saved_gen0_min_size;
15822         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = current_no_gc_region_info.saved_gen3_min_size;
15823     }
15824 #endif //MULTIPLE_HEAPS
15825 }
15826
15827 start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size,
15828                                                              BOOL loh_size_known, 
15829                                                              uint64_t loh_size,
15830                                                              BOOL disallow_full_blocking)
15831 {
15832     if (current_no_gc_region_info.started)
15833     {
15834         return start_no_gc_in_progress;
15835     }
15836
15837     start_no_gc_region_status status = start_no_gc_success;
15838
15839     save_data_for_no_gc();
15840     settings.pause_mode = pause_no_gc;
15841     current_no_gc_region_info.start_status = start_no_gc_success;
15842
15843     uint64_t allocation_no_gc_loh = 0;
15844     uint64_t allocation_no_gc_soh = 0;
15845     assert(total_size != 0);
15846     if (loh_size_known)
15847     {
15848         assert(loh_size != 0);
15849         assert(loh_size <= total_size);
15850         allocation_no_gc_loh = loh_size;
15851         allocation_no_gc_soh = total_size - loh_size;
15852     }
15853     else
15854     {
15855         allocation_no_gc_soh = total_size;
15856         allocation_no_gc_loh = total_size;
15857     }
15858
15859     int soh_align_const = get_alignment_constant (TRUE);
15860     size_t max_soh_allocated = soh_segment_size - segment_info_size - eph_gen_starts_size;
15861     size_t size_per_heap = 0;
15862     const double scale_factor = 1.05;
15863
15864     int num_heaps = 1;
15865 #ifdef MULTIPLE_HEAPS
15866     num_heaps = n_heaps;
15867 #endif // MULTIPLE_HEAPS
15868
15869     uint64_t total_allowed_soh_allocation = max_soh_allocated * num_heaps;
15870     // [LOCALGC TODO]
15871     // In theory, the upper limit here is the physical memory of the machine, not
15872     // SIZE_T_MAX. This is not true today because total_physical_mem can be
15873     // larger than SIZE_T_MAX if running in wow64 on a machine with more than
15874     // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing
15875     // more freely between branches, it would be good to clean this up to use
15876     // total_physical_mem instead of SIZE_T_MAX.
15877     assert(total_allowed_soh_allocation <= SIZE_T_MAX);
15878     uint64_t total_allowed_loh_allocation = SIZE_T_MAX;
15879     uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast<uint64_t>(total_allowed_soh_allocation / scale_factor) : 0;
15880     uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast<uint64_t>(total_allowed_loh_allocation / scale_factor) : 0;
15881
15882     if (allocation_no_gc_soh > total_allowed_soh_alloc_scaled ||
15883         allocation_no_gc_loh > total_allowed_loh_alloc_scaled)
15884     {
15885         status = start_no_gc_too_large;
15886         goto done;
15887     }
15888
15889     if (allocation_no_gc_soh > 0)
15890     {
15891         allocation_no_gc_soh = static_cast<uint64_t>(allocation_no_gc_soh * scale_factor);
15892         allocation_no_gc_soh = min (allocation_no_gc_soh, total_allowed_soh_alloc_scaled);
15893     }
15894
15895     if (allocation_no_gc_loh > 0)
15896     {
15897         allocation_no_gc_loh = static_cast<uint64_t>(allocation_no_gc_loh * scale_factor);
15898         allocation_no_gc_loh = min (allocation_no_gc_loh, total_allowed_loh_alloc_scaled);
15899     }
15900
15901     if (disallow_full_blocking)
15902         current_no_gc_region_info.minimal_gc_p = TRUE;
15903
15904     if (allocation_no_gc_soh != 0)
15905     {
15906         current_no_gc_region_info.soh_allocation_size = static_cast<size_t>(allocation_no_gc_soh);
15907         size_per_heap = current_no_gc_region_info.soh_allocation_size;
15908 #ifdef MULTIPLE_HEAPS
15909         size_per_heap /= n_heaps;
15910         for (int i = 0; i < n_heaps; i++)
15911         {
15912             // due to heap balancing we need to allow some room before we even look to balance to another heap.
15913             g_heaps[i]->soh_allocation_no_gc = min (Align ((size_per_heap + min_balance_threshold), soh_align_const), max_soh_allocated);
15914         }
15915 #else //MULTIPLE_HEAPS
15916         soh_allocation_no_gc = min (Align (size_per_heap, soh_align_const), max_soh_allocated);
15917 #endif //MULTIPLE_HEAPS
15918     }
15919
15920     if (allocation_no_gc_loh != 0)
15921     {
15922         current_no_gc_region_info.loh_allocation_size = static_cast<size_t>(allocation_no_gc_loh);
15923         size_per_heap = current_no_gc_region_info.loh_allocation_size;
15924 #ifdef MULTIPLE_HEAPS
15925         size_per_heap /= n_heaps;
15926         for (int i = 0; i < n_heaps; i++)
15927             g_heaps[i]->loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
15928 #else //MULTIPLE_HEAPS
15929         loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
15930 #endif //MULTIPLE_HEAPS
15931     }
15932
15933 done:
15934     if (status != start_no_gc_success)
15935         restore_data_for_no_gc();
15936     return status;
15937 }
15938
15939 void gc_heap::handle_failure_for_no_gc()
15940 {
15941     gc_heap::restore_data_for_no_gc();
15942     // sets current_no_gc_region_info.started to FALSE here.
15943     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
15944 }
15945
15946 start_no_gc_region_status gc_heap::get_start_no_gc_region_status()
15947 {
15948     return current_no_gc_region_info.start_status;
15949 }
15950
15951 void gc_heap::record_gcs_during_no_gc()
15952 {
15953     if (current_no_gc_region_info.started)
15954     {
15955         current_no_gc_region_info.num_gcs++;
15956         if (is_induced (settings.reason))
15957             current_no_gc_region_info.num_gcs_induced++;
15958     }
15959 }
15960
15961 BOOL gc_heap::find_loh_free_for_no_gc()
15962 {
15963     allocator* loh_allocator = generation_allocator (generation_of (max_generation + 1));
15964     size_t sz_list = loh_allocator->first_bucket_size();
15965     size_t size = loh_allocation_no_gc;
15966     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
15967     {
15968         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
15969         {
15970             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
15971             while (free_list)
15972             {
15973                 size_t free_list_size = unused_array_size(free_list);
15974
15975                 if (free_list_size > loh_allocation_no_gc)
15976                 {
15977                     dprintf (3, ("free item %Ix(%Id) for no gc", (size_t)free_list, free_list_size));
15978                     return TRUE;
15979                 }
15980
15981                 free_list = free_list_slot (free_list); 
15982             }
15983         }
15984         sz_list = sz_list * 2;
15985     }
15986
15987     return FALSE;
15988 }
15989
15990 BOOL gc_heap::find_loh_space_for_no_gc()
15991 {
15992     saved_loh_segment_no_gc = 0;
15993
15994     if (find_loh_free_for_no_gc())
15995         return TRUE;
15996
15997     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
15998
15999     while (seg)
16000     {
16001         size_t remaining = heap_segment_reserved (seg) - heap_segment_allocated (seg);
16002         if (remaining >= loh_allocation_no_gc)
16003         {
16004             saved_loh_segment_no_gc = seg;
16005             break;
16006         }
16007         seg = heap_segment_next (seg);
16008     }
16009
16010     if (!saved_loh_segment_no_gc && current_no_gc_region_info.minimal_gc_p)
16011     {
16012         // If no full GC is allowed, we try to get a new seg right away.
16013         saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc)
16014 #ifdef MULTIPLE_HEAPS
16015                                                       , this
16016 #endif //MULTIPLE_HEAPS
16017                                                       );
16018     }
16019
16020     return (saved_loh_segment_no_gc != 0);
16021 }
16022
16023 BOOL gc_heap::loh_allocated_for_no_gc()
16024 {
16025     if (!saved_loh_segment_no_gc)
16026         return FALSE;
16027
16028     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16029     do 
16030     {
16031         if (seg == saved_loh_segment_no_gc)
16032         {
16033             return FALSE;
16034         }
16035         seg = heap_segment_next (seg);
16036     } while (seg);
16037
16038     return TRUE;
16039 }
16040
16041 BOOL gc_heap::commit_loh_for_no_gc (heap_segment* seg)
16042 {
16043     uint8_t* end_committed = heap_segment_allocated (seg) + loh_allocation_no_gc;
16044     assert (end_committed <= heap_segment_reserved (seg));
16045     return (grow_heap_segment (seg, end_committed));
16046 }
16047
16048 void gc_heap::thread_no_gc_loh_segments()
16049 {
16050 #ifdef MULTIPLE_HEAPS
16051     for (int i = 0; i < n_heaps; i++)
16052     {
16053         gc_heap* hp = g_heaps[i];
16054         if (hp->loh_allocated_for_no_gc())
16055         {
16056             hp->thread_loh_segment (hp->saved_loh_segment_no_gc);
16057             hp->saved_loh_segment_no_gc = 0;
16058         }
16059     }
16060 #else //MULTIPLE_HEAPS
16061     if (loh_allocated_for_no_gc())
16062     {
16063         thread_loh_segment (saved_loh_segment_no_gc);
16064         saved_loh_segment_no_gc = 0;
16065     }
16066 #endif //MULTIPLE_HEAPS    
16067 }
16068
16069 void gc_heap::set_loh_allocations_for_no_gc()
16070 {
16071     if (current_no_gc_region_info.loh_allocation_size != 0)
16072     {
16073         dynamic_data* dd = dynamic_data_of (max_generation + 1);
16074         dd_new_allocation (dd) = loh_allocation_no_gc;
16075         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16076     }
16077 }
16078
16079 void gc_heap::set_soh_allocations_for_no_gc()
16080 {
16081     if (current_no_gc_region_info.soh_allocation_size != 0)
16082     {
16083         dynamic_data* dd = dynamic_data_of (0);
16084         dd_new_allocation (dd) = soh_allocation_no_gc;
16085         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16086 #ifdef MULTIPLE_HEAPS
16087         alloc_context_count = 0;
16088 #endif //MULTIPLE_HEAPS
16089     }
16090 }
16091
16092 void gc_heap::set_allocations_for_no_gc()
16093 {
16094 #ifdef MULTIPLE_HEAPS
16095     for (int i = 0; i < n_heaps; i++)
16096     {
16097         gc_heap* hp = g_heaps[i];
16098         hp->set_loh_allocations_for_no_gc();
16099         hp->set_soh_allocations_for_no_gc();
16100     }
16101 #else //MULTIPLE_HEAPS
16102     set_loh_allocations_for_no_gc();
16103     set_soh_allocations_for_no_gc();
16104 #endif //MULTIPLE_HEAPS
16105 }
16106
16107 BOOL gc_heap::should_proceed_for_no_gc()
16108 {
16109     BOOL gc_requested = FALSE;
16110     BOOL loh_full_gc_requested = FALSE;
16111     BOOL soh_full_gc_requested = FALSE;
16112     BOOL no_gc_requested = FALSE;
16113     BOOL get_new_loh_segments = FALSE;
16114
16115     if (current_no_gc_region_info.soh_allocation_size)
16116     {
16117 #ifdef MULTIPLE_HEAPS
16118         for (int i = 0; i < n_heaps; i++)
16119         {
16120             gc_heap* hp = g_heaps[i];
16121             if ((size_t)(heap_segment_reserved (hp->ephemeral_heap_segment) - hp->alloc_allocated) < hp->soh_allocation_no_gc)
16122             {
16123                 gc_requested = TRUE;
16124                 break;
16125             }
16126         }
16127 #else //MULTIPLE_HEAPS
16128         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated) < soh_allocation_no_gc)
16129             gc_requested = TRUE;
16130 #endif //MULTIPLE_HEAPS
16131
16132         if (!gc_requested)
16133         {
16134 #ifdef MULTIPLE_HEAPS
16135             for (int i = 0; i < n_heaps; i++)
16136             {
16137                 gc_heap* hp = g_heaps[i];
16138                 if (!(hp->grow_heap_segment (hp->ephemeral_heap_segment, (hp->alloc_allocated + hp->soh_allocation_no_gc))))
16139                 {
16140                     soh_full_gc_requested = TRUE;
16141                     break;
16142                 }
16143             }
16144 #else //MULTIPLE_HEAPS
16145             if (!grow_heap_segment (ephemeral_heap_segment, (alloc_allocated + soh_allocation_no_gc)))
16146                 soh_full_gc_requested = TRUE;
16147 #endif //MULTIPLE_HEAPS
16148         }
16149     }
16150
16151     if (!current_no_gc_region_info.minimal_gc_p && gc_requested)
16152     {
16153         soh_full_gc_requested = TRUE;
16154     }
16155
16156     no_gc_requested = !(soh_full_gc_requested || gc_requested);
16157
16158     if (soh_full_gc_requested && current_no_gc_region_info.minimal_gc_p)
16159     {
16160         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16161         goto done;
16162     }
16163
16164     if (!soh_full_gc_requested && current_no_gc_region_info.loh_allocation_size)
16165     {
16166         // Check to see if we have enough reserved space. 
16167 #ifdef MULTIPLE_HEAPS
16168         for (int i = 0; i < n_heaps; i++)
16169         {
16170             gc_heap* hp = g_heaps[i];
16171             if (!hp->find_loh_space_for_no_gc())
16172             {
16173                 loh_full_gc_requested = TRUE;
16174                 break;
16175             }
16176         }
16177 #else //MULTIPLE_HEAPS
16178         if (!find_loh_space_for_no_gc())
16179             loh_full_gc_requested = TRUE;
16180 #endif //MULTIPLE_HEAPS
16181
16182         // Check to see if we have committed space.
16183         if (!loh_full_gc_requested)
16184         {
16185 #ifdef MULTIPLE_HEAPS
16186             for (int i = 0; i < n_heaps; i++)
16187             {
16188                 gc_heap* hp = g_heaps[i];
16189                 if (hp->saved_loh_segment_no_gc &&!hp->commit_loh_for_no_gc (hp->saved_loh_segment_no_gc))
16190                 {
16191                     loh_full_gc_requested = TRUE;
16192                     break;
16193                 }
16194             }
16195 #else //MULTIPLE_HEAPS
16196             if (saved_loh_segment_no_gc && !commit_loh_for_no_gc (saved_loh_segment_no_gc))
16197                 loh_full_gc_requested = TRUE;
16198 #endif //MULTIPLE_HEAPS
16199         }
16200     }
16201
16202     if (loh_full_gc_requested || soh_full_gc_requested)
16203     {
16204         if (current_no_gc_region_info.minimal_gc_p)
16205             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16206     }
16207
16208     no_gc_requested = !(loh_full_gc_requested || soh_full_gc_requested || gc_requested);
16209
16210     if (current_no_gc_region_info.start_status == start_no_gc_success)
16211     {
16212         if (no_gc_requested)
16213             set_allocations_for_no_gc();
16214     }
16215
16216 done:
16217
16218     if ((current_no_gc_region_info.start_status == start_no_gc_success) && !no_gc_requested)
16219         return TRUE;
16220     else
16221     {
16222         // We are done with starting the no_gc_region.
16223         current_no_gc_region_info.started = TRUE;
16224         return FALSE;
16225     }
16226 }
16227
16228 end_no_gc_region_status gc_heap::end_no_gc_region()
16229 {
16230     dprintf (1, ("end no gc called"));
16231
16232     end_no_gc_region_status status = end_no_gc_success;
16233
16234     if (!(current_no_gc_region_info.started))
16235         status = end_no_gc_not_in_progress;
16236     if (current_no_gc_region_info.num_gcs_induced)
16237         status = end_no_gc_induced;
16238     else if (current_no_gc_region_info.num_gcs)
16239         status = end_no_gc_alloc_exceeded;
16240
16241     if (settings.pause_mode == pause_no_gc)
16242         restore_data_for_no_gc();
16243
16244     // sets current_no_gc_region_info.started to FALSE here.
16245     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16246
16247     return status;
16248 }
16249
16250 //update counters
16251 void gc_heap::update_collection_counts ()
16252 {
16253     dynamic_data* dd0 = dynamic_data_of (0);
16254     dd_gc_clock (dd0) += 1;
16255
16256     size_t now = GetHighPrecisionTimeStamp();
16257
16258     for (int i = 0; i <= settings.condemned_generation;i++)
16259     {
16260         dynamic_data* dd = dynamic_data_of (i);
16261         dd_collection_count (dd)++;
16262         //this is needed by the linear allocation model
16263         if (i == max_generation)
16264             dd_collection_count (dynamic_data_of (max_generation+1))++;
16265         dd_gc_clock (dd) = dd_gc_clock (dd0);
16266         dd_time_clock (dd) = now;
16267     }
16268 }
16269
16270 BOOL gc_heap::expand_soh_with_minimal_gc()
16271 {
16272     if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) >= soh_allocation_no_gc)
16273         return TRUE;
16274
16275     heap_segment* new_seg = soh_get_segment_to_expand();
16276     if (new_seg)
16277     {
16278         if (g_gc_card_table != card_table)
16279             copy_brick_card_table();
16280
16281         settings.promotion = TRUE;
16282         settings.demotion = FALSE;
16283         ephemeral_promotion = TRUE;
16284         int condemned_gen_number = max_generation - 1;
16285
16286         generation* gen = 0;
16287         int align_const = get_alignment_constant (TRUE);
16288
16289         for (int i = 0; i <= condemned_gen_number; i++)
16290         {
16291             gen = generation_of (i);
16292             saved_ephemeral_plan_start[i] = generation_allocation_start (gen);
16293             saved_ephemeral_plan_start_size[i] = Align (size (generation_allocation_start (gen)), align_const);
16294         }
16295
16296         // We do need to clear the bricks here as we are converting a bunch of ephemeral objects to gen2
16297         // and need to make sure that there are no left over bricks from the previous GCs for the space 
16298         // we just used for gen0 allocation. We will need to go through the bricks for these objects for 
16299         // ephemeral GCs later.
16300         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
16301              b < brick_of (align_on_brick (heap_segment_allocated (ephemeral_heap_segment)));
16302              b++)
16303         {
16304             set_brick (b, -1);
16305         }
16306
16307         size_t ephemeral_size = (heap_segment_allocated (ephemeral_heap_segment) - 
16308                                 generation_allocation_start (generation_of (max_generation - 1)));
16309         heap_segment_next (ephemeral_heap_segment) = new_seg;
16310         ephemeral_heap_segment = new_seg;
16311         uint8_t*  start = heap_segment_mem (ephemeral_heap_segment);
16312
16313         for (int i = condemned_gen_number; i >= 0; i--)
16314         {
16315             gen = generation_of (i);
16316             size_t gen_start_size = Align (min_obj_size);
16317             make_generation (generation_table[i], ephemeral_heap_segment, start, 0);
16318             generation_plan_allocation_start (gen) = start;
16319             generation_plan_allocation_start_size (gen) = gen_start_size;
16320             start += gen_start_size;
16321         }
16322         heap_segment_used (ephemeral_heap_segment) = start - plug_skew;
16323         heap_segment_plan_allocated (ephemeral_heap_segment) = start;
16324
16325         fix_generation_bounds (condemned_gen_number, generation_of (0));
16326
16327         dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
16328         dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));
16329
16330         adjust_ephemeral_limits();
16331         return TRUE;
16332     }
16333     else
16334         return FALSE;
16335 }
16336
16337 // Only to be done on the thread that calls restart in a join for server GC
16338 // and reset the oom status per heap.
16339 void gc_heap::check_and_set_no_gc_oom()
16340 {
16341 #ifdef MULTIPLE_HEAPS
16342     for (int i = 0; i < n_heaps; i++)
16343     {
16344         gc_heap* hp = g_heaps[i];
16345         if (hp->no_gc_oom_p)
16346         {
16347             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16348             hp->no_gc_oom_p = false;
16349         }
16350     }
16351 #else
16352     if (no_gc_oom_p)
16353     {
16354         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16355         no_gc_oom_p = false;
16356     }
16357 #endif //MULTIPLE_HEAPS
16358 }
16359
16360 void gc_heap::allocate_for_no_gc_after_gc()
16361 {
16362     if (current_no_gc_region_info.minimal_gc_p)
16363         repair_allocation_contexts (TRUE);
16364
16365     no_gc_oom_p = false;
16366
16367     if (current_no_gc_region_info.start_status != start_no_gc_no_memory)
16368     {
16369         if (current_no_gc_region_info.soh_allocation_size != 0)
16370         {
16371             if (((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) < soh_allocation_no_gc) ||
16372                 (!grow_heap_segment (ephemeral_heap_segment, (heap_segment_allocated (ephemeral_heap_segment) + soh_allocation_no_gc))))
16373             {
16374                 no_gc_oom_p = true;
16375             }
16376
16377 #ifdef MULTIPLE_HEAPS
16378             gc_t_join.join(this, gc_join_after_commit_soh_no_gc);
16379             if (gc_t_join.joined())
16380             {
16381 #endif //MULTIPLE_HEAPS
16382
16383                 check_and_set_no_gc_oom();
16384
16385 #ifdef MULTIPLE_HEAPS
16386                 gc_t_join.restart();
16387             }
16388 #endif //MULTIPLE_HEAPS
16389         }
16390
16391         if ((current_no_gc_region_info.start_status == start_no_gc_success) &&
16392             !(current_no_gc_region_info.minimal_gc_p) && 
16393             (current_no_gc_region_info.loh_allocation_size != 0))
16394         {
16395             gc_policy = policy_compact;
16396             saved_loh_segment_no_gc = 0;
16397
16398             if (!find_loh_free_for_no_gc())
16399             {
16400                 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16401                 BOOL found_seg_p = FALSE;
16402                 while (seg)
16403                 {
16404                     if ((size_t)(heap_segment_reserved (seg) - heap_segment_allocated (seg)) >= loh_allocation_no_gc)
16405                     {
16406                         found_seg_p = TRUE;
16407                         if (!commit_loh_for_no_gc (seg))
16408                         {
16409                             no_gc_oom_p = true;
16410                             break;
16411                         }
16412                     }
16413                     seg = heap_segment_next (seg);
16414                 }
16415
16416                 if (!found_seg_p)
16417                     gc_policy = policy_expand;
16418             }
16419
16420 #ifdef MULTIPLE_HEAPS
16421             gc_t_join.join(this, gc_join_expand_loh_no_gc);
16422             if (gc_t_join.joined())
16423             {
16424                 check_and_set_no_gc_oom();
16425
16426                 if (current_no_gc_region_info.start_status == start_no_gc_success)
16427                 {
16428                     for (int i = 0; i < n_heaps; i++)
16429                     {
16430                         gc_heap* hp = g_heaps[i];
16431                         if (hp->gc_policy == policy_expand)
16432                         {
16433                             hp->saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc), hp);
16434                             if (!(hp->saved_loh_segment_no_gc))
16435                             {
16436                                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16437                                 break;
16438                             }
16439                         }
16440                     }
16441                 }
16442
16443                 gc_t_join.restart();
16444             }
16445 #else //MULTIPLE_HEAPS
16446             check_and_set_no_gc_oom();
16447
16448             if ((current_no_gc_region_info.start_status == start_no_gc_success) && (gc_policy == policy_expand))
16449             {
16450                 saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc));
16451                 if (!saved_loh_segment_no_gc)
16452                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16453             }
16454 #endif //MULTIPLE_HEAPS
16455
16456             if ((current_no_gc_region_info.start_status == start_no_gc_success) && saved_loh_segment_no_gc)
16457             {
16458                 if (!commit_loh_for_no_gc (saved_loh_segment_no_gc))
16459                 {
16460                     no_gc_oom_p = true;
16461                 }
16462             }
16463         }
16464     }
16465
16466 #ifdef MULTIPLE_HEAPS
16467     gc_t_join.join(this, gc_join_final_no_gc);
16468     if (gc_t_join.joined())
16469     {
16470 #endif //MULTIPLE_HEAPS
16471
16472         check_and_set_no_gc_oom();
16473
16474         if (current_no_gc_region_info.start_status == start_no_gc_success)
16475         {
16476             set_allocations_for_no_gc();
16477             current_no_gc_region_info.started = TRUE;
16478         }
16479
16480 #ifdef MULTIPLE_HEAPS
16481         gc_t_join.restart();
16482     }
16483 #endif //MULTIPLE_HEAPS
16484 }
16485
16486 void gc_heap::init_records()
16487 {
16488     memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
16489     gc_data_per_heap.heap_index = heap_number;
16490     if (heap_number == 0)
16491         memset (&gc_data_global, 0, sizeof (gc_data_global));
16492
16493 #ifdef GC_CONFIG_DRIVEN
16494     memset (interesting_data_per_gc, 0, sizeof (interesting_data_per_gc));
16495 #endif //GC_CONFIG_DRIVEN
16496 }
16497
16498 int gc_heap::garbage_collect (int n)
16499 {
16500     //reset the number of alloc contexts
16501     alloc_contexts_used = 0;
16502
16503     fix_allocation_contexts (TRUE);
16504 #ifdef MULTIPLE_HEAPS
16505 #ifdef JOIN_STATS
16506     gc_t_join.start_ts(this);
16507 #endif //JOIN_STATS
16508     clear_gen0_bricks();
16509 #endif //MULTIPLE_HEAPS
16510
16511     if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p)
16512     {
16513 #ifdef MULTIPLE_HEAPS
16514         gc_t_join.join(this, gc_join_minimal_gc);
16515         if (gc_t_join.joined())
16516         {
16517 #endif //MULTIPLE_HEAPS
16518
16519 #ifdef MULTIPLE_HEAPS
16520             // this is serialized because we need to get a segment
16521             for (int i = 0; i < n_heaps; i++)
16522             {
16523                 if (!(g_heaps[i]->expand_soh_with_minimal_gc()))
16524                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16525             }
16526 #else
16527             if (!expand_soh_with_minimal_gc())
16528                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16529 #endif //MULTIPLE_HEAPS
16530
16531             update_collection_counts_for_no_gc();
16532
16533 #ifdef MULTIPLE_HEAPS
16534             gc_t_join.restart();
16535         }
16536 #endif //MULTIPLE_HEAPS
16537
16538         goto done;
16539     }
16540
16541     init_records();
16542     memset (&fgm_result, 0, sizeof (fgm_result));
16543
16544     settings.reason = gc_trigger_reason;
16545     verify_pinned_queue_p = FALSE;
16546
16547 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
16548         num_pinned_objects = 0;
16549 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
16550
16551 #ifdef STRESS_HEAP
16552     if (settings.reason == reason_gcstress)
16553     {
16554         settings.reason = reason_induced;
16555         settings.stress_induced = TRUE;
16556     }
16557 #endif // STRESS_HEAP
16558
16559 #ifdef MULTIPLE_HEAPS
16560     //align all heaps on the max generation to condemn
16561     dprintf (3, ("Joining for max generation to condemn"));
16562     condemned_generation_num = generation_to_condemn (n, 
16563                                                       &blocking_collection, 
16564                                                       &elevation_requested, 
16565                                                       FALSE);
16566     gc_t_join.join(this, gc_join_generation_determined);
16567     if (gc_t_join.joined())
16568 #endif //MULTIPLE_HEAPS
16569     {
16570 #ifdef MULTIPLE_HEAPS
16571 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16572         //delete old slots from the segment table
16573         seg_table->delete_old_slots();
16574 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16575         for (int i = 0; i < n_heaps; i++)
16576         {
16577             //copy the card and brick tables
16578             if (g_gc_card_table != g_heaps[i]->card_table)
16579             {
16580                 g_heaps[i]->copy_brick_card_table();
16581             }
16582
16583             g_heaps[i]->rearrange_large_heap_segments();
16584             if (!recursive_gc_sync::background_running_p())
16585             {
16586                 g_heaps[i]->rearrange_small_heap_segments();
16587             }
16588         }
16589 #else //MULTIPLE_HEAPS
16590 #ifdef BACKGROUND_GC
16591             //delete old slots from the segment table
16592 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16593             seg_table->delete_old_slots();
16594 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16595             rearrange_large_heap_segments();
16596             if (!recursive_gc_sync::background_running_p())
16597             {
16598                 rearrange_small_heap_segments();
16599             }
16600 #endif //BACKGROUND_GC
16601         // check for card table growth
16602         if (g_gc_card_table != card_table)
16603             copy_brick_card_table();
16604
16605 #endif //MULTIPLE_HEAPS
16606
16607         BOOL should_evaluate_elevation = FALSE;
16608         BOOL should_do_blocking_collection = FALSE;
16609
16610 #ifdef MULTIPLE_HEAPS
16611         int gen_max = condemned_generation_num;
16612         for (int i = 0; i < n_heaps; i++)
16613         {
16614             if (gen_max < g_heaps[i]->condemned_generation_num)
16615                 gen_max = g_heaps[i]->condemned_generation_num;
16616             if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested))
16617                 should_evaluate_elevation = TRUE;
16618             if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
16619                 should_do_blocking_collection = TRUE;
16620         }
16621
16622         settings.condemned_generation = gen_max;
16623 #else //MULTIPLE_HEAPS
16624         settings.condemned_generation = generation_to_condemn (n, 
16625                                                             &blocking_collection, 
16626                                                             &elevation_requested, 
16627                                                             FALSE);
16628         should_evaluate_elevation = elevation_requested;
16629         should_do_blocking_collection = blocking_collection;
16630 #endif //MULTIPLE_HEAPS
16631
16632         settings.condemned_generation = joined_generation_to_condemn (
16633                                             should_evaluate_elevation, 
16634                                             settings.condemned_generation,
16635                                             &should_do_blocking_collection
16636                                             STRESS_HEAP_ARG(n)
16637                                             );
16638
16639         STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, 
16640                 "condemned generation num: %d\n", settings.condemned_generation);
16641
16642         record_gcs_during_no_gc();
16643
16644         if (settings.condemned_generation > 1)
16645             settings.promotion = TRUE;
16646
16647 #ifdef HEAP_ANALYZE
16648         // At this point we've decided what generation is condemned
16649         // See if we've been requested to analyze survivors after the mark phase
16650         if (GCToEEInterface::AnalyzeSurvivorsRequested(settings.condemned_generation))
16651         {
16652             heap_analyze_enabled = TRUE;
16653         }
16654 #endif // HEAP_ANALYZE
16655
16656         GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced);
16657
16658 #ifdef BACKGROUND_GC
16659         if ((settings.condemned_generation == max_generation) &&
16660             (recursive_gc_sync::background_running_p()))
16661         {
16662             //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work
16663             // because we have to collect 0 and 1 properly
16664             // in particular, the allocation contexts are gone.
16665             // For now, it is simpler to collect max_generation-1
16666             settings.condemned_generation = max_generation - 1;
16667             dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
16668         }
16669
16670         if ((settings.condemned_generation == max_generation) &&
16671             (should_do_blocking_collection == FALSE) &&
16672             gc_can_use_concurrent &&
16673             !temp_disable_concurrent_p &&                 
16674             ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
16675         {
16676             keep_bgc_threads_p = TRUE;
16677             c_write (settings.concurrent,  TRUE);
16678         }
16679 #endif //BACKGROUND_GC
16680
16681         settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1;
16682
16683         // Call the EE for start of GC work
16684         // just one thread for MP GC
16685         GCToEEInterface::GcStartWork (settings.condemned_generation,
16686                                  max_generation);            
16687
16688         // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
16689         // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
16690         // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
16691         // fired in gc1.
16692         do_pre_gc();
16693
16694 #ifdef MULTIPLE_HEAPS
16695         gc_start_event.Reset();
16696         //start all threads on the roots.
16697         dprintf(3, ("Starting all gc threads for gc"));
16698         gc_t_join.restart();
16699 #endif //MULTIPLE_HEAPS
16700     }
16701
16702     {
16703         int gen_num_for_data = max_generation + 1;
16704         for (int i = 0; i <= gen_num_for_data; i++)
16705         {
16706             gc_data_per_heap.gen_data[i].size_before = generation_size (i);
16707             generation* gen = generation_of (i);
16708             gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
16709             gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
16710         }
16711     }
16712     descr_generations (TRUE);
16713 //    descr_card_table();
16714
16715 #ifdef VERIFY_HEAP
16716     if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
16717        !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_POST_GC_ONLY))
16718     {
16719         verify_heap (TRUE);
16720     }
16721     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK)
16722         checkGCWriteBarrier();
16723
16724 #endif // VERIFY_HEAP
16725
16726 #ifdef BACKGROUND_GC
16727     if (settings.concurrent)
16728     {
16729         // We need to save the settings because we'll need to restore it after each FGC.
16730         assert (settings.condemned_generation == max_generation);
16731         settings.compaction = FALSE;
16732         saved_bgc_settings = settings;
16733
16734 #ifdef MULTIPLE_HEAPS
16735         if (heap_number == 0)
16736         {
16737             for (int i = 0; i < n_heaps; i++)
16738             {
16739                 prepare_bgc_thread (g_heaps[i]);
16740             }
16741             dprintf (2, ("setting bgc_threads_sync_event"));
16742             bgc_threads_sync_event.Set();
16743         }
16744         else
16745         {
16746             bgc_threads_sync_event.Wait(INFINITE, FALSE);
16747             dprintf (2, ("bgc_threads_sync_event is signalled"));
16748         }
16749 #else
16750         prepare_bgc_thread(0);
16751 #endif //MULTIPLE_HEAPS
16752
16753 #ifdef MULTIPLE_HEAPS
16754         gc_t_join.join(this, gc_join_start_bgc);
16755         if (gc_t_join.joined())
16756 #endif //MULTIPLE_HEAPS
16757         {
16758             do_concurrent_p = TRUE;
16759             do_ephemeral_gc_p = FALSE;
16760 #ifdef MULTIPLE_HEAPS
16761             dprintf(2, ("Joined to perform a background GC"));
16762
16763             for (int i = 0; i < n_heaps; i++)
16764             {
16765                 gc_heap* hp = g_heaps[i];
16766                 if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
16767                 {
16768                     do_concurrent_p = FALSE;
16769                     break;
16770                 }
16771                 else
16772                 {
16773                     hp->background_saved_lowest_address = hp->lowest_address;
16774                     hp->background_saved_highest_address = hp->highest_address;
16775                 }
16776             }
16777 #else
16778             do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
16779             if (do_concurrent_p)
16780             {
16781                 background_saved_lowest_address = lowest_address;
16782                 background_saved_highest_address = highest_address;
16783             }
16784 #endif //MULTIPLE_HEAPS
16785
16786             if (do_concurrent_p)
16787             {
16788 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
16789                 SoftwareWriteWatch::EnableForGCHeap();
16790 #endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
16791
16792 #ifdef MULTIPLE_HEAPS
16793                 for (int i = 0; i < n_heaps; i++)
16794                     g_heaps[i]->current_bgc_state = bgc_initialized;
16795 #else
16796                 current_bgc_state = bgc_initialized;
16797 #endif //MULTIPLE_HEAPS
16798
16799                 int gen = check_for_ephemeral_alloc();
16800                 // always do a gen1 GC before we start BGC. 
16801                 // This is temporary for testing purpose.
16802                 //int gen = max_generation - 1;
16803                 dont_restart_ee_p = TRUE;
16804                 if (gen == -1)
16805                 {
16806                     // If we decide to not do a GC before the BGC we need to 
16807                     // restore the gen0 alloc context.
16808 #ifdef MULTIPLE_HEAPS
16809                     for (int i = 0; i < n_heaps; i++)
16810                     {
16811                         generation_allocation_pointer (g_heaps[i]->generation_of (0)) =  0;
16812                         generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
16813                     }
16814 #else
16815                     generation_allocation_pointer (youngest_generation) =  0;
16816                     generation_allocation_limit (youngest_generation) = 0;
16817 #endif //MULTIPLE_HEAPS
16818                 }
16819                 else
16820                 {
16821                     do_ephemeral_gc_p = TRUE;
16822
16823                     settings.init_mechanisms();
16824                     settings.condemned_generation = gen;
16825                     settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2;
16826                     do_pre_gc();
16827
16828                     // TODO BACKGROUND_GC need to add the profiling stuff here.
16829                     dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
16830                 }
16831
16832                 //clear the cards so they don't bleed in gen 1 during collection
16833                 // shouldn't this always be done at the beginning of any GC?
16834                 //clear_card_for_addresses (
16835                 //    generation_allocation_start (generation_of (0)),
16836                 //    heap_segment_allocated (ephemeral_heap_segment));
16837
16838                 if (!do_ephemeral_gc_p)
16839                 {
16840                     do_background_gc();
16841                 }
16842             }
16843             else
16844             {
16845                 settings.compaction = TRUE;
16846                 c_write (settings.concurrent, FALSE);
16847             }
16848
16849 #ifdef MULTIPLE_HEAPS
16850             gc_t_join.restart();
16851 #endif //MULTIPLE_HEAPS
16852         }
16853
16854         if (do_concurrent_p)
16855         {
16856             // At this point we are sure we'll be starting a BGC, so save its per heap data here.
16857             // global data is only calculated at the end of the GC so we don't need to worry about
16858             // FGCs overwriting it.
16859             memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap));
16860             memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
16861
16862             if (do_ephemeral_gc_p)
16863             {
16864                 dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
16865
16866                 gen_to_condemn_reasons.init();
16867                 gen_to_condemn_reasons.set_condition (gen_before_bgc);
16868                 gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
16869                 gc1();
16870 #ifdef MULTIPLE_HEAPS
16871                 gc_t_join.join(this, gc_join_bgc_after_ephemeral);
16872                 if (gc_t_join.joined())
16873 #endif //MULTIPLE_HEAPS
16874                 {
16875 #ifdef MULTIPLE_HEAPS
16876                     do_post_gc();
16877 #endif //MULTIPLE_HEAPS
16878                     settings = saved_bgc_settings;
16879                     assert (settings.concurrent);
16880
16881                     do_background_gc();
16882
16883 #ifdef MULTIPLE_HEAPS
16884                     gc_t_join.restart();
16885 #endif //MULTIPLE_HEAPS
16886                 }
16887             }
16888         }
16889         else
16890         {
16891             dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
16892             gc1();
16893         }
16894     }
16895     else
16896 #endif //BACKGROUND_GC
16897     {
16898         gc1();
16899     }
16900 #ifndef MULTIPLE_HEAPS
16901     allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp();
16902     allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
16903     fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
16904 #endif //MULTIPLE_HEAPS
16905
16906 done:
16907     if (settings.pause_mode == pause_no_gc)
16908         allocate_for_no_gc_after_gc();
16909
16910     int gn = settings.condemned_generation;
16911     return gn;
16912 }
16913
16914 #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
16915
16916 inline
16917 size_t& gc_heap::promoted_bytes(int thread)
16918 {
16919 #ifdef MULTIPLE_HEAPS
16920     return g_promoted [thread*16];
16921 #else //MULTIPLE_HEAPS
16922     UNREFERENCED_PARAMETER(thread);
16923     return g_promoted;
16924 #endif //MULTIPLE_HEAPS
16925 }
16926
16927 #ifdef INTERIOR_POINTERS
16928 heap_segment* gc_heap::find_segment (uint8_t* interior, BOOL small_segment_only_p)
16929 {
16930 #ifdef SEG_MAPPING_TABLE
16931     heap_segment* seg = seg_mapping_table_segment_of (interior);
16932     if (seg)
16933     {
16934         if (small_segment_only_p && heap_segment_loh_p (seg))
16935             return 0;
16936     }
16937     return seg;
16938 #else //SEG_MAPPING_TABLE
16939 #ifdef MULTIPLE_HEAPS
16940     for (int i = 0; i < gc_heap::n_heaps; i++)
16941     {
16942         gc_heap* h = gc_heap::g_heaps [i];
16943         hs = h->find_segment_per_heap (o, small_segment_only_p);
16944         if (hs)
16945         {
16946             break;
16947         }        
16948     }
16949 #else
16950     {
16951         gc_heap* h = pGenGCHeap;
16952         hs = h->find_segment_per_heap (o, small_segment_only_p);
16953     }
16954 #endif //MULTIPLE_HEAPS
16955 #endif //SEG_MAPPING_TABLE
16956 }
16957
16958 heap_segment* gc_heap::find_segment_per_heap (uint8_t* interior, BOOL small_segment_only_p)
16959 {
16960 #ifdef SEG_MAPPING_TABLE
16961     return find_segment (interior, small_segment_only_p);
16962 #else //SEG_MAPPING_TABLE
16963     if (in_range_for_segment (interior, ephemeral_heap_segment))
16964     {
16965         return ephemeral_heap_segment;
16966     }
16967     else
16968     {
16969         heap_segment* found_seg = 0;
16970
16971         {
16972             heap_segment* seg = generation_start_segment (generation_of (max_generation));
16973             do
16974             {
16975                 if (in_range_for_segment (interior, seg))
16976                 {
16977                     found_seg = seg;
16978                     goto end_find_segment;
16979                 }
16980
16981             } while ((seg = heap_segment_next (seg)) != 0);
16982         }
16983         if (!small_segment_only_p)
16984         {
16985 #ifdef BACKGROUND_GC
16986             {
16987                 ptrdiff_t delta = 0;
16988                 heap_segment* seg = segment_of (interior, delta);
16989                 if (seg && in_range_for_segment (interior, seg))
16990                 {
16991                     found_seg = seg;
16992                 }
16993                 goto end_find_segment;
16994             }
16995 #else //BACKGROUND_GC
16996             heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
16997             do
16998             {
16999                 if (in_range_for_segment(interior, seg))
17000                 {
17001                     found_seg = seg;
17002                     goto end_find_segment;
17003                 }
17004
17005             } while ((seg = heap_segment_next (seg)) != 0);
17006 #endif //BACKGROUND_GC
17007         }
17008 end_find_segment:
17009
17010         return found_seg;
17011     }
17012 #endif //SEG_MAPPING_TABLE
17013 }
17014 #endif //INTERIOR_POINTERS
17015
17016 #if !defined(_DEBUG) && !defined(__GNUC__)
17017 inline // This causes link errors if global optimization is off
17018 #endif //!_DEBUG && !__GNUC__
17019 gc_heap* gc_heap::heap_of (uint8_t* o)
17020 {
17021 #ifdef MULTIPLE_HEAPS
17022     if (o == 0)
17023         return g_heaps [0];
17024 #ifdef SEG_MAPPING_TABLE
17025     gc_heap* hp = seg_mapping_table_heap_of (o);
17026     return (hp ? hp : g_heaps[0]);
17027 #else //SEG_MAPPING_TABLE
17028     ptrdiff_t delta = 0;
17029     heap_segment* seg = segment_of (o, delta);
17030     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17031 #endif //SEG_MAPPING_TABLE
17032 #else //MULTIPLE_HEAPS
17033     UNREFERENCED_PARAMETER(o);
17034     return __this;
17035 #endif //MULTIPLE_HEAPS
17036 }
17037
17038 inline
17039 gc_heap* gc_heap::heap_of_gc (uint8_t* o)
17040 {
17041 #ifdef MULTIPLE_HEAPS
17042     if (o == 0)
17043         return g_heaps [0];
17044 #ifdef SEG_MAPPING_TABLE
17045     gc_heap* hp = seg_mapping_table_heap_of_gc (o);
17046     return (hp ? hp : g_heaps[0]);
17047 #else //SEG_MAPPING_TABLE
17048     ptrdiff_t delta = 0;
17049     heap_segment* seg = segment_of (o, delta);
17050     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17051 #endif //SEG_MAPPING_TABLE
17052 #else //MULTIPLE_HEAPS
17053     UNREFERENCED_PARAMETER(o);
17054     return __this;
17055 #endif //MULTIPLE_HEAPS
17056 }
17057
17058 #ifdef INTERIOR_POINTERS
17059 // will find all heap objects (large and small)
17060 uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
17061 {
17062     if (!gen0_bricks_cleared)
17063     {
17064 #ifdef MULTIPLE_HEAPS
17065         assert (!"Should have already been done in server GC");
17066 #endif //MULTIPLE_HEAPS
17067         gen0_bricks_cleared = TRUE;
17068         //initialize brick table for gen 0
17069         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
17070              b < brick_of (align_on_brick
17071                            (heap_segment_allocated (ephemeral_heap_segment)));
17072              b++)
17073         {
17074             set_brick (b, -1);
17075         }
17076     }
17077 #ifdef FFIND_OBJECT
17078     //indicate that in the future this needs to be done during allocation
17079 #ifdef MULTIPLE_HEAPS
17080     gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
17081 #else
17082     gen0_must_clear_bricks = FFIND_DECAY;
17083 #endif //MULTIPLE_HEAPS
17084 #endif //FFIND_OBJECT
17085
17086     int brick_entry = get_brick_entry(brick_of (interior));
17087     if (brick_entry == 0)
17088     {
17089         // this is a pointer to a large object
17090         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17091         if (seg
17092 #ifdef FEATURE_CONSERVATIVE_GC
17093             && (GCConfig::GetConservativeGC() || interior <= heap_segment_allocated(seg))
17094 #endif
17095             )
17096         {
17097             // If interior falls within the first free object at the beginning of a generation,
17098             // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
17099             int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
17100 #ifdef FEATURE_CONSERVATIVE_GC
17101                                                        || (GCConfig::GetConservativeGC() && !heap_segment_loh_p (seg))
17102 #endif
17103                                                       );
17104             //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
17105             assert (interior < heap_segment_allocated (seg));
17106
17107             uint8_t* o = heap_segment_mem (seg);
17108             while (o < heap_segment_allocated (seg))
17109             {
17110                 uint8_t* next_o = o + Align (size (o), align_const);
17111                 assert (next_o > o);
17112                 if ((o <= interior) && (interior < next_o))
17113                 return o;
17114                 o = next_o;
17115             }
17116             return 0;
17117         }
17118         else
17119         {
17120             return 0;
17121         }
17122     }
17123     else if (interior >= low)
17124     {
17125         heap_segment* seg = find_segment_per_heap (interior, TRUE);
17126         if (seg)
17127         {
17128 #ifdef FEATURE_CONSERVATIVE_GC
17129             if (interior >= heap_segment_allocated (seg))
17130                 return 0;
17131 #else
17132             assert (interior < heap_segment_allocated (seg));
17133 #endif
17134             uint8_t* o = find_first_object (interior, heap_segment_mem (seg));
17135             return o;
17136         }
17137         else
17138             return 0;
17139     }
17140     else
17141         return 0;
17142 }
17143
17144 uint8_t*
17145 gc_heap::find_object_for_relocation (uint8_t* interior, uint8_t* low, uint8_t* high)
17146 {
17147     uint8_t* old_address = interior;
17148     if (!((old_address >= low) && (old_address < high)))
17149         return 0;
17150     uint8_t* plug = 0;
17151     size_t  brick = brick_of (old_address);
17152     int    brick_entry =  brick_table [ brick ];
17153     if (brick_entry != 0)
17154     {
17155     retry:
17156         {
17157             while (brick_entry < 0)
17158             {
17159                 brick = (brick + brick_entry);
17160                 brick_entry =  brick_table [ brick ];
17161             }
17162             uint8_t* old_loc = old_address;
17163             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
17164                                       old_loc);
17165             if (node <= old_loc)
17166                 plug = node;
17167             else
17168             {
17169                 brick = brick - 1;
17170                 brick_entry =  brick_table [ brick ];
17171                 goto retry;
17172             }
17173
17174         }
17175         assert (plug);
17176         //find the object by going along the plug
17177         uint8_t* o = plug;
17178         while (o <= interior)
17179         {
17180             uint8_t* next_o = o + Align (size (o));
17181             assert (next_o > o);
17182             if (next_o > interior)
17183             {
17184                 break;
17185             }
17186             o = next_o;
17187         }
17188         assert ((o <= interior) && ((o + Align (size (o))) > interior));
17189         return o;
17190     }
17191     else
17192     {
17193         // this is a pointer to a large object
17194         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17195         if (seg)
17196         {
17197             assert (interior < heap_segment_allocated (seg));
17198
17199             uint8_t* o = heap_segment_mem (seg);
17200             while (o < heap_segment_allocated (seg))
17201             {
17202                 uint8_t* next_o = o + Align (size (o));
17203                 assert (next_o > o);
17204                 if ((o < interior) && (interior < next_o))
17205                 return o;
17206                 o = next_o;
17207             }
17208             return 0;
17209         }
17210         else
17211             {
17212             return 0;
17213         }
17214     }
17215 }
17216 #else //INTERIOR_POINTERS
17217 inline
17218 uint8_t* gc_heap::find_object (uint8_t* o, uint8_t* low)
17219 {
17220     return o;
17221 }
17222 #endif //INTERIOR_POINTERS
17223
17224 #ifdef MARK_LIST
17225 #ifdef GC_CONFIG_DRIVEN
17226 #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;}
17227 #else
17228 #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;}
17229 #endif //GC_CONFIG_DRIVEN
17230 #else //MARK_LIST
17231 #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17232 #endif //MARK_LIST
17233
17234 #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17235
17236 #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
17237
17238 inline
17239 BOOL gc_heap::gc_mark1 (uint8_t* o)
17240 {
17241     BOOL marked = !marked (o);
17242     set_marked (o);
17243     dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
17244     return marked;
17245 }
17246
17247 inline
17248 BOOL gc_heap::gc_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17249 {
17250     BOOL marked = FALSE;
17251     if ((o >= low) && (o < high))
17252         marked = gc_mark1 (o);
17253 #ifdef MULTIPLE_HEAPS
17254     else if (o)
17255     {
17256         //find the heap
17257         gc_heap* hp = heap_of_gc (o);
17258         assert (hp);
17259         if ((o >= hp->gc_low) && (o < hp->gc_high))
17260             marked = gc_mark1 (o);
17261     }
17262 #ifdef SNOOP_STATS
17263     snoop_stat.objects_checked_count++;
17264
17265     if (marked)
17266     {
17267         snoop_stat.objects_marked_count++;
17268     }
17269     if (!o)
17270     {
17271         snoop_stat.zero_ref_count++;
17272     }
17273
17274 #endif //SNOOP_STATS
17275 #endif //MULTIPLE_HEAPS
17276     return marked;
17277 }
17278
17279 #ifdef BACKGROUND_GC
17280
17281 inline
17282 BOOL gc_heap::background_marked (uint8_t* o)
17283 {
17284     return mark_array_marked (o);
17285 }
17286 inline
17287 BOOL gc_heap::background_mark1 (uint8_t* o)
17288 {
17289     BOOL to_mark = !mark_array_marked (o);
17290
17291     dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
17292     if (to_mark)
17293     {
17294         mark_array_set_marked (o);
17295         dprintf (4, ("n*%Ix*n", (size_t)o));
17296         return TRUE;
17297     }
17298     else
17299         return FALSE;
17300 }
17301
17302 // TODO: we could consider filtering out NULL's here instead of going to 
17303 // look for it on other heaps
17304 inline
17305 BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17306 {
17307     BOOL marked = FALSE;
17308     if ((o >= low) && (o < high))
17309         marked = background_mark1 (o);
17310 #ifdef MULTIPLE_HEAPS
17311     else if (o)
17312     {
17313         //find the heap
17314         gc_heap* hp = heap_of (o);
17315         assert (hp);
17316         if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
17317             marked = background_mark1 (o);
17318     }
17319 #endif //MULTIPLE_HEAPS
17320     return marked;
17321 }
17322
17323 #endif //BACKGROUND_GC
17324
17325 inline
17326 uint8_t* gc_heap::next_end (heap_segment* seg, uint8_t* f)
17327 {
17328     if (seg == ephemeral_heap_segment)
17329         return  f;
17330     else
17331         return  heap_segment_allocated (seg);
17332 }
17333
17334 #define new_start() {if (ppstop <= start) {break;} else {parm = start}}
17335 #define ignore_start 0
17336 #define use_start 1
17337
17338 #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp)      \
17339 {                                                                           \
17340     CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt));           \
17341     CGCDescSeries* cur = map->GetHighestSeries();                           \
17342     ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries();                        \
17343                                                                             \
17344     if (cnt >= 0)                                                           \
17345     {                                                                       \
17346         CGCDescSeries* last = map->GetLowestSeries();                       \
17347         uint8_t** parm = 0;                                                 \
17348         do                                                                  \
17349         {                                                                   \
17350             assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset()));     \
17351             parm = (uint8_t**)((o) + cur->GetSeriesOffset());               \
17352             uint8_t** ppstop =                                              \
17353                 (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\
17354             if (!start_useful || (uint8_t*)ppstop > (start))                \
17355             {                                                               \
17356                 if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\
17357                 while (parm < ppstop)                                       \
17358                 {                                                           \
17359                    {exp}                                                    \
17360                    parm++;                                                  \
17361                 }                                                           \
17362             }                                                               \
17363             cur--;                                                          \
17364                                                                             \
17365         } while (cur >= last);                                              \
17366     }                                                                       \
17367     else                                                                    \
17368     {                                                                       \
17369         /* Handle the repeating case - array of valuetypes */               \
17370         uint8_t** parm = (uint8_t**)((o) + cur->startoffset);               \
17371         if (start_useful && start > (uint8_t*)parm)                         \
17372         {                                                                   \
17373             ptrdiff_t cs = mt->RawGetComponentSize();                         \
17374             parm = (uint8_t**)((uint8_t*)parm + (((start) - (uint8_t*)parm)/cs)*cs); \
17375         }                                                                   \
17376         while ((uint8_t*)parm < ((o)+(size)-plug_skew))                     \
17377         {                                                                   \
17378             for (ptrdiff_t __i = 0; __i > cnt; __i--)                         \
17379             {                                                               \
17380                 HALF_SIZE_T skip =  cur->val_serie[__i].skip;               \
17381                 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs;              \
17382                 uint8_t** ppstop = parm + nptrs;                            \
17383                 if (!start_useful || (uint8_t*)ppstop > (start))            \
17384                 {                                                           \
17385                     if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);      \
17386                     do                                                      \
17387                     {                                                       \
17388                        {exp}                                                \
17389                        parm++;                                              \
17390                     } while (parm < ppstop);                                \
17391                 }                                                           \
17392                 parm = (uint8_t**)((uint8_t*)ppstop + skip);                \
17393             }                                                               \
17394         }                                                                   \
17395     }                                                                       \
17396 }
17397
17398 #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
17399
17400 // 1 thing to note about this macro:
17401 // 1) you can use *parm safely but in general you don't want to use parm 
17402 // because for the collectible types it's not an address on the managed heap.
17403 #ifndef COLLECTIBLE_CLASS
17404 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17405 {                                                                           \
17406     if (header(o)->ContainsPointers())                                      \
17407     {                                                                       \
17408         go_through_object_nostart(mt,o,size,parm,exp);                      \
17409     }                                                                       \
17410 }
17411 #else //COLLECTIBLE_CLASS
17412 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17413 {                                                                           \
17414     if (header(o)->Collectible())                                           \
17415     {                                                                       \
17416         uint8_t* class_obj = get_class_object (o);                             \
17417         uint8_t** parm = &class_obj;                                           \
17418         do {exp} while (false);                                             \
17419     }                                                                       \
17420     if (header(o)->ContainsPointers())                                      \
17421     {                                                                       \
17422         go_through_object_nostart(mt,o,size,parm,exp);                      \
17423     }                                                                       \
17424 }
17425 #endif //COLLECTIBLE_CLASS
17426
17427 // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
17428 void gc_heap::enque_pinned_plug (uint8_t* plug,
17429                                  BOOL save_pre_plug_info_p, 
17430                                  uint8_t* last_object_in_last_plug)
17431 {
17432     if (mark_stack_array_length <= mark_stack_tos)
17433     {
17434         if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
17435         {
17436             // we don't want to continue here due to security
17437             // risks. This happens very rarely and fixing it in the
17438             // way so that we can continue is a bit involved and will
17439             // not be done in Dev10.
17440             GCToEEInterface::HandleFatalError(CORINFO_EXCEPTION_GC);
17441         }
17442     }
17443
17444     dprintf (3, ("enqueuing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d", 
17445         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)));
17446     mark& m = mark_stack_array[mark_stack_tos];
17447     m.first = plug;
17448     // Must be set now because if we have a short object we'll need the value of saved_pre_p.
17449     m.saved_pre_p = save_pre_plug_info_p;
17450
17451     if (save_pre_plug_info_p)
17452     {
17453 #ifdef SHORT_PLUGS
17454         BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17455         if (is_padded)
17456             clear_plug_padded (last_object_in_last_plug);
17457 #endif //SHORT_PLUGS
17458         memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17459 #ifdef SHORT_PLUGS
17460         if (is_padded)
17461             set_plug_padded (last_object_in_last_plug);
17462 #endif //SHORT_PLUGS
17463
17464         memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17465
17466         // If the last object in the last plug is too short, it requires special handling.
17467         size_t last_obj_size = plug - last_object_in_last_plug;
17468         if (last_obj_size < min_pre_pin_obj_size)
17469         {
17470             record_interesting_data_point (idp_pre_short);
17471 #ifdef SHORT_PLUGS
17472             if (is_padded)
17473                 record_interesting_data_point (idp_pre_short_padded);
17474 #endif //SHORT_PLUGS
17475             dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!", 
17476                          last_object_in_last_plug, plug));
17477             // Need to set the short bit regardless of having refs or not because we need to 
17478             // indicate that this object is not walkable.
17479             m.set_pre_short();
17480
17481 #ifdef COLLECTIBLE_CLASS
17482             if (is_collectible (last_object_in_last_plug))
17483             {
17484                 m.set_pre_short_collectible();
17485             }
17486 #endif //COLLECTIBLE_CLASS
17487
17488             if (contain_pointers (last_object_in_last_plug))
17489             {
17490                 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17491
17492                 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17493                     {
17494                         size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17495                         dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17496                         m.set_pre_short_bit (gap_offset);
17497                     }
17498                 );
17499             }
17500         }
17501     }
17502
17503     m.saved_post_p = FALSE;
17504 }
17505
17506 void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_object_in_last_plug, uint8_t* post_plug)
17507 {
17508     UNREFERENCED_PARAMETER(last_pinned_plug);
17509
17510     mark& m = mark_stack_array[mark_stack_tos - 1];
17511     assert (last_pinned_plug == m.first);
17512     m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]);
17513
17514 #ifdef SHORT_PLUGS
17515     BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17516     if (is_padded)
17517         clear_plug_padded (last_object_in_last_plug);
17518 #endif //SHORT_PLUGS
17519     memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17520 #ifdef SHORT_PLUGS
17521     if (is_padded)
17522         set_plug_padded (last_object_in_last_plug);
17523 #endif //SHORT_PLUGS
17524
17525     memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17526
17527     // This is important - we need to clear all bits here except the last one.
17528     m.saved_post_p = TRUE;
17529
17530 #ifdef _DEBUG
17531     m.saved_post_plug_debug.gap = 1;
17532 #endif //_DEBUG
17533
17534     dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
17535
17536     size_t last_obj_size = post_plug - last_object_in_last_plug;
17537     if (last_obj_size < min_pre_pin_obj_size)
17538     {
17539         dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
17540         record_interesting_data_point (idp_post_short);
17541 #ifdef SHORT_PLUGS
17542         if (is_padded)
17543             record_interesting_data_point (idp_post_short_padded);
17544 #endif //SHORT_PLUGS
17545         m.set_post_short();
17546         verify_pinned_queue_p = TRUE;
17547
17548 #ifdef COLLECTIBLE_CLASS
17549         if (is_collectible (last_object_in_last_plug))
17550         {
17551             m.set_post_short_collectible();
17552         }
17553 #endif //COLLECTIBLE_CLASS
17554
17555         if (contain_pointers (last_object_in_last_plug))
17556         {
17557             dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17558
17559             // TODO: since we won't be able to walk this object in relocation, we still need to
17560             // take care of collectible assemblies here.
17561             go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17562                 {
17563                     size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17564                     dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17565                     m.set_post_short_bit (gap_offset);
17566                 }
17567             );
17568         }
17569     }
17570 }
17571
17572 //#define PREFETCH
17573 #ifdef PREFETCH
17574 __declspec(naked) void __fastcall Prefetch(void* addr)
17575 {
17576    __asm {
17577        PREFETCHT0 [ECX]
17578         ret
17579     };
17580 }
17581 #else //PREFETCH
17582 inline void Prefetch (void* addr)
17583 {
17584     UNREFERENCED_PARAMETER(addr);
17585 }
17586 #endif //PREFETCH
17587 #ifdef MH_SC_MARK
17588 inline
17589 VOLATILE(uint8_t*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
17590 {
17591     return ((VOLATILE(uint8_t*)*)(hp->mark_stack_array))[index];
17592 }
17593
17594 #endif //MH_SC_MARK
17595
17596 #define stolen 2
17597 #define partial 1
17598 #define partial_object 3
17599 inline 
17600 uint8_t* ref_from_slot (uint8_t* r)
17601 {
17602     return (uint8_t*)((size_t)r & ~(stolen | partial));
17603 }
17604 inline
17605 BOOL stolen_p (uint8_t* r)
17606 {
17607     return (((size_t)r&2) && !((size_t)r&1));
17608 }
17609 inline 
17610 BOOL ready_p (uint8_t* r)
17611 {
17612     return ((size_t)r != 1);
17613 }
17614 inline
17615 BOOL partial_p (uint8_t* r)
17616 {
17617     return (((size_t)r&1) && !((size_t)r&2));
17618 }
17619 inline 
17620 BOOL straight_ref_p (uint8_t* r)
17621 {
17622     return (!stolen_p (r) && !partial_p (r));
17623 }
17624 inline 
17625 BOOL partial_object_p (uint8_t* r)
17626 {
17627     return (((size_t)r & partial_object) == partial_object);
17628 }
17629 inline
17630 BOOL ref_p (uint8_t* r)
17631 {
17632     return (straight_ref_p (r) || partial_object_p (r));
17633 }
17634
17635 void gc_heap::mark_object_simple1 (uint8_t* oo, uint8_t* start THREAD_NUMBER_DCL)
17636 {
17637     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)mark_stack_array;
17638     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)&mark_stack_array[mark_stack_array_length];
17639     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_base = mark_stack_tos;
17640 #ifdef SORT_MARK_STACK
17641     SERVER_SC_MARK_VOLATILE(uint8_t*)* sorted_tos = mark_stack_base;
17642 #endif //SORT_MARK_STACK
17643
17644     // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't 
17645     // update mark list.
17646     BOOL  full_p = (settings.condemned_generation == max_generation);
17647
17648     assert ((start >= oo) && (start < oo+size(oo)));
17649
17650 #ifndef MH_SC_MARK
17651     *mark_stack_tos = oo;
17652 #endif //!MH_SC_MARK
17653
17654     while (1)
17655     {
17656 #ifdef MULTIPLE_HEAPS
17657 #else  //MULTIPLE_HEAPS
17658         const int thread = 0;
17659 #endif //MULTIPLE_HEAPS
17660
17661         if (oo && ((size_t)oo != 4))
17662         {
17663             size_t s = 0; 
17664             if (stolen_p (oo))
17665             {
17666                 --mark_stack_tos;
17667                 goto next_level;
17668             }
17669             else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
17670             {
17671                 BOOL overflow_p = FALSE;
17672
17673                 if (mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit  - 1))
17674                 {
17675                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
17676                     if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
17677                     {
17678                         overflow_p = TRUE;
17679                     }
17680                 }
17681                 
17682                 if (overflow_p == FALSE)
17683                 {
17684                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17685
17686                     go_through_object_cl (method_table(oo), oo, s, ppslot,
17687                                           {
17688                                               uint8_t* o = *ppslot;
17689                                               Prefetch(o);
17690                                               if (gc_mark (o, gc_low, gc_high))
17691                                               {
17692                                                   if (full_p)
17693                                                   {
17694                                                       m_boundary_fullgc (o);
17695                                                   }
17696                                                   else
17697                                                   {
17698                                                       m_boundary (o);
17699                                                   }
17700                                                   size_t obj_size = size (o);
17701                                                   promoted_bytes (thread) += obj_size;
17702                                                   if (contain_pointers_or_collectible (o))
17703                                                   {
17704                                                       *(mark_stack_tos++) = o;
17705                                                   }
17706                                               }
17707                                           }
17708                         );
17709                 }
17710                 else
17711                 {
17712                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17713                     min_overflow_address = min (min_overflow_address, oo);
17714                     max_overflow_address = max (max_overflow_address, oo);
17715                 }
17716             }
17717             else
17718             {
17719                 if (partial_p (oo))
17720                 {
17721                     start = ref_from_slot (oo);
17722                     oo = ref_from_slot (*(--mark_stack_tos));
17723                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
17724                     assert ((oo < start) && (start < (oo + size (oo))));
17725                 }
17726 #ifdef COLLECTIBLE_CLASS
17727                 else
17728                 {
17729                     // If there's a class object, push it now. We are guaranteed to have the slot since
17730                     // we just popped one object off.
17731                     if (is_collectible (oo))
17732                     {
17733                         uint8_t* class_obj = get_class_object (oo);
17734                         if (gc_mark (class_obj, gc_low, gc_high))
17735                         {
17736                             if (full_p)
17737                             {
17738                                 m_boundary_fullgc (class_obj);
17739                             }
17740                             else
17741                             {
17742                                 m_boundary (class_obj);
17743                             }
17744
17745                             size_t obj_size = size (class_obj);
17746                             promoted_bytes (thread) += obj_size;
17747                             *(mark_stack_tos++) = class_obj;
17748                             // The code below expects that the oo is still stored in the stack slot that was
17749                             // just popped and it "pushes" it back just by incrementing the mark_stack_tos. 
17750                             // But the class_obj has just overwritten that stack slot and so the oo needs to
17751                             // be stored to the new slot that's pointed to by the mark_stack_tos.
17752                             *mark_stack_tos = oo;
17753                         }
17754                     }
17755                 }
17756 #endif //COLLECTIBLE_CLASS
17757
17758                 s = size (oo);
17759                 
17760                 BOOL overflow_p = FALSE;
17761             
17762                 if (mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
17763                 {
17764                     overflow_p = TRUE;
17765                 }
17766                 if (overflow_p == FALSE)
17767                 {
17768                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17769
17770                     //push the object and its current 
17771                     SERVER_SC_MARK_VOLATILE(uint8_t*)* place = ++mark_stack_tos;
17772                     mark_stack_tos++;
17773 #ifdef MH_SC_MARK
17774                     *(place-1) = 0;
17775                     *(place) = (uint8_t*)partial;
17776 #endif //MH_SC_MARK
17777                     int i = num_partial_refs; 
17778                     uint8_t* ref_to_continue = 0;
17779
17780                     go_through_object (method_table(oo), oo, s, ppslot,
17781                                        start, use_start, (oo + s),
17782                                        {
17783                                            uint8_t* o = *ppslot;
17784                                            Prefetch(o);
17785                                            if (gc_mark (o, gc_low, gc_high))
17786                                            {
17787                                                 if (full_p)
17788                                                 {
17789                                                     m_boundary_fullgc (o);
17790                                                 }
17791                                                 else
17792                                                 {
17793                                                     m_boundary (o);
17794                                                 }
17795                                                 size_t obj_size = size (o);
17796                                                 promoted_bytes (thread) += obj_size;
17797                                                 if (contain_pointers_or_collectible (o))
17798                                                 {
17799                                                     *(mark_stack_tos++) = o;
17800                                                     if (--i == 0)
17801                                                     {
17802                                                         ref_to_continue = (uint8_t*)((size_t)(ppslot+1) | partial);
17803                                                         goto more_to_do;
17804                                                     }
17805
17806                                                 }
17807                                            }
17808
17809                                        }
17810                         );
17811                     //we are finished with this object
17812                     assert (ref_to_continue == 0);
17813 #ifdef MH_SC_MARK
17814                     assert ((*(place-1)) == (uint8_t*)0);
17815 #else //MH_SC_MARK
17816                     *(place-1) = 0;
17817 #endif //MH_SC_MARK
17818                     *place = 0; 
17819                     // shouldn't we decrease tos by 2 here??
17820
17821 more_to_do:
17822                     if (ref_to_continue)
17823                     {
17824                         //update the start
17825 #ifdef MH_SC_MARK
17826                         assert ((*(place-1)) == (uint8_t*)0);
17827                         *(place-1) = (uint8_t*)((size_t)oo | partial_object);
17828                         assert (((*place) == (uint8_t*)1) || ((*place) == (uint8_t*)2));
17829 #endif //MH_SC_MARK
17830                         *place = ref_to_continue;
17831                     }
17832                 }
17833                 else
17834                 {
17835                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17836                     min_overflow_address = min (min_overflow_address, oo);
17837                     max_overflow_address = max (max_overflow_address, oo);
17838                 }
17839             }
17840 #ifdef SORT_MARK_STACK
17841             if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
17842             {
17843                 rqsort1 (sorted_tos, mark_stack_tos-1);
17844                 sorted_tos = mark_stack_tos-1;
17845             }
17846 #endif //SORT_MARK_STACK
17847         }
17848     next_level:
17849         if (!(mark_stack_empty_p()))
17850         {
17851             oo = *(--mark_stack_tos);
17852             start = oo;
17853
17854 #ifdef SORT_MARK_STACK
17855             sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
17856 #endif //SORT_MARK_STACK
17857         }
17858         else
17859             break;
17860     }
17861 }
17862
17863 #ifdef MH_SC_MARK
17864 BOOL same_numa_node_p (int hn1, int hn2)
17865 {
17866     return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
17867 }
17868
17869 int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
17870 {
17871     int hn = (current_buddy+1)%n_heaps;
17872     while (hn != current_buddy)
17873     {
17874         if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
17875             return hn;
17876         hn = (hn+1)%n_heaps;
17877     }
17878     return current_buddy;
17879 }
17880
17881 void 
17882 gc_heap::mark_steal()
17883 {
17884     mark_stack_busy() = 0;
17885     //clear the mark stack in the snooping range
17886     for (int i = 0; i < max_snoop_level; i++)
17887     {
17888         ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
17889     }
17890
17891     //pick the next heap as our buddy
17892     int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
17893
17894 #ifdef SNOOP_STATS
17895         dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
17896         uint32_t begin_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
17897 #endif //SNOOP_STATS
17898
17899     int idle_loop_count = 0; 
17900     int first_not_ready_level = 0;
17901
17902     while (1)
17903     {
17904         gc_heap* hp = g_heaps [thpn];
17905         int level = first_not_ready_level;
17906         first_not_ready_level = 0; 
17907
17908         while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
17909         {
17910             idle_loop_count = 0; 
17911 #ifdef SNOOP_STATS
17912             snoop_stat.busy_count++;
17913             dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix", 
17914                                  heap_number, level, (int)((uint8_t**)(hp->mark_stack_array))[level]));
17915 #endif //SNOOP_STATS
17916
17917             uint8_t* o = ref_mark_stack (hp, level);
17918
17919             uint8_t* start = o;
17920             if (ref_p (o))
17921             {
17922                 mark_stack_busy() = 1;
17923
17924                 BOOL success = TRUE;
17925                 uint8_t* next = (ref_mark_stack (hp, level+1));
17926                 if (ref_p (next))
17927                 {
17928                     if (((size_t)o > 4) && !partial_object_p (o))
17929                     {
17930                         //this is a normal object, not a partial mark tuple
17931                         //success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
17932                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), (uint8_t*)4, o)==o);
17933 #ifdef SNOOP_STATS
17934                         snoop_stat.interlocked_count++;
17935                         if (success)
17936                             snoop_stat.normal_count++;
17937 #endif //SNOOP_STATS
17938                     }
17939                     else
17940                     {
17941                         //it is a stolen entry, or beginning/ending of a partial mark
17942                         level++;
17943 #ifdef SNOOP_STATS
17944                         snoop_stat.stolen_or_pm_count++;
17945 #endif //SNOOP_STATS
17946                         success = FALSE;
17947                     }
17948                 }
17949                 else if (stolen_p (next))
17950                 {
17951                     //ignore the stolen guy and go to the next level
17952                     success = FALSE;
17953                     level+=2;
17954 #ifdef SNOOP_STATS
17955                     snoop_stat.stolen_entry_count++;
17956 #endif //SNOOP_STATS
17957                 }
17958                 else
17959                 {
17960                     assert (partial_p (next));
17961                     start = ref_from_slot (next);
17962                     //re-read the object
17963                     o = ref_from_slot (ref_mark_stack (hp, level));
17964                     if (o && start)
17965                     {
17966                         //steal the object
17967                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level+1), (uint8_t*)stolen, next)==next);
17968 #ifdef SNOOP_STATS
17969                         snoop_stat.interlocked_count++;
17970                         if (success)
17971                         {
17972                             snoop_stat.partial_mark_parent_count++;                    
17973                         }
17974 #endif //SNOOP_STATS
17975                     }
17976                     else
17977                     {
17978                         // stack is not ready, or o is completely different from the last time we read from this stack level.
17979                         // go up 2 levels to steal children or totally unrelated objects.
17980                         success = FALSE;
17981                         if (first_not_ready_level == 0)
17982                         {
17983                             first_not_ready_level = level;
17984                         }
17985                         level+=2;
17986 #ifdef SNOOP_STATS
17987                         snoop_stat.pm_not_ready_count++;
17988 #endif //SNOOP_STATS                        
17989                     }
17990                 }
17991                 if (success)
17992                 {
17993
17994 #ifdef SNOOP_STATS
17995                     dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
17996                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
17997                             (GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
17998                     uint32_t start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
17999 #endif //SNOOP_STATS
18000
18001                     mark_object_simple1 (o, start, heap_number);
18002
18003 #ifdef SNOOP_STATS
18004                     dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
18005                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18006                             (GCToOSInterface::GetLowPrecisionTimeStamp()-start_tick),(GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18007 #endif //SNOOP_STATS
18008
18009                     mark_stack_busy() = 0;
18010
18011                     //clear the mark stack in snooping range
18012                     for (int i = 0; i < max_snoop_level; i++)
18013                     {
18014                         if (((uint8_t**)mark_stack_array)[i] != 0)
18015                         {
18016                             ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18017 #ifdef SNOOP_STATS
18018                             snoop_stat.stack_bottom_clear_count++;
18019 #endif //SNOOP_STATS
18020                         }
18021                     }
18022
18023                     level = 0; 
18024                 }
18025                 mark_stack_busy() = 0;
18026             }
18027             else
18028             {
18029                 //slot is either partial or stolen
18030                 level++;
18031             }
18032         }
18033         if ((first_not_ready_level != 0) && hp->mark_stack_busy())
18034         {
18035             continue;
18036         } 
18037         if (!hp->mark_stack_busy())
18038         {
18039             first_not_ready_level = 0; 
18040             idle_loop_count++;
18041
18042             if ((idle_loop_count % (6) )==1)
18043             {
18044 #ifdef SNOOP_STATS
18045                 snoop_stat.switch_to_thread_count++;
18046 #endif //SNOOP_STATS
18047                 GCToOSInterface::Sleep(1);
18048             }
18049             int free_count = 1;
18050 #ifdef SNOOP_STATS
18051             snoop_stat.stack_idle_count++;
18052             //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
18053 #endif //SNOOP_STATS
18054             for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
18055             {
18056                 if (!((g_heaps [hpn])->mark_stack_busy()))
18057                 {
18058                     free_count++;
18059 #ifdef SNOOP_STATS
18060                 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
18061 #endif //SNOOP_STATS
18062                 }
18063                 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
18064                 {
18065                     thpn = hpn;
18066                     break;
18067                 }
18068                 hpn = (hpn+1)%n_heaps;
18069                 YieldProcessor();
18070             }
18071             if (free_count == n_heaps)
18072             {
18073                 break;
18074             }
18075         }
18076     }
18077 }
18078
18079 inline
18080 BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
18081 {
18082 #ifdef SNOOP_STATS
18083     snoop_stat.check_level_count++;
18084 #endif //SNOOP_STATS
18085     return (next_heap->mark_stack_busy()>=1);
18086 }
18087 #endif //MH_SC_MARK
18088
18089 #ifdef SNOOP_STATS
18090 void gc_heap::print_snoop_stat()
18091 {
18092     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18093         "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
18094     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
18095         snoop_stat.heap_index,
18096         snoop_stat.objects_checked_count,
18097         snoop_stat.zero_ref_count,
18098         snoop_stat.objects_marked_count,
18099         snoop_stat.stolen_stack_count,
18100         snoop_stat.partial_stack_count,
18101         snoop_stat.normal_stack_count,
18102         snoop_stat.non_stack_count));
18103     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18104         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
18105     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18106         snoop_stat.heap_index,
18107         snoop_stat.check_level_count,
18108         snoop_stat.busy_count,
18109         snoop_stat.interlocked_count,
18110         snoop_stat.partial_mark_parent_count,
18111         snoop_stat.stolen_or_pm_count,
18112         snoop_stat.stolen_entry_count,
18113         snoop_stat.pm_not_ready_count,
18114         snoop_stat.normal_count,
18115         snoop_stat.stack_bottom_clear_count));
18116
18117     printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n", 
18118         "heap", "check", "zero", "mark", "idle", "switch");
18119     printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
18120         snoop_stat.heap_index,
18121         snoop_stat.objects_checked_count,
18122         snoop_stat.zero_ref_count,
18123         snoop_stat.objects_marked_count,
18124         snoop_stat.stack_idle_count,
18125         snoop_stat.switch_to_thread_count);
18126     printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
18127         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18128     printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18129         snoop_stat.heap_index,
18130         snoop_stat.check_level_count,
18131         snoop_stat.busy_count,
18132         snoop_stat.interlocked_count,
18133         snoop_stat.partial_mark_parent_count,
18134         snoop_stat.stolen_or_pm_count,
18135         snoop_stat.stolen_entry_count,
18136         snoop_stat.pm_not_ready_count,
18137         snoop_stat.normal_count,
18138         snoop_stat.stack_bottom_clear_count);
18139 }
18140 #endif //SNOOP_STATS
18141
18142 #ifdef HEAP_ANALYZE
18143 void
18144 gc_heap::ha_mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18145 {
18146     if (!internal_root_array)
18147     {
18148         internal_root_array = new (nothrow) uint8_t* [internal_root_array_length];
18149         if (!internal_root_array)
18150         {
18151             heap_analyze_success = FALSE;
18152         }
18153     }
18154
18155     if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
18156     {
18157         size_t new_size = 2*internal_root_array_length;
18158
18159         uint64_t available_physical = 0;
18160         get_memory_info (NULL, &available_physical);
18161         if (new_size > (size_t)(available_physical / 10))
18162         {
18163             heap_analyze_success = FALSE;
18164         }
18165         else
18166         {
18167             uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18168             if (tmp)
18169             {
18170                 memcpy (tmp, internal_root_array,
18171                         internal_root_array_length*sizeof (uint8_t*));
18172                 delete[] internal_root_array;
18173                 internal_root_array = tmp;
18174                 internal_root_array_length = new_size;
18175             }
18176             else
18177             {
18178                 heap_analyze_success = FALSE;
18179             }
18180         }
18181     }
18182
18183     if (heap_analyze_success)
18184     {
18185         PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
18186
18187         uint8_t* ref = (uint8_t*)po;
18188         if (!current_obj || 
18189             !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
18190         {
18191             gc_heap* hp = gc_heap::heap_of (ref);
18192             current_obj = hp->find_object (ref, hp->lowest_address);
18193             current_obj_size = size (current_obj);
18194
18195             internal_root_array[internal_root_array_index] = current_obj;
18196             internal_root_array_index++;
18197         }
18198     }
18199
18200     mark_object_simple (po THREAD_NUMBER_ARG);
18201 }
18202 #endif //HEAP_ANALYZE
18203
18204 //this method assumes that *po is in the [low. high[ range
18205 void
18206 gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18207 {
18208     uint8_t* o = *po;
18209 #ifdef MULTIPLE_HEAPS
18210 #else  //MULTIPLE_HEAPS
18211     const int thread = 0;
18212 #endif //MULTIPLE_HEAPS
18213     {
18214 #ifdef SNOOP_STATS
18215         snoop_stat.objects_checked_count++;
18216 #endif //SNOOP_STATS
18217
18218         if (gc_mark1 (o))
18219         {
18220             m_boundary (o);
18221             size_t s = size (o);
18222             promoted_bytes (thread) += s;
18223             {
18224                 go_through_object_cl (method_table(o), o, s, poo,
18225                                         {
18226                                             uint8_t* oo = *poo;
18227                                             if (gc_mark (oo, gc_low, gc_high))
18228                                             {
18229                                                 m_boundary (oo);
18230                                                 size_t obj_size = size (oo);
18231                                                 promoted_bytes (thread) += obj_size;
18232
18233                                                 if (contain_pointers_or_collectible (oo))
18234                                                     mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
18235                                             }
18236                                         }
18237                     );
18238             }
18239         }
18240     }
18241 }
18242
18243 inline
18244 uint8_t* gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL)
18245 {
18246     if ((o >= gc_low) && (o < gc_high))
18247         mark_object_simple (&o THREAD_NUMBER_ARG);
18248 #ifdef MULTIPLE_HEAPS
18249     else if (o)
18250     {
18251         //find the heap
18252         gc_heap* hp = heap_of (o);
18253         assert (hp);
18254         if ((o >= hp->gc_low) && (o < hp->gc_high))
18255             mark_object_simple (&o THREAD_NUMBER_ARG);
18256     }
18257 #endif //MULTIPLE_HEAPS
18258
18259     return o;
18260 }
18261
18262 #ifdef BACKGROUND_GC
18263
18264 void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL)
18265 {
18266     uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
18267
18268 #ifdef SORT_MARK_STACK
18269     uint8_t** sorted_tos = background_mark_stack_array;
18270 #endif //SORT_MARK_STACK
18271
18272     background_mark_stack_tos = background_mark_stack_array;
18273
18274     while (1)
18275     {
18276 #ifdef MULTIPLE_HEAPS
18277 #else  //MULTIPLE_HEAPS
18278         const int thread = 0;
18279 #endif //MULTIPLE_HEAPS
18280         if (oo)
18281         {
18282             size_t s = 0; 
18283             if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18284             {
18285                 BOOL overflow_p = FALSE;
18286             
18287                 if (background_mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1))
18288                 {
18289                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18290                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18291                     if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
18292                     {
18293                         dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs", 
18294                             heap_number,
18295                             (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
18296                             method_table(oo), 
18297                             num_pointers));
18298
18299                         bgc_overflow_count++;
18300                         overflow_p = TRUE;
18301                     }
18302                 }
18303             
18304                 if (overflow_p == FALSE)
18305                 {
18306                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18307
18308                     go_through_object_cl (method_table(oo), oo, s, ppslot,
18309                     {
18310                         uint8_t* o = *ppslot;
18311                         Prefetch(o);
18312                         if (background_mark (o, 
18313                                              background_saved_lowest_address, 
18314                                              background_saved_highest_address))
18315                         {
18316                             //m_boundary (o);
18317                             size_t obj_size = size (o);
18318                             bpromoted_bytes (thread) += obj_size;
18319                             if (contain_pointers_or_collectible (o))
18320                             {
18321                                 *(background_mark_stack_tos++) = o;
18322
18323                             }
18324                         }
18325                     }
18326                         );
18327                 }
18328                 else
18329                 {
18330                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18331                     background_min_overflow_address = min (background_min_overflow_address, oo);
18332                     background_max_overflow_address = max (background_max_overflow_address, oo);
18333                 }
18334             }
18335             else 
18336             {
18337                 uint8_t* start = oo;
18338                 if ((size_t)oo & 1)
18339                 {
18340                     oo = (uint8_t*)((size_t)oo & ~1);
18341                     start = *(--background_mark_stack_tos);
18342                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18343                 }
18344 #ifdef COLLECTIBLE_CLASS
18345                 else
18346                 {
18347                     // If there's a class object, push it now. We are guaranteed to have the slot since
18348                     // we just popped one object off.
18349                     if (is_collectible (oo))
18350                     {
18351                         uint8_t* class_obj = get_class_object (oo);
18352                         if (background_mark (class_obj, 
18353                                             background_saved_lowest_address, 
18354                                             background_saved_highest_address))
18355                         {
18356                             size_t obj_size = size (class_obj);
18357                             bpromoted_bytes (thread) += obj_size;
18358
18359                             *(background_mark_stack_tos++) = class_obj;
18360                         }
18361                     }
18362                 }
18363 #endif //COLLECTIBLE_CLASS
18364
18365                 s = size (oo);
18366                 
18367                 BOOL overflow_p = FALSE;
18368             
18369                 if (background_mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
18370                 {
18371                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18372                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18373
18374                     dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id", 
18375                         heap_number,
18376                         (size_t)(mark_stack_limit - background_mark_stack_tos),
18377                         oo,
18378                         method_table(oo), 
18379                         start,
18380                         num_pointers));
18381
18382                     bgc_overflow_count++;
18383                     overflow_p = TRUE;
18384                 }
18385                 if (overflow_p == FALSE)
18386                 {
18387                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18388
18389                     //push the object and its current 
18390                     uint8_t** place = background_mark_stack_tos++;
18391                     *(place) = start;
18392                     *(background_mark_stack_tos++) = (uint8_t*)((size_t)oo | 1);
18393
18394                     int i = num_partial_refs; 
18395
18396                     go_through_object (method_table(oo), oo, s, ppslot,
18397                                        start, use_start, (oo + s),
18398                     {
18399                         uint8_t* o = *ppslot;
18400                         Prefetch(o);
18401
18402                         if (background_mark (o, 
18403                                             background_saved_lowest_address, 
18404                                             background_saved_highest_address))
18405                         {
18406                             //m_boundary (o);
18407                             size_t obj_size = size (o);
18408                             bpromoted_bytes (thread) += obj_size;
18409                             if (contain_pointers_or_collectible (o))
18410                             {
18411                                 *(background_mark_stack_tos++) = o;
18412                                 if (--i == 0)
18413                                 {
18414                                     //update the start
18415                                     *place = (uint8_t*)(ppslot+1);
18416                                     goto more_to_do;
18417                                 }
18418
18419                             }
18420                         }
18421
18422                     }
18423                         );
18424                     //we are finished with this object
18425                     *place = 0; 
18426                     *(place+1) = 0;
18427
18428                 more_to_do:;
18429                 }
18430                 else
18431                 {
18432                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18433                     background_min_overflow_address = min (background_min_overflow_address, oo);
18434                     background_max_overflow_address = max (background_max_overflow_address, oo);
18435                 }
18436             }
18437         }
18438 #ifdef SORT_MARK_STACK
18439         if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18440         {
18441             rqsort1 (sorted_tos, background_mark_stack_tos-1);
18442             sorted_tos = background_mark_stack_tos-1;
18443         }
18444 #endif //SORT_MARK_STACK
18445
18446         allow_fgc();
18447
18448         if (!(background_mark_stack_tos == background_mark_stack_array))
18449         {
18450             oo = *(--background_mark_stack_tos);
18451
18452 #ifdef SORT_MARK_STACK
18453             sorted_tos = (uint8_t**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
18454 #endif //SORT_MARK_STACK
18455         }
18456         else
18457             break;
18458     }
18459
18460     assert (background_mark_stack_tos == background_mark_stack_array);
18461
18462
18463 }
18464
18465 //this version is different than the foreground GC because
18466 //it can't keep pointers to the inside of an object
18467 //while calling background_mark_simple1. The object could be moved
18468 //by an intervening foreground gc.
18469 //this method assumes that *po is in the [low. high[ range
18470 void
18471 gc_heap::background_mark_simple (uint8_t* o THREAD_NUMBER_DCL)
18472 {
18473 #ifdef MULTIPLE_HEAPS
18474 #else  //MULTIPLE_HEAPS
18475     const int thread = 0;
18476 #endif //MULTIPLE_HEAPS
18477     {
18478         dprintf (3, ("bmarking %Ix", o));
18479         
18480         if (background_mark1 (o))
18481         {
18482             //m_boundary (o);
18483             size_t s = size (o);
18484             bpromoted_bytes (thread) += s;
18485
18486             if (contain_pointers_or_collectible (o))
18487             {
18488                 background_mark_simple1 (o THREAD_NUMBER_ARG);
18489             }
18490         }
18491     }
18492 }
18493
18494 inline
18495 uint8_t* gc_heap::background_mark_object (uint8_t* o THREAD_NUMBER_DCL)
18496 {
18497     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
18498     {
18499         background_mark_simple (o THREAD_NUMBER_ARG);
18500     }
18501     else
18502     {
18503         if (o)
18504         {
18505             dprintf (3, ("or-%Ix", o));
18506         }
18507     }
18508     return o;
18509 }
18510
18511 void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, uint32_t flags)
18512 {
18513     UNREFERENCED_PARAMETER(sc);
18514
18515     assert (settings.concurrent);
18516     uint8_t* o = (uint8_t*)object;
18517
18518     gc_heap* hp = gc_heap::heap_of (o);
18519 #ifdef INTERIOR_POINTERS
18520     if (flags & GC_CALL_INTERIOR)
18521     {
18522         o = hp->find_object (o, background_saved_lowest_address);
18523     }
18524 #endif //INTERIOR_POINTERS
18525
18526     if (!background_object_marked (o, FALSE))
18527     {
18528         FATAL_GC_ERROR();
18529     }
18530 }
18531
18532 void gc_heap::background_promote (Object** ppObject, ScanContext* sc, uint32_t flags)
18533 {
18534     UNREFERENCED_PARAMETER(sc);
18535     //in order to save space on the array, mark the object,
18536     //knowing that it will be visited later
18537     assert (settings.concurrent);
18538
18539     THREAD_NUMBER_FROM_CONTEXT;
18540 #ifndef MULTIPLE_HEAPS
18541     const int thread = 0;
18542 #endif //!MULTIPLE_HEAPS
18543
18544     uint8_t* o = (uint8_t*)*ppObject;
18545
18546     if (o == 0)
18547         return;
18548
18549 #ifdef DEBUG_DestroyedHandleValue
18550     // we can race with destroy handle during concurrent scan
18551     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
18552         return;
18553 #endif //DEBUG_DestroyedHandleValue
18554
18555     HEAP_FROM_THREAD;
18556
18557     gc_heap* hp = gc_heap::heap_of (o);
18558
18559     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
18560     {
18561         return;
18562     }
18563
18564 #ifdef INTERIOR_POINTERS
18565     if (flags & GC_CALL_INTERIOR)
18566     {
18567         o = hp->find_object (o, hp->background_saved_lowest_address);
18568         if (o == 0)
18569             return;
18570     }
18571 #endif //INTERIOR_POINTERS
18572
18573 #ifdef FEATURE_CONSERVATIVE_GC
18574     // For conservative GC, a value on stack may point to middle of a free object.
18575     // In this case, we don't need to promote the pointer.
18576     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
18577     {
18578         return;
18579     }
18580 #endif //FEATURE_CONSERVATIVE_GC
18581
18582 #ifdef _DEBUG
18583     ((CObjectHeader*)o)->Validate();
18584 #endif //_DEBUG
18585
18586     dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
18587
18588     //needs to be called before the marking because it is possible for a foreground
18589     //gc to take place during the mark and move the object
18590     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
18591
18592     hpt->background_mark_simple (o THREAD_NUMBER_ARG);
18593 }
18594
18595 //used by the ephemeral collection to scan the local background structures
18596 //containing references.
18597 void
18598 gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
18599 {
18600     ScanContext sc;
18601     if (pSC == 0)
18602         pSC = &sc;
18603
18604     pSC->thread_number = hn;
18605
18606 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
18607     pSC->pCurrentDomain = 0;
18608 #endif
18609
18610     BOOL relocate_p = (fn == &GCHeap::Relocate);
18611
18612     dprintf (3, ("Scanning background mark list"));
18613
18614     //scan mark_list
18615     size_t mark_list_finger = 0;
18616     while (mark_list_finger < c_mark_list_index)
18617     {
18618         uint8_t** o = &c_mark_list [mark_list_finger];
18619         if (!relocate_p)
18620         {
18621             // We may not be able to calculate the size during relocate as POPO
18622             // may have written over the object.
18623             size_t s = size (*o);
18624             assert (Align (s) >= Align (min_obj_size));
18625             dprintf(3,("background root %Ix", (size_t)*o));
18626         }
18627         (*fn) ((Object**)o, pSC, 0);
18628         mark_list_finger++;
18629     }
18630
18631     //scan the mark stack
18632     dprintf (3, ("Scanning background mark stack"));
18633
18634     uint8_t** finger = background_mark_stack_array;
18635     while (finger < background_mark_stack_tos)
18636     {
18637         if ((finger + 1) < background_mark_stack_tos)
18638         {
18639             // We need to check for the partial mark case here.
18640             uint8_t* parent_obj = *(finger + 1);
18641             if ((size_t)parent_obj & 1)
18642             {
18643                 uint8_t* place = *finger;
18644                 size_t place_offset = 0;
18645                 uint8_t* real_parent_obj = (uint8_t*)((size_t)parent_obj & ~1);
18646
18647                 if (relocate_p)
18648                 {
18649                     *(finger + 1) = real_parent_obj;
18650                     place_offset = place - real_parent_obj;
18651                     dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
18652                     (*fn) ((Object**)(finger + 1), pSC, 0);
18653                     real_parent_obj = *(finger + 1);
18654                     *finger = real_parent_obj + place_offset;
18655                     *(finger + 1) = (uint8_t*)((size_t)real_parent_obj | 1);
18656                     dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
18657                 }
18658                 else
18659                 {
18660                     uint8_t** temp = &real_parent_obj;
18661                     dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
18662                     (*fn) ((Object**)temp, pSC, 0);
18663                 }
18664
18665                 finger += 2;
18666                 continue;
18667             }
18668         }
18669         dprintf(3,("background root %Ix", (size_t)*finger));
18670         (*fn) ((Object**)finger, pSC, 0);
18671         finger++;
18672     }
18673 }
18674
18675 inline
18676 void gc_heap::background_mark_through_object (uint8_t* oo THREAD_NUMBER_DCL)
18677 {
18678     if (contain_pointers (oo))
18679     {
18680         size_t total_refs = 0;
18681         size_t s = size (oo);
18682         go_through_object_nostart (method_table(oo), oo, s, po,
18683                           {
18684                             uint8_t* o = *po;
18685                             total_refs++;
18686                             background_mark_object (o THREAD_NUMBER_ARG);
18687                           }
18688             );
18689
18690         dprintf (3,("Background marking through %Ix went through %Id refs", 
18691                           (size_t)oo,
18692                            total_refs));
18693     }
18694 }
18695
18696 uint8_t* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
18697 {
18698     if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
18699     {
18700         // for now we stop at where gen1 started when we started processing 
18701         return background_min_soh_overflow_address;
18702     }
18703     else
18704     {
18705         return heap_segment_allocated (seg);
18706     }
18707 }
18708
18709 uint8_t* gc_heap::background_first_overflow (uint8_t* min_add,
18710                                           heap_segment* seg,
18711                                           BOOL concurrent_p, 
18712                                           BOOL small_object_p)
18713 {
18714     uint8_t* o = 0;
18715
18716     if (small_object_p)
18717     {
18718         if (in_range_for_segment (min_add, seg))
18719         {
18720             // min_add was the beginning of gen1 when we did the concurrent
18721             // overflow. Now we could be in a situation where min_add is
18722             // actually the same as allocated for that segment (because
18723             // we expanded heap), in which case we can not call 
18724             // find first on this address or we will AV.
18725             if (min_add >= heap_segment_allocated (seg))
18726             {
18727                 return min_add;
18728             }
18729             else
18730             {
18731                 if (concurrent_p && 
18732                     ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
18733                 {
18734                     return background_min_soh_overflow_address;
18735                 }
18736                 else
18737                 {
18738                     o = find_first_object (min_add, heap_segment_mem (seg));
18739                     return o;
18740                 }
18741             }
18742         }
18743     }
18744
18745     o = max (heap_segment_mem (seg), min_add);
18746     return o;
18747 }
18748
18749 void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
18750                                                          uint8_t* min_add, uint8_t* max_add,
18751                                                          BOOL concurrent_p)
18752 {
18753     if (concurrent_p)
18754     {
18755         current_bgc_state = bgc_overflow_soh;
18756     }
18757
18758     size_t total_marked_objects = 0;
18759
18760 #ifdef MULTIPLE_HEAPS
18761     int thread = heap_number;
18762 #endif //MULTIPLE_HEAPS
18763
18764     exclusive_sync* loh_alloc_lock = 0;
18765
18766     dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
18767 #ifdef MULTIPLE_HEAPS
18768     // We don't have each heap scan all heaps concurrently because we are worried about
18769     // multiple threads calling things like find_first_object.
18770     int h_start = (concurrent_p ? heap_number : 0);
18771     int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
18772     for (int hi = h_start; hi < h_end; hi++)
18773     {
18774         gc_heap*  hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
18775
18776 #else
18777     {
18778         gc_heap*  hp = 0;
18779
18780 #endif //MULTIPLE_HEAPS
18781         BOOL small_object_segments = TRUE;
18782         int align_const = get_alignment_constant (small_object_segments);
18783         generation* gen = hp->generation_of (condemned_gen_number);
18784         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
18785         PREFIX_ASSUME(seg != NULL);
18786         loh_alloc_lock = hp->bgc_alloc_lock;
18787
18788         uint8_t* o = hp->background_first_overflow (min_add,
18789                                                     seg, 
18790                                                     concurrent_p, 
18791                                                     small_object_segments);
18792
18793         while (1)
18794         {
18795             while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
18796             {
18797                 dprintf (3, ("considering %Ix", (size_t)o));
18798
18799                 size_t s;
18800
18801                 if (concurrent_p && !small_object_segments)
18802                 {
18803                     loh_alloc_lock->bgc_mark_set (o);
18804
18805                     if (((CObjectHeader*)o)->IsFree())
18806                     {
18807                         s = unused_array_size (o);
18808                     }
18809                     else
18810                     {
18811                         s = size (o);
18812                     }
18813                 }
18814                 else
18815                 {
18816                     s = size (o);
18817                 }
18818
18819                 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
18820                 {
18821                     total_marked_objects++;
18822                     go_through_object_cl (method_table(o), o, s, poo,
18823                                           uint8_t* oo = *poo;
18824                                           background_mark_object (oo THREAD_NUMBER_ARG);
18825                                          );
18826                 }
18827
18828                 if (concurrent_p && !small_object_segments)
18829                 {
18830                     loh_alloc_lock->bgc_mark_done ();
18831                 }
18832
18833                 o = o + Align (s, align_const);
18834
18835                 if (concurrent_p)
18836                 {
18837                     allow_fgc();
18838                 }
18839             }
18840
18841             dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)", 
18842                 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
18843
18844             if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
18845                 (seg = heap_segment_next_in_range (seg)) == 0)
18846             {
18847                 if (small_object_segments)
18848                 {
18849                     if (concurrent_p)
18850                     {
18851                         current_bgc_state = bgc_overflow_loh;
18852                     }
18853
18854                     dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
18855                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
18856                     concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
18857                     total_marked_objects = 0;
18858                     small_object_segments = FALSE;
18859                     align_const = get_alignment_constant (small_object_segments);
18860                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
18861
18862                     PREFIX_ASSUME(seg != NULL);
18863
18864                     o = max (heap_segment_mem (seg), min_add);
18865                     continue;
18866                 }
18867                 else
18868                 {
18869                     dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
18870                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
18871                     break;
18872                 }
18873             } 
18874             else
18875             {
18876                 o = hp->background_first_overflow (min_add, 
18877                                                    seg, 
18878                                                    concurrent_p, 
18879                                                    small_object_segments);
18880                 continue;
18881             }
18882         }
18883     }
18884 }
18885
18886 BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
18887 {
18888     BOOL grow_mark_array_p = TRUE;
18889
18890     if (concurrent_p)
18891     {
18892         assert (!processed_soh_overflow_p);
18893
18894         if ((background_max_overflow_address != 0) &&
18895             (background_min_overflow_address != MAX_PTR))
18896         {
18897             // We have overflow to process but we know we can't process the ephemeral generations
18898             // now (we actually could process till the current gen1 start but since we are going to 
18899             // make overflow per segment, for now I'll just stop at the saved gen1 start.
18900             saved_overflow_ephemeral_seg = ephemeral_heap_segment;
18901             background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
18902             background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
18903         }
18904     }
18905     else
18906     {
18907         assert ((saved_overflow_ephemeral_seg == 0) || 
18908                 ((background_max_soh_overflow_address != 0) &&
18909                  (background_min_soh_overflow_address != MAX_PTR)));
18910         
18911         if (!processed_soh_overflow_p)
18912         {
18913             // if there was no more overflow we just need to process what we didn't process 
18914             // on the saved ephemeral segment.
18915             if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
18916             {
18917                 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
18918                 grow_mark_array_p = FALSE;
18919             }
18920
18921             background_min_overflow_address = min (background_min_overflow_address, 
18922                                                 background_min_soh_overflow_address);
18923             background_max_overflow_address = max (background_max_overflow_address,
18924                                                 background_max_soh_overflow_address);
18925             processed_soh_overflow_p = TRUE;
18926         }
18927     }
18928
18929     BOOL  overflow_p = FALSE;
18930 recheck:
18931     if ((! ((background_max_overflow_address == 0)) ||
18932          ! ((background_min_overflow_address == MAX_PTR))))
18933     {
18934         overflow_p = TRUE;
18935
18936         if (grow_mark_array_p)
18937         {
18938             // Try to grow the array.
18939             size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
18940
18941             if ((new_size * sizeof(mark)) > 100*1024)
18942             {
18943                 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
18944
18945                 new_size = min(new_max_size, new_size);
18946             }
18947
18948             if ((background_mark_stack_array_length < new_size) && 
18949                 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
18950             {
18951                 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
18952
18953                 uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18954                 if (tmp)
18955                 {
18956                     delete background_mark_stack_array;
18957                     background_mark_stack_array = tmp;
18958                     background_mark_stack_array_length = new_size;
18959                     background_mark_stack_tos = background_mark_stack_array;
18960                 }
18961             }
18962         }
18963         else
18964         {
18965             grow_mark_array_p = TRUE;
18966         }
18967
18968         uint8_t*  min_add = background_min_overflow_address;
18969         uint8_t*  max_add = background_max_overflow_address;
18970
18971         background_max_overflow_address = 0;
18972         background_min_overflow_address = MAX_PTR;
18973
18974         background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
18975         if (!concurrent_p)
18976         {        
18977             goto recheck;
18978         }
18979     }
18980
18981     return overflow_p;
18982 }
18983
18984 #endif //BACKGROUND_GC
18985
18986 inline
18987 void gc_heap::mark_through_object (uint8_t* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
18988 {
18989 #ifndef COLLECTIBLE_CLASS
18990     UNREFERENCED_PARAMETER(mark_class_object_p);
18991     BOOL to_mark_class_object = FALSE;
18992 #else //COLLECTIBLE_CLASS
18993     BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
18994 #endif //COLLECTIBLE_CLASS
18995     if (contain_pointers (oo) || to_mark_class_object)
18996     {
18997         dprintf(3,( "Marking through %Ix", (size_t)oo));
18998         size_t s = size (oo);
18999
19000 #ifdef COLLECTIBLE_CLASS
19001         if (to_mark_class_object)
19002         {
19003             uint8_t* class_obj = get_class_object (oo);
19004             mark_object (class_obj THREAD_NUMBER_ARG);
19005         }
19006 #endif //COLLECTIBLE_CLASS
19007
19008         if (contain_pointers (oo))
19009         {
19010             go_through_object_nostart (method_table(oo), oo, s, po,
19011                                 uint8_t* o = *po;
19012                                 mark_object (o THREAD_NUMBER_ARG);
19013                                 );
19014         }
19015     }
19016 }
19017
19018 size_t gc_heap::get_total_heap_size()
19019 {
19020     size_t total_heap_size = 0;
19021
19022 #ifdef MULTIPLE_HEAPS
19023     int hn = 0;
19024
19025     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19026     {
19027         gc_heap* hp2 = gc_heap::g_heaps [hn];
19028         total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
19029     }
19030 #else
19031     total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
19032 #endif //MULTIPLE_HEAPS
19033
19034     return total_heap_size;
19035 }
19036
19037 size_t gc_heap::get_total_fragmentation()
19038 {
19039     size_t total_fragmentation = 0;
19040
19041 #ifdef MULTIPLE_HEAPS
19042     for (int i = 0; i < gc_heap::n_heaps; i++)
19043     {
19044         gc_heap* hp = gc_heap::g_heaps[i];
19045 #else //MULTIPLE_HEAPS
19046     {
19047         gc_heap* hp = pGenGCHeap;
19048 #endif //MULTIPLE_HEAPS
19049         for (int i = 0; i <= (max_generation + 1); i++)
19050         {
19051             generation* gen = hp->generation_of (i);
19052             total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen));
19053         }
19054     }
19055
19056     return total_fragmentation;
19057 }
19058
19059 size_t gc_heap::committed_size()
19060 {
19061     generation* gen = generation_of (max_generation);
19062     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19063     size_t total_committed = 0;
19064
19065     while (1)
19066     {
19067         total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19068
19069         seg = heap_segment_next (seg);
19070         if (!seg)
19071         {
19072             if (gen != large_object_generation)
19073             {
19074                 gen = generation_of (max_generation + 1);
19075                 seg = generation_start_segment (gen);
19076             }
19077             else
19078                 break;
19079         }
19080     }
19081
19082     return total_committed;
19083 }
19084
19085 size_t gc_heap::get_total_committed_size()
19086 {
19087     size_t total_committed = 0;
19088
19089 #ifdef MULTIPLE_HEAPS
19090     int hn = 0;
19091
19092     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19093     {
19094         gc_heap* hp = gc_heap::g_heaps [hn];
19095         total_committed += hp->committed_size();
19096     }
19097 #else
19098     total_committed = committed_size();
19099 #endif //MULTIPLE_HEAPS
19100
19101     return total_committed;
19102 }
19103
19104 void gc_heap::get_memory_info (uint32_t* memory_load, 
19105                                uint64_t* available_physical,
19106                                uint64_t* available_page_file)
19107 {
19108     GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file);
19109 }
19110
19111 void fire_mark_event (int heap_num, int root_type, size_t bytes_marked)
19112 {
19113     dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id", heap_num, root_type, bytes_marked));
19114     FIRE_EVENT(GCMarkWithType, heap_num, root_type, bytes_marked);
19115 }
19116
19117 //returns TRUE is an overflow happened.
19118 BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
19119 {
19120     size_t last_promoted_bytes = promoted_bytes (heap_number);
19121     BOOL  overflow_p = FALSE;
19122 recheck:
19123     if ((! (max_overflow_address == 0) ||
19124          ! (min_overflow_address == MAX_PTR)))
19125     {
19126         overflow_p = TRUE;
19127         // Try to grow the array.
19128         size_t new_size =
19129             max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
19130
19131         if ((new_size * sizeof(mark)) > 100*1024)
19132         {
19133             size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19134
19135             new_size = min(new_max_size, new_size);
19136         }
19137
19138         if ((mark_stack_array_length < new_size) && 
19139             ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
19140         {
19141             mark* tmp = new (nothrow) mark [new_size];
19142             if (tmp)
19143             {
19144                 delete mark_stack_array;
19145                 mark_stack_array = tmp;
19146                 mark_stack_array_length = new_size;
19147             }
19148         }
19149
19150         uint8_t*  min_add = min_overflow_address;
19151         uint8_t*  max_add = max_overflow_address;
19152         max_overflow_address = 0;
19153         min_overflow_address = MAX_PTR;
19154         process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
19155         goto recheck;
19156     }
19157
19158     size_t current_promoted_bytes = promoted_bytes (heap_number);
19159
19160     if (current_promoted_bytes != last_promoted_bytes)
19161         fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes));
19162     return overflow_p;
19163 }
19164
19165 void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
19166                                               uint8_t* min_add, uint8_t* max_add)
19167 {
19168 #ifdef MULTIPLE_HEAPS
19169     int thread = heap_number;
19170 #endif //MULTIPLE_HEAPS
19171     BOOL  full_p = (condemned_gen_number == max_generation);
19172
19173         dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19174 #ifdef MULTIPLE_HEAPS
19175             for (int hi = 0; hi < n_heaps; hi++)
19176             {
19177                 gc_heap*  hp = g_heaps [(heap_number + hi) % n_heaps];
19178
19179 #else
19180             {
19181                 gc_heap*  hp = 0;
19182
19183 #endif //MULTIPLE_HEAPS
19184         BOOL small_object_segments = TRUE;
19185         int align_const = get_alignment_constant (small_object_segments);
19186         generation* gen = hp->generation_of (condemned_gen_number);
19187         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19188         
19189         PREFIX_ASSUME(seg != NULL);
19190         uint8_t*  o = max (heap_segment_mem (seg), min_add);
19191         while (1)
19192         {
19193             uint8_t*  end = heap_segment_allocated (seg);
19194
19195             while ((o < end) && (o <= max_add))
19196             {
19197                 assert ((min_add <= o) && (max_add >= o));
19198                 dprintf (3, ("considering %Ix", (size_t)o));
19199                 if (marked (o))
19200                 {
19201                     mark_through_object (o, TRUE THREAD_NUMBER_ARG);
19202                 }
19203
19204                 o = o + Align (size (o), align_const);
19205             }
19206
19207             if (( seg = heap_segment_next_in_range (seg)) == 0)
19208             {
19209                 if (small_object_segments && full_p)
19210                 {
19211                     small_object_segments = FALSE;
19212                     align_const = get_alignment_constant (small_object_segments);
19213                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19214
19215                     PREFIX_ASSUME(seg != NULL);
19216
19217                     o = max (heap_segment_mem (seg), min_add);
19218                     continue;
19219                 }
19220                 else
19221                 {
19222                     break;
19223                 } 
19224             } 
19225             else
19226             {
19227                 o = max (heap_segment_mem (seg), min_add);
19228                 continue;
19229             }
19230         }
19231     }
19232 }
19233
19234 // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
19235 // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
19236 // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
19237 // promotion scan multiple times.
19238 // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
19239 // also has the effect of processing any mark stack overflow.
19240
19241 #ifdef MULTIPLE_HEAPS
19242 // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
19243 // worker threads synchronized. The algorithms are sufficiently divergent that we have different
19244 // implementations based on whether MULTIPLE_HEAPS is defined or not.
19245 //
19246 // Define some static variables used for synchronization in the method below. These should really be defined
19247 // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
19248 //
19249 // A note about the synchronization used within this method. Communication between the worker threads is
19250 // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
19251 // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
19252 // protection of a join.
19253 static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
19254 static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
19255 static VOLATILE(BOOL) s_fScanRequired;
19256 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19257 {
19258     // Whenever we call this method there may have been preceding object promotions. So set
19259     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19260     // based on the how the scanning proceeded).
19261     s_fUnscannedPromotions = TRUE;
19262
19263     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
19264     // the state of this thread's portion of the dependent handle table. That's because promotions on other
19265     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
19266     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
19267     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
19268     // as all the others or they'll get out of step).
19269     while (true)
19270     {
19271         // The various worker threads are all currently racing in this code. We need to work out if at least
19272         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
19273         // dependent handle table when both of the following conditions apply:
19274         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
19275         //     object happens to correspond to a primary in one of our handles we might potentially have to
19276         //     promote the associated secondary).
19277         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
19278         //
19279         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
19280         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
19281         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
19282         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
19283         // follows below. Note that we can't read this outside of the join since on any iteration apart from
19284         // the first threads will be racing between reading this value and completing their previous
19285         // iteration's table scan.
19286         //
19287         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
19288         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
19289         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
19290         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
19291         // we're safely joined.
19292         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19293             s_fUnpromotedHandles = TRUE;
19294
19295         // Synchronize all the threads so we can read our state variables safely. The shared variable
19296         // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
19297         // a single thread inside the join.
19298         gc_t_join.join(this, gc_join_scan_dependent_handles);
19299         if (gc_t_join.joined())
19300         {
19301             // We're synchronized so it's safe to read our shared state variables. We update another shared
19302             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
19303             // the loop. We scan if there has been at least one object promotion since last time and at least
19304             // one thread has a dependent handle table with a potential handle promotion possible.
19305             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
19306
19307             // Reset our shared state variables (ready to be set again on this scan or with a good initial
19308             // value for the next call if we're terminating the loop).
19309             s_fUnscannedPromotions = FALSE;
19310             s_fUnpromotedHandles = FALSE;
19311
19312             if (!s_fScanRequired)
19313             {
19314                 // We're terminating the loop. Perform any last operations that require single threaded access.
19315                 if (!initial_scan_p)
19316                 {
19317                     // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
19318                     // load balance if some of the heaps have an abnormally large workload.
19319                     uint8_t* all_heaps_max = 0;
19320                     uint8_t* all_heaps_min = MAX_PTR;
19321                     int i;
19322                     for (i = 0; i < n_heaps; i++)
19323                     {
19324                         if (all_heaps_max < g_heaps[i]->max_overflow_address)
19325                             all_heaps_max = g_heaps[i]->max_overflow_address;
19326                         if (all_heaps_min > g_heaps[i]->min_overflow_address)
19327                             all_heaps_min = g_heaps[i]->min_overflow_address;
19328                     }
19329                     for (i = 0; i < n_heaps; i++)
19330                     {
19331                         g_heaps[i]->max_overflow_address = all_heaps_max;
19332                         g_heaps[i]->min_overflow_address = all_heaps_min;
19333                     }
19334                 }
19335             }
19336
19337             // Restart all the workers.
19338             dprintf(3, ("Starting all gc thread mark stack overflow processing"));
19339             gc_t_join.restart();
19340         }
19341
19342         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19343         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
19344         // global flag indicating that at least one object promotion may have occurred (the usual comment
19345         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
19346         // exit the method since we unconditionally set this variable on method entry anyway).
19347         if (process_mark_overflow(condemned_gen_number))
19348             s_fUnscannedPromotions = TRUE;
19349
19350         // If we decided that no scan was required we can terminate the loop now.
19351         if (!s_fScanRequired)
19352             break;
19353
19354         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
19355         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
19356         // could miss noting the promotion of some primary objects).
19357         gc_t_join.join(this, gc_join_rescan_dependent_handles);
19358         if (gc_t_join.joined())
19359         {
19360             // Restart all the workers.
19361             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
19362             gc_t_join.restart();
19363         }
19364
19365         // If the portion of the dependent handle table managed by this worker has handles that could still be
19366         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
19367         // could require a rescan of handles on this or other workers.
19368         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19369             if (GCScan::GcDhReScan(sc))
19370                 s_fUnscannedPromotions = TRUE;
19371     }
19372 }
19373 #else //MULTIPLE_HEAPS
19374 // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
19375 // threads synchronized.
19376 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19377 {
19378     UNREFERENCED_PARAMETER(initial_scan_p);
19379
19380     // Whenever we call this method there may have been preceding object promotions. So set
19381     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19382     // based on the how the scanning proceeded).
19383     bool fUnscannedPromotions = true;
19384
19385     // Loop until there are either no more dependent handles that can have their secondary promoted or we've
19386     // managed to perform a scan without promoting anything new.
19387     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
19388     {
19389         // On each iteration of the loop start with the assumption that no further objects have been promoted.
19390         fUnscannedPromotions = false;
19391
19392         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19393         // being visible. If there was an overflow (process_mark_overflow returned true) then additional
19394         // objects now appear to be promoted and we should set the flag.
19395         if (process_mark_overflow(condemned_gen_number))
19396             fUnscannedPromotions = true;
19397
19398         // Perform the scan and set the flag if any promotions resulted.
19399         if (GCScan::GcDhReScan(sc))
19400             fUnscannedPromotions = true;
19401     }
19402
19403     // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
19404     // scan any handles at all this is the processing of overflows that may have occurred prior to this method
19405     // invocation).
19406     process_mark_overflow(condemned_gen_number);
19407 }
19408 #endif //MULTIPLE_HEAPS
19409
19410 void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
19411 {
19412     assert (settings.concurrent == FALSE);
19413
19414     ScanContext sc;
19415     sc.thread_number = heap_number;
19416     sc.promotion = TRUE;
19417     sc.concurrent = FALSE;
19418
19419     dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
19420     BOOL  full_p = (condemned_gen_number == max_generation);
19421
19422 #ifdef TIME_GC
19423     unsigned start;
19424     unsigned finish;
19425     start = GetCycleCount32();
19426 #endif //TIME_GC
19427
19428     int gen_to_init = condemned_gen_number;
19429     if (condemned_gen_number == max_generation)
19430     {
19431         gen_to_init = max_generation + 1;
19432     }
19433     for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
19434     {
19435         dynamic_data* dd = dynamic_data_of (gen_idx);
19436         dd_begin_data_size (dd) = generation_size (gen_idx) - 
19437                                    dd_fragmentation (dd) -
19438                                    Align (size (generation_allocation_start (generation_of (gen_idx))));
19439         dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
19440         dd_survived_size (dd) = 0;
19441         dd_pinned_survived_size (dd) = 0;
19442         dd_artificial_pinned_survived_size (dd) = 0;
19443         dd_added_pinned_size (dd) = 0;
19444 #ifdef SHORT_PLUGS
19445         dd_padding_size (dd) = 0;
19446 #endif //SHORT_PLUGS
19447 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
19448         dd_num_npinned_plugs (dd) = 0;
19449 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
19450     }
19451
19452 #ifdef FFIND_OBJECT
19453     if (gen0_must_clear_bricks > 0)
19454         gen0_must_clear_bricks--;
19455 #endif //FFIND_OBJECT
19456
19457     size_t last_promoted_bytes = 0;
19458
19459     promoted_bytes (heap_number) = 0;
19460     reset_mark_stack();
19461
19462 #ifdef SNOOP_STATS
19463     memset (&snoop_stat, 0, sizeof(snoop_stat));
19464     snoop_stat.heap_index = heap_number;
19465 #endif //SNOOP_STATS
19466
19467 #ifdef MH_SC_MARK
19468     if (full_p)
19469     {
19470         //initialize the mark stack
19471         for (int i = 0; i < max_snoop_level; i++)
19472         {
19473             ((uint8_t**)(mark_stack_array))[i] = 0;
19474         }
19475
19476         mark_stack_busy() = 1;
19477     }
19478 #endif //MH_SC_MARK
19479
19480     static uint32_t num_sizedrefs = 0;
19481
19482 #ifdef MH_SC_MARK
19483     static BOOL do_mark_steal_p = FALSE;
19484 #endif //MH_SC_MARK
19485
19486 #ifdef MULTIPLE_HEAPS
19487     gc_t_join.join(this, gc_join_begin_mark_phase);
19488     if (gc_t_join.joined())
19489     {
19490 #endif //MULTIPLE_HEAPS
19491
19492         num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
19493
19494 #ifdef MULTIPLE_HEAPS
19495
19496 #ifdef MH_SC_MARK
19497         if (full_p)
19498         {
19499             size_t total_heap_size = get_total_heap_size();
19500
19501             if (total_heap_size > (100 * 1024 * 1024))
19502             {
19503                 do_mark_steal_p = TRUE;
19504             }
19505             else
19506             {
19507                 do_mark_steal_p = FALSE;
19508             }
19509         }
19510         else
19511         {
19512             do_mark_steal_p = FALSE;
19513         }
19514 #endif //MH_SC_MARK
19515
19516         gc_t_join.restart();
19517     }
19518 #endif //MULTIPLE_HEAPS
19519
19520     {
19521
19522 #ifdef MARK_LIST
19523         //set up the mark lists from g_mark_list
19524         assert (g_mark_list);
19525 #ifdef MULTIPLE_HEAPS
19526         mark_list = &g_mark_list [heap_number*mark_list_size];
19527 #else
19528         mark_list = g_mark_list;
19529 #endif //MULTIPLE_HEAPS
19530         //dont use the mark list for full gc
19531         //because multiple segments are more complex to handle and the list
19532         //is likely to overflow
19533         if (condemned_gen_number != max_generation)
19534             mark_list_end = &mark_list [mark_list_size-1];
19535         else
19536             mark_list_end = &mark_list [0];
19537         mark_list_index = &mark_list [0];
19538 #endif //MARK_LIST
19539
19540         shigh = (uint8_t*) 0;
19541         slow  = MAX_PTR;
19542
19543         //%type%  category = quote (mark);
19544
19545         if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
19546         {
19547             GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19548             fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes));
19549             last_promoted_bytes = promoted_bytes (heap_number);
19550
19551 #ifdef MULTIPLE_HEAPS
19552             gc_t_join.join(this, gc_join_scan_sizedref_done);
19553             if (gc_t_join.joined())
19554             {
19555                 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
19556                 gc_t_join.restart();
19557             }
19558 #endif //MULTIPLE_HEAPS
19559         }
19560     
19561         dprintf(3,("Marking Roots"));
19562
19563         GCScan::GcScanRoots(GCHeap::Promote,
19564                                 condemned_gen_number, max_generation,
19565                                 &sc);
19566
19567         fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes));
19568         last_promoted_bytes = promoted_bytes (heap_number);
19569
19570 #ifdef BACKGROUND_GC
19571         if (recursive_gc_sync::background_running_p())
19572         {
19573             scan_background_roots (GCHeap::Promote, heap_number, &sc);
19574         }
19575 #endif //BACKGROUND_GC
19576
19577 #ifdef FEATURE_PREMORTEM_FINALIZATION
19578         dprintf(3, ("Marking finalization data"));
19579         finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
19580 #endif // FEATURE_PREMORTEM_FINALIZATION
19581
19582         fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes));
19583         last_promoted_bytes = promoted_bytes (heap_number);
19584
19585 // MTHTS
19586         {
19587
19588             dprintf(3,("Marking handle table"));
19589             GCScan::GcScanHandles(GCHeap::Promote,
19590                                       condemned_gen_number, max_generation,
19591                                       &sc);
19592             fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes));
19593             last_promoted_bytes = promoted_bytes (heap_number);
19594         }
19595
19596 #ifdef TRACE_GC
19597         size_t promoted_before_cards = promoted_bytes (heap_number);
19598 #endif //TRACE_GC
19599
19600         dprintf (3, ("before cards: %Id", promoted_before_cards));
19601         if (!full_p)
19602         {
19603 #ifdef CARD_BUNDLE
19604 #ifdef MULTIPLE_HEAPS
19605             if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
19606             {
19607 #endif //MULTIPLE_HEAPS
19608
19609 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
19610                 // If we are manually managing card bundles, every write to the card table should already be
19611                 // accounted for in the card bundle table so there's nothing to update here.
19612                 update_card_table_bundle();
19613 #endif
19614                 if (card_bundles_enabled())
19615                 {
19616                     verify_card_bundles();
19617                 }
19618
19619 #ifdef MULTIPLE_HEAPS
19620                 gc_t_join.r_restart();
19621             }
19622 #endif //MULTIPLE_HEAPS
19623 #endif //CARD_BUNDLE
19624
19625             card_fn mark_object_fn = &gc_heap::mark_object_simple;
19626 #ifdef HEAP_ANALYZE
19627             heap_analyze_success = TRUE;
19628             if (heap_analyze_enabled)
19629             {
19630                 internal_root_array_index = 0;
19631                 current_obj = 0;
19632                 current_obj_size = 0;
19633                 mark_object_fn = &gc_heap::ha_mark_object_simple;
19634             }
19635 #endif //HEAP_ANALYZE
19636
19637             dprintf(3,("Marking cross generation pointers"));
19638             mark_through_cards_for_segments (mark_object_fn, FALSE);
19639
19640             dprintf(3,("Marking cross generation pointers for large objects"));
19641             mark_through_cards_for_large_objects (mark_object_fn, FALSE);
19642
19643             dprintf (3, ("marked by cards: %Id", 
19644                 (promoted_bytes (heap_number) - promoted_before_cards)));
19645             fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes));
19646             last_promoted_bytes = promoted_bytes (heap_number);
19647         }
19648     }
19649
19650 #ifdef MH_SC_MARK
19651     if (do_mark_steal_p)
19652     {
19653         mark_steal();
19654     }
19655 #endif //MH_SC_MARK
19656
19657     // Dependent handles need to be scanned with a special algorithm (see the header comment on
19658     // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
19659     // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
19660     // but in a common case (where there are no dependent handles that are due to be collected) it allows us
19661     // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
19662     // iterations if required and will also perform processing of any mark stack overflow once the dependent
19663     // handle table has been fully promoted.
19664     GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19665     scan_dependent_handles(condemned_gen_number, &sc, true);
19666
19667 #ifdef MULTIPLE_HEAPS
19668     dprintf(3, ("Joining for short weak handle scan"));
19669     gc_t_join.join(this, gc_join_null_dead_short_weak);
19670     if (gc_t_join.joined())
19671 #endif //MULTIPLE_HEAPS
19672     {
19673 #ifdef HEAP_ANALYZE
19674         heap_analyze_enabled = FALSE;
19675         GCToEEInterface::AnalyzeSurvivorsFinished(condemned_gen_number);
19676 #endif // HEAP_ANALYZE
19677         GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
19678
19679 #ifdef MULTIPLE_HEAPS
19680         if (!full_p)
19681         {
19682             // we used r_join and need to reinitialize states for it here.
19683             gc_t_join.r_init();
19684         }
19685
19686         //start all threads on the roots.
19687         dprintf(3, ("Starting all gc thread for short weak handle scan"));
19688         gc_t_join.restart();
19689 #endif //MULTIPLE_HEAPS
19690
19691     }
19692
19693     // null out the target of short weakref that were not promoted.
19694     GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
19695
19696 // MTHTS: keep by single thread
19697 #ifdef MULTIPLE_HEAPS
19698     dprintf(3, ("Joining for finalization"));
19699     gc_t_join.join(this, gc_join_scan_finalization);
19700     if (gc_t_join.joined())
19701 #endif //MULTIPLE_HEAPS
19702
19703     {
19704 #ifdef MULTIPLE_HEAPS
19705         //start all threads on the roots.
19706         dprintf(3, ("Starting all gc thread for Finalization"));
19707         gc_t_join.restart();
19708 #endif //MULTIPLE_HEAPS
19709     }
19710
19711     //Handle finalization.
19712     size_t promoted_bytes_live = promoted_bytes (heap_number);
19713
19714 #ifdef FEATURE_PREMORTEM_FINALIZATION
19715     dprintf (3, ("Finalize marking"));
19716     finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
19717
19718     GCToEEInterface::DiagWalkFReachableObjects(__this);
19719 #endif // FEATURE_PREMORTEM_FINALIZATION
19720
19721     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
19722     // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
19723     scan_dependent_handles(condemned_gen_number, &sc, false);
19724
19725 #ifdef MULTIPLE_HEAPS
19726     dprintf(3, ("Joining for weak pointer deletion"));
19727     gc_t_join.join(this, gc_join_null_dead_long_weak);
19728     if (gc_t_join.joined())
19729     {
19730         //start all threads on the roots.
19731         dprintf(3, ("Starting all gc thread for weak pointer deletion"));
19732         gc_t_join.restart();
19733     }
19734 #endif //MULTIPLE_HEAPS
19735
19736     // null out the target of long weakref that were not promoted.
19737     GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19738
19739 // MTHTS: keep by single thread
19740 #ifdef MULTIPLE_HEAPS
19741 #ifdef MARK_LIST
19742 #ifdef PARALLEL_MARK_LIST_SORT
19743 //    unsigned long start = GetCycleCount32();
19744     sort_mark_list();
19745 //    printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
19746 #endif //PARALLEL_MARK_LIST_SORT
19747 #endif //MARK_LIST
19748
19749     dprintf (3, ("Joining for sync block cache entry scanning"));
19750     gc_t_join.join(this, gc_join_null_dead_syncblk);
19751     if (gc_t_join.joined())
19752 #endif //MULTIPLE_HEAPS
19753     {
19754         // scan for deleted entries in the syncblk cache
19755         GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
19756
19757 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
19758         if (g_fEnableARM)
19759         {
19760             size_t promoted_all_heaps = 0;
19761 #ifdef MULTIPLE_HEAPS
19762             for (int i = 0; i < n_heaps; i++)
19763             {
19764                 promoted_all_heaps += promoted_bytes (i);
19765             }
19766 #else
19767             promoted_all_heaps = promoted_bytes (heap_number);
19768 #endif //MULTIPLE_HEAPS
19769             SystemDomain::RecordTotalSurvivedBytes (promoted_all_heaps);
19770         }
19771 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
19772
19773 #ifdef MULTIPLE_HEAPS
19774
19775 #ifdef MARK_LIST
19776 #ifndef PARALLEL_MARK_LIST_SORT
19777         //compact g_mark_list and sort it.
19778         combine_mark_lists();
19779 #endif //PARALLEL_MARK_LIST_SORT
19780 #endif //MARK_LIST
19781
19782         //decide on promotion
19783         if (!settings.promotion)
19784         {
19785             size_t m = 0;
19786             for (int n = 0; n <= condemned_gen_number;n++)
19787             {
19788                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.1);
19789             }
19790
19791             for (int i = 0; i < n_heaps; i++)
19792             {
19793                 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
19794                                                                      max_generation));
19795                 size_t older_gen_size = (dd_current_size (dd) +
19796                                          (dd_desired_allocation (dd) -
19797                                          dd_new_allocation (dd)));
19798
19799                 if ((m > (older_gen_size)) ||
19800                     (promoted_bytes (i) > m))
19801                 {
19802                     settings.promotion = TRUE;
19803                 }
19804             }
19805         }
19806
19807 #ifdef SNOOP_STATS
19808         if (do_mark_steal_p)
19809         {
19810             size_t objects_checked_count = 0;
19811             size_t zero_ref_count = 0;
19812             size_t objects_marked_count = 0;
19813             size_t check_level_count = 0;
19814             size_t busy_count = 0;
19815             size_t interlocked_count = 0;
19816             size_t partial_mark_parent_count = 0;
19817             size_t stolen_or_pm_count = 0; 
19818             size_t stolen_entry_count = 0; 
19819             size_t pm_not_ready_count = 0; 
19820             size_t normal_count = 0;
19821             size_t stack_bottom_clear_count = 0;
19822
19823             for (int i = 0; i < n_heaps; i++)
19824             {
19825                 gc_heap* hp = g_heaps[i];
19826                 hp->print_snoop_stat();
19827                 objects_checked_count += hp->snoop_stat.objects_checked_count;
19828                 zero_ref_count += hp->snoop_stat.zero_ref_count;
19829                 objects_marked_count += hp->snoop_stat.objects_marked_count;
19830                 check_level_count += hp->snoop_stat.check_level_count;
19831                 busy_count += hp->snoop_stat.busy_count;
19832                 interlocked_count += hp->snoop_stat.interlocked_count;
19833                 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
19834                 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
19835                 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
19836                 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
19837                 normal_count += hp->snoop_stat.normal_count;
19838                 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
19839             }
19840
19841             fflush (stdout);
19842
19843             printf ("-------total stats-------\n");
19844             printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
19845                 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
19846             printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
19847                 objects_checked_count,
19848                 zero_ref_count,
19849                 objects_marked_count,
19850                 check_level_count,
19851                 busy_count,
19852                 interlocked_count,
19853                 partial_mark_parent_count,
19854                 stolen_or_pm_count,
19855                 stolen_entry_count,
19856                 pm_not_ready_count,
19857                 normal_count,
19858                 stack_bottom_clear_count);
19859         }
19860 #endif //SNOOP_STATS
19861
19862         //start all threads.
19863         dprintf(3, ("Starting all threads for end of mark phase"));
19864         gc_t_join.restart();
19865 #else //MULTIPLE_HEAPS
19866
19867         //decide on promotion
19868         if (!settings.promotion)
19869         {
19870             size_t m = 0;
19871             for (int n = 0; n <= condemned_gen_number;n++)
19872             {
19873                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06);
19874             }
19875             dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
19876                                                      max_generation));
19877             size_t older_gen_size = (dd_current_size (dd) +
19878                                      (dd_desired_allocation (dd) -
19879                                      dd_new_allocation (dd)));
19880
19881             dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
19882                          m, promoted_bytes (heap_number), older_gen_size));
19883
19884             if ((m > older_gen_size) ||
19885                     (promoted_bytes (heap_number) > m))
19886             {
19887                 settings.promotion = TRUE;
19888             }
19889         }
19890
19891 #endif //MULTIPLE_HEAPS
19892     }
19893
19894 #ifdef MULTIPLE_HEAPS
19895 #ifdef MARK_LIST
19896 #ifdef PARALLEL_MARK_LIST_SORT
19897 //    start = GetCycleCount32();
19898     merge_mark_lists();
19899 //    printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
19900 #endif //PARALLEL_MARK_LIST_SORT
19901 #endif //MARK_LIST
19902 #endif //MULTIPLE_HEAPS
19903
19904 #ifdef BACKGROUND_GC
19905     total_promoted_bytes = promoted_bytes (heap_number);
19906 #endif //BACKGROUND_GC
19907
19908     promoted_bytes (heap_number) -= promoted_bytes_live;
19909
19910 #ifdef TIME_GC
19911         finish = GetCycleCount32();
19912         mark_time = finish - start;
19913 #endif //TIME_GC
19914
19915     dprintf(2,("---- End of mark phase ----"));
19916 }
19917
19918 inline
19919 void gc_heap::pin_object (uint8_t* o, uint8_t** ppObject, uint8_t* low, uint8_t* high)
19920 {
19921     dprintf (3, ("Pinning %Ix", (size_t)o));
19922     if ((o >= low) && (o < high))
19923     {
19924         dprintf(3,("^%Ix^", (size_t)o));
19925         set_pinned (o);
19926
19927 #ifdef FEATURE_EVENT_TRACE        
19928         if(EVENT_ENABLED(PinObjectAtGCTime))
19929         {
19930             fire_etw_pin_object_event(o, ppObject);
19931         }
19932 #endif // FEATURE_EVENT_TRACE
19933
19934 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
19935         num_pinned_objects++;
19936 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
19937     }
19938 }
19939
19940 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
19941 size_t gc_heap::get_total_pinned_objects()
19942 {
19943 #ifdef MULTIPLE_HEAPS
19944     size_t total_num_pinned_objects = 0;
19945     for (int i = 0; i < gc_heap::n_heaps; i++)
19946     {
19947         gc_heap* hp = gc_heap::g_heaps[i];
19948         total_num_pinned_objects += hp->num_pinned_objects;
19949     }
19950     return total_num_pinned_objects;
19951 #else //MULTIPLE_HEAPS
19952     return num_pinned_objects;
19953 #endif //MULTIPLE_HEAPS
19954 }
19955 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
19956
19957 void gc_heap::reset_mark_stack ()
19958 {
19959     reset_pinned_queue();
19960     max_overflow_address = 0;
19961     min_overflow_address = MAX_PTR;
19962 }
19963
19964 #ifdef FEATURE_STRUCTALIGN
19965 //
19966 // The word with left child, right child, and align info is laid out as follows:
19967 //
19968 //      |   upper short word   |   lower short word   |
19969 //      |<------------> <----->|<------------> <----->|
19970 //      |  left child   info hi| right child   info lo|
19971 // x86: |    10 bits     6 bits|   10 bits      6 bits|
19972 //
19973 // where left/right child are signed values and concat(info hi, info lo) is unsigned.
19974 //
19975 // The "align info" encodes two numbers: the required alignment (a power of two)
19976 // and the misalignment (the number of machine words the destination address needs
19977 // to be adjusted by to provide alignment - so this number is always smaller than
19978 // the required alignment).  Thus, the two can be represented as the "logical or"
19979 // of the two numbers.  Note that the actual pad is computed from the misalignment
19980 // by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
19981 //
19982
19983 // The number of bits in a brick.
19984 #if defined (_TARGET_AMD64_)
19985 #define brick_bits (12)
19986 #else
19987 #define brick_bits (11)
19988 #endif //_TARGET_AMD64_
19989 C_ASSERT(brick_size == (1 << brick_bits));
19990
19991 // The number of bits needed to represent the offset to a child node.
19992 // "brick_bits + 1" allows us to represent a signed offset within a brick.
19993 #define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
19994
19995 // The number of bits in each of the pad hi, pad lo fields.
19996 #define pad_bits (sizeof(short) * 8 - child_bits)
19997
19998 #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
19999 #define pad_mask ((1 << pad_bits) - 1)
20000 #define pad_from_short(w) ((size_t)(w) & pad_mask)
20001 #else // FEATURE_STRUCTALIGN
20002 #define child_from_short(w) (w)
20003 #endif // FEATURE_STRUCTALIGN
20004
20005 inline
20006 short node_left_child(uint8_t* node)
20007 {
20008     return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20009 }
20010
20011 inline
20012 void set_node_left_child(uint8_t* node, ptrdiff_t val)
20013 {
20014     assert (val > -(ptrdiff_t)brick_size);
20015     assert (val < (ptrdiff_t)brick_size);
20016     assert (Aligned (val));
20017 #ifdef FEATURE_STRUCTALIGN
20018     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20019     ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20020 #else // FEATURE_STRUCTALIGN
20021     ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
20022 #endif // FEATURE_STRUCTALIGN
20023     assert (node_left_child (node) == val);
20024 }
20025
20026 inline
20027 short node_right_child(uint8_t* node)
20028 {
20029     return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20030 }
20031
20032 inline
20033 void set_node_right_child(uint8_t* node, ptrdiff_t val)
20034 {
20035     assert (val > -(ptrdiff_t)brick_size);
20036     assert (val < (ptrdiff_t)brick_size);
20037     assert (Aligned (val));
20038 #ifdef FEATURE_STRUCTALIGN
20039     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20040     ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20041 #else // FEATURE_STRUCTALIGN
20042     ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
20043 #endif // FEATURE_STRUCTALIGN
20044     assert (node_right_child (node) == val);
20045 }
20046
20047 #ifdef FEATURE_STRUCTALIGN
20048 void node_aligninfo (uint8_t* node, int& requiredAlignment, ptrdiff_t& pad)
20049 {
20050     // Extract the single-number aligninfo from the fields.
20051     short left = ((plug_and_pair*)node)[-1].m_pair.left;
20052     short right = ((plug_and_pair*)node)[-1].m_pair.right;
20053     ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
20054     ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
20055
20056     // Replicate the topmost bit into all lower bits.
20057     ptrdiff_t x = aligninfo;
20058     x |= x >> 8;
20059     x |= x >> 4;
20060     x |= x >> 2;
20061     x |= x >> 1;
20062
20063     // Clear all bits but the highest.
20064     requiredAlignment = (int)(x ^ (x >> 1));
20065     pad = aligninfo - requiredAlignment;
20066     pad += AdjustmentForMinPadSize(pad, requiredAlignment);
20067 }
20068
20069 inline
20070 ptrdiff_t node_alignpad (uint8_t* node)
20071 {
20072     int requiredAlignment;
20073     ptrdiff_t alignpad;
20074     node_aligninfo (node, requiredAlignment, alignpad);
20075     return alignpad;
20076 }
20077
20078 void clear_node_aligninfo (uint8_t* node)
20079 {
20080     ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
20081     ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
20082 }
20083
20084 void set_node_aligninfo (uint8_t* node, int requiredAlignment, ptrdiff_t pad)
20085 {
20086     // Encode the alignment requirement and alignment offset as a single number
20087     // as described above.
20088     ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
20089     assert (Aligned (aligninfo));
20090     ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
20091     assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
20092
20093     ptrdiff_t hi = aligninfo_shifted >> pad_bits;
20094     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
20095     ((plug_and_pair*)node)[-1].m_pair.left |= hi;
20096
20097     ptrdiff_t lo = aligninfo_shifted & pad_mask;
20098     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
20099     ((plug_and_pair*)node)[-1].m_pair.right |= lo;
20100
20101 #ifdef _DEBUG
20102     int requiredAlignment2;
20103     ptrdiff_t pad2;
20104     node_aligninfo (node, requiredAlignment2, pad2);
20105     assert (requiredAlignment == requiredAlignment2);
20106     assert (pad == pad2);
20107 #endif // _DEBUG
20108 }
20109 #endif // FEATURE_STRUCTALIGN
20110
20111 inline
20112 void loh_set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20113 {
20114     ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
20115     *place = val;
20116 }
20117
20118 inline
20119 ptrdiff_t loh_node_relocation_distance(uint8_t* node)
20120 {
20121     return (((loh_obj_and_pad*)node)[-1].reloc);
20122 }
20123
20124 inline
20125 ptrdiff_t node_relocation_distance (uint8_t* node)
20126 {
20127     return (((plug_and_reloc*)(node))[-1].reloc & ~3);
20128 }
20129
20130 inline
20131 void set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20132 {
20133     assert (val == (val & ~3));
20134     ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
20135     //clear the left bit and the relocation field
20136     *place &= 1;
20137     // store the value
20138     *place |= val;
20139 }
20140
20141 #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
20142
20143 #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
20144
20145 #ifndef FEATURE_STRUCTALIGN
20146 void set_node_realigned(uint8_t* node)
20147 {
20148     ((plug_and_reloc*)(node))[-1].reloc |= 1;
20149 }
20150
20151 void clear_node_realigned(uint8_t* node)
20152 {
20153 #ifdef RESPECT_LARGE_ALIGNMENT
20154     ((plug_and_reloc*)(node))[-1].reloc &= ~1;
20155 #else
20156     UNREFERENCED_PARAMETER(node);
20157 #endif //RESPECT_LARGE_ALIGNMENT
20158 }
20159 #endif // FEATURE_STRUCTALIGN
20160
20161 inline
20162 size_t  node_gap_size (uint8_t* node)
20163 {
20164     return ((plug_and_gap *)node)[-1].gap;
20165 }
20166
20167 void set_gap_size (uint8_t* node, size_t size)
20168 {
20169     assert (Aligned (size));
20170
20171     // clear the 2 uint32_t used by the node.
20172     ((plug_and_gap *)node)[-1].reloc = 0;
20173     ((plug_and_gap *)node)[-1].lr =0;
20174     ((plug_and_gap *)node)[-1].gap = size;
20175
20176     assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
20177
20178 }
20179
20180 uint8_t* gc_heap::insert_node (uint8_t* new_node, size_t sequence_number,
20181                    uint8_t* tree, uint8_t* last_node)
20182 {
20183     dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
20184                  (size_t)new_node, brick_of(new_node), 
20185                  (size_t)tree, brick_of(tree), 
20186                  (size_t)last_node, brick_of(last_node),
20187                  sequence_number));
20188     if (power_of_two_p (sequence_number))
20189     {
20190         set_node_left_child (new_node, (tree - new_node));
20191         dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
20192         tree = new_node;
20193     }
20194     else
20195     {
20196         if (oddp (sequence_number))
20197         {
20198             set_node_right_child (last_node, (new_node - last_node));
20199             dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
20200         }
20201         else
20202         {
20203             uint8_t*  earlier_node = tree;
20204             size_t imax = logcount(sequence_number) - 2;
20205             for (size_t i = 0; i != imax; i++)
20206             {
20207                 earlier_node = earlier_node + node_right_child (earlier_node);
20208             }
20209             int tmp_offset = node_right_child (earlier_node);
20210             assert (tmp_offset); // should never be empty
20211             set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
20212             set_node_right_child (earlier_node, (new_node - earlier_node));
20213
20214             dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix", 
20215                 new_node, ((earlier_node + tmp_offset ) - new_node),
20216                 earlier_node, (new_node - earlier_node)));
20217         }
20218     }
20219     return tree;
20220 }
20221
20222 size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick,
20223                                     uint8_t* x, uint8_t* plug_end)
20224 {
20225     dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
20226         tree, current_brick, x, plug_end));
20227
20228     if (tree != NULL)
20229     {
20230         dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix", 
20231             current_brick, (size_t)(tree - brick_address (current_brick)), tree));
20232         set_brick (current_brick, (tree - brick_address (current_brick)));
20233     }
20234     else
20235     {
20236         dprintf (3, ("b- %Ix->-1", current_brick));
20237         set_brick (current_brick, -1);
20238     }
20239     size_t  b = 1 + current_brick;
20240     ptrdiff_t  offset = 0;
20241     size_t last_br = brick_of (plug_end-1);
20242     current_brick = brick_of (x-1);
20243     dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
20244     while (b <= current_brick)
20245     {
20246         if (b <= last_br)
20247         {
20248             set_brick (b, --offset);
20249         }
20250         else
20251         {
20252             set_brick (b,-1);
20253         }
20254         b++;
20255     }
20256     return brick_of (x);
20257 }
20258
20259 void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate)
20260 {
20261 #ifdef BIT64
20262     // We should never demote big plugs to gen0.
20263     if (gen == youngest_generation)
20264     {
20265         heap_segment* seg = ephemeral_heap_segment;
20266         size_t mark_stack_large_bos = mark_stack_bos;
20267         size_t large_plug_pos = 0;
20268         while (mark_stack_large_bos < mark_stack_tos)
20269         {
20270             if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
20271             {
20272                 while (mark_stack_bos <= mark_stack_large_bos)
20273                 {
20274                     size_t entry = deque_pinned_plug();
20275                     size_t len = pinned_len (pinned_plug_of (entry));
20276                     uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20277                     if (len > demotion_plug_len_th)
20278                     {
20279                         dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
20280                     }
20281                     pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
20282                     assert(mark_stack_array[entry].len == 0 ||
20283                             mark_stack_array[entry].len >= Align(min_obj_size));
20284                     generation_allocation_pointer (consing_gen) = plug + len;
20285                     generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
20286                     set_allocator_next_pin (consing_gen);
20287                 }
20288             }
20289
20290             mark_stack_large_bos++;
20291         }
20292     }
20293 #endif // BIT64
20294
20295     generation_plan_allocation_start (gen) =
20296         allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
20297     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20298     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20299     if (next_plug_to_allocate)
20300     {
20301         size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
20302         if (allocation_left > dist_to_next_plug)
20303         {
20304             allocation_left = dist_to_next_plug;
20305         }
20306     }
20307     if (allocation_left < Align (min_obj_size))
20308     {
20309         generation_plan_allocation_start_size (gen) += allocation_left;
20310         generation_allocation_pointer (consing_gen) += allocation_left;
20311     }
20312
20313     dprintf (1, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)", gen->gen_num, 
20314         generation_plan_allocation_start (gen),
20315         generation_plan_allocation_start_size (gen),
20316         generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen),
20317         next_plug_to_allocate));
20318 }
20319
20320 void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
20321 {
20322     BOOL adjacentp = FALSE;
20323
20324     generation_plan_allocation_start (gen) =  
20325         allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0, 
20326 #ifdef SHORT_PLUGS
20327                                    FALSE, NULL, 
20328 #endif //SHORT_PLUGS
20329                                    FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
20330
20331     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20332     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20333     if ((allocation_left < Align (min_obj_size)) && 
20334          (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
20335     {
20336         generation_plan_allocation_start_size (gen) += allocation_left;
20337         generation_allocation_pointer (consing_gen) += allocation_left;
20338     }
20339
20340     dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num, 
20341         generation_plan_allocation_start (consing_gen),
20342         generation_allocation_pointer (consing_gen), 
20343         generation_allocation_limit (consing_gen))); 
20344 }
20345
20346 void gc_heap::plan_generation_starts (generation*& consing_gen)
20347 {
20348     //make sure that every generation has a planned allocation start
20349     int  gen_number = settings.condemned_generation;
20350     while (gen_number >= 0)
20351     {
20352         if (gen_number < max_generation)
20353         {
20354             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20355         }
20356         generation* gen = generation_of (gen_number);
20357         if (0 == generation_plan_allocation_start (gen))
20358         {
20359             plan_generation_start (gen, consing_gen, 0);
20360             assert (generation_plan_allocation_start (gen));
20361         }
20362         gen_number--;
20363     }
20364     // now we know the planned allocation size
20365     heap_segment_plan_allocated (ephemeral_heap_segment) =
20366         generation_allocation_pointer (consing_gen);
20367 }
20368
20369 void gc_heap::advance_pins_for_demotion (generation* gen)
20370 {
20371     uint8_t* original_youngest_start = generation_allocation_start (youngest_generation);
20372     heap_segment* seg = ephemeral_heap_segment;
20373
20374     if ((!(pinned_plug_que_empty_p())))
20375     {
20376         size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
20377         size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
20378         size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
20379         float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
20380         float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
20381         if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
20382         {
20383             while (!pinned_plug_que_empty_p() &&
20384                     (pinned_plug (oldest_pin()) < original_youngest_start))
20385             {
20386                 size_t entry = deque_pinned_plug();
20387                 size_t len = pinned_len (pinned_plug_of (entry));
20388                 uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20389                 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
20390                 assert(mark_stack_array[entry].len == 0 ||
20391                         mark_stack_array[entry].len >= Align(min_obj_size));
20392                 generation_allocation_pointer (gen) = plug + len;
20393                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20394                 set_allocator_next_pin (gen);
20395
20396                 //Add the size of the pinned plug to the right pinned allocations
20397                 //find out which gen this pinned plug came from 
20398                 int frgn = object_gennum (plug);
20399                 if ((frgn != (int)max_generation) && settings.promotion)
20400                 {
20401                     int togn = object_gennum_plan (plug);
20402                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20403                     if (frgn < togn)
20404                     {
20405                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
20406                     }
20407                 }
20408
20409                 dprintf (2, ("skipping gap %d, pin %Ix (%Id)", 
20410                     pinned_len (pinned_plug_of (entry)), plug, len));
20411             }
20412         }
20413         dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d", 
20414             gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
20415     }
20416 }
20417
20418 void gc_heap::process_ephemeral_boundaries (uint8_t* x,
20419                                             int& active_new_gen_number,
20420                                             int& active_old_gen_number,
20421                                             generation*& consing_gen,
20422                                             BOOL& allocate_in_condemned)
20423 {
20424 retry:
20425     if ((active_old_gen_number > 0) &&
20426         (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
20427     {
20428         dprintf (1, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
20429
20430         if (!pinned_plug_que_empty_p())
20431         {
20432             dprintf (1, ("oldest pin: %Ix(%Id)",
20433                 pinned_plug (oldest_pin()), 
20434                 (x - pinned_plug (oldest_pin()))));
20435         }
20436
20437         if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
20438         {
20439             active_new_gen_number--;
20440         }
20441
20442         active_old_gen_number--;
20443         assert ((!settings.promotion) || (active_new_gen_number>0));
20444
20445         if (active_new_gen_number == (max_generation - 1))
20446         {
20447 #ifdef FREE_USAGE_STATS
20448             if (settings.condemned_generation == max_generation)
20449             {
20450                 // We need to do this before we skip the rest of the pinned plugs.
20451                 generation* gen_2 = generation_of (max_generation);
20452                 generation* gen_1 = generation_of (max_generation - 1);
20453
20454                 size_t total_num_pinned_free_spaces_left = 0;
20455
20456                 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
20457                 for (int j = 0; j < NUM_GEN_POWER2; j++)
20458                 {
20459                     dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)", 
20460                         heap_number, 
20461                         settings.gc_index,
20462                         (j + 10), 
20463                         gen_2->gen_current_pinned_free_spaces[j],
20464                         gen_2->gen_plugs[j], gen_1->gen_plugs[j],
20465                         (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
20466
20467                     total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
20468                 }
20469
20470                 float pinned_free_list_efficiency = 0;
20471                 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
20472                 if (total_pinned_free_space != 0)
20473                 {
20474                     pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
20475                 }
20476
20477                 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
20478                             heap_number,
20479                             generation_allocated_in_pinned_free (gen_2),
20480                             total_pinned_free_space, 
20481                             (int)(pinned_free_list_efficiency * 100),
20482                             generation_pinned_free_obj_space (gen_2),
20483                             total_num_pinned_free_spaces_left));
20484             }
20485 #endif //FREE_USAGE_STATS
20486
20487             //Go past all of the pinned plugs for this generation.
20488             while (!pinned_plug_que_empty_p() &&
20489                    (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
20490             {
20491                 size_t  entry = deque_pinned_plug();
20492                 mark*  m = pinned_plug_of (entry);
20493                 uint8_t*  plug = pinned_plug (m);
20494                 size_t  len = pinned_len (m);
20495                 // detect pinned block in different segment (later) than
20496                 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
20497                 // adjust the allocation segment along the way (at the end it will
20498                 // be the ephemeral segment.
20499                 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
20500
20501                 PREFIX_ASSUME(nseg != NULL);
20502
20503                 while (!((plug >= generation_allocation_pointer (consing_gen))&&
20504                         (plug < heap_segment_allocated (nseg))))
20505                 {
20506                     //adjust the end of the segment to be the end of the plug
20507                     assert (generation_allocation_pointer (consing_gen)>=
20508                             heap_segment_mem (nseg));
20509                     assert (generation_allocation_pointer (consing_gen)<=
20510                             heap_segment_committed (nseg));
20511
20512                     heap_segment_plan_allocated (nseg) =
20513                         generation_allocation_pointer (consing_gen);
20514                     //switch allocation segment
20515                     nseg = heap_segment_next_rw (nseg);
20516                     generation_allocation_segment (consing_gen) = nseg;
20517                     //reset the allocation pointer and limits
20518                     generation_allocation_pointer (consing_gen) =
20519                         heap_segment_mem (nseg);
20520                 }
20521                 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
20522                 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
20523                 generation_allocation_pointer (consing_gen) = plug + len;
20524                 generation_allocation_limit (consing_gen) =
20525                     generation_allocation_pointer (consing_gen);
20526             }
20527             allocate_in_condemned = TRUE;
20528             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20529         }
20530
20531         if (active_new_gen_number != max_generation)
20532         {
20533             if (active_new_gen_number == (max_generation - 1))
20534             {
20535                 maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
20536                 if (!demote_gen1_p)
20537                     advance_pins_for_demotion (consing_gen);
20538             }
20539
20540             plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
20541                 
20542             dprintf (1, ("process eph: allocated gen%d start at %Ix", 
20543                 active_new_gen_number,
20544                 generation_plan_allocation_start (generation_of (active_new_gen_number))));
20545
20546             if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
20547             {
20548                 uint8_t* pplug = pinned_plug (oldest_pin());
20549                 if (object_gennum (pplug) > 0)
20550                 {
20551                     demotion_low = pplug;
20552                     dprintf (3, ("process eph: dlow->%Ix", demotion_low));
20553                 }
20554             }
20555
20556             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
20557         }
20558
20559         goto retry;
20560     }
20561 }
20562
20563 inline
20564 void gc_heap::seg_clear_mark_bits (heap_segment* seg)
20565 {
20566     uint8_t* o = heap_segment_mem (seg);
20567     while (o < heap_segment_allocated (seg))
20568     {
20569         if (marked (o))
20570         {
20571             clear_marked (o);
20572         }
20573         o = o  + Align (size (o));
20574     }
20575 }
20576
20577 #ifdef FEATURE_BASICFREEZE
20578 void gc_heap::sweep_ro_segments (heap_segment* start_seg)
20579 {
20580     //go through all of the segment in range and reset the mark bit
20581     //TODO works only on small object segments
20582
20583     heap_segment* seg = start_seg;
20584
20585     while (seg)
20586     {
20587         if (heap_segment_read_only_p (seg) &&
20588             heap_segment_in_range_p (seg))
20589         {
20590 #ifdef BACKGROUND_GC
20591             if (settings.concurrent)
20592             {
20593                 seg_clear_mark_array_bits_soh (seg);
20594             }
20595             else
20596             {
20597                 seg_clear_mark_bits (seg);
20598             }
20599 #else //BACKGROUND_GC
20600
20601 #ifdef MARK_ARRAY
20602             if(gc_can_use_concurrent)
20603             {
20604                 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
20605                               min (heap_segment_allocated (seg), highest_address),
20606                               FALSE); // read_only segments need the mark clear
20607             }
20608 #else //MARK_ARRAY
20609             seg_clear_mark_bits (seg);
20610 #endif //MARK_ARRAY
20611
20612 #endif //BACKGROUND_GC
20613         }
20614         seg = heap_segment_next (seg);
20615     }
20616 }
20617 #endif // FEATURE_BASICFREEZE
20618
20619 #ifdef FEATURE_LOH_COMPACTION
20620 inline
20621 BOOL gc_heap::loh_pinned_plug_que_empty_p()
20622 {
20623     return (loh_pinned_queue_bos == loh_pinned_queue_tos);
20624 }
20625
20626 void gc_heap::loh_set_allocator_next_pin()
20627 {
20628     if (!(loh_pinned_plug_que_empty_p()))
20629     {
20630         mark*  oldest_entry = loh_oldest_pin();
20631         uint8_t* plug = pinned_plug (oldest_entry);
20632         generation* gen = large_object_generation;
20633         if ((plug >= generation_allocation_pointer (gen)) &&
20634             (plug <  generation_allocation_limit (gen)))
20635         {
20636             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
20637         }
20638         else
20639             assert (!((plug < generation_allocation_pointer (gen)) &&
20640                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
20641     }
20642 }
20643
20644 size_t gc_heap::loh_deque_pinned_plug ()
20645 {
20646     size_t m = loh_pinned_queue_bos;
20647     loh_pinned_queue_bos++;
20648     return m;
20649 }
20650
20651 inline
20652 mark* gc_heap::loh_pinned_plug_of (size_t bos)
20653 {
20654     return &loh_pinned_queue[bos];
20655 }
20656
20657 inline
20658 mark* gc_heap::loh_oldest_pin()
20659 {
20660     return loh_pinned_plug_of (loh_pinned_queue_bos);
20661 }
20662
20663 // If we can't grow the queue, then don't compact.
20664 BOOL gc_heap::loh_enque_pinned_plug (uint8_t* plug, size_t len)
20665 {
20666     assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
20667
20668     if (loh_pinned_queue_length <= loh_pinned_queue_tos)
20669     {
20670         if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
20671         {
20672             return FALSE;
20673         }
20674     }
20675     dprintf (3, (" P: %Ix(%Id)", plug, len));
20676     mark& m = loh_pinned_queue[loh_pinned_queue_tos];
20677     m.first = plug;
20678     m.len = len;
20679     loh_pinned_queue_tos++;
20680     loh_set_allocator_next_pin();
20681     return TRUE;
20682 }
20683
20684 inline
20685 BOOL gc_heap::loh_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit)
20686 {
20687     dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)", 
20688         size, 
20689         (2* AlignQword (loh_padding_obj_size) +  size),
20690         alloc_pointer,
20691         alloc_limit,
20692         (alloc_limit - alloc_pointer)));
20693
20694     return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) +  size) <= alloc_limit);
20695 }
20696
20697 uint8_t* gc_heap::loh_allocate_in_condemned (uint8_t* old_loc, size_t size)
20698 {
20699     UNREFERENCED_PARAMETER(old_loc);
20700
20701     generation* gen = large_object_generation;
20702     dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id", 
20703         generation_allocation_pointer (gen),
20704         generation_allocation_limit (gen),
20705         size));
20706
20707 retry:
20708     {
20709         heap_segment* seg = generation_allocation_segment (gen);
20710         if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
20711         {
20712             if ((!(loh_pinned_plug_que_empty_p()) &&
20713                  (generation_allocation_limit (gen) ==
20714                   pinned_plug (loh_oldest_pin()))))
20715             {
20716                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
20717                 size_t len = pinned_len (m);
20718                 uint8_t* plug = pinned_plug (m);
20719                 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
20720                 pinned_len (m) = plug - generation_allocation_pointer (gen);
20721                 generation_allocation_pointer (gen) = plug + len;
20722                 
20723                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20724                 loh_set_allocator_next_pin();
20725                 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)", 
20726                     generation_allocation_pointer (gen), 
20727                     generation_allocation_limit (gen),
20728                     (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20729
20730                 goto retry;
20731             }
20732
20733             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
20734             {
20735                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20736                 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
20737             }
20738             else
20739             {
20740                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
20741                 {
20742                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
20743                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20744                     dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
20745                 }
20746                 else
20747                 {
20748                     if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
20749                         (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
20750                     {
20751                         dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
20752                                          (generation_allocation_pointer (gen) + size)));
20753
20754                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
20755                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20756
20757                         dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)", 
20758                             generation_allocation_pointer (gen), 
20759                             generation_allocation_limit (gen),
20760                             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20761                     }
20762                     else
20763                     {
20764                         heap_segment* next_seg = heap_segment_next (seg);
20765                         assert (generation_allocation_pointer (gen)>=
20766                                 heap_segment_mem (seg));
20767                         // Verify that all pinned plugs for this segment are consumed
20768                         if (!loh_pinned_plug_que_empty_p() &&
20769                             ((pinned_plug (loh_oldest_pin()) <
20770                               heap_segment_allocated (seg)) &&
20771                              (pinned_plug (loh_oldest_pin()) >=
20772                               generation_allocation_pointer (gen))))
20773                         {
20774                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
20775                                          pinned_plug (loh_oldest_pin())));
20776                             dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
20777                             FATAL_GC_ERROR();
20778                         }
20779                         assert (generation_allocation_pointer (gen)>=
20780                                 heap_segment_mem (seg));
20781                         assert (generation_allocation_pointer (gen)<=
20782                                 heap_segment_committed (seg));
20783                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
20784
20785                         if (next_seg)
20786                         {
20787                             // for LOH do we want to try starting from the first LOH every time though?
20788                             generation_allocation_segment (gen) = next_seg;
20789                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
20790                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
20791
20792                             dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)", 
20793                                 generation_allocation_pointer (gen), 
20794                                 generation_allocation_limit (gen),
20795                                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20796                         }
20797                         else
20798                         {
20799                             dprintf (1, ("We ran out of space compacting, shouldn't happen"));
20800                             FATAL_GC_ERROR();
20801                         }
20802                     }
20803                 }
20804             }
20805             loh_set_allocator_next_pin();
20806
20807             dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)", 
20808                 generation_allocation_pointer (gen), 
20809                 generation_allocation_limit (gen),
20810                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20811
20812             goto retry;
20813         }
20814     }
20815
20816     {
20817         assert (generation_allocation_pointer (gen)>=
20818                 heap_segment_mem (generation_allocation_segment (gen)));
20819         uint8_t* result = generation_allocation_pointer (gen);
20820         size_t loh_pad = AlignQword (loh_padding_obj_size);
20821
20822         generation_allocation_pointer (gen) += size + loh_pad;
20823         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
20824
20825         dprintf (1235, ("p: %Ix, l: %Ix (%Id)", 
20826             generation_allocation_pointer (gen), 
20827             generation_allocation_limit (gen),
20828             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20829
20830         assert (result + loh_pad);
20831         return result + loh_pad;
20832     }
20833 }
20834
20835 BOOL gc_heap::should_compact_loh()
20836 {
20837     return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
20838 }
20839
20840 inline
20841 void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
20842 {
20843     if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
20844     {
20845         if (all_heaps_compacted_p)
20846         {
20847             // If the compaction mode says to compact once and we are going to compact LOH, 
20848             // we need to revert it back to no compaction.
20849             loh_compaction_mode = loh_compaction_default;
20850         }
20851     }
20852 }
20853
20854 BOOL gc_heap::plan_loh()
20855 {
20856     if (!loh_pinned_queue)
20857     {
20858         loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
20859         if (!loh_pinned_queue)
20860         {
20861             dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction", 
20862                          LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
20863             return FALSE;
20864         }
20865
20866         loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
20867     }
20868
20869     if (heap_number == 0)
20870         loh_pinned_queue_decay = LOH_PIN_DECAY;
20871
20872     loh_pinned_queue_tos = 0;
20873     loh_pinned_queue_bos = 0;
20874     
20875     generation* gen        = large_object_generation;
20876     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
20877     PREFIX_ASSUME(start_seg != NULL);
20878     heap_segment* seg      = start_seg;
20879     uint8_t* o             = generation_allocation_start (gen);
20880
20881     dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n", 
20882         generation_size (max_generation + 1), 
20883         generation_free_list_space (gen),
20884         generation_free_obj_space (gen)));
20885
20886     while (seg)
20887     {
20888         heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
20889         seg = heap_segment_next (seg);
20890     }
20891
20892     seg = start_seg;
20893
20894     //Skip the generation gap object
20895     o = o + AlignQword (size (o));
20896     // We don't need to ever realloc gen3 start so don't touch it.
20897     heap_segment_plan_allocated (seg) = o;
20898     generation_allocation_pointer (gen) = o;
20899     generation_allocation_limit (gen) = generation_allocation_pointer (gen);
20900     generation_allocation_segment (gen) = start_seg;
20901
20902     uint8_t* free_space_start = o;
20903     uint8_t* free_space_end = o;
20904     uint8_t* new_address = 0;
20905
20906     while (1)
20907     {
20908         if (o >= heap_segment_allocated (seg))
20909         {
20910             seg = heap_segment_next (seg);
20911             if (seg == 0)
20912             {
20913                 break;
20914             }
20915
20916             o = heap_segment_mem (seg);
20917         }
20918
20919         if (marked (o))
20920         {
20921             free_space_end = o;
20922             size_t size = AlignQword (size (o));
20923             dprintf (1235, ("%Ix(%Id) M", o, size));
20924
20925             if (pinned (o))
20926             {
20927                 // We don't clear the pinned bit yet so we can check in 
20928                 // compact phase how big a free object we should allocate
20929                 // in front of the pinned object. We use the reloc address
20930                 // field to store this.
20931                 if (!loh_enque_pinned_plug (o, size))
20932                 {
20933                     return FALSE;
20934                 }
20935                 new_address = o;
20936             }
20937             else
20938             {
20939                 new_address = loh_allocate_in_condemned (o, size);
20940             }
20941
20942             loh_set_node_relocation_distance (o, (new_address - o));
20943             dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
20944
20945             o = o + size;
20946             free_space_start = o;
20947             if (o < heap_segment_allocated (seg))
20948             {
20949                 assert (!marked (o));
20950             }
20951         }
20952         else
20953         {
20954             while (o < heap_segment_allocated (seg) && !marked (o))
20955             {
20956                 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_gc_pFreeObjectMethodTable) ? 1 : 0)));
20957                 o = o + AlignQword (size (o));
20958             }
20959         }
20960     }
20961
20962     while (!loh_pinned_plug_que_empty_p())
20963     {
20964         mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
20965         size_t len = pinned_len (m);
20966         uint8_t* plug = pinned_plug (m);
20967
20968         // detect pinned block in different segment (later) than
20969         // allocation segment
20970         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
20971
20972         while ((plug < generation_allocation_pointer (gen)) ||
20973                (plug >= heap_segment_allocated (nseg)))
20974         {
20975             assert ((plug < heap_segment_mem (nseg)) ||
20976                     (plug > heap_segment_reserved (nseg)));
20977             //adjust the end of the segment to be the end of the plug
20978             assert (generation_allocation_pointer (gen)>=
20979                     heap_segment_mem (nseg));
20980             assert (generation_allocation_pointer (gen)<=
20981                     heap_segment_committed (nseg));
20982
20983             heap_segment_plan_allocated (nseg) =
20984                 generation_allocation_pointer (gen);
20985             //switch allocation segment
20986             nseg = heap_segment_next_rw (nseg);
20987             generation_allocation_segment (gen) = nseg;
20988             //reset the allocation pointer and limits
20989             generation_allocation_pointer (gen) =
20990                 heap_segment_mem (nseg);
20991         }
20992
20993         dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
20994         pinned_len (m) = plug - generation_allocation_pointer (gen);
20995         generation_allocation_pointer (gen) = plug + len;
20996     }
20997
20998     heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
20999     generation_allocation_pointer (gen) = 0;
21000     generation_allocation_limit (gen) = 0;
21001
21002     return TRUE;
21003 }
21004
21005 void gc_heap::compact_loh()
21006 {
21007     assert (should_compact_loh());
21008
21009     generation* gen        = large_object_generation;
21010     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21011     PREFIX_ASSUME(start_seg != NULL);
21012     heap_segment* seg      = start_seg;
21013     heap_segment* prev_seg = 0;
21014     uint8_t* o             = generation_allocation_start (gen);
21015
21016     //Skip the generation gap object
21017     o = o + AlignQword (size (o));
21018     // We don't need to ever realloc gen3 start so don't touch it.
21019     uint8_t* free_space_start = o;
21020     uint8_t* free_space_end = o;
21021     generation_allocator (gen)->clear();
21022     generation_free_list_space (gen) = 0;
21023     generation_free_obj_space (gen) = 0;
21024
21025     loh_pinned_queue_bos = 0;
21026
21027     while (1)
21028     {
21029         if (o >= heap_segment_allocated (seg))
21030         {
21031             heap_segment* next_seg = heap_segment_next (seg);
21032
21033             if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
21034                 (seg != start_seg) && !heap_segment_read_only_p (seg))
21035             {
21036                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
21037                 assert (prev_seg);
21038                 heap_segment_next (prev_seg) = next_seg;
21039                 heap_segment_next (seg) = freeable_large_heap_segment;
21040                 freeable_large_heap_segment = seg;
21041             }
21042             else
21043             {
21044                 if (!heap_segment_read_only_p (seg))
21045                 {
21046                     // We grew the segment to accommodate allocations.
21047                     if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
21048                     {
21049                         if ((heap_segment_plan_allocated (seg) - plug_skew)  > heap_segment_used (seg))
21050                         {
21051                             heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
21052                         }
21053                     }
21054
21055                     heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
21056                     dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
21057                     decommit_heap_segment_pages (seg, 0);
21058                     dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
21059                         seg, 
21060                         heap_segment_allocated (seg),
21061                         heap_segment_used (seg),
21062                         heap_segment_committed (seg)));
21063                     //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
21064                     dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
21065                 }
21066                 prev_seg = seg;
21067             }
21068
21069             seg = next_seg;
21070             if (seg == 0)
21071                 break;
21072             else
21073             {
21074                 o = heap_segment_mem (seg);
21075             }
21076         }
21077
21078         if (marked (o))
21079         {
21080             free_space_end = o;
21081             size_t size = AlignQword (size (o));
21082
21083             size_t loh_pad;
21084             uint8_t* reloc = o;
21085             clear_marked (o);
21086
21087             if (pinned (o))
21088             {
21089                 // We are relying on the fact the pinned objects are always looked at in the same order 
21090                 // in plan phase and in compact phase.
21091                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21092                 uint8_t* plug = pinned_plug (m);
21093                 assert (plug == o);
21094
21095                 loh_pad = pinned_len (m);
21096                 clear_pinned (o);
21097             }
21098             else
21099             {
21100                 loh_pad = AlignQword (loh_padding_obj_size);
21101
21102                 reloc += loh_node_relocation_distance (o);
21103                 gcmemcopy (reloc, o, size, TRUE);
21104             }
21105
21106             thread_gap ((reloc - loh_pad), loh_pad, gen);
21107
21108             o = o + size;
21109             free_space_start = o;
21110             if (o < heap_segment_allocated (seg))
21111             {
21112                 assert (!marked (o));
21113             }
21114         }
21115         else
21116         {
21117             while (o < heap_segment_allocated (seg) && !marked (o))
21118             {
21119                 o = o + AlignQword (size (o));
21120             }
21121         }
21122     }
21123
21124     assert (loh_pinned_plug_que_empty_p());
21125
21126     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21127         generation_size (max_generation + 1), 
21128         generation_free_list_space (gen),
21129         generation_free_obj_space (gen)));
21130 }
21131
21132 void gc_heap::relocate_in_loh_compact()
21133 {
21134     generation* gen        = large_object_generation;
21135     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21136     uint8_t* o             = generation_allocation_start (gen);
21137
21138     //Skip the generation gap object
21139     o = o + AlignQword (size (o));
21140
21141     relocate_args args;
21142     args.low = gc_low;
21143     args.high = gc_high;
21144     args.last_plug = 0;
21145
21146     while (1)
21147     {
21148         if (o >= heap_segment_allocated (seg))
21149         {
21150             seg = heap_segment_next (seg);
21151             if (seg == 0)
21152             {
21153                 break;
21154             }
21155
21156             o = heap_segment_mem (seg);
21157         }
21158
21159         if (marked (o))
21160         {
21161             size_t size = AlignQword (size (o));
21162
21163             check_class_object_demotion (o);
21164             if (contain_pointers (o))
21165             {
21166                 go_through_object_nostart (method_table (o), o, size(o), pval,
21167                 {
21168                     reloc_survivor_helper (pval);
21169                 });
21170             }
21171
21172             o = o + size;
21173             if (o < heap_segment_allocated (seg))
21174             {
21175                 assert (!marked (o));
21176             }
21177         }
21178         else
21179         {
21180             while (o < heap_segment_allocated (seg) && !marked (o))
21181             {
21182                 o = o + AlignQword (size (o));
21183             }
21184         }
21185     }
21186
21187     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21188         generation_size (max_generation + 1), 
21189         generation_free_list_space (gen),
21190         generation_free_obj_space (gen)));
21191 }
21192
21193 void gc_heap::walk_relocation_for_loh (void* profiling_context, record_surv_fn fn)
21194 {
21195     generation* gen        = large_object_generation;
21196     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21197     uint8_t* o             = generation_allocation_start (gen);
21198
21199     //Skip the generation gap object
21200     o = o + AlignQword (size (o));
21201
21202     while (1)
21203     {
21204         if (o >= heap_segment_allocated (seg))
21205         {
21206             seg = heap_segment_next (seg);
21207             if (seg == 0)
21208             {
21209                 break;
21210             }
21211
21212             o = heap_segment_mem (seg);
21213         }
21214
21215         if (marked (o))
21216         {
21217             size_t size = AlignQword (size (o));
21218
21219             ptrdiff_t reloc = loh_node_relocation_distance (o);
21220
21221             STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
21222
21223             fn (o, (o + size), reloc, profiling_context, !!settings.compaction, false);
21224
21225             o = o + size;
21226             if (o < heap_segment_allocated (seg))
21227             {
21228                 assert (!marked (o));
21229             }
21230         }
21231         else
21232         {
21233             while (o < heap_segment_allocated (seg) && !marked (o))
21234             {
21235                 o = o + AlignQword (size (o));
21236             }
21237         }
21238     }
21239 }
21240
21241 BOOL gc_heap::loh_object_p (uint8_t* o)
21242 {
21243 #ifdef MULTIPLE_HEAPS
21244     gc_heap* hp = gc_heap::g_heaps [0];
21245     int brick_entry = hp->brick_table[hp->brick_of (o)];
21246 #else //MULTIPLE_HEAPS
21247     int brick_entry = brick_table[brick_of (o)];
21248 #endif //MULTIPLE_HEAPS
21249
21250     return (brick_entry == 0);
21251 }
21252 #endif //FEATURE_LOH_COMPACTION
21253
21254 void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p, 
21255                                       BOOL& last_pinned_plug_p, 
21256                                       BOOL& pinned_plug_p,
21257                                       size_t ps,
21258                                       size_t& artificial_pinned_size)
21259 {
21260     last_npinned_plug_p = FALSE;
21261     last_pinned_plug_p = TRUE;
21262     pinned_plug_p = TRUE;
21263     artificial_pinned_size = ps;
21264 }
21265
21266 // Because we have the artificial pinning, we can't guarantee that pinned and npinned
21267 // plugs are always interleaved.
21268 void gc_heap::store_plug_gap_info (uint8_t* plug_start,
21269                                    uint8_t* plug_end,
21270                                    BOOL& last_npinned_plug_p, 
21271                                    BOOL& last_pinned_plug_p, 
21272                                    uint8_t*& last_pinned_plug,
21273                                    BOOL& pinned_plug_p,
21274                                    uint8_t* last_object_in_last_plug,
21275                                    BOOL& merge_with_last_pin_p,
21276                                    // this is only for verification purpose
21277                                    size_t last_plug_len)
21278 {
21279     UNREFERENCED_PARAMETER(last_plug_len);
21280
21281     if (!last_npinned_plug_p && !last_pinned_plug_p)
21282     {
21283         //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
21284         dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
21285         assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
21286         set_gap_size (plug_start, plug_start - plug_end);
21287     }
21288
21289     if (pinned (plug_start))
21290     {
21291         BOOL save_pre_plug_info_p = FALSE;
21292
21293         if (last_npinned_plug_p || last_pinned_plug_p)
21294         {
21295             //if (last_plug_len == Align (min_obj_size))
21296             //{
21297             //    dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
21298             //    GCToOSInterface::DebugBreak();
21299             //}
21300             save_pre_plug_info_p = TRUE;
21301         }
21302
21303         pinned_plug_p = TRUE;
21304         last_npinned_plug_p = FALSE;
21305
21306         if (last_pinned_plug_p)
21307         {
21308             dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
21309             merge_with_last_pin_p = TRUE;
21310         }
21311         else
21312         {
21313             last_pinned_plug_p = TRUE;
21314             last_pinned_plug = plug_start;
21315                 
21316             enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
21317
21318             if (save_pre_plug_info_p)
21319             {
21320                 set_gap_size (plug_start, sizeof (gap_reloc_pair));
21321             }
21322         }
21323     }
21324     else
21325     {
21326         if (last_pinned_plug_p)
21327         {
21328             //if (Align (last_plug_len) < min_pre_pin_obj_size)
21329             //{
21330             //    dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
21331             //    GCToOSInterface::DebugBreak();
21332             //}
21333
21334             save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
21335             set_gap_size (plug_start, sizeof (gap_reloc_pair));
21336
21337             verify_pins_with_post_plug_info("after saving post plug info");
21338         }
21339         last_npinned_plug_p = TRUE;
21340         last_pinned_plug_p = FALSE;
21341     }
21342 }
21343
21344 void gc_heap::record_interesting_data_point (interesting_data_point idp)
21345 {
21346 #ifdef GC_CONFIG_DRIVEN
21347     (interesting_data_per_gc[idp])++;
21348 #else
21349     UNREFERENCED_PARAMETER(idp);
21350 #endif //GC_CONFIG_DRIVEN
21351 }
21352
21353 #ifdef _PREFAST_
21354 #pragma warning(push)
21355 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
21356 #endif //_PREFAST_
21357 void gc_heap::plan_phase (int condemned_gen_number)
21358 {
21359     size_t old_gen2_allocated = 0;
21360     size_t old_gen2_size = 0;
21361
21362     if (condemned_gen_number == (max_generation - 1))
21363     {
21364         old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
21365         old_gen2_size = generation_size (max_generation);
21366     }
21367
21368     assert (settings.concurrent == FALSE);
21369
21370     // %type%  category = quote (plan);
21371 #ifdef TIME_GC
21372     unsigned start;
21373     unsigned finish;
21374     start = GetCycleCount32();
21375 #endif //TIME_GC
21376
21377     dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
21378                 condemned_gen_number, settings.promotion ? 1 : 0));
21379
21380     generation*  condemned_gen1 = generation_of (condemned_gen_number);
21381
21382 #ifdef MARK_LIST
21383     BOOL use_mark_list = FALSE;
21384     uint8_t** mark_list_next = &mark_list[0];
21385 #ifdef GC_CONFIG_DRIVEN
21386     dprintf (3, ("total number of marked objects: %Id (%Id)",
21387                  (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0]))));
21388 #else
21389     dprintf (3, ("mark_list length: %Id",
21390                  (mark_list_index - &mark_list[0])));
21391 #endif //GC_CONFIG_DRIVEN
21392
21393     if ((condemned_gen_number < max_generation) &&
21394         (mark_list_index <= mark_list_end) 
21395 #ifdef BACKGROUND_GC        
21396         && (!recursive_gc_sync::background_running_p())
21397 #endif //BACKGROUND_GC
21398         )
21399     {
21400 #ifndef MULTIPLE_HEAPS
21401         _sort (&mark_list[0], mark_list_index-1, 0);
21402         //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
21403         //verify_qsort_array (&mark_list[0], mark_list_index-1);
21404 #endif //!MULTIPLE_HEAPS
21405         use_mark_list = TRUE;
21406         get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit);
21407     }
21408     else
21409     {
21410         dprintf (3, ("mark_list not used"));
21411     }
21412
21413 #endif //MARK_LIST
21414
21415 #ifdef FEATURE_BASICFREEZE
21416     if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
21417         ro_segments_in_range)
21418     {
21419         sweep_ro_segments (generation_start_segment (condemned_gen1));
21420     }
21421 #endif // FEATURE_BASICFREEZE
21422
21423 #ifndef MULTIPLE_HEAPS
21424     if (shigh != (uint8_t*)0)
21425     {
21426         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21427
21428         PREFIX_ASSUME(seg != NULL);
21429
21430         heap_segment* fseg = seg;
21431         do
21432         {
21433             if (slow > heap_segment_mem (seg) &&
21434                 slow < heap_segment_reserved (seg))
21435             {
21436                 if (seg == fseg)
21437                 {
21438                     uint8_t* o = generation_allocation_start (condemned_gen1) +
21439                         Align (size (generation_allocation_start (condemned_gen1)));
21440                     if (slow > o)
21441                     {
21442                         assert ((slow - o) >= (int)Align (min_obj_size));
21443 #ifdef BACKGROUND_GC
21444                         if (current_c_gc_state == c_gc_state_marking)
21445                         {
21446                             bgc_clear_batch_mark_array_bits (o, slow);
21447                         }
21448 #endif //BACKGROUND_GC
21449                         make_unused_array (o, slow - o);
21450                     }
21451                 } 
21452                 else
21453                 {
21454                     assert (condemned_gen_number == max_generation);
21455                     make_unused_array (heap_segment_mem (seg),
21456                                        slow - heap_segment_mem (seg));
21457                 }
21458             }
21459             if (in_range_for_segment (shigh, seg))
21460             {
21461 #ifdef BACKGROUND_GC
21462                 if (current_c_gc_state == c_gc_state_marking)
21463                 {
21464                     bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
21465                 }
21466 #endif //BACKGROUND_GC
21467                 heap_segment_allocated (seg) = shigh + Align (size (shigh));
21468             }
21469             // test if the segment is in the range of [slow, shigh]
21470             if (!((heap_segment_reserved (seg) >= slow) &&
21471                   (heap_segment_mem (seg) <= shigh)))
21472             {
21473                 // shorten it to minimum
21474                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
21475             }
21476             seg = heap_segment_next_rw (seg);
21477         } while (seg);
21478     }
21479     else
21480     {
21481         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21482
21483         PREFIX_ASSUME(seg != NULL);
21484
21485         heap_segment* sseg = seg;
21486         do
21487         {
21488             // shorten it to minimum
21489             if (seg == sseg)
21490             {
21491                 // no survivors make all generations look empty
21492                 uint8_t* o = generation_allocation_start (condemned_gen1) +
21493                     Align (size (generation_allocation_start (condemned_gen1)));
21494 #ifdef BACKGROUND_GC
21495                 if (current_c_gc_state == c_gc_state_marking)
21496                 {
21497                     bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
21498                 }
21499 #endif //BACKGROUND_GC
21500                 heap_segment_allocated (seg) = o;
21501             }
21502             else
21503             {
21504                 assert (condemned_gen_number == max_generation);
21505 #ifdef BACKGROUND_GC
21506                 if (current_c_gc_state == c_gc_state_marking)
21507                 {
21508                     bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
21509                 }
21510 #endif //BACKGROUND_GC
21511                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
21512             }
21513             seg = heap_segment_next_rw (seg);
21514         } while (seg);
21515     }
21516
21517 #endif //MULTIPLE_HEAPS
21518
21519     heap_segment*  seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
21520
21521     PREFIX_ASSUME(seg1 != NULL);
21522
21523     uint8_t*  end = heap_segment_allocated (seg1);
21524     uint8_t*  first_condemned_address = generation_allocation_start (condemned_gen1);
21525     uint8_t*  x = first_condemned_address;
21526
21527     assert (!marked (x));
21528     uint8_t*  plug_end = x;
21529     uint8_t*  tree = 0;
21530     size_t  sequence_number = 0;
21531     uint8_t*  last_node = 0;
21532     size_t  current_brick = brick_of (x);
21533     BOOL  allocate_in_condemned = ((condemned_gen_number == max_generation)||
21534                                    (settings.promotion == FALSE));
21535     int  active_old_gen_number = condemned_gen_number;
21536     int  active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
21537                                   (1 + condemned_gen_number));
21538     generation*  older_gen = 0;
21539     generation* consing_gen = condemned_gen1;
21540     alloc_list  r_free_list [MAX_BUCKET_COUNT];
21541
21542     size_t r_free_list_space = 0;
21543     size_t r_free_obj_space = 0;
21544     size_t r_older_gen_free_list_allocated = 0;
21545     size_t r_older_gen_condemned_allocated = 0;
21546     size_t r_older_gen_end_seg_allocated = 0;
21547     uint8_t*  r_allocation_pointer = 0;
21548     uint8_t*  r_allocation_limit = 0;
21549     uint8_t* r_allocation_start_region = 0;
21550     heap_segment*  r_allocation_segment = 0;
21551 #ifdef FREE_USAGE_STATS
21552     size_t r_older_gen_free_space[NUM_GEN_POWER2];
21553 #endif //FREE_USAGE_STATS
21554
21555     if ((condemned_gen_number < max_generation))
21556     {
21557         older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
21558         generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
21559
21560         r_free_list_space = generation_free_list_space (older_gen);
21561         r_free_obj_space = generation_free_obj_space (older_gen);
21562 #ifdef FREE_USAGE_STATS
21563         memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
21564 #endif //FREE_USAGE_STATS
21565         generation_allocate_end_seg_p (older_gen) = FALSE;
21566         r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
21567         r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
21568         r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
21569         r_allocation_limit = generation_allocation_limit (older_gen);
21570         r_allocation_pointer = generation_allocation_pointer (older_gen);
21571         r_allocation_start_region = generation_allocation_context_start_region (older_gen);
21572         r_allocation_segment = generation_allocation_segment (older_gen);
21573         heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
21574
21575         PREFIX_ASSUME(start_seg != NULL);
21576
21577         if (start_seg != ephemeral_heap_segment)
21578         {
21579             assert (condemned_gen_number == (max_generation - 1));
21580             while (start_seg && (start_seg != ephemeral_heap_segment))
21581             {
21582                 assert (heap_segment_allocated (start_seg) >=
21583                         heap_segment_mem (start_seg));
21584                 assert (heap_segment_allocated (start_seg) <=
21585                         heap_segment_reserved (start_seg));
21586                 heap_segment_plan_allocated (start_seg) =
21587                     heap_segment_allocated (start_seg);
21588                 start_seg = heap_segment_next_rw (start_seg);
21589             }
21590         }
21591     }
21592
21593     //reset all of the segment allocated sizes
21594     {
21595         heap_segment*  seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
21596
21597         PREFIX_ASSUME(seg2 != NULL);
21598
21599         while (seg2)
21600         {
21601             heap_segment_plan_allocated (seg2) =
21602                 heap_segment_mem (seg2);
21603             seg2 = heap_segment_next_rw (seg2);
21604         }
21605     }
21606     int  condemned_gn = condemned_gen_number;
21607
21608     int bottom_gen = 0;
21609     init_free_and_plug();
21610
21611     while (condemned_gn >= bottom_gen)
21612     {
21613         generation*  condemned_gen2 = generation_of (condemned_gn);
21614         generation_allocator (condemned_gen2)->clear();
21615         generation_free_list_space (condemned_gen2) = 0;
21616         generation_free_obj_space (condemned_gen2) = 0;
21617         generation_allocation_size (condemned_gen2) = 0;
21618         generation_condemned_allocated (condemned_gen2) = 0; 
21619         generation_pinned_allocated (condemned_gen2) = 0; 
21620         generation_free_list_allocated(condemned_gen2) = 0; 
21621         generation_end_seg_allocated (condemned_gen2) = 0; 
21622         generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
21623         generation_pinned_allocation_compact_size (condemned_gen2) = 0;
21624 #ifdef FREE_USAGE_STATS
21625         generation_pinned_free_obj_space (condemned_gen2) = 0;
21626         generation_allocated_in_pinned_free (condemned_gen2) = 0;
21627         generation_allocated_since_last_pin (condemned_gen2) = 0;
21628 #endif //FREE_USAGE_STATS
21629         generation_plan_allocation_start (condemned_gen2) = 0;
21630         generation_allocation_segment (condemned_gen2) =
21631             heap_segment_rw (generation_start_segment (condemned_gen2));
21632
21633         PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
21634
21635         if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
21636         {
21637             generation_allocation_pointer (condemned_gen2) =
21638                 heap_segment_mem (generation_allocation_segment (condemned_gen2));
21639         }
21640         else
21641         {
21642             generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
21643         }
21644
21645         generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21646         generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21647
21648         condemned_gn--;
21649     }
21650
21651     BOOL allocate_first_generation_start = FALSE;
21652     
21653     if (allocate_in_condemned)
21654     {
21655         allocate_first_generation_start = TRUE;
21656     }
21657
21658     dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21659
21660     demotion_low = MAX_PTR;
21661     demotion_high = heap_segment_allocated (ephemeral_heap_segment);
21662
21663     // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
21664     // from gen1. They should get promoted to gen2.
21665     demote_gen1_p = !(settings.promotion && 
21666                       (settings.condemned_generation == (max_generation - 1)) && 
21667                       gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
21668
21669     total_ephemeral_size = 0;
21670
21671     print_free_and_plug ("BP");
21672
21673     for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
21674     {
21675         generation* temp_gen = generation_of (gen_idx);
21676
21677         dprintf (2, ("gen%d start %Ix, plan start %Ix",
21678             gen_idx, 
21679             generation_allocation_start (temp_gen),
21680             generation_plan_allocation_start (temp_gen)));
21681     }
21682
21683     BOOL fire_pinned_plug_events_p = EVENT_ENABLED(PinPlugAtGCTime);
21684     size_t last_plug_len = 0;
21685
21686     while (1)
21687     {
21688         if (x >= end)
21689         {
21690             assert (x == end);
21691             assert (heap_segment_allocated (seg1) == end);
21692             heap_segment_allocated (seg1) = plug_end;
21693
21694             current_brick = update_brick_table (tree, current_brick, x, plug_end);
21695             dprintf (3, ("end of seg: new tree, sequence# 0"));
21696             sequence_number = 0;
21697             tree = 0;
21698
21699             if (heap_segment_next_rw (seg1))
21700             {
21701                 seg1 = heap_segment_next_rw (seg1);
21702                 end = heap_segment_allocated (seg1);
21703                 plug_end = x = heap_segment_mem (seg1);
21704                 current_brick = brick_of (x);
21705                 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21706                 continue;
21707             }
21708             else
21709             {
21710                 break;
21711             }
21712         }
21713
21714         BOOL last_npinned_plug_p = FALSE;
21715         BOOL last_pinned_plug_p = FALSE;
21716
21717         // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
21718         // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
21719         // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
21720         uint8_t* last_pinned_plug = 0;
21721         size_t num_pinned_plugs_in_plug = 0;
21722
21723         uint8_t* last_object_in_plug = 0;
21724
21725         while ((x < end) && marked (x))
21726         {
21727             uint8_t*  plug_start = x;
21728             uint8_t*  saved_plug_end = plug_end;
21729             BOOL   pinned_plug_p = FALSE;
21730             BOOL   npin_before_pin_p = FALSE;
21731             BOOL   saved_last_npinned_plug_p = last_npinned_plug_p;
21732             uint8_t*  saved_last_object_in_plug = last_object_in_plug;
21733             BOOL   merge_with_last_pin_p = FALSE;
21734
21735             size_t added_pinning_size = 0;
21736             size_t artificial_pinned_size = 0;
21737
21738             store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p, 
21739                                  last_pinned_plug, pinned_plug_p, last_object_in_plug, 
21740                                  merge_with_last_pin_p, last_plug_len);
21741
21742 #ifdef FEATURE_STRUCTALIGN
21743             int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
21744             size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
21745 #endif // FEATURE_STRUCTALIGN
21746
21747             {
21748                 uint8_t* xl = x;
21749                 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
21750                 {
21751                     assert (xl < end);
21752                     if (pinned(xl))
21753                     {
21754                         clear_pinned (xl);
21755                     }
21756 #ifdef FEATURE_STRUCTALIGN
21757                     else
21758                     {
21759                         int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
21760                         if (obj_requiredAlignment > requiredAlignment)
21761                         {
21762                             requiredAlignment = obj_requiredAlignment;
21763                             alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
21764                         }
21765                     }
21766 #endif // FEATURE_STRUCTALIGN
21767
21768                     clear_marked (xl);
21769
21770                     dprintf(4, ("+%Ix+", (size_t)xl));
21771                     assert ((size (xl) > 0));
21772                     assert ((size (xl) <= LARGE_OBJECT_SIZE));
21773
21774                     last_object_in_plug = xl;
21775
21776                     xl = xl + Align (size (xl));
21777                     Prefetch (xl);
21778                 }
21779
21780                 BOOL next_object_marked_p = ((xl < end) && marked (xl));
21781
21782                 if (pinned_plug_p)
21783                 {
21784                     // If it is pinned we need to extend to the next marked object as we can't use part of
21785                     // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
21786                     // references but for now I am just using the next non pinned object for that).
21787                     if (next_object_marked_p) 
21788                     {
21789                         clear_marked (xl);
21790                         last_object_in_plug = xl;
21791                         size_t extra_size = Align (size (xl));
21792                         xl = xl + extra_size;
21793                         added_pinning_size = extra_size;
21794                     }
21795                 }
21796                 else
21797                 {
21798                     if (next_object_marked_p)
21799                         npin_before_pin_p = TRUE;
21800                 }
21801
21802                 assert (xl <= end);
21803                 x = xl;
21804             }
21805             dprintf (3, ( "%Ix[", (size_t)x));
21806             plug_end = x;
21807             size_t ps = plug_end - plug_start;
21808             last_plug_len = ps;
21809             dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
21810             uint8_t*  new_address = 0;
21811
21812             if (!pinned_plug_p)
21813             {
21814                 if (allocate_in_condemned &&
21815                     (settings.condemned_generation == max_generation) &&
21816                     (ps > OS_PAGE_SIZE))
21817                 {
21818                     ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
21819                     //reloc should >=0 except when we relocate
21820                     //across segments and the dest seg is higher then the src
21821
21822                     if ((ps > (8*OS_PAGE_SIZE)) &&
21823                         (reloc > 0) &&
21824                         ((size_t)reloc < (ps/16)))
21825                     {
21826                         dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
21827                                      (size_t)plug_start, reloc));
21828                         // The last plug couldn't have been a npinned plug or it would have
21829                         // included this plug.
21830                         assert (!saved_last_npinned_plug_p);
21831
21832                         if (last_pinned_plug)
21833                         {
21834                             dprintf (3, ("artificially pinned plug merged with last pinned plug"));
21835                             merge_with_last_pin_p = TRUE;
21836                         }
21837                         else
21838                         {
21839                             enque_pinned_plug (plug_start, FALSE, 0);
21840                             last_pinned_plug = plug_start;
21841                         }
21842
21843                         convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
21844                                                 ps, artificial_pinned_size);
21845                     }
21846                 }
21847             }
21848
21849             if (allocate_first_generation_start)
21850             {
21851                 allocate_first_generation_start = FALSE;
21852                 plan_generation_start (condemned_gen1, consing_gen, plug_start);
21853                 assert (generation_plan_allocation_start (condemned_gen1));
21854             }
21855
21856             if (seg1 == ephemeral_heap_segment)
21857             {
21858                 process_ephemeral_boundaries (plug_start, active_new_gen_number,
21859                                               active_old_gen_number,
21860                                               consing_gen,
21861                                               allocate_in_condemned);
21862             }
21863
21864             dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
21865
21866             dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
21867             dd_survived_size (dd_active_old) += ps;
21868
21869             BOOL convert_to_pinned_p = FALSE;
21870
21871             if (!pinned_plug_p)
21872             {
21873 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
21874                 dd_num_npinned_plugs (dd_active_old)++;
21875 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
21876
21877                 add_gen_plug (active_old_gen_number, ps);
21878
21879                 if (allocate_in_condemned)
21880                 {
21881                     verify_pins_with_post_plug_info("before aic");
21882
21883                     new_address =
21884                         allocate_in_condemned_generations (consing_gen,
21885                                                            ps,
21886                                                            active_old_gen_number,
21887 #ifdef SHORT_PLUGS
21888                                                            &convert_to_pinned_p,
21889                                                            (npin_before_pin_p ? plug_end : 0),
21890                                                            seg1,
21891 #endif //SHORT_PLUGS
21892                                                            plug_start REQD_ALIGN_AND_OFFSET_ARG);
21893                     verify_pins_with_post_plug_info("after aic");
21894                 }
21895                 else
21896                 {
21897                     new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
21898
21899                     if (new_address != 0)
21900                     {
21901                         if (settings.condemned_generation == (max_generation - 1))
21902                         {
21903                             dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
21904                                 plug_start, plug_end,
21905                                 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
21906                                 (size_t)(plug_end - plug_start)));
21907                         }
21908                     }
21909                     else
21910                     {
21911                         allocate_in_condemned = TRUE;
21912
21913                         new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number, 
21914 #ifdef SHORT_PLUGS
21915                                                                          &convert_to_pinned_p,
21916                                                                          (npin_before_pin_p ? plug_end : 0),
21917                                                                          seg1,
21918 #endif //SHORT_PLUGS
21919                                                                          plug_start REQD_ALIGN_AND_OFFSET_ARG);
21920                     }
21921                 }
21922
21923                 if (convert_to_pinned_p)
21924                 {
21925                     assert (last_npinned_plug_p != FALSE);
21926                     assert (last_pinned_plug_p == FALSE);
21927                     convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
21928                                             ps, artificial_pinned_size);
21929                     enque_pinned_plug (plug_start, FALSE, 0);
21930                     last_pinned_plug = plug_start;
21931                 }
21932                 else
21933                 {
21934                     if (!new_address)
21935                     {
21936                         //verify that we are at then end of the ephemeral segment
21937                         assert (generation_allocation_segment (consing_gen) ==
21938                                 ephemeral_heap_segment);
21939                         //verify that we are near the end
21940                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
21941                                 heap_segment_allocated (ephemeral_heap_segment));
21942                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
21943                                 (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
21944                     }
21945                     else
21946                     {
21947 #ifdef SIMPLE_DPRINTF
21948                         dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)",
21949                             (size_t)(node_gap_size (plug_start)), 
21950                             plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
21951                                 (size_t)new_address + ps, ps, 
21952                                 (is_plug_padded (plug_start) ? 1 : 0)));
21953 #endif //SIMPLE_DPRINTF
21954
21955 #ifdef SHORT_PLUGS
21956                         if (is_plug_padded (plug_start))
21957                         {
21958                             dprintf (3, ("%Ix was padded", plug_start));
21959                             dd_padding_size (dd_active_old) += Align (min_obj_size);
21960                         }
21961 #endif //SHORT_PLUGS
21962                     }
21963                 }
21964             }
21965
21966             if (pinned_plug_p)
21967             {
21968                 if (fire_pinned_plug_events_p)
21969                     FIRE_EVENT(PinPlugAtGCTime, plug_start, plug_end, 
21970                                (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start)));
21971
21972                 if (merge_with_last_pin_p)
21973                 {
21974                     merge_with_last_pinned_plug (last_pinned_plug, ps);
21975                 }
21976                 else
21977                 {
21978                     assert (last_pinned_plug == plug_start);
21979                     set_pinned_info (plug_start, ps, consing_gen);
21980                 }
21981
21982                 new_address = plug_start;
21983
21984                 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)",
21985                             (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
21986                             (size_t)plug_end, ps,
21987                             (merge_with_last_pin_p ? 1 : 0)));
21988
21989                 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
21990                 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
21991                 dd_added_pinned_size (dd_active_old) += added_pinning_size;
21992                 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
21993
21994                 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
21995                 {
21996                     last_gen1_pin_end = plug_end;
21997                 }
21998             }
21999
22000 #ifdef _DEBUG
22001             // detect forward allocation in the same segment
22002             assert (!((new_address > plug_start) &&
22003                 (new_address < heap_segment_reserved (seg1))));
22004 #endif //_DEBUG
22005
22006             if (!merge_with_last_pin_p)
22007             {
22008                 if (current_brick != brick_of (plug_start))
22009                 {
22010                     current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
22011                     sequence_number = 0;
22012                     tree = 0;
22013                 }
22014
22015                 set_node_relocation_distance (plug_start, (new_address - plug_start));
22016                 if (last_node && (node_relocation_distance (last_node) ==
22017                                   (node_relocation_distance (plug_start) +
22018                                    node_gap_size (plug_start))))
22019                 {
22020                     //dprintf(3,( " Lb"));
22021                     dprintf (3, ("%Ix Lb", plug_start));
22022                     set_node_left (plug_start);
22023                 }
22024                 if (0 == sequence_number)
22025                 {
22026                     dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
22027                     tree = plug_start;
22028                 }
22029
22030                 verify_pins_with_post_plug_info("before insert node");
22031
22032                 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
22033                 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
22034                 last_node = plug_start;
22035
22036 #ifdef _DEBUG
22037                 // If we detect if the last plug is pinned plug right before us, we should save this gap info
22038                 if (!pinned_plug_p)
22039                 {
22040                     if (mark_stack_tos > 0)
22041                     {
22042                         mark& m = mark_stack_array[mark_stack_tos - 1];
22043                         if (m.has_post_plug_info())
22044                         {
22045                             uint8_t* post_plug_info_start = m.saved_post_plug_info_start;
22046                             size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
22047                             if ((uint8_t*)current_plug_gap_start == post_plug_info_start)
22048                             {
22049                                 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
22050                                     *current_plug_gap_start, *(current_plug_gap_start + 1),
22051                                     *(current_plug_gap_start + 2)));
22052                                 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
22053                             }
22054                         }
22055                     }
22056                 }
22057 #endif //_DEBUG
22058
22059                 verify_pins_with_post_plug_info("after insert node");
22060             }
22061         }
22062         
22063         if (num_pinned_plugs_in_plug > 1)
22064         {
22065             dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
22066         }
22067
22068         {
22069 #ifdef MARK_LIST
22070             if (use_mark_list)
22071             {
22072                while ((mark_list_next < mark_list_index) &&
22073                       (*mark_list_next <= x))
22074                {
22075                    mark_list_next++;
22076                }
22077                if ((mark_list_next < mark_list_index)
22078 #ifdef MULTIPLE_HEAPS
22079                    && (*mark_list_next < end) //for multiple segments
22080 #endif //MULTIPLE_HEAPS
22081                    )
22082                    x = *mark_list_next;
22083                else
22084                    x = end;
22085             }
22086             else
22087 #endif //MARK_LIST
22088             {
22089                 uint8_t* xl = x;
22090 #ifdef BACKGROUND_GC
22091                 if (current_c_gc_state == c_gc_state_marking)
22092                 {
22093                     assert (recursive_gc_sync::background_running_p());
22094                     while ((xl < end) && !marked (xl))
22095                     {
22096                         dprintf (4, ("-%Ix-", (size_t)xl));
22097                         assert ((size (xl) > 0));
22098                         background_object_marked (xl, TRUE);
22099                         xl = xl + Align (size (xl));
22100                         Prefetch (xl);
22101                     }
22102                 }
22103                 else
22104 #endif //BACKGROUND_GC
22105                 {
22106                     while ((xl < end) && !marked (xl))
22107                     {
22108                         dprintf (4, ("-%Ix-", (size_t)xl));
22109                         assert ((size (xl) > 0));
22110                         xl = xl + Align (size (xl));
22111                         Prefetch (xl);
22112                     }
22113                 }
22114                 assert (xl <= end);
22115                 x = xl;
22116             }
22117         }
22118     }
22119
22120     while (!pinned_plug_que_empty_p())
22121     {
22122         if (settings.promotion)
22123         {
22124             uint8_t* pplug = pinned_plug (oldest_pin());
22125             if (in_range_for_segment (pplug, ephemeral_heap_segment))
22126             {
22127                 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
22128                 //allocate all of the generation gaps
22129                 while (active_new_gen_number > 0)
22130                 {
22131                     active_new_gen_number--;
22132
22133                     if (active_new_gen_number == (max_generation - 1))
22134                     {
22135                         maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
22136                         if (!demote_gen1_p)
22137                             advance_pins_for_demotion (consing_gen);
22138                     }
22139
22140                     generation* gen = generation_of (active_new_gen_number);
22141                     plan_generation_start (gen, consing_gen, 0);
22142
22143                     if (demotion_low == MAX_PTR)
22144                     {
22145                         demotion_low = pplug;
22146                         dprintf (3, ("end plan: dlow->%Ix", demotion_low));
22147                     }
22148
22149                     dprintf (2, ("(%d)gen%d plan start: %Ix", 
22150                                   heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
22151                     assert (generation_plan_allocation_start (gen));
22152                 }
22153             }
22154         }
22155
22156         if (pinned_plug_que_empty_p())
22157             break;
22158
22159         size_t  entry = deque_pinned_plug();
22160         mark*  m = pinned_plug_of (entry);
22161         uint8_t*  plug = pinned_plug (m);
22162         size_t  len = pinned_len (m);
22163
22164         // detect pinned block in different segment (later) than
22165         // allocation segment
22166         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
22167
22168         while ((plug < generation_allocation_pointer (consing_gen)) ||
22169                (plug >= heap_segment_allocated (nseg)))
22170         {
22171             assert ((plug < heap_segment_mem (nseg)) ||
22172                     (plug > heap_segment_reserved (nseg)));
22173             //adjust the end of the segment to be the end of the plug
22174             assert (generation_allocation_pointer (consing_gen)>=
22175                     heap_segment_mem (nseg));
22176             assert (generation_allocation_pointer (consing_gen)<=
22177                     heap_segment_committed (nseg));
22178
22179             heap_segment_plan_allocated (nseg) =
22180                 generation_allocation_pointer (consing_gen);
22181             //switch allocation segment
22182             nseg = heap_segment_next_rw (nseg);
22183             generation_allocation_segment (consing_gen) = nseg;
22184             //reset the allocation pointer and limits
22185             generation_allocation_pointer (consing_gen) =
22186                 heap_segment_mem (nseg);
22187         }
22188
22189         set_new_pin_info (m, generation_allocation_pointer (consing_gen));
22190         dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
22191             (size_t)(brick_table[brick_of (plug)])));
22192
22193         generation_allocation_pointer (consing_gen) = plug + len;
22194         generation_allocation_limit (consing_gen) =
22195             generation_allocation_pointer (consing_gen);
22196         //Add the size of the pinned plug to the right pinned allocations
22197         //find out which gen this pinned plug came from 
22198         int frgn = object_gennum (plug);
22199         if ((frgn != (int)max_generation) && settings.promotion)
22200         {
22201             generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
22202         }
22203
22204     }
22205
22206     plan_generation_starts (consing_gen);
22207     print_free_and_plug ("AP");
22208
22209     {
22210 #ifdef SIMPLE_DPRINTF
22211         for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22212         {
22213             generation* temp_gen = generation_of (gen_idx);
22214             dynamic_data* temp_dd = dynamic_data_of (gen_idx);
22215
22216             int added_pinning_ratio = 0;
22217             int artificial_pinned_ratio = 0;
22218
22219             if (dd_pinned_survived_size (temp_dd) != 0)
22220             {
22221                 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22222                 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22223             }
22224
22225             size_t padding_size = 
22226 #ifdef SHORT_PLUGS
22227                 dd_padding_size (temp_dd);
22228 #else
22229                 0;
22230 #endif //SHORT_PLUGS
22231             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",
22232                 gen_idx, 
22233                 generation_allocation_start (temp_gen),
22234                 generation_plan_allocation_start (temp_gen),
22235                 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
22236                 generation_allocation_size (temp_gen),
22237                 generation_pinned_allocation_compact_size (temp_gen),
22238                 generation_pinned_allocation_sweep_size (temp_gen),
22239                 dd_survived_size (temp_dd),
22240                 dd_pinned_survived_size (temp_dd),
22241                 added_pinning_ratio,
22242                 artificial_pinned_ratio,
22243                 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
22244                 padding_size));
22245         }
22246 #endif //SIMPLE_DPRINTF
22247     }
22248
22249     if (settings.condemned_generation == (max_generation - 1 ))
22250     {
22251         size_t plan_gen2_size = generation_plan_size (max_generation);
22252         size_t growth = plan_gen2_size - old_gen2_size;
22253
22254         if (growth > 0)
22255         {
22256             dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
22257                 growth, generation_end_seg_allocated (generation_of (max_generation)), 
22258                 generation_condemned_allocated (generation_of (max_generation - 1))));
22259         }
22260         else
22261         {
22262             dprintf (1, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
22263                 (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)), 
22264                 generation_condemned_allocated (generation_of (max_generation - 1))));
22265         }
22266
22267         generation* older_gen = generation_of (settings.condemned_generation + 1);
22268         size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
22269         size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
22270         size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated;
22271         size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated;
22272
22273         dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
22274                     r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
22275                     r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen), 
22276                     r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
22277
22278         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", 
22279             free_list_allocated, rejected_free_space, end_seg_allocated,
22280             condemned_allocated, generation_condemned_allocated (generation_of (settings.condemned_generation))));
22281
22282         maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info);
22283         maxgen_size_info->free_list_allocated = free_list_allocated;
22284         maxgen_size_info->free_list_rejected = rejected_free_space;
22285         maxgen_size_info->end_seg_allocated = end_seg_allocated;
22286         maxgen_size_info->condemned_allocated = condemned_allocated;
22287         maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance;
22288         maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance;
22289
22290 #ifdef FREE_USAGE_STATS
22291         int free_list_efficiency = 0;
22292         if ((free_list_allocated + rejected_free_space) != 0)
22293             free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100);
22294
22295         int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100);
22296
22297         dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%",
22298                     older_gen->gen_num,
22299                     free_list_efficiency, running_free_list_efficiency));
22300
22301         dprintf (1, ("gen2 free list change"));
22302         for (int j = 0; j < NUM_GEN_POWER2; j++)
22303         {
22304             dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id", 
22305                 heap_number, 
22306                 settings.gc_index,
22307                 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j], 
22308                 (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
22309                 (generation_of(max_generation - 1))->gen_plugs[j]));
22310         }
22311 #endif //FREE_USAGE_STATS
22312     }
22313
22314     size_t fragmentation =
22315         generation_fragmentation (generation_of (condemned_gen_number),
22316                                   consing_gen,
22317                                   heap_segment_allocated (ephemeral_heap_segment));
22318
22319     dprintf (2,("Fragmentation: %Id", fragmentation));
22320     dprintf (2,("---- End of Plan phase ----"));
22321
22322 #ifdef TIME_GC
22323     finish = GetCycleCount32();
22324     plan_time = finish - start;
22325 #endif //TIME_GC
22326
22327     // We may update write barrier code.  We assume here EE has been suspended if we are on a GC thread.
22328     assert(IsGCInProgress());
22329
22330     BOOL should_expand = FALSE;
22331     BOOL should_compact= FALSE;
22332     ephemeral_promotion = FALSE;
22333
22334 #ifdef BIT64
22335     if ((!settings.concurrent) &&
22336         ((condemned_gen_number < max_generation) && 
22337          ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
22338     {
22339         dprintf (2, ("gen0 reduction count is %d, condemning %d, mem load %d",
22340                      settings.gen0_reduction_count,
22341                      condemned_gen_number,
22342                      settings.entry_memory_load));
22343         should_compact = TRUE;
22344
22345         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, 
22346             ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load));
22347
22348         if ((condemned_gen_number >= (max_generation - 1)) && 
22349             dt_low_ephemeral_space_p (tuning_deciding_expansion))
22350         {
22351             dprintf (2, ("Not enough space for all ephemeral generations with compaction"));
22352             should_expand = TRUE;
22353         }
22354     }
22355     else
22356     {
22357 #endif // BIT64
22358         should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
22359 #ifdef BIT64
22360     }
22361 #endif // BIT64
22362
22363 #ifdef FEATURE_LOH_COMPACTION
22364     loh_compacted_p = FALSE;
22365 #endif //FEATURE_LOH_COMPACTION
22366
22367     if (condemned_gen_number == max_generation)
22368     {
22369 #ifdef FEATURE_LOH_COMPACTION
22370         if (settings.loh_compaction)
22371         {
22372             if (plan_loh())
22373             {
22374                 should_compact = TRUE;
22375                 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced);
22376                 loh_compacted_p = TRUE;
22377             }
22378         }
22379         else
22380         {
22381             if ((heap_number == 0) && (loh_pinned_queue))
22382             {
22383                 loh_pinned_queue_decay--;
22384
22385                 if (!loh_pinned_queue_decay)
22386                 {
22387                     delete loh_pinned_queue;
22388                     loh_pinned_queue = 0;
22389                 }
22390             }
22391         }
22392
22393         if (!loh_compacted_p)
22394 #endif //FEATURE_LOH_COMPACTION
22395         {
22396             GCToEEInterface::DiagWalkLOHSurvivors(__this);
22397             sweep_large_objects();
22398         }
22399     }
22400     else
22401     {
22402         settings.loh_compaction = FALSE;
22403     }
22404
22405 #ifdef MULTIPLE_HEAPS
22406
22407     new_heap_segment = NULL;
22408
22409     if (should_compact && should_expand)
22410         gc_policy = policy_expand;
22411     else if (should_compact)
22412         gc_policy = policy_compact;
22413     else
22414         gc_policy = policy_sweep;
22415
22416     //vote for result of should_compact
22417     dprintf (3, ("Joining for compaction decision"));
22418     gc_t_join.join(this, gc_join_decide_on_compaction);
22419     if (gc_t_join.joined())
22420     {
22421         //safe place to delete large heap segments
22422         if (condemned_gen_number == max_generation)
22423         {
22424             for (int i = 0; i < n_heaps; i++)
22425             {
22426                 g_heaps [i]->rearrange_large_heap_segments ();
22427             }
22428         }
22429
22430         settings.demotion = FALSE;
22431         int pol_max = policy_sweep;
22432 #ifdef GC_CONFIG_DRIVEN
22433         BOOL is_compaction_mandatory = FALSE;
22434 #endif //GC_CONFIG_DRIVEN
22435
22436         int i;
22437         for (i = 0; i < n_heaps; i++)
22438         {
22439             if (pol_max < g_heaps[i]->gc_policy)
22440                 pol_max = policy_compact;
22441             // set the demotion flag is any of the heap has demotion
22442             if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
22443             {
22444                 (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit);
22445                 settings.demotion = TRUE;
22446             }
22447
22448 #ifdef GC_CONFIG_DRIVEN
22449             if (!is_compaction_mandatory)
22450             {
22451                 int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact);
22452                 if (compact_reason >= 0)
22453                 {
22454                     if (gc_heap_compact_reason_mandatory_p[compact_reason])
22455                         is_compaction_mandatory = TRUE;
22456                 }
22457             }
22458 #endif //GC_CONFIG_DRIVEN
22459         }
22460
22461 #ifdef GC_CONFIG_DRIVEN
22462         if (!is_compaction_mandatory)
22463         {
22464             // If compaction is not mandatory we can feel free to change it to a sweeping GC.
22465             // Note that we may want to change this to only checking every so often instead of every single GC.
22466             if (should_do_sweeping_gc (pol_max >= policy_compact))
22467             {
22468                 pol_max = policy_sweep;
22469             }
22470             else
22471             {
22472                 if (pol_max == policy_sweep)
22473                     pol_max = policy_compact;
22474             }
22475         }
22476 #endif //GC_CONFIG_DRIVEN
22477
22478         for (i = 0; i < n_heaps; i++)
22479         {
22480             if (pol_max > g_heaps[i]->gc_policy)
22481                 g_heaps[i]->gc_policy = pol_max;
22482             //get the segment while we are serialized
22483             if (g_heaps[i]->gc_policy == policy_expand)
22484             {
22485                 g_heaps[i]->new_heap_segment =
22486                      g_heaps[i]->soh_get_segment_to_expand();
22487                 if (!g_heaps[i]->new_heap_segment)
22488                 {
22489                     set_expand_in_full_gc (condemned_gen_number);
22490                     //we are out of memory, cancel the expansion
22491                     g_heaps[i]->gc_policy = policy_compact;
22492                 }
22493             }
22494         }
22495
22496         BOOL is_full_compacting_gc = FALSE;
22497
22498         if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
22499         {
22500             full_gc_counts[gc_type_compacting]++;
22501             is_full_compacting_gc = TRUE;
22502         }
22503
22504         for (i = 0; i < n_heaps; i++)
22505         {
22506             //copy the card and brick tables
22507             if (g_gc_card_table!= g_heaps[i]->card_table)
22508             {
22509                 g_heaps[i]->copy_brick_card_table();
22510             }
22511
22512             if (is_full_compacting_gc)
22513             {
22514                 g_heaps[i]->loh_alloc_since_cg = 0;
22515             }
22516         }
22517
22518         //start all threads on the roots.
22519         dprintf(3, ("Starting all gc threads after compaction decision"));
22520         gc_t_join.restart();
22521     }
22522
22523     //reset the local variable accordingly
22524     should_compact = (gc_policy >= policy_compact);
22525     should_expand  = (gc_policy >= policy_expand);
22526
22527 #else //MULTIPLE_HEAPS
22528
22529     //safe place to delete large heap segments
22530     if (condemned_gen_number == max_generation)
22531     {
22532         rearrange_large_heap_segments ();
22533     }
22534
22535     settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
22536     if (settings.demotion)
22537         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
22538
22539 #ifdef GC_CONFIG_DRIVEN
22540     BOOL is_compaction_mandatory = FALSE;
22541     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
22542     if (compact_reason >= 0)
22543         is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason];
22544
22545     if (!is_compaction_mandatory)
22546     {
22547         if (should_do_sweeping_gc (should_compact))
22548             should_compact = FALSE;
22549         else
22550             should_compact = TRUE;
22551     }
22552 #endif //GC_CONFIG_DRIVEN
22553
22554     if (should_compact && (condemned_gen_number == max_generation))
22555     {
22556         full_gc_counts[gc_type_compacting]++;
22557         loh_alloc_since_cg = 0;
22558     }
22559 #endif //MULTIPLE_HEAPS
22560
22561     if (should_compact)
22562     {
22563         dprintf (2,( "**** Doing Compacting GC ****"));
22564
22565         if (should_expand)
22566         {
22567 #ifndef MULTIPLE_HEAPS
22568             heap_segment* new_heap_segment = soh_get_segment_to_expand();
22569 #endif //!MULTIPLE_HEAPS
22570             if (new_heap_segment)
22571             {
22572                 consing_gen = expand_heap(condemned_gen_number,
22573                                           consing_gen,
22574                                           new_heap_segment);
22575             }
22576
22577             // If we couldn't get a new segment, or we were able to 
22578             // reserve one but no space to commit, we couldn't
22579             // expand heap.
22580             if (ephemeral_heap_segment != new_heap_segment)
22581             {
22582                 set_expand_in_full_gc (condemned_gen_number);
22583                 should_expand = FALSE;
22584             }
22585         }
22586         generation_allocation_limit (condemned_gen1) =
22587             generation_allocation_pointer (condemned_gen1);
22588         if ((condemned_gen_number < max_generation))
22589         {
22590             generation_allocator (older_gen)->commit_alloc_list_changes();
22591
22592             // Fix the allocation area of the older generation
22593             fix_older_allocation_area (older_gen);
22594         }
22595         assert (generation_allocation_segment (consing_gen) ==
22596                 ephemeral_heap_segment);
22597
22598         GCToEEInterface::DiagWalkSurvivors(__this);
22599
22600         relocate_phase (condemned_gen_number, first_condemned_address);
22601         compact_phase (condemned_gen_number, first_condemned_address,
22602                        (!settings.demotion && settings.promotion));
22603         fix_generation_bounds (condemned_gen_number, consing_gen);
22604         assert (generation_allocation_limit (youngest_generation) ==
22605                 generation_allocation_pointer (youngest_generation));
22606         if (condemned_gen_number >= (max_generation -1))
22607         {
22608 #ifdef MULTIPLE_HEAPS
22609             // this needs be serialized just because we have one
22610             // segment_standby_list/seg_table for all heaps. We should make it at least
22611             // so that when hoarding is not on we don't need this join because
22612             // decommitting memory can take a long time.
22613             //must serialize on deleting segments
22614             gc_t_join.join(this, gc_join_rearrange_segs_compaction);
22615             if (gc_t_join.joined())
22616             {
22617                 for (int i = 0; i < n_heaps; i++)
22618                 {
22619                     g_heaps[i]->rearrange_heap_segments(TRUE);
22620                 }
22621                 gc_t_join.restart();
22622             }
22623 #else
22624             rearrange_heap_segments(TRUE);
22625 #endif //MULTIPLE_HEAPS
22626
22627             if (should_expand)
22628             {
22629                 //fix the start_segment for the ephemeral generations
22630                 for (int i = 0; i < max_generation; i++)
22631                 {
22632                     generation* gen = generation_of (i);
22633                     generation_start_segment (gen) = ephemeral_heap_segment;
22634                     generation_allocation_segment (gen) = ephemeral_heap_segment;
22635                 }
22636             }
22637         }
22638
22639         {
22640 #ifdef FEATURE_PREMORTEM_FINALIZATION
22641             finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
22642                                                        (!settings.demotion && settings.promotion));
22643 #endif // FEATURE_PREMORTEM_FINALIZATION
22644
22645 #ifdef MULTIPLE_HEAPS
22646             dprintf(3, ("Joining after end of compaction"));
22647             gc_t_join.join(this, gc_join_adjust_handle_age_compact);
22648             if (gc_t_join.joined())
22649 #endif //MULTIPLE_HEAPS
22650             {
22651 #ifdef MULTIPLE_HEAPS
22652                 //join all threads to make sure they are synchronized
22653                 dprintf(3, ("Restarting after Promotion granted"));
22654                 gc_t_join.restart();
22655 #endif //MULTIPLE_HEAPS
22656             }
22657
22658             ScanContext sc;
22659             sc.thread_number = heap_number;
22660             sc.promotion = FALSE;
22661             sc.concurrent = FALSE;
22662             // new generations bounds are set can call this guy
22663             if (settings.promotion && !settings.demotion)
22664             {
22665                 dprintf (2, ("Promoting EE roots for gen %d",
22666                              condemned_gen_number));
22667                 GCScan::GcPromotionsGranted(condemned_gen_number,
22668                                                 max_generation, &sc);
22669             }
22670             else if (settings.demotion)
22671             {
22672                 dprintf (2, ("Demoting EE roots for gen %d",
22673                              condemned_gen_number));
22674                 GCScan::GcDemote (condemned_gen_number, max_generation, &sc);
22675             }
22676         }
22677
22678         {
22679             gen0_big_free_spaces = 0;
22680
22681             reset_pinned_queue_bos();
22682             unsigned int  gen_number = min (max_generation, 1 + condemned_gen_number);
22683             generation*  gen = generation_of (gen_number);
22684             uint8_t*  low = generation_allocation_start (generation_of (gen_number-1));
22685             uint8_t*  high =  heap_segment_allocated (ephemeral_heap_segment);
22686             
22687             while (!pinned_plug_que_empty_p())
22688             {
22689                 mark*  m = pinned_plug_of (deque_pinned_plug());
22690                 size_t len = pinned_len (m);
22691                 uint8_t*  arr = (pinned_plug (m) - len);
22692                 dprintf(3,("free [%Ix %Ix[ pin",
22693                             (size_t)arr, (size_t)arr + len));
22694                 if (len != 0)
22695                 {
22696                     assert (len >= Align (min_obj_size));
22697                     make_unused_array (arr, len);
22698                     // fix fully contained bricks + first one
22699                     // if the array goes beyond the first brick
22700                     size_t start_brick = brick_of (arr);
22701                     size_t end_brick = brick_of (arr + len);
22702                     if (end_brick != start_brick)
22703                     {
22704                         dprintf (3,
22705                                     ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
22706                                     start_brick, end_brick, (size_t)arr));
22707                         set_brick (start_brick,
22708                                     arr - brick_address (start_brick));
22709                         size_t brick = start_brick+1;
22710                         while (brick < end_brick)
22711                         {
22712                             set_brick (brick, start_brick - brick);
22713                             brick++;
22714                         }
22715                     }
22716
22717                     //when we take an old segment to make the new
22718                     //ephemeral segment. we can have a bunch of
22719                     //pinned plugs out of order going to the new ephemeral seg
22720                     //and then the next plugs go back to max_generation
22721                     if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
22722                         (heap_segment_reserved (ephemeral_heap_segment) > arr))
22723                     {
22724
22725                         while ((low <= arr) && (high > arr))
22726                         {
22727                             gen_number--;
22728                             assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
22729                                     settings.demotion || !settings.promotion);
22730                             dprintf (3, ("new free list generation %d", gen_number));
22731
22732                             gen = generation_of (gen_number);
22733                             if (gen_number >= 1)
22734                                 low = generation_allocation_start (generation_of (gen_number-1));
22735                             else
22736                                 low = high;
22737                         }
22738                     }
22739                     else
22740                     {
22741                         dprintf (3, ("new free list generation %d", max_generation));
22742                         gen_number = max_generation;
22743                         gen = generation_of (gen_number);
22744                     }
22745
22746                     dprintf(3,("threading it into generation %d", gen_number));
22747                     thread_gap (arr, len, gen);
22748                     add_gen_free (gen_number, len);
22749                 }
22750             }
22751         }
22752
22753 #ifdef _DEBUG
22754         for (int x = 0; x <= max_generation; x++)
22755         {
22756             assert (generation_allocation_start (generation_of (x)));
22757         }
22758 #endif //_DEBUG
22759
22760         if (!settings.demotion && settings.promotion)
22761         {
22762             //clear card for generation 1. generation 0 is empty
22763             clear_card_for_addresses (
22764                 generation_allocation_start (generation_of (1)),
22765                 generation_allocation_start (generation_of (0)));
22766         }
22767         if (settings.promotion && !settings.demotion)
22768         {
22769             uint8_t* start = generation_allocation_start (youngest_generation);
22770             MAYBE_UNUSED_VAR(start);
22771             assert (heap_segment_allocated (ephemeral_heap_segment) ==
22772                     (start + Align (size (start))));
22773         }
22774     }
22775     else
22776     {
22777         //force promotion for sweep
22778         settings.promotion = TRUE;
22779         settings.compaction = FALSE;
22780
22781         ScanContext sc;
22782         sc.thread_number = heap_number;
22783         sc.promotion = FALSE;
22784         sc.concurrent = FALSE;
22785
22786         dprintf (2, ("**** Doing Mark and Sweep GC****"));
22787
22788         if ((condemned_gen_number < max_generation))
22789         {
22790             generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
22791             generation_free_list_space (older_gen) = r_free_list_space;
22792             generation_free_obj_space (older_gen) = r_free_obj_space;
22793             generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
22794             generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
22795             generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
22796             generation_allocation_limit (older_gen) = r_allocation_limit;
22797             generation_allocation_pointer (older_gen) = r_allocation_pointer;
22798             generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
22799             generation_allocation_segment (older_gen) = r_allocation_segment;
22800         }
22801
22802         if ((condemned_gen_number < max_generation))
22803         {
22804             // Fix the allocation area of the older generation
22805             fix_older_allocation_area (older_gen);
22806         }
22807
22808         GCToEEInterface::DiagWalkSurvivors(__this);
22809
22810         gen0_big_free_spaces = 0;
22811         make_free_lists (condemned_gen_number);
22812         recover_saved_pinned_info();
22813
22814 #ifdef FEATURE_PREMORTEM_FINALIZATION
22815         finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
22816 #endif // FEATURE_PREMORTEM_FINALIZATION
22817 // MTHTS: leave single thread for HT processing on plan_phase
22818 #ifdef MULTIPLE_HEAPS
22819         dprintf(3, ("Joining after end of sweep"));
22820         gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
22821         if (gc_t_join.joined())
22822 #endif //MULTIPLE_HEAPS
22823         {
22824             GCScan::GcPromotionsGranted(condemned_gen_number,
22825                                             max_generation, &sc);
22826             if (condemned_gen_number >= (max_generation -1))
22827             {
22828 #ifdef MULTIPLE_HEAPS
22829                 for (int i = 0; i < n_heaps; i++)
22830                 {
22831                     g_heaps[i]->rearrange_heap_segments(FALSE);
22832                 }
22833 #else
22834                 rearrange_heap_segments(FALSE);
22835 #endif //MULTIPLE_HEAPS
22836             }
22837
22838 #ifdef MULTIPLE_HEAPS
22839             //join all threads to make sure they are synchronized
22840             dprintf(3, ("Restarting after Promotion granted"));
22841             gc_t_join.restart();
22842 #endif //MULTIPLE_HEAPS
22843         }
22844
22845 #ifdef _DEBUG
22846         for (int x = 0; x <= max_generation; x++)
22847         {
22848             assert (generation_allocation_start (generation_of (x)));
22849         }
22850 #endif //_DEBUG
22851
22852         //clear card for generation 1. generation 0 is empty
22853         clear_card_for_addresses (
22854             generation_allocation_start (generation_of (1)),
22855             generation_allocation_start (generation_of (0)));
22856         assert ((heap_segment_allocated (ephemeral_heap_segment) ==
22857                  (generation_allocation_start (youngest_generation) +
22858                   Align (min_obj_size))));
22859     }
22860
22861     //verify_partial();
22862 }
22863 #ifdef _PREFAST_
22864 #pragma warning(pop)
22865 #endif //_PREFAST_
22866
22867
22868 /*****************************
22869 Called after compact phase to fix all generation gaps
22870 ********************************/
22871 void gc_heap::fix_generation_bounds (int condemned_gen_number,
22872                                      generation* consing_gen)
22873 {
22874     UNREFERENCED_PARAMETER(consing_gen);
22875
22876     assert (generation_allocation_segment (consing_gen) ==
22877             ephemeral_heap_segment);
22878
22879     //assign the planned allocation start to the generation
22880     int gen_number = condemned_gen_number;
22881     int bottom_gen = 0;
22882
22883     while (gen_number >= bottom_gen)
22884     {
22885         generation*  gen = generation_of (gen_number);
22886         dprintf(3,("Fixing generation pointers for %Ix", gen_number));
22887         if ((gen_number < max_generation) && ephemeral_promotion)
22888         {
22889             make_unused_array (saved_ephemeral_plan_start[gen_number], 
22890                                saved_ephemeral_plan_start_size[gen_number]);
22891         }
22892         reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
22893         make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
22894         dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
22895         gen_number--;
22896     }
22897 #ifdef MULTIPLE_HEAPS
22898     if (ephemeral_promotion)
22899     {
22900         //we are creating a generation fault. set the cards.
22901         // and we are only doing this for multiple heaps because in the single heap scenario the 
22902         // new ephemeral generations will be empty and there'll be no need to set cards for the
22903         // old ephemeral generations that got promoted into max_generation.
22904         ptrdiff_t delta = 0;
22905 #ifdef SEG_MAPPING_TABLE
22906         heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
22907 #else //SEG_MAPPING_TABLE
22908         heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
22909 #endif //SEG_MAPPING_TABLE
22910
22911         assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
22912         size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
22913         size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
22914         while (card != end_card)
22915         {
22916             set_card (card);
22917             card++;
22918         }
22919     }
22920 #endif //MULTIPLE_HEAPS
22921     {
22922         alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
22923         //reset the allocated size
22924         uint8_t* start = generation_allocation_start (youngest_generation);
22925         MAYBE_UNUSED_VAR(start);
22926         if (settings.promotion && !settings.demotion)
22927         {
22928             assert ((start + Align (size (start))) ==
22929                     heap_segment_plan_allocated(ephemeral_heap_segment));
22930         }
22931
22932         heap_segment_allocated(ephemeral_heap_segment)=
22933             heap_segment_plan_allocated(ephemeral_heap_segment);
22934     }
22935 }
22936
22937 uint8_t* gc_heap::generation_limit (int gen_number)
22938 {
22939     if (settings.promotion)
22940     {
22941         if (gen_number <= 1)
22942             return heap_segment_reserved (ephemeral_heap_segment);
22943         else
22944             return generation_allocation_start (generation_of ((gen_number - 2)));
22945     }
22946     else
22947     {
22948         if (gen_number <= 0)
22949             return heap_segment_reserved (ephemeral_heap_segment);
22950         else
22951             return generation_allocation_start (generation_of ((gen_number - 1)));
22952     }
22953 }
22954
22955 BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
22956 {
22957     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
22958     size_t size = Align (min_obj_size)*(condemned_gen_number+1);
22959     assert ((start + size) <=
22960             heap_segment_reserved (ephemeral_heap_segment));
22961     if ((start + size) >
22962         heap_segment_committed (ephemeral_heap_segment))
22963     {
22964         if (!grow_heap_segment (ephemeral_heap_segment, start + size))
22965         {
22966             return FALSE;
22967         }
22968     }
22969     return TRUE;
22970 }
22971
22972 uint8_t* gc_heap::allocate_at_end (size_t size)
22973 {
22974     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
22975     size = Align (size);
22976     uint8_t* result = start;
22977     // only called to allocate a min obj so can't overflow here.
22978     assert ((start + size) <=
22979             heap_segment_reserved (ephemeral_heap_segment));
22980     //ensure_gap_allocation took care of it
22981     assert ((start + size) <=
22982             heap_segment_committed (ephemeral_heap_segment));
22983     heap_segment_allocated (ephemeral_heap_segment) += size;
22984     return result;
22985 }
22986
22987
22988 void gc_heap::make_free_lists (int condemned_gen_number)
22989 {
22990 #ifdef TIME_GC
22991     unsigned start;
22992     unsigned finish;
22993     start = GetCycleCount32();
22994 #endif //TIME_GC
22995
22996     //Promotion has to happen in sweep case.
22997     assert (settings.promotion);
22998
22999     generation* condemned_gen = generation_of (condemned_gen_number);
23000     uint8_t* start_address = generation_allocation_start (condemned_gen);
23001
23002     size_t  current_brick = brick_of (start_address);
23003     heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23004
23005     PREFIX_ASSUME(current_heap_segment != NULL);
23006
23007     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
23008     size_t  end_brick = brick_of (end_address-1);
23009     make_free_args args;
23010     args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
23011     args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
23012                               MAX_PTR :
23013                               (generation_limit (args.free_list_gen_number)));
23014     args.free_list_gen = generation_of (args.free_list_gen_number);
23015     args.highest_plug = 0;
23016
23017     if ((start_address < end_address) ||
23018         (condemned_gen_number == max_generation))
23019     {
23020         while (1)
23021         {
23022             if ((current_brick > end_brick))
23023             {
23024                 if (args.current_gen_limit == MAX_PTR)
23025                 {
23026                     //We had an empty segment
23027                     //need to allocate the generation start
23028
23029                     generation* gen = generation_of (max_generation);
23030
23031                     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
23032
23033                     PREFIX_ASSUME(start_seg != NULL);
23034
23035                     uint8_t* gap = heap_segment_mem (start_seg);
23036
23037                     generation_allocation_start (gen) = gap;
23038                     heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
23039                     make_unused_array (gap, Align (min_obj_size));
23040                     reset_allocation_pointers (gen, gap);
23041                     dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
23042                                  max_generation, (size_t)gap));
23043                     args.current_gen_limit = generation_limit (args.free_list_gen_number);
23044                 }
23045                 if (heap_segment_next_rw (current_heap_segment))
23046                 {
23047                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
23048                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
23049                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23050
23051                     continue;
23052                 }
23053                 else
23054                 {
23055                     break;
23056                 }
23057             }
23058             {
23059                 int brick_entry =  brick_table [ current_brick ];
23060                 if ((brick_entry >= 0))
23061                 {
23062                     make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
23063                     dprintf(3,("Fixing brick entry %Ix to %Ix",
23064                                current_brick, (size_t)args.highest_plug));
23065                     set_brick (current_brick,
23066                                (args.highest_plug - brick_address (current_brick)));
23067                 }
23068                 else
23069                 {
23070                     if ((brick_entry > -32768))
23071                     {
23072
23073 #ifdef _DEBUG
23074                         ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
23075                         if ((brick_entry != -32767) && (! ((offset == brick_entry))))
23076                         {
23077                             assert ((brick_entry == -1));
23078                         }
23079 #endif //_DEBUG
23080                         //init to -1 for faster find_first_object
23081                         set_brick (current_brick, -1);
23082                     }
23083                 }
23084             }
23085             current_brick++;
23086         }
23087     }
23088     {
23089         int bottom_gen = 0;
23090         args.free_list_gen_number--;
23091         while (args.free_list_gen_number >= bottom_gen)
23092         {
23093             uint8_t*  gap = 0;
23094             generation* gen2 = generation_of (args.free_list_gen_number);
23095             gap = allocate_at_end (Align(min_obj_size));
23096             generation_allocation_start (gen2) = gap;
23097             reset_allocation_pointers (gen2, gap);
23098             dprintf(3,("Fixing generation start of %d to: %Ix",
23099                        args.free_list_gen_number, (size_t)gap));
23100             PREFIX_ASSUME(gap != NULL);
23101             make_unused_array (gap, Align (min_obj_size));
23102
23103             args.free_list_gen_number--;
23104         }
23105
23106         //reset the allocated size
23107         uint8_t* start2 = generation_allocation_start (youngest_generation);
23108         alloc_allocated = start2 + Align (size (start2));
23109     }
23110
23111 #ifdef TIME_GC
23112     finish = GetCycleCount32();
23113     sweep_time = finish - start;
23114 #endif //TIME_GC
23115 }
23116
23117 void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args)
23118 {
23119     assert ((tree != NULL));
23120     {
23121         int  right_node = node_right_child (tree);
23122         int left_node = node_left_child (tree);
23123         args->highest_plug = 0;
23124         if (! (0 == tree))
23125         {
23126             if (! (0 == left_node))
23127             {
23128                 make_free_list_in_brick (tree + left_node, args);
23129
23130             }
23131             {
23132                 uint8_t*  plug = tree;
23133                 size_t  gap_size = node_gap_size (tree);
23134                 uint8_t*  gap = (plug - gap_size);
23135                 dprintf (3,("Making free list %Ix len %d in %d",
23136                 //dprintf (3,("F: %Ix len %Ix in %d",
23137                         (size_t)gap, gap_size, args->free_list_gen_number));
23138                 args->highest_plug = tree;
23139 #ifdef SHORT_PLUGS
23140                 if (is_plug_padded (plug))
23141                 {
23142                     dprintf (3, ("%Ix padded", plug));
23143                     clear_plug_padded (plug);
23144                 }
23145 #endif //SHORT_PLUGS
23146             gen_crossing:
23147                 {
23148                     if ((args->current_gen_limit == MAX_PTR) ||
23149                         ((plug >= args->current_gen_limit) &&
23150                          ephemeral_pointer_p (plug)))
23151                     {
23152                         dprintf(3,(" Crossing Generation boundary at %Ix",
23153                                (size_t)args->current_gen_limit));
23154                         if (!(args->current_gen_limit == MAX_PTR))
23155                         {
23156                             args->free_list_gen_number--;
23157                             args->free_list_gen = generation_of (args->free_list_gen_number);
23158                         }
23159                         dprintf(3,( " Fixing generation start of %d to: %Ix",
23160                                 args->free_list_gen_number, (size_t)gap));
23161
23162                         reset_allocation_pointers (args->free_list_gen, gap);
23163                         args->current_gen_limit = generation_limit (args->free_list_gen_number);
23164
23165                         if ((gap_size >= (2*Align (min_obj_size))))
23166                         {
23167                             dprintf(3,(" Splitting the gap in two %Id left",
23168                                    gap_size));
23169                             make_unused_array (gap, Align(min_obj_size));
23170                             gap_size = (gap_size - Align(min_obj_size));
23171                             gap = (gap + Align(min_obj_size));
23172                         }
23173                         else
23174                         {
23175                             make_unused_array (gap, gap_size);
23176                             gap_size = 0;
23177                         }
23178                         goto gen_crossing;
23179                     }
23180                 }
23181
23182                 thread_gap (gap, gap_size, args->free_list_gen);
23183                 add_gen_free (args->free_list_gen->gen_num, gap_size);
23184             }
23185             if (! (0 == right_node))
23186             {
23187                 make_free_list_in_brick (tree + right_node, args);
23188             }
23189         }
23190     }
23191 }
23192
23193 void gc_heap::thread_gap (uint8_t* gap_start, size_t size, generation*  gen)
23194 {
23195     assert (generation_allocation_start (gen));
23196     if ((size > 0))
23197     {
23198         if ((gen->gen_num == 0) && (size > CLR_SIZE))
23199         {
23200             gen0_big_free_spaces += size;
23201         }
23202
23203         assert ((heap_segment_rw (generation_start_segment (gen))!=
23204                  ephemeral_heap_segment) ||
23205                 (gap_start > generation_allocation_start (gen)));
23206         // The beginning of a segment gap is not aligned
23207         assert (size >= Align (min_obj_size));
23208         make_unused_array (gap_start, size, 
23209                           (!settings.concurrent && (gen != youngest_generation)),
23210                           (gen->gen_num == max_generation));
23211         dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
23212
23213         if ((size >= min_free_list))
23214         {
23215             generation_free_list_space (gen) += size;
23216             generation_allocator (gen)->thread_item (gap_start, size);
23217         }
23218         else
23219         {
23220             generation_free_obj_space (gen) += size;
23221         }
23222     }
23223 }
23224
23225 void gc_heap::loh_thread_gap_front (uint8_t* gap_start, size_t size, generation*  gen)
23226 {
23227     assert (generation_allocation_start (gen));
23228     if (size >= min_free_list)
23229     {
23230         generation_free_list_space (gen) += size;
23231         generation_allocator (gen)->thread_item_front (gap_start, size);
23232     }
23233 }
23234
23235 void gc_heap::make_unused_array (uint8_t* x, size_t size, BOOL clearp, BOOL resetp)
23236 {
23237     dprintf (3, ("Making unused array [%Ix, %Ix[",
23238         (size_t)x, (size_t)(x+size)));
23239     assert (size >= Align (min_obj_size));
23240
23241 //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
23242 //    check_batch_mark_array_bits (x, x+size);
23243 //#endif //VERIFY_HEAP && BACKGROUND_GC
23244
23245     if (resetp)
23246         reset_memory (x, size);
23247
23248     ((CObjectHeader*)x)->SetFree(size);
23249
23250 #ifdef BIT64
23251
23252 #if BIGENDIAN
23253 #error "This won't work on big endian platforms"
23254 #endif
23255
23256     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23257
23258     if (size_as_object < size)
23259     {
23260         //
23261         // If the size is more than 4GB, we need to create multiple objects because of
23262         // the Array::m_NumComponents is uint32_t and the high 32 bits of unused array
23263         // size is ignored in regular object size computation.
23264         //
23265         uint8_t * tmp = x + size_as_object;
23266         size_t remaining_size = size - size_as_object;
23267
23268         while (remaining_size > UINT32_MAX)
23269         {
23270             // Make sure that there will be at least Align(min_obj_size) left
23271             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23272                 - Align (min_obj_size, get_alignment_constant (FALSE));
23273
23274             ((CObjectHeader*)tmp)->SetFree(current_size);
23275
23276             remaining_size -= current_size;
23277             tmp += current_size;
23278         }
23279
23280         ((CObjectHeader*)tmp)->SetFree(remaining_size);
23281     }
23282 #endif
23283
23284     if (clearp)
23285         clear_card_for_addresses (x, x + Align(size));
23286 }
23287
23288 // Clear memory set by make_unused_array.
23289 void gc_heap::clear_unused_array (uint8_t* x, size_t size)
23290 {
23291     // Also clear the sync block
23292     *(((PTR_PTR)x)-1) = 0;
23293
23294     ((CObjectHeader*)x)->UnsetFree();
23295
23296 #ifdef BIT64
23297
23298 #if BIGENDIAN
23299 #error "This won't work on big endian platforms"
23300 #endif
23301
23302     // The memory could have been cleared in the meantime. We have to mirror the algorithm
23303     // from make_unused_array since we cannot depend on the object sizes in memory.
23304     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23305
23306     if (size_as_object < size)
23307     {
23308         uint8_t * tmp = x + size_as_object;
23309         size_t remaining_size = size - size_as_object;
23310
23311         while (remaining_size > UINT32_MAX)
23312         {
23313             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23314                 - Align (min_obj_size, get_alignment_constant (FALSE));
23315
23316             ((CObjectHeader*)tmp)->UnsetFree();
23317
23318             remaining_size -= current_size;
23319             tmp += current_size;
23320         }
23321
23322         ((CObjectHeader*)tmp)->UnsetFree();
23323     }
23324 #else
23325     UNREFERENCED_PARAMETER(size);
23326 #endif
23327 }
23328
23329 inline
23330 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address)
23331 {
23332     uint8_t* candidate = 0;
23333     int cn;
23334     while (1)
23335     {
23336         if (tree < old_address)
23337         {
23338             if ((cn = node_right_child (tree)) != 0)
23339             {
23340                 assert (candidate < tree);
23341                 candidate = tree;
23342                 tree = tree + cn;
23343                 Prefetch (tree - 8);
23344                 continue;
23345             }
23346             else
23347                 break;
23348         }
23349         else if (tree > old_address)
23350         {
23351             if ((cn = node_left_child (tree)) != 0)
23352             {
23353                 tree = tree + cn;
23354                 Prefetch (tree - 8);
23355                 continue;
23356             }
23357             else
23358                 break;
23359         } else
23360             break;
23361     }
23362     if (tree <= old_address)
23363         return tree;
23364     else if (candidate)
23365         return candidate;
23366     else
23367         return tree;
23368 }
23369
23370 #ifdef FEATURE_BASICFREEZE
23371 bool gc_heap::frozen_object_p (Object* obj)
23372 {
23373     heap_segment* pSegment = gc_heap::find_segment ((uint8_t*)obj, FALSE);
23374     _ASSERTE(pSegment);
23375
23376     return heap_segment_read_only_p(pSegment);
23377 }
23378 #endif // FEATURE_BASICFREEZE
23379
23380 #ifdef FEATURE_REDHAWK
23381 // TODO: this was added on RH, we have not done perf runs to see if this is the right
23382 // thing to do for other versions of the CLR.
23383 inline
23384 #endif // FEATURE_REDHAWK
23385 void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL)
23386 {
23387     uint8_t* old_address = *pold_address;
23388     if (!((old_address >= gc_low) && (old_address < gc_high)))
23389 #ifdef MULTIPLE_HEAPS
23390     {
23391         UNREFERENCED_PARAMETER(thread);
23392         if (old_address == 0)
23393             return;
23394         gc_heap* hp = heap_of (old_address);
23395         if ((hp == this) ||
23396             !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
23397             return;
23398     }
23399 #else //MULTIPLE_HEAPS
23400         return ;
23401 #endif //MULTIPLE_HEAPS
23402     // delta translates old_address into address_gc (old_address);
23403     size_t  brick = brick_of (old_address);
23404     int    brick_entry =  brick_table [ brick ];
23405     uint8_t*  new_address = old_address;
23406     if (! ((brick_entry == 0)))
23407     {
23408     retry:
23409         {
23410             while (brick_entry < 0)
23411             {
23412                 brick = (brick + brick_entry);
23413                 brick_entry =  brick_table [ brick ];
23414             }
23415             uint8_t* old_loc = old_address;
23416
23417             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
23418                                       old_loc);
23419             if ((node <= old_loc))
23420                 new_address = (old_address + node_relocation_distance (node));
23421             else
23422             {
23423                 if (node_left_p (node))
23424                 {
23425                     dprintf(3,(" L: %Ix", (size_t)node));
23426                     new_address = (old_address +
23427                                    (node_relocation_distance (node) +
23428                                     node_gap_size (node)));
23429                 }
23430                 else
23431                 {
23432                     brick = brick - 1;
23433                     brick_entry =  brick_table [ brick ];
23434                     goto retry;
23435                 }
23436             }
23437         }
23438
23439         *pold_address = new_address;
23440         return;
23441     }
23442
23443 #ifdef FEATURE_LOH_COMPACTION
23444     if (loh_compacted_p
23445 #ifdef FEATURE_BASICFREEZE
23446         && !frozen_object_p((Object*)old_address)
23447 #endif // FEATURE_BASICFREEZE
23448         )
23449     {
23450         *pold_address = old_address + loh_node_relocation_distance (old_address);
23451     }
23452     else
23453 #endif //FEATURE_LOH_COMPACTION
23454     {
23455         *pold_address = new_address;
23456     }
23457 }
23458
23459 inline void 
23460 gc_heap::check_class_object_demotion (uint8_t* obj)
23461 {
23462 #ifdef COLLECTIBLE_CLASS
23463     if (is_collectible(obj))
23464     {
23465         check_class_object_demotion_internal (obj);
23466     }
23467 #else
23468     UNREFERENCED_PARAMETER(obj);
23469 #endif //COLLECTIBLE_CLASS
23470 }
23471
23472 #ifdef COLLECTIBLE_CLASS
23473 NOINLINE void 
23474 gc_heap::check_class_object_demotion_internal (uint8_t* obj)
23475 {
23476     if (settings.demotion)
23477     {
23478 #ifdef MULTIPLE_HEAPS
23479         // We set the card without checking the demotion range 'cause at this point
23480         // the handle that points to the loader allocator object may or may not have
23481         // been relocated by other GC threads. 
23482         set_card (card_of (obj));
23483 #else
23484         THREAD_FROM_HEAP;
23485         uint8_t* class_obj = get_class_object (obj);
23486         dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
23487         uint8_t* temp_class_obj = class_obj;
23488         uint8_t** temp = &temp_class_obj;
23489         relocate_address (temp THREAD_NUMBER_ARG);
23490
23491         check_demotion_helper (temp, obj);
23492 #endif //MULTIPLE_HEAPS
23493     }
23494 }
23495
23496 #endif //COLLECTIBLE_CLASS
23497
23498 inline void
23499 gc_heap::check_demotion_helper (uint8_t** pval, uint8_t* parent_obj)
23500 {
23501     // detect if we are demoting an object
23502     if ((*pval < demotion_high) &&
23503         (*pval >= demotion_low))
23504     {
23505         dprintf(3, ("setting card %Ix:%Ix",
23506                     card_of((uint8_t*)pval),
23507                     (size_t)pval));
23508
23509         set_card (card_of (parent_obj));
23510     }
23511 #ifdef MULTIPLE_HEAPS
23512     else if (settings.demotion)
23513     {
23514         dprintf (4, ("Demotion active, computing heap_of object"));
23515         gc_heap* hp = heap_of (*pval);
23516         if ((*pval < hp->demotion_high) &&
23517             (*pval >= hp->demotion_low))
23518         {
23519             dprintf(3, ("setting card %Ix:%Ix",
23520                         card_of((uint8_t*)pval),
23521                         (size_t)pval));
23522
23523             set_card (card_of (parent_obj));
23524         }
23525     }
23526 #endif //MULTIPLE_HEAPS
23527 }
23528
23529 inline void
23530 gc_heap::reloc_survivor_helper (uint8_t** pval)
23531 {
23532     THREAD_FROM_HEAP;
23533     relocate_address (pval THREAD_NUMBER_ARG);
23534
23535     check_demotion_helper (pval, (uint8_t*)pval);
23536 }
23537
23538 inline void
23539 gc_heap::relocate_obj_helper (uint8_t* x, size_t s)
23540 {
23541     THREAD_FROM_HEAP;
23542     if (contain_pointers (x))
23543     {
23544         dprintf (3, ("$%Ix$", (size_t)x));
23545
23546         go_through_object_nostart (method_table(x), x, s, pval,
23547                             {
23548                                 uint8_t* child = *pval;
23549                                 reloc_survivor_helper (pval);
23550                                 if (child)
23551                                 {
23552                                     dprintf (3, ("%Ix->%Ix->%Ix", (uint8_t*)pval, child, *pval));
23553                                 }
23554                             });
23555
23556     }
23557     check_class_object_demotion (x);
23558 }
23559
23560 inline 
23561 void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc)
23562 {
23563     THREAD_FROM_HEAP;
23564
23565     uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0);
23566     relocate_address (address_to_reloc THREAD_NUMBER_ARG);
23567     if (address_to_reloc)
23568     {
23569         dprintf (3, ("SR %Ix: %Ix->%Ix", (uint8_t*)address_to_reloc, old_val, *address_to_reloc));
23570     }
23571
23572     //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval);
23573     uint8_t* relocated_addr = *address_to_reloc;
23574     if ((relocated_addr < demotion_high) &&
23575         (relocated_addr >= demotion_low))
23576     {
23577         dprintf (3, ("set card for location %Ix(%Ix)",
23578                     (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23579
23580         set_card (card_of ((uint8_t*)address_to_set_card));
23581     }
23582 #ifdef MULTIPLE_HEAPS
23583     else if (settings.demotion)
23584     {
23585         gc_heap* hp = heap_of (relocated_addr);
23586         if ((relocated_addr < hp->demotion_high) &&
23587             (relocated_addr >= hp->demotion_low))
23588         {
23589             dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)",
23590                         relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23591
23592             set_card (card_of ((uint8_t*)address_to_set_card));
23593         }
23594     }
23595 #endif //MULTIPLE_HEAPS
23596 }
23597
23598 void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
23599 {
23600     THREAD_FROM_HEAP;
23601     uint8_t* plug = pinned_plug (pinned_plug_entry);
23602     uint8_t* pre_plug_start = plug - sizeof (plug_and_gap);
23603     // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
23604     // address. Consider this scenario: 
23605     // gen1 start | 3-ptr sized NP | PP
23606     // 0          | 0x18           | 0x30
23607     // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
23608     // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
23609     // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree). 
23610     pre_plug_start += sizeof (uint8_t*);
23611     uint8_t** old_address = &pre_plug_start;
23612
23613     uint8_t* old_val = (old_address ? *old_address : 0);
23614     relocate_address (old_address THREAD_NUMBER_ARG);
23615     if (old_address)
23616     {
23617         dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix", 
23618             (uint8_t*)old_address, old_val, *old_address, (pre_plug_start - sizeof (uint8_t*))));
23619     }
23620
23621     pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (uint8_t*));
23622 }
23623
23624 inline
23625 void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned)
23626 {
23627     THREAD_FROM_HEAP;
23628     uint8_t* plug = pinned_plug (pinned_plug_entry);
23629
23630     if (!is_pinned)
23631     {
23632         //// Temporary - we just wanna make sure we are doing things right when padding is needed.
23633         //if ((x + s) < plug)
23634         //{
23635         //    dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix", 
23636         //        x, (x + s), (plug- (x + s)), plug));
23637         //    GCToOSInterface::DebugBreak();
23638         //}
23639
23640         relocate_pre_plug_info (pinned_plug_entry);
23641     }
23642
23643     verify_pins_with_post_plug_info("after relocate_pre_plug_info");
23644
23645     uint8_t* saved_plug_info_start = 0;
23646     uint8_t** saved_info_to_relocate = 0;
23647
23648     if (is_pinned)
23649     {
23650         saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start());
23651         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23652     }
23653     else
23654     {
23655         saved_plug_info_start = (plug - sizeof (plug_and_gap));
23656         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
23657     }
23658     
23659     uint8_t** current_saved_info_to_relocate = 0;
23660     uint8_t* child = 0;
23661
23662     dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
23663
23664     if (contain_pointers (x))
23665     {
23666         dprintf (3,("$%Ix$", (size_t)x));
23667
23668         go_through_object_nostart (method_table(x), x, s, pval,
23669         {
23670             dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (uint8_t*)pval, *pval));
23671
23672             if ((uint8_t*)pval >= end)
23673             {
23674                 current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**);
23675                 child = *current_saved_info_to_relocate;
23676                 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
23677                 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
23678                     (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
23679             }
23680             else
23681             {
23682                 reloc_survivor_helper (pval);
23683             }
23684         });
23685     }
23686
23687     check_class_object_demotion (x);
23688 }
23689
23690 void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end)
23691 {
23692     uint8_t*  x = plug;
23693     while (x < plug_end)
23694     {
23695         size_t s = size (x);
23696         uint8_t* next_obj = x + Align (s);
23697         Prefetch (next_obj);
23698         relocate_obj_helper (x, s);
23699         assert (s > 0);
23700         x = next_obj;
23701     }
23702 }
23703
23704 // if we expanded, right now we are not handling it as We are not saving the new reloc info.
23705 void gc_heap::verify_pins_with_post_plug_info (const char* msg)
23706 {
23707 #if defined  (_DEBUG) && defined (VERIFY_HEAP)
23708     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
23709     {
23710         if (!verify_pinned_queue_p)
23711             return;
23712
23713         if (settings.heap_expansion)
23714             return;
23715
23716         for (size_t i = 0; i < mark_stack_tos; i++)
23717         {
23718             mark& m = mark_stack_array[i];
23719
23720             mark* pinned_plug_entry = pinned_plug_of(i);
23721
23722             if (pinned_plug_entry->has_post_plug_info() && 
23723                 pinned_plug_entry->post_short_p() && 
23724                 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
23725             {
23726                 uint8_t* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
23727                 // object after pin
23728                 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d", 
23729                     next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
23730                     (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
23731
23732                 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
23733
23734                 if (node_gap_size (next_obj) != *post_plug_debug)
23735                 {
23736                     dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix", 
23737                         next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
23738                     FATAL_GC_ERROR();
23739                 }
23740                 post_plug_debug++;
23741                 // can't do node_relocation_distance here as it clears the left bit.
23742                 //if (node_relocation_distance (next_obj) != *post_plug_debug)
23743                 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
23744                 {
23745                     dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix", 
23746                         next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
23747                     FATAL_GC_ERROR();
23748                 }
23749                 if (node_left_child (next_obj) > 0)
23750                 {
23751                     dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
23752                     FATAL_GC_ERROR();
23753                 }
23754             }
23755         }
23756
23757         dprintf (3, ("%s verified", msg));
23758     }
23759 #else // _DEBUG && VERIFY_HEAP
23760     UNREFERENCED_PARAMETER(msg);
23761 #endif // _DEBUG && VERIFY_HEAP
23762 }
23763
23764 #ifdef COLLECTIBLE_CLASS
23765 // We don't want to burn another ptr size space for pinned plugs to record this so just 
23766 // set the card unconditionally for collectible objects if we are demoting.
23767 inline void
23768 gc_heap::unconditional_set_card_collectible (uint8_t* obj)
23769 {
23770     if (settings.demotion)
23771     {
23772         set_card (card_of (obj));
23773     }
23774 }
23775 #endif //COLLECTIBLE_CLASS
23776
23777 void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry)
23778 {
23779     uint8_t*  x = plug;
23780     uint8_t* p_plug = pinned_plug (pinned_plug_entry);
23781     BOOL is_pinned = (plug == p_plug);
23782     BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
23783
23784     plug_end += sizeof (gap_reloc_pair);
23785
23786     //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
23787     dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
23788
23789     verify_pins_with_post_plug_info("begin reloc short surv");
23790
23791     while (x < plug_end)
23792     {
23793         if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
23794         {
23795             dprintf (3, ("last obj %Ix is short", x));
23796
23797             if (is_pinned)
23798             {
23799 #ifdef COLLECTIBLE_CLASS
23800                 if (pinned_plug_entry->post_short_collectible_p())
23801                     unconditional_set_card_collectible (x);
23802 #endif //COLLECTIBLE_CLASS
23803
23804                 // Relocate the saved references based on bits set.
23805                 uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start());
23806                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23807                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
23808                 {
23809                     if (pinned_plug_entry->post_short_bit_p (i))
23810                     {
23811                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
23812                     }
23813                 }
23814             }
23815             else
23816             {
23817 #ifdef COLLECTIBLE_CLASS
23818                 if (pinned_plug_entry->pre_short_collectible_p())
23819                     unconditional_set_card_collectible (x);
23820 #endif //COLLECTIBLE_CLASS
23821
23822                 relocate_pre_plug_info (pinned_plug_entry);
23823
23824                 // Relocate the saved references based on bits set.
23825                 uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap));
23826                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
23827                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
23828                 {
23829                     if (pinned_plug_entry->pre_short_bit_p (i))
23830                     {
23831                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
23832                     }
23833                 }
23834             }
23835
23836             break;
23837         }
23838
23839         size_t s = size (x);
23840         uint8_t* next_obj = x + Align (s);
23841         Prefetch (next_obj);
23842
23843         if (next_obj >= plug_end) 
23844         {
23845             dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix", 
23846                 next_obj, plug, plug_end));
23847
23848             verify_pins_with_post_plug_info("before reloc short obj");
23849
23850             relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
23851         }
23852         else
23853         {
23854             relocate_obj_helper (x, s);
23855         }
23856
23857         assert (s > 0);
23858         x = next_obj;
23859     }
23860
23861     verify_pins_with_post_plug_info("end reloc short surv");
23862 }
23863
23864 void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end,
23865                                           BOOL check_last_object_p, 
23866                                           mark* pinned_plug_entry)
23867 {
23868     //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
23869     dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
23870
23871     if (check_last_object_p)
23872     {
23873         relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
23874     }
23875     else
23876     {
23877         relocate_survivor_helper (plug, plug_end);
23878     }
23879 }
23880
23881 void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args)
23882 {
23883     assert ((tree != NULL));
23884
23885     dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
23886         tree, args->last_plug, 
23887         (tree + node_left_child (tree)),
23888         (tree + node_right_child (tree)),
23889         node_gap_size (tree)));
23890
23891     if (node_left_child (tree))
23892     {
23893         relocate_survivors_in_brick (tree + node_left_child (tree), args);
23894     }
23895     {
23896         uint8_t*  plug = tree;
23897         BOOL   has_post_plug_info_p = FALSE;
23898         BOOL   has_pre_plug_info_p = FALSE;
23899
23900         if (tree == oldest_pinned_plug)
23901         {
23902             args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
23903                                                                &has_post_plug_info_p);
23904             assert (tree == pinned_plug (args->pinned_plug_entry));
23905
23906             dprintf (3, ("tree is the oldest pin: %Ix", tree));
23907         }
23908         if (args->last_plug)
23909         {
23910             size_t  gap_size = node_gap_size (tree);
23911             uint8_t*  gap = (plug - gap_size);
23912             dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
23913             assert (gap_size >= Align (min_obj_size));
23914             uint8_t*  last_plug_end = gap;
23915
23916             BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
23917
23918             {
23919                 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
23920             }
23921         }
23922         else
23923         {
23924             assert (!has_pre_plug_info_p);
23925         }
23926
23927         args->last_plug = plug;
23928         args->is_shortened = has_post_plug_info_p;
23929         if (has_post_plug_info_p)
23930         {
23931             dprintf (3, ("setting %Ix as shortened", plug));
23932         }
23933         dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
23934     }
23935     if (node_right_child (tree))
23936     {
23937         relocate_survivors_in_brick (tree + node_right_child (tree), args);
23938     }
23939 }
23940
23941 inline
23942 void gc_heap::update_oldest_pinned_plug()
23943 {
23944     oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
23945 }
23946
23947 void gc_heap::relocate_survivors (int condemned_gen_number,
23948                                   uint8_t* first_condemned_address)
23949 {
23950     generation* condemned_gen = generation_of (condemned_gen_number);
23951     uint8_t*  start_address = first_condemned_address;
23952     size_t  current_brick = brick_of (start_address);
23953     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23954
23955     PREFIX_ASSUME(current_heap_segment != NULL);
23956
23957     uint8_t*  end_address = 0;
23958
23959     reset_pinned_queue_bos();
23960     update_oldest_pinned_plug();
23961     
23962     end_address = heap_segment_allocated (current_heap_segment);
23963
23964     size_t  end_brick = brick_of (end_address - 1);
23965     relocate_args args;
23966     args.low = gc_low;
23967     args.high = gc_high;
23968     args.is_shortened = FALSE;
23969     args.pinned_plug_entry = 0;
23970     args.last_plug = 0;
23971     while (1)
23972     {
23973         if (current_brick > end_brick)
23974         {
23975             if (args.last_plug)
23976             {
23977                 {
23978                     assert (!(args.is_shortened));
23979                     relocate_survivors_in_plug (args.last_plug,
23980                                                 heap_segment_allocated (current_heap_segment),
23981                                                 args.is_shortened, 
23982                                                 args.pinned_plug_entry);
23983                 }
23984
23985                 args.last_plug = 0;
23986             }
23987
23988             if (heap_segment_next_rw (current_heap_segment))
23989             {
23990                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
23991                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
23992                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23993                 continue;
23994             }
23995             else
23996             {
23997                 break;
23998             }
23999         }
24000         {
24001             int brick_entry =  brick_table [ current_brick ];
24002
24003             if (brick_entry >= 0)
24004             {
24005                 relocate_survivors_in_brick (brick_address (current_brick) +
24006                                              brick_entry -1,
24007                                              &args);
24008             }
24009         }
24010         current_brick++;
24011     }
24012 }
24013
24014 void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args)
24015 {
24016     if (check_last_object_p)
24017     {
24018         size += sizeof (gap_reloc_pair);
24019         mark* entry = args->pinned_plug_entry;
24020
24021         if (args->is_shortened)
24022         {
24023             assert (entry->has_post_plug_info());
24024             entry->swap_post_plug_and_saved_for_profiler();
24025         }
24026         else
24027         {
24028             assert (entry->has_pre_plug_info());
24029             entry->swap_pre_plug_and_saved_for_profiler();
24030         }
24031     }
24032
24033     ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
24034     STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
24035     ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
24036
24037     (args->fn) (plug, (plug + size), reloc, args->profiling_context, !!settings.compaction, false);
24038
24039     if (check_last_object_p)
24040     {
24041         mark* entry = args->pinned_plug_entry;
24042
24043         if (args->is_shortened)
24044         {
24045             entry->swap_post_plug_and_saved_for_profiler();
24046         }
24047         else
24048         {
24049             entry->swap_pre_plug_and_saved_for_profiler();
24050         }
24051     }
24052 }
24053
24054 void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args)
24055 {
24056     assert ((tree != NULL));
24057     if (node_left_child (tree))
24058     {
24059         walk_relocation_in_brick (tree + node_left_child (tree), args);
24060     }
24061
24062     uint8_t*  plug = tree;
24063     BOOL   has_pre_plug_info_p = FALSE;
24064     BOOL   has_post_plug_info_p = FALSE;
24065
24066     if (tree == oldest_pinned_plug)
24067     {
24068         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24069                                                            &has_post_plug_info_p);
24070         assert (tree == pinned_plug (args->pinned_plug_entry));
24071     }
24072
24073     if (args->last_plug != 0)
24074     {
24075         size_t gap_size = node_gap_size (tree);
24076         uint8_t*  gap = (plug - gap_size);
24077         uint8_t*  last_plug_end = gap;
24078         size_t last_plug_size = (last_plug_end - args->last_plug);
24079         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
24080             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24081         
24082         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24083         if (!check_last_object_p)
24084         {
24085             assert (last_plug_size >= Align (min_obj_size));
24086         }
24087
24088         walk_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24089     }
24090     else
24091     {
24092         assert (!has_pre_plug_info_p);
24093     }
24094
24095     dprintf (3, ("set args last plug to plug: %Ix", plug));
24096     args->last_plug = plug;
24097     args->is_shortened = has_post_plug_info_p;
24098
24099     if (node_right_child (tree))
24100     {
24101         walk_relocation_in_brick (tree + node_right_child (tree), args);
24102     }
24103 }
24104
24105 void gc_heap::walk_relocation (void* profiling_context, record_surv_fn fn)
24106 {
24107     generation* condemned_gen = generation_of (settings.condemned_generation);
24108     uint8_t*  start_address = generation_allocation_start (condemned_gen);
24109     size_t  current_brick = brick_of (start_address);
24110     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24111
24112     PREFIX_ASSUME(current_heap_segment != NULL);
24113
24114     reset_pinned_queue_bos();
24115     update_oldest_pinned_plug();
24116     size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24117     walk_relocate_args args;
24118     args.is_shortened = FALSE;
24119     args.pinned_plug_entry = 0;
24120     args.last_plug = 0;
24121     args.profiling_context = profiling_context;
24122     args.fn = fn;
24123
24124     while (1)
24125     {
24126         if (current_brick > end_brick)
24127         {
24128             if (args.last_plug)
24129             {
24130                 walk_plug (args.last_plug, 
24131                            (heap_segment_allocated (current_heap_segment) - args.last_plug), 
24132                            args.is_shortened,
24133                            &args);
24134                 args.last_plug = 0;
24135             }
24136             if (heap_segment_next_rw (current_heap_segment))
24137             {
24138                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24139                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24140                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24141                 continue;
24142             }
24143             else
24144             {
24145                 break;
24146             }
24147         }
24148         {
24149             int brick_entry =  brick_table [ current_brick ];
24150             if (brick_entry >= 0)
24151             {
24152                 walk_relocation_in_brick (brick_address (current_brick) +
24153                                           brick_entry - 1,
24154                                           &args);
24155             }
24156         }
24157         current_brick++;
24158     }
24159 }
24160
24161 void gc_heap::walk_survivors (record_surv_fn fn, void* context, walk_surv_type type)
24162 {
24163     if (type == walk_for_gc)
24164         walk_survivors_relocation (context, fn);
24165 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24166     else if (type == walk_for_bgc)
24167         walk_survivors_for_bgc (context, fn);
24168 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
24169     else if (type == walk_for_loh)
24170         walk_survivors_for_loh (context, fn);
24171     else
24172         assert (!"unknown type!");
24173 }
24174
24175 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24176 void gc_heap::walk_survivors_for_bgc (void* profiling_context, record_surv_fn fn)
24177 {
24178     // This should only be called for BGCs
24179     assert(settings.concurrent);
24180
24181     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24182
24183     BOOL small_object_segments = TRUE;
24184     int align_const = get_alignment_constant (small_object_segments);
24185
24186     while (1)
24187     {
24188         if (seg == 0)
24189         {
24190             if (small_object_segments)
24191             {
24192                 //switch to large segment
24193                 small_object_segments = FALSE;
24194
24195                 align_const = get_alignment_constant (small_object_segments);
24196                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24197
24198                 PREFIX_ASSUME(seg != NULL);
24199
24200                 continue;
24201             }
24202             else 
24203                 break;
24204         }
24205
24206         uint8_t* o = heap_segment_mem (seg);
24207         uint8_t* end = heap_segment_allocated (seg);
24208
24209         while (o < end)
24210         {
24211             if (method_table(o) == g_gc_pFreeObjectMethodTable)
24212             {
24213                 o += Align (size (o), align_const);
24214                 continue;
24215             }
24216
24217             // It's survived. Make a fake plug, starting at o,
24218             // and send the event
24219
24220             uint8_t* plug_start = o;
24221
24222             while (method_table(o) != g_gc_pFreeObjectMethodTable)
24223             {
24224                 o += Align (size (o), align_const);
24225                 if (o >= end)
24226                 {
24227                     break;
24228                 }
24229             }
24230                 
24231             uint8_t* plug_end = o;
24232
24233             fn (plug_start, 
24234                 plug_end,
24235                 0,              // Reloc distance == 0 as this is non-compacting
24236                 profiling_context,
24237                 false,          // Non-compacting
24238                 true);          // BGC
24239         }
24240
24241         seg = heap_segment_next (seg);
24242     }
24243 }
24244 #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24245
24246 void gc_heap::relocate_phase (int condemned_gen_number,
24247                               uint8_t* first_condemned_address)
24248 {
24249     ScanContext sc;
24250     sc.thread_number = heap_number;
24251     sc.promotion = FALSE;
24252     sc.concurrent = FALSE;
24253
24254
24255 #ifdef TIME_GC
24256         unsigned start;
24257         unsigned finish;
24258         start = GetCycleCount32();
24259 #endif //TIME_GC
24260
24261 //  %type%  category = quote (relocate);
24262     dprintf (2,("---- Relocate phase -----"));
24263
24264 #ifdef MULTIPLE_HEAPS
24265     //join all threads to make sure they are synchronized
24266     dprintf(3, ("Joining after end of plan"));
24267     gc_t_join.join(this, gc_join_begin_relocate_phase);
24268     if (gc_t_join.joined())
24269 #endif //MULTIPLE_HEAPS
24270
24271     {
24272 #ifdef MULTIPLE_HEAPS
24273
24274         //join all threads to make sure they are synchronized
24275         dprintf(3, ("Restarting for relocation"));
24276         gc_t_join.restart();
24277 #endif //MULTIPLE_HEAPS
24278     }
24279
24280     dprintf(3,("Relocating roots"));
24281     GCScan::GcScanRoots(GCHeap::Relocate,
24282                             condemned_gen_number, max_generation, &sc);
24283
24284     verify_pins_with_post_plug_info("after reloc stack");
24285
24286 #ifdef BACKGROUND_GC
24287     if (recursive_gc_sync::background_running_p())
24288     {
24289         scan_background_roots (GCHeap::Relocate, heap_number, &sc);
24290     }
24291 #endif //BACKGROUND_GC
24292
24293     if (condemned_gen_number != max_generation)
24294     {
24295         dprintf(3,("Relocating cross generation pointers"));
24296         mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
24297         verify_pins_with_post_plug_info("after reloc cards");
24298     }
24299     if (condemned_gen_number != max_generation)
24300     {
24301         dprintf(3,("Relocating cross generation pointers for large objects"));
24302         mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
24303     }
24304     else
24305     {
24306 #ifdef FEATURE_LOH_COMPACTION
24307         if (loh_compacted_p)
24308         {
24309             assert (settings.condemned_generation == max_generation);
24310             relocate_in_loh_compact();
24311         }
24312         else
24313 #endif //FEATURE_LOH_COMPACTION
24314         {
24315             relocate_in_large_objects ();
24316         }
24317     }
24318     {
24319         dprintf(3,("Relocating survivors"));
24320         relocate_survivors (condemned_gen_number,
24321                             first_condemned_address);
24322     }
24323
24324 #ifdef FEATURE_PREMORTEM_FINALIZATION
24325         dprintf(3,("Relocating finalization data"));
24326         finalize_queue->RelocateFinalizationData (condemned_gen_number,
24327                                                        __this);
24328 #endif // FEATURE_PREMORTEM_FINALIZATION
24329
24330
24331 // MTHTS
24332     {
24333         dprintf(3,("Relocating handle table"));
24334         GCScan::GcScanHandles(GCHeap::Relocate,
24335                                   condemned_gen_number, max_generation, &sc);
24336     }
24337
24338 #ifdef MULTIPLE_HEAPS
24339     //join all threads to make sure they are synchronized
24340     dprintf(3, ("Joining after end of relocation"));
24341     gc_t_join.join(this, gc_join_relocate_phase_done);
24342
24343 #endif //MULTIPLE_HEAPS
24344
24345 #ifdef TIME_GC
24346         finish = GetCycleCount32();
24347         reloc_time = finish - start;
24348 #endif //TIME_GC
24349
24350     dprintf(2,( "---- End of Relocate phase ----"));
24351 }
24352
24353 // This compares to see if tree is the current pinned plug and returns info
24354 // for this pinned plug. Also advances the pinned queue if that's the case.
24355 //
24356 // We don't change the values of the plug info if tree is not the same as 
24357 // the current pinned plug - the caller is responsible for setting the right
24358 // values to begin with.
24359 //
24360 // POPO TODO: We are keeping this temporarily as this is also used by realloc 
24361 // where it passes FALSE to deque_p, change it to use the same optimization 
24362 // as relocate. Not as essential since realloc is already a slow path.
24363 mark* gc_heap::get_next_pinned_entry (uint8_t* tree,
24364                                       BOOL* has_pre_plug_info_p, 
24365                                       BOOL* has_post_plug_info_p,
24366                                       BOOL deque_p)
24367 {
24368     if (!pinned_plug_que_empty_p())
24369     {
24370         mark* oldest_entry = oldest_pin();
24371         uint8_t* oldest_plug = pinned_plug (oldest_entry);
24372         if (tree == oldest_plug)
24373         {
24374             *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24375             *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24376
24377             if (deque_p)
24378             {
24379                 deque_pinned_plug();
24380             }
24381
24382             dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d", 
24383                 tree, 
24384                 (*has_pre_plug_info_p ? 1 : 0),
24385                 (*has_post_plug_info_p ? 1 : 0)));
24386
24387             return oldest_entry;
24388         }
24389     }
24390
24391     return NULL;
24392 }
24393
24394 // This also deques the oldest entry and update the oldest plug
24395 mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p, 
24396                                         BOOL* has_post_plug_info_p)
24397 {
24398     mark* oldest_entry = oldest_pin();
24399     *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24400     *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24401
24402     deque_pinned_plug();
24403     update_oldest_pinned_plug();
24404     return oldest_entry;
24405 }
24406
24407 inline
24408 void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24409 {
24410     if (copy_cards_p)
24411         copy_cards_for_addresses (dest, src, len);
24412     else
24413         clear_card_for_addresses (dest, dest + len);
24414 }
24415
24416 // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
24417 // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
24418 // we won't need to individually recover each overwritten part of plugs.
24419 inline
24420 void  gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24421 {
24422     if (dest != src)
24423     {
24424 #ifdef BACKGROUND_GC
24425         if (current_c_gc_state == c_gc_state_marking) 
24426         {
24427             //TODO: should look to see whether we should consider changing this
24428             // to copy a consecutive region of the mark array instead.
24429             copy_mark_bits_for_addresses (dest, src, len);
24430         }
24431 #endif //BACKGROUND_GC
24432         //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24433         dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24434         memcopy (dest - plug_skew, src - plug_skew, len);
24435 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24436         if (SoftwareWriteWatch::IsEnabledForGCHeap())
24437         {
24438             // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC
24439             // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the
24440             // object at (src + len), so it can be ignored anyway.
24441             SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew);
24442         }
24443 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24444         copy_cards_range (dest, src, len, copy_cards_p);
24445     }
24446 }
24447
24448 void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args)
24449 {
24450     args->print();
24451     uint8_t* reloc_plug = plug + args->last_plug_relocation;
24452
24453     if (check_last_object_p)
24454     {
24455         size += sizeof (gap_reloc_pair);
24456         mark* entry = args->pinned_plug_entry;
24457
24458         if (args->is_shortened)
24459         {
24460             assert (entry->has_post_plug_info());
24461             entry->swap_post_plug_and_saved();
24462         }
24463         else
24464         {
24465             assert (entry->has_pre_plug_info());
24466             entry->swap_pre_plug_and_saved();
24467         }
24468     }
24469
24470     int  old_brick_entry =  brick_table [brick_of (plug)];
24471
24472     assert (node_relocation_distance (plug) == args->last_plug_relocation);
24473
24474 #ifdef FEATURE_STRUCTALIGN
24475     ptrdiff_t alignpad = node_alignpad(plug);
24476     if (alignpad)
24477     {
24478         make_unused_array (reloc_plug - alignpad, alignpad);
24479         if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
24480         {
24481             // The alignment padding is straddling one or more bricks;
24482             // it has to be the last "object" of its first brick.
24483             fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
24484         }
24485     }
24486 #else // FEATURE_STRUCTALIGN
24487     size_t unused_arr_size = 0; 
24488     BOOL  already_padded_p = FALSE;
24489 #ifdef SHORT_PLUGS
24490     if (is_plug_padded (plug))
24491     {
24492         already_padded_p = TRUE;
24493         clear_plug_padded (plug);
24494         unused_arr_size = Align (min_obj_size);
24495     }
24496 #endif //SHORT_PLUGS
24497     if (node_realigned (plug))
24498     {
24499         unused_arr_size += switch_alignment_size (already_padded_p);
24500     }
24501
24502     if (unused_arr_size != 0) 
24503     {
24504         make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
24505
24506         if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
24507         {
24508             dprintf (3, ("fix B for padding: %Id: %Ix->%Ix", 
24509                 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
24510             // The alignment padding is straddling one or more bricks;
24511             // it has to be the last "object" of its first brick.
24512             fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
24513         }
24514     }
24515 #endif // FEATURE_STRUCTALIGN
24516
24517 #ifdef SHORT_PLUGS
24518     if (is_plug_padded (plug))
24519     {
24520         make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
24521
24522         if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
24523         {
24524             // The alignment padding is straddling one or more bricks;
24525             // it has to be the last "object" of its first brick.
24526             fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
24527         }
24528     }
24529 #endif //SHORT_PLUGS
24530
24531     gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
24532
24533     if (args->check_gennum_p)
24534     {
24535         int src_gennum = args->src_gennum;
24536         if (src_gennum == -1)
24537         {
24538             src_gennum = object_gennum (plug);
24539         }
24540
24541         int dest_gennum = object_gennum_plan (reloc_plug);
24542
24543         if (src_gennum < dest_gennum)
24544         {
24545             generation_allocation_size (generation_of (dest_gennum)) += size;
24546         }
24547     }
24548
24549     size_t current_reloc_brick = args->current_compacted_brick;
24550
24551     if (brick_of (reloc_plug) != current_reloc_brick)
24552     {
24553         dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix", 
24554             current_reloc_brick, brick_of (reloc_plug)));
24555
24556         if (args->before_last_plug)
24557         {
24558             dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
24559                      current_reloc_brick,
24560                      args->before_last_plug, 
24561                      (args->before_last_plug - brick_address (current_reloc_brick))));
24562
24563             {
24564                 set_brick (current_reloc_brick,
24565                         args->before_last_plug - brick_address (current_reloc_brick));
24566             }
24567         }
24568         current_reloc_brick = brick_of (reloc_plug);
24569     }
24570     size_t end_brick = brick_of (reloc_plug + size-1);
24571     if (end_brick != current_reloc_brick)
24572     {
24573         // The plug is straddling one or more bricks
24574         // It has to be the last plug of its first brick
24575         dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
24576                  current_reloc_brick, (size_t)reloc_plug,
24577                  (reloc_plug - brick_address (current_reloc_brick))));
24578
24579         {
24580             set_brick (current_reloc_brick,
24581                     reloc_plug - brick_address (current_reloc_brick));
24582         }
24583         // update all intervening brick
24584         size_t brick = current_reloc_brick + 1;
24585         dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
24586             brick, (end_brick - 1)));
24587         while (brick < end_brick)
24588         {
24589             set_brick (brick, -1);
24590             brick++;
24591         }
24592         // code last brick offset as a plug address
24593         args->before_last_plug = brick_address (end_brick) -1;
24594         current_reloc_brick = end_brick;
24595         dprintf (3, ("setting before last to %Ix, last brick to %Ix",
24596             args->before_last_plug, current_reloc_brick));
24597     } 
24598     else
24599     {
24600         dprintf (3, ("still in the same brick: %Ix", end_brick));
24601         args->before_last_plug = reloc_plug;
24602     }
24603     args->current_compacted_brick = current_reloc_brick;
24604
24605     if (check_last_object_p)
24606     {
24607         mark* entry = args->pinned_plug_entry;
24608
24609         if (args->is_shortened)
24610         {
24611             entry->swap_post_plug_and_saved();
24612         }
24613         else
24614         {
24615             entry->swap_pre_plug_and_saved();
24616         }
24617     }
24618 }
24619
24620 void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args)
24621 {
24622     assert (tree != NULL);
24623     int   left_node = node_left_child (tree);
24624     int   right_node = node_right_child (tree);
24625     ptrdiff_t relocation = node_relocation_distance (tree);
24626
24627     args->print();
24628
24629     if (left_node)
24630     {
24631         dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
24632         compact_in_brick ((tree + left_node), args);
24633     }
24634
24635     uint8_t*  plug = tree;
24636     BOOL   has_pre_plug_info_p = FALSE;
24637     BOOL   has_post_plug_info_p = FALSE;
24638
24639     if (tree == oldest_pinned_plug)
24640     {
24641         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24642                                                            &has_post_plug_info_p);
24643         assert (tree == pinned_plug (args->pinned_plug_entry));
24644     }
24645
24646     if (args->last_plug != 0)
24647     {
24648         size_t gap_size = node_gap_size (tree);
24649         uint8_t*  gap = (plug - gap_size);
24650         uint8_t*  last_plug_end = gap;
24651         size_t last_plug_size = (last_plug_end - args->last_plug);
24652         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
24653             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24654         
24655         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24656         if (!check_last_object_p)
24657         {
24658             assert (last_plug_size >= Align (min_obj_size));
24659         }
24660
24661         compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24662     }
24663     else
24664     {
24665         assert (!has_pre_plug_info_p);
24666     }
24667
24668     dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
24669     args->last_plug = plug;
24670     args->last_plug_relocation = relocation;
24671     args->is_shortened = has_post_plug_info_p;
24672
24673     if (right_node)
24674     {
24675         dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
24676         compact_in_brick ((tree + right_node), args);
24677     }
24678 }
24679
24680 void gc_heap::recover_saved_pinned_info()
24681 {
24682     reset_pinned_queue_bos();
24683
24684     while (!(pinned_plug_que_empty_p()))
24685     {
24686         mark* oldest_entry = oldest_pin();
24687         oldest_entry->recover_plug_info();
24688 #ifdef GC_CONFIG_DRIVEN
24689         if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info())
24690             record_interesting_data_point (idp_pre_and_post_pin);
24691         else if (oldest_entry->has_pre_plug_info())
24692             record_interesting_data_point (idp_pre_pin);
24693         else if (oldest_entry->has_post_plug_info())
24694             record_interesting_data_point (idp_post_pin);
24695 #endif //GC_CONFIG_DRIVEN
24696
24697         deque_pinned_plug();
24698     }
24699 }
24700
24701 void gc_heap::compact_phase (int condemned_gen_number,
24702                              uint8_t*  first_condemned_address,
24703                              BOOL clear_cards)
24704 {
24705 //  %type%  category = quote (compact);
24706 #ifdef TIME_GC
24707         unsigned start;
24708         unsigned finish;
24709         start = GetCycleCount32();
24710 #endif //TIME_GC
24711     generation*   condemned_gen = generation_of (condemned_gen_number);
24712     uint8_t*  start_address = first_condemned_address;
24713     size_t   current_brick = brick_of (start_address);
24714     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24715
24716     PREFIX_ASSUME(current_heap_segment != NULL);
24717
24718     reset_pinned_queue_bos();
24719     update_oldest_pinned_plug();
24720
24721     BOOL reused_seg = expand_reused_seg_p();
24722     if (reused_seg)
24723     {
24724         for (int i = 1; i <= max_generation; i++)
24725         {
24726             generation_allocation_size (generation_of (i)) = 0;
24727         }
24728     }
24729
24730     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
24731
24732     size_t  end_brick = brick_of (end_address-1);
24733     compact_args args;
24734     args.last_plug = 0;
24735     args.before_last_plug = 0;
24736     args.current_compacted_brick = ~((size_t)1);
24737     args.is_shortened = FALSE;
24738     args.pinned_plug_entry = 0;
24739     args.copy_cards_p =  (condemned_gen_number >= 1) || !clear_cards;
24740     args.check_gennum_p = reused_seg;
24741     if (args.check_gennum_p)
24742     {
24743         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
24744     }
24745
24746     dprintf (2,("---- Compact Phase: %Ix(%Ix)----", 
24747         first_condemned_address, brick_of (first_condemned_address)));
24748
24749 #ifdef MULTIPLE_HEAPS
24750     //restart
24751     if (gc_t_join.joined())
24752     {
24753 #endif //MULTIPLE_HEAPS
24754
24755 #ifdef MULTIPLE_HEAPS
24756         dprintf(3, ("Restarting for compaction"));
24757         gc_t_join.restart();
24758     }
24759 #endif //MULTIPLE_HEAPS
24760
24761     reset_pinned_queue_bos();
24762
24763 #ifdef FEATURE_LOH_COMPACTION
24764     if (loh_compacted_p)
24765     {
24766         compact_loh();
24767     }
24768 #endif //FEATURE_LOH_COMPACTION
24769
24770     if ((start_address < end_address) ||
24771         (condemned_gen_number == max_generation))
24772     {
24773         while (1)
24774         {
24775             if (current_brick > end_brick)
24776             {
24777                 if (args.last_plug != 0)
24778                 {
24779                     dprintf (3, ("compacting last plug: %Ix", args.last_plug))
24780                     compact_plug (args.last_plug,
24781                                   (heap_segment_allocated (current_heap_segment) - args.last_plug),
24782                                   args.is_shortened,
24783                                   &args);
24784                 }
24785
24786                 if (heap_segment_next_rw (current_heap_segment))
24787                 {
24788                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
24789                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
24790                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24791                     args.last_plug = 0;
24792                     if (args.check_gennum_p)
24793                     {
24794                         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
24795                     }
24796                     continue;
24797                 }
24798                 else
24799                 {
24800                     if (args.before_last_plug !=0)
24801                     {
24802                         dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
24803                                     args.current_compacted_brick, (size_t)args.before_last_plug));
24804                         assert (args.current_compacted_brick != ~1u);
24805                         set_brick (args.current_compacted_brick,
24806                                    args.before_last_plug - brick_address (args.current_compacted_brick));
24807                     }
24808                     break;
24809                 }
24810             }
24811             {
24812                 int  brick_entry =  brick_table [ current_brick ];
24813                 dprintf (3, ("B: %Ix(%Ix)->%Ix", 
24814                     current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
24815
24816                 if (brick_entry >= 0)
24817                 {
24818                     compact_in_brick ((brick_address (current_brick) + brick_entry -1),
24819                                       &args);
24820
24821                 }
24822             }
24823             current_brick++;
24824         }
24825     }
24826
24827     recover_saved_pinned_info();
24828
24829 #ifdef TIME_GC
24830     finish = GetCycleCount32();
24831     compact_time = finish - start;
24832 #endif //TIME_GC
24833
24834     concurrent_print_time_delta ("compact end");
24835
24836     dprintf(2,("---- End of Compact phase ----"));
24837 }
24838
24839 #ifdef MULTIPLE_HEAPS
24840
24841 #ifdef _MSC_VER
24842 #pragma warning(push)
24843 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
24844 #endif //_MSC_VER
24845 void gc_heap::gc_thread_stub (void* arg)
24846 {
24847     gc_heap* heap = (gc_heap*)arg;
24848     if (!gc_thread_no_affinitize_p)
24849     {
24850         GCThreadAffinity affinity;
24851         affinity.Group = GCThreadAffinity::None;
24852         affinity.Processor = GCThreadAffinity::None;
24853
24854         // We are about to set affinity for GC threads. It is a good place to set up NUMA and
24855         // CPU groups because the process mask, processor number, and group number are all
24856         // readily available.
24857         if (GCToOSInterface::CanEnableGCCPUGroups())
24858             set_thread_group_affinity_for_heap(heap->heap_number, &affinity);
24859         else
24860             set_thread_affinity_mask_for_heap(heap->heap_number, &affinity);
24861
24862         if (!GCToOSInterface::SetThreadAffinity(&affinity))
24863         {
24864             dprintf(1, ("Failed to set thread affinity for server GC thread"));
24865         }
24866     }
24867
24868     // server GC threads run at a higher priority than normal.
24869     GCToOSInterface::BoostThreadPriority();
24870     _alloca (256*heap->heap_number);
24871     heap->gc_thread_function();
24872 }
24873 #ifdef _MSC_VER
24874 #pragma warning(pop)
24875 #endif //_MSC_VER
24876
24877 #endif //MULTIPLE_HEAPS
24878
24879 #ifdef BACKGROUND_GC
24880
24881 #ifdef _MSC_VER
24882 #pragma warning(push)
24883 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
24884 #endif //_MSC_VER
24885 void gc_heap::bgc_thread_stub (void* arg)
24886 {
24887     gc_heap* heap = (gc_heap*)arg;
24888     heap->bgc_thread = GCToEEInterface::GetThread();
24889     assert(heap->bgc_thread != nullptr);
24890     heap->bgc_thread_function();
24891 }
24892 #ifdef _MSC_VER
24893 #pragma warning(pop)
24894 #endif //_MSC_VER
24895
24896 #endif //BACKGROUND_GC
24897
24898 /*------------------ Background GC ----------------------------*/
24899
24900 #ifdef BACKGROUND_GC
24901
24902 void gc_heap::background_drain_mark_list (int thread)
24903 {
24904     UNREFERENCED_PARAMETER(thread);
24905
24906     size_t saved_c_mark_list_index = c_mark_list_index;
24907
24908     if (saved_c_mark_list_index)
24909     {
24910         concurrent_print_time_delta ("SML");
24911     }
24912     while (c_mark_list_index != 0)
24913     {
24914         size_t current_index = c_mark_list_index - 1;
24915         uint8_t* o = c_mark_list [current_index];
24916         background_mark_object (o THREAD_NUMBER_ARG);
24917         c_mark_list_index--;
24918     }
24919     if (saved_c_mark_list_index)
24920     {
24921
24922         concurrent_print_time_delta ("EML");
24923     }
24924
24925     fire_drain_mark_list_event (saved_c_mark_list_index);
24926 }
24927
24928
24929 // The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
24930 #ifdef MULTIPLE_HEAPS
24931 // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
24932 // them. So we can use the same static variables.
24933 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
24934 {
24935     // Whenever we call this method there may have been preceding object promotions. So set
24936     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
24937     // based on the how the scanning proceeded).
24938     s_fUnscannedPromotions = TRUE;
24939
24940     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
24941     // the state of this thread's portion of the dependent handle table. That's because promotions on other
24942     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
24943     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
24944     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
24945     // as all the others or they'll get out of step).
24946     while (true)
24947     {
24948         // The various worker threads are all currently racing in this code. We need to work out if at least
24949         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
24950         // dependent handle table when both of the following conditions apply:
24951         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
24952         //     object happens to correspond to a primary in one of our handles we might potentially have to
24953         //     promote the associated secondary).
24954         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
24955         //
24956         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
24957         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
24958         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
24959         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
24960         // follows below. Note that we can't read this outside of the join since on any iteration apart from
24961         // the first threads will be racing between reading this value and completing their previous
24962         // iteration's table scan.
24963         //
24964         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
24965         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
24966         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
24967         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
24968         // we're safely joined.
24969         if (GCScan::GcDhUnpromotedHandlesExist(sc))
24970             s_fUnpromotedHandles = TRUE;
24971
24972         // Synchronize all the threads so we can read our state variables safely. The following shared
24973         // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
24974         // single thread inside the join.
24975         bgc_t_join.join(this, gc_join_scan_dependent_handles);
24976         if (bgc_t_join.joined())
24977         {
24978             // We're synchronized so it's safe to read our shared state variables. We update another shared
24979             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
24980             // the loop. We scan if there has been at least one object promotion since last time and at least
24981             // one thread has a dependent handle table with a potential handle promotion possible.
24982             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
24983
24984             // Reset our shared state variables (ready to be set again on this scan or with a good initial
24985             // value for the next call if we're terminating the loop).
24986             s_fUnscannedPromotions = FALSE;
24987             s_fUnpromotedHandles = FALSE;
24988
24989             if (!s_fScanRequired)
24990             {
24991                 uint8_t* all_heaps_max = 0;
24992                 uint8_t* all_heaps_min = MAX_PTR;
24993                 int i;
24994                 for (i = 0; i < n_heaps; i++)
24995                 {
24996                     if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
24997                         all_heaps_max = g_heaps[i]->background_max_overflow_address;
24998                     if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
24999                         all_heaps_min = g_heaps[i]->background_min_overflow_address;
25000                 }
25001                 for (i = 0; i < n_heaps; i++)
25002                 {
25003                     g_heaps[i]->background_max_overflow_address = all_heaps_max;
25004                     g_heaps[i]->background_min_overflow_address = all_heaps_min;
25005                 }
25006             }
25007
25008             // Restart all the workers.
25009             dprintf(2, ("Starting all gc thread mark stack overflow processing"));
25010             bgc_t_join.restart();
25011         }
25012
25013         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25014         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
25015         // global flag indicating that at least one object promotion may have occurred (the usual comment
25016         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
25017         // exit the method since we unconditionally set this variable on method entry anyway).
25018         if (background_process_mark_overflow (sc->concurrent))
25019             s_fUnscannedPromotions = TRUE;
25020
25021         // If we decided that no scan was required we can terminate the loop now.
25022         if (!s_fScanRequired)
25023             break;
25024
25025         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
25026         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
25027         // could miss noting the promotion of some primary objects).
25028         bgc_t_join.join(this, gc_join_rescan_dependent_handles);
25029         if (bgc_t_join.joined())
25030         {
25031             // Restart all the workers.
25032             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
25033             bgc_t_join.restart();
25034         }
25035
25036         // If the portion of the dependent handle table managed by this worker has handles that could still be
25037         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
25038         // could require a rescan of handles on this or other workers.
25039         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25040             if (GCScan::GcDhReScan(sc))
25041                 s_fUnscannedPromotions = TRUE;
25042     }
25043 }
25044 #else
25045 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25046 {
25047     // Whenever we call this method there may have been preceding object promotions. So set
25048     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25049     // based on the how the scanning proceeded).
25050     bool fUnscannedPromotions = true;
25051
25052     // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
25053     // scan without performing any new promotions.
25054     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
25055     {
25056         // On each iteration of the loop start with the assumption that no further objects have been promoted.
25057         fUnscannedPromotions = false;
25058
25059         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25060         // being visible. If there was an overflow (background_process_mark_overflow returned true) then
25061         // additional objects now appear to be promoted and we should set the flag.
25062         if (background_process_mark_overflow (sc->concurrent))
25063             fUnscannedPromotions = true;
25064
25065         // Perform the scan and set the flag if any promotions resulted.
25066         if (GCScan::GcDhReScan (sc))
25067             fUnscannedPromotions = true;
25068     }
25069
25070     // Perform a last processing of any overflowed mark stack.
25071     background_process_mark_overflow (sc->concurrent);
25072 }
25073 #endif //MULTIPLE_HEAPS
25074
25075 void gc_heap::recover_bgc_settings()
25076 {
25077     if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
25078     {
25079         dprintf (2, ("restoring bgc settings"));
25080         settings = saved_bgc_settings;
25081         GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
25082     }
25083 }
25084
25085 void gc_heap::allow_fgc()
25086 {
25087     assert (bgc_thread == GCToEEInterface::GetThread());
25088     bool bToggleGC = false;
25089
25090     if (g_fSuspensionPending > 0)
25091     {
25092         bToggleGC = GCToEEInterface::EnablePreemptiveGC();
25093         if (bToggleGC)
25094         {
25095             GCToEEInterface::DisablePreemptiveGC();
25096         }
25097     }
25098 }
25099
25100 BOOL gc_heap::should_commit_mark_array()
25101 {
25102     return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
25103 }
25104
25105 void gc_heap::clear_commit_flag()
25106 {
25107     generation* gen = generation_of (max_generation);
25108     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25109     while (1)
25110     {
25111         if (seg == 0)
25112         {
25113             if (gen != large_object_generation)
25114             {
25115                 gen = large_object_generation;
25116                 seg = heap_segment_in_range (generation_start_segment (gen));
25117             }
25118             else
25119             {
25120                 break;
25121             }
25122         }
25123
25124         if (seg->flags & heap_segment_flags_ma_committed)
25125         {
25126             seg->flags &= ~heap_segment_flags_ma_committed;
25127         }
25128
25129         if (seg->flags & heap_segment_flags_ma_pcommitted)
25130         {
25131             seg->flags &= ~heap_segment_flags_ma_pcommitted;
25132         }
25133
25134         seg = heap_segment_next (seg);
25135     }
25136 }
25137
25138 void gc_heap::clear_commit_flag_global()
25139 {
25140 #ifdef MULTIPLE_HEAPS
25141     for (int i = 0; i < n_heaps; i++)
25142     {
25143         g_heaps[i]->clear_commit_flag();
25144     }
25145 #else
25146     clear_commit_flag();
25147 #endif //MULTIPLE_HEAPS
25148 }
25149
25150 void gc_heap::verify_mark_array_cleared (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25151 {
25152 #ifdef _DEBUG
25153     size_t  markw = mark_word_of (begin);
25154     size_t  markw_end = mark_word_of (end);
25155
25156     while (markw < markw_end)
25157     {
25158         if (mark_array_addr[markw])
25159         {
25160             dprintf  (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
25161                             markw, mark_array_addr[markw], mark_word_address (markw)));
25162             FATAL_GC_ERROR();
25163         }
25164         markw++;
25165     }
25166 #else // _DEBUG
25167     UNREFERENCED_PARAMETER(begin);
25168     UNREFERENCED_PARAMETER(end);
25169     UNREFERENCED_PARAMETER(mark_array_addr);
25170 #endif //_DEBUG
25171 }
25172
25173 void gc_heap::verify_mark_array_cleared (heap_segment* seg, uint32_t* mark_array_addr)
25174 {
25175     verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
25176 }
25177
25178 BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, 
25179                                          heap_segment* seg,
25180                                          uint32_t* new_card_table,
25181                                          uint8_t* new_lowest_address)
25182 {
25183     UNREFERENCED_PARAMETER(hp); // compiler bug? -- this *is*, indeed, referenced
25184
25185     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25186     uint8_t* end = heap_segment_reserved (seg);
25187
25188     uint8_t* lowest = hp->background_saved_lowest_address;
25189     uint8_t* highest = hp->background_saved_highest_address;
25190
25191     uint8_t* commit_start = NULL;
25192     uint8_t* commit_end = NULL;
25193     size_t commit_flag = 0;
25194
25195     if ((highest >= start) &&
25196         (lowest <= end))
25197     {
25198         if ((start >= lowest) && (end <= highest))
25199         {
25200             dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25201                                     start, end, lowest, highest));
25202             commit_flag = heap_segment_flags_ma_committed;
25203         }
25204         else
25205         {
25206             dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25207                                     start, end, lowest, highest));
25208             commit_flag = heap_segment_flags_ma_pcommitted;
25209         }
25210
25211         commit_start = max (lowest, start);
25212         commit_end = min (highest, end);
25213
25214         if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
25215         {
25216             return FALSE;
25217         }
25218
25219         if (new_card_table == 0)
25220         {
25221             new_card_table = g_gc_card_table;
25222         }
25223
25224         if (hp->card_table != new_card_table)
25225         {
25226             if (new_lowest_address == 0)
25227             {
25228                 new_lowest_address = g_gc_lowest_address;
25229             }
25230
25231             uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))];
25232             uint32_t* ma = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
25233
25234             dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix", 
25235                                     hp->card_table, new_card_table,
25236                                     hp->mark_array, ma));
25237
25238             if (!commit_mark_array_by_range (commit_start, commit_end, ma))
25239             {
25240                 return FALSE;
25241             }
25242         }
25243
25244         seg->flags |= commit_flag;
25245     }
25246
25247     return TRUE;
25248 }
25249
25250 BOOL gc_heap::commit_mark_array_by_range (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25251 {
25252     size_t beg_word = mark_word_of (begin);
25253     size_t end_word = mark_word_of (align_on_mark_word (end));
25254     uint8_t* commit_start = align_lower_page ((uint8_t*)&mark_array_addr[beg_word]);
25255     uint8_t* commit_end = align_on_page ((uint8_t*)&mark_array_addr[end_word]);
25256     size_t size = (size_t)(commit_end - commit_start);
25257
25258 #ifdef SIMPLE_DPRINTF
25259     dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
25260                             begin, end,
25261                             beg_word, end_word,
25262                             (end_word - beg_word) * sizeof (uint32_t),
25263                             &mark_array_addr[beg_word],
25264                             &mark_array_addr[end_word],
25265                             (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
25266                             commit_start, commit_end,
25267                             size));
25268 #endif //SIMPLE_DPRINTF
25269
25270     if (GCToOSInterface::VirtualCommit (commit_start, size))
25271     {
25272         // We can only verify the mark array is cleared from begin to end, the first and the last
25273         // page aren't necessarily all cleared 'cause they could be used by other segments or 
25274         // card bundle.
25275         verify_mark_array_cleared (begin, end, mark_array_addr);
25276         return TRUE;
25277     }
25278     else
25279     {
25280         dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (uint32_t)));
25281         return FALSE;
25282     }
25283 }
25284
25285 BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, uint32_t* new_mark_array_addr)
25286 {
25287     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25288     uint8_t* end = heap_segment_reserved (seg);
25289
25290 #ifdef MULTIPLE_HEAPS
25291     uint8_t* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
25292     uint8_t* highest = heap_segment_heap (seg)->background_saved_highest_address;
25293 #else
25294     uint8_t* lowest = background_saved_lowest_address;
25295     uint8_t* highest = background_saved_highest_address;
25296 #endif //MULTIPLE_HEAPS
25297
25298     if ((highest >= start) &&
25299         (lowest <= end))
25300     {
25301         start = max (lowest, start);
25302         end = min (highest, end);
25303         if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
25304         {
25305             return FALSE;
25306         }
25307     }
25308
25309     return TRUE;
25310 }
25311
25312 BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, uint32_t* mark_array_addr)
25313 {
25314     dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix",
25315         seg,
25316         heap_segment_reserved (seg),
25317         mark_array_addr));
25318     uint8_t* start = (heap_segment_read_only_p (seg) ? heap_segment_mem (seg) : (uint8_t*)seg);
25319
25320     return commit_mark_array_by_range (start, heap_segment_reserved (seg), mark_array_addr);
25321 }
25322
25323 BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr)
25324 {
25325     UNREFERENCED_PARAMETER(mark_array_addr);
25326
25327     dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix", 
25328                             lowest_address, highest_address, mark_array));
25329
25330     generation* gen = generation_of (max_generation);
25331     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25332     while (1)
25333     {
25334         if (seg == 0)
25335         {
25336             if (gen != large_object_generation)
25337             {
25338                 gen = large_object_generation;
25339                 seg = heap_segment_in_range (generation_start_segment (gen));
25340             }
25341             else
25342             {
25343                 break;
25344             }
25345         }
25346
25347         dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
25348
25349         if (!(seg->flags & heap_segment_flags_ma_committed))
25350         {
25351             // For ro segments they could always be only partially in range so we'd
25352             // be calling this at the beginning of every BGC. We are not making this 
25353             // more efficient right now - ro segments are currently only used by redhawk.
25354             if (heap_segment_read_only_p (seg))
25355             {
25356                 if ((heap_segment_mem (seg) >= lowest_address) && 
25357                     (heap_segment_reserved (seg) <= highest_address))
25358                 {
25359                     if (commit_mark_array_by_seg (seg, mark_array))
25360                     {
25361                         seg->flags |= heap_segment_flags_ma_committed;
25362                     }
25363                     else
25364                     {
25365                         return FALSE;
25366                     }
25367                 }
25368                 else
25369                 {
25370                     uint8_t* start = max (lowest_address, heap_segment_mem (seg));
25371                     uint8_t* end = min (highest_address, heap_segment_reserved (seg));
25372                     if (commit_mark_array_by_range (start, end, mark_array))
25373                     {
25374                         seg->flags |= heap_segment_flags_ma_pcommitted;
25375                     }
25376                     else
25377                     {
25378                         return FALSE;
25379                     }
25380                 }
25381             }
25382             else
25383             {
25384                 // For normal segments they are by design completely in range so just 
25385                 // commit the whole mark array for each seg.
25386                 if (commit_mark_array_by_seg (seg, mark_array))
25387                 {
25388                     if (seg->flags & heap_segment_flags_ma_pcommitted)
25389                     {
25390                         seg->flags &= ~heap_segment_flags_ma_pcommitted;
25391                     }
25392                     seg->flags |= heap_segment_flags_ma_committed;
25393                 }
25394                 else
25395                 {
25396                     return FALSE;
25397                 }
25398             }
25399         }
25400
25401         seg = heap_segment_next (seg);
25402     }
25403
25404     return TRUE;
25405 }
25406
25407 // This function doesn't check the commit flag since it's for a new array -
25408 // the mark_array flag for these segments will remain the same.
25409 BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr)
25410 {
25411     dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
25412     generation* gen = generation_of (max_generation);
25413     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25414     while (1)
25415     {
25416         if (seg == 0)
25417         {
25418             if (gen != large_object_generation)
25419             {
25420                 gen = large_object_generation;
25421                 seg = heap_segment_in_range (generation_start_segment (gen));
25422             }
25423             else
25424             {
25425                 break;
25426             }
25427         }
25428
25429         if (!commit_mark_array_with_check (seg, new_mark_array_addr))
25430         {
25431             return FALSE;
25432         }
25433
25434         seg = heap_segment_next (seg);
25435     }
25436
25437 #ifdef MULTIPLE_HEAPS
25438     if (new_heap_segment)
25439     {
25440         if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
25441         {
25442             return FALSE;
25443         }        
25444     }
25445 #endif //MULTIPLE_HEAPS
25446
25447     return TRUE;
25448 }
25449
25450 BOOL gc_heap::commit_new_mark_array_global (uint32_t* new_mark_array)
25451 {
25452 #ifdef MULTIPLE_HEAPS
25453     for (int i = 0; i < n_heaps; i++)
25454     {
25455         if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
25456         {
25457             return FALSE;
25458         }
25459     }
25460 #else
25461     if (!commit_new_mark_array (new_mark_array))
25462     {
25463         return FALSE;
25464     }
25465 #endif //MULTIPLE_HEAPS
25466
25467     return TRUE;
25468 }
25469
25470 void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
25471 {
25472     // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
25473     // been set to NULL. 
25474     if (mark_array == NULL)
25475     {
25476         return;
25477     }
25478
25479     dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
25480
25481     size_t flags = seg->flags;
25482
25483     if ((flags & heap_segment_flags_ma_committed) ||
25484         (flags & heap_segment_flags_ma_pcommitted))
25485     {
25486         uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25487         uint8_t* end = heap_segment_reserved (seg);
25488
25489         if (flags & heap_segment_flags_ma_pcommitted)
25490         {
25491             start = max (lowest_address, start);
25492             end = min (highest_address, end);
25493         }
25494
25495         size_t beg_word = mark_word_of (start);
25496         size_t end_word = mark_word_of (align_on_mark_word (end));
25497         uint8_t* decommit_start = align_on_page ((uint8_t*)&mark_array[beg_word]);
25498         uint8_t* decommit_end = align_lower_page ((uint8_t*)&mark_array[end_word]);
25499         size_t size = (size_t)(decommit_end - decommit_start);
25500
25501 #ifdef SIMPLE_DPRINTF
25502         dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
25503                                 seg,
25504                                 beg_word, end_word,
25505                                 (end_word - beg_word) * sizeof (uint32_t),
25506                                 &mark_array[beg_word],
25507                                 &mark_array[end_word],
25508                                 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
25509                                 decommit_start, decommit_end,
25510                                 size));
25511 #endif //SIMPLE_DPRINTF
25512         
25513         if (decommit_start < decommit_end)
25514         {
25515             if (!GCToOSInterface::VirtualDecommit (decommit_start, size))
25516             {
25517                 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualDecommit on %Ix for %Id bytes failed", 
25518                                         decommit_start, size));
25519                 assert (!"decommit failed");
25520             }
25521         }
25522
25523         dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
25524     }
25525 }
25526
25527 void gc_heap::background_mark_phase ()
25528 {
25529     verify_mark_array_cleared();
25530
25531     ScanContext sc;
25532     sc.thread_number = heap_number;
25533     sc.promotion = TRUE;
25534     sc.concurrent = FALSE;
25535
25536     THREAD_FROM_HEAP;
25537     BOOL cooperative_mode = TRUE;
25538 #ifndef MULTIPLE_HEAPS
25539     const int thread = heap_number;
25540 #endif //!MULTIPLE_HEAPS
25541
25542     dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
25543
25544     assert (settings.concurrent);
25545
25546 #ifdef TIME_GC
25547     unsigned start;
25548     unsigned finish;
25549     start = GetCycleCount32();
25550 #endif //TIME_GC
25551
25552 #ifdef FFIND_OBJECT
25553     if (gen0_must_clear_bricks > 0)
25554         gen0_must_clear_bricks--;
25555 #endif //FFIND_OBJECT
25556
25557     background_soh_alloc_count = 0;
25558     background_loh_alloc_count = 0;
25559     bgc_overflow_count = 0;
25560
25561     bpromoted_bytes (heap_number) = 0;
25562     static uint32_t num_sizedrefs = 0;
25563
25564     background_min_overflow_address = MAX_PTR;
25565     background_max_overflow_address = 0;
25566     background_min_soh_overflow_address = MAX_PTR;
25567     background_max_soh_overflow_address = 0;
25568     processed_soh_overflow_p = FALSE;
25569
25570     {
25571         //set up the mark lists from g_mark_list
25572         assert (g_mark_list);
25573         mark_list = g_mark_list;
25574         //dont use the mark list for full gc
25575         //because multiple segments are more complex to handle and the list
25576         //is likely to overflow
25577         mark_list_end = &mark_list [0];
25578         mark_list_index = &mark_list [0];
25579
25580         c_mark_list_index = 0;
25581
25582         shigh = (uint8_t*) 0;
25583         slow  = MAX_PTR;
25584
25585         generation*   gen = generation_of (max_generation);
25586
25587         dprintf(3,("BGC: stack marking"));
25588         sc.concurrent = TRUE;
25589
25590         GCScan::GcScanRoots(background_promote_callback,
25591                                 max_generation, max_generation,
25592                                 &sc);
25593     }
25594
25595     {
25596         dprintf(3,("BGC: finalization marking"));
25597         finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
25598     }
25599
25600     size_t total_loh_size = generation_size (max_generation + 1);
25601     bgc_begin_loh_size = total_loh_size;
25602     bgc_alloc_spin_loh = 0;
25603     bgc_loh_size_increased = 0;
25604     bgc_loh_allocated_in_free = 0;
25605     size_t total_soh_size = generation_sizes (generation_of (max_generation));
25606
25607     dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25608
25609     {
25610         //concurrent_print_time_delta ("copying stack roots");
25611         concurrent_print_time_delta ("CS");
25612
25613         FIRE_EVENT(BGC1stNonConEnd);
25614
25615         expanded_in_fgc = FALSE;
25616         saved_overflow_ephemeral_seg = 0;
25617         current_bgc_state = bgc_reset_ww;
25618
25619         // we don't need a join here - just whichever thread that gets here
25620         // first can change the states and call restart_vm.
25621         // this is not true - we can't let the EE run when we are scanning stack.
25622         // since we now allow reset ww to run concurrently and have a join for it,
25623         // we can do restart ee on the 1st thread that got here. Make sure we handle the 
25624         // sizedref handles correctly.
25625 #ifdef MULTIPLE_HEAPS
25626         bgc_t_join.join(this, gc_join_restart_ee);
25627         if (bgc_t_join.joined())
25628 #endif //MULTIPLE_HEAPS
25629         {
25630 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25631             // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset
25632             // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while
25633             // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below.
25634 #ifdef WRITE_WATCH
25635             concurrent_print_time_delta ("CRWW begin");
25636
25637 #ifdef MULTIPLE_HEAPS
25638             for (int i = 0; i < n_heaps; i++)
25639             {
25640                 g_heaps[i]->reset_write_watch (FALSE);
25641             }
25642 #else
25643             reset_write_watch (FALSE);
25644 #endif //MULTIPLE_HEAPS
25645
25646             concurrent_print_time_delta ("CRWW");
25647 #endif //WRITE_WATCH
25648 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25649
25650             num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
25651
25652             // this c_write is not really necessary because restart_vm
25653             // has an instruction that will flush the cpu cache (interlocked
25654             // or whatever) but we don't want to rely on that.
25655             dprintf (BGC_LOG, ("setting cm_in_progress"));
25656             c_write (cm_in_progress, TRUE);
25657
25658             //restart all thread, doing the marking from the array
25659             assert (dont_restart_ee_p);
25660             dont_restart_ee_p = FALSE;
25661
25662             restart_vm();
25663             GCToOSInterface::YieldThread (0);
25664 #ifdef MULTIPLE_HEAPS
25665             dprintf(3, ("Starting all gc threads for gc"));
25666             bgc_t_join.restart();
25667 #endif //MULTIPLE_HEAPS
25668         }
25669
25670 #ifdef MULTIPLE_HEAPS
25671         bgc_t_join.join(this, gc_join_after_reset);
25672         if (bgc_t_join.joined())
25673 #endif //MULTIPLE_HEAPS
25674         {
25675             disable_preemptive (true);
25676
25677 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25678             // When software write watch is enabled, resetting write watch is done while the runtime is suspended above. The
25679             // post-reset call to revisit_written_pages is only necessary for concurrent reset_write_watch, to discard dirtied
25680             // pages during the concurrent reset.
25681
25682 #ifdef WRITE_WATCH
25683             concurrent_print_time_delta ("CRWW begin");
25684
25685 #ifdef MULTIPLE_HEAPS
25686             for (int i = 0; i < n_heaps; i++)
25687             {
25688                 g_heaps[i]->reset_write_watch (TRUE);
25689             }
25690 #else
25691             reset_write_watch (TRUE);
25692 #endif //MULTIPLE_HEAPS
25693
25694             concurrent_print_time_delta ("CRWW");
25695 #endif //WRITE_WATCH
25696
25697 #ifdef MULTIPLE_HEAPS
25698             for (int i = 0; i < n_heaps; i++)
25699             {
25700                 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
25701             }
25702 #else
25703             revisit_written_pages (TRUE, TRUE);
25704 #endif //MULTIPLE_HEAPS
25705
25706             concurrent_print_time_delta ("CRW");
25707 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25708
25709 #ifdef MULTIPLE_HEAPS
25710             for (int i = 0; i < n_heaps; i++)
25711             {
25712                 g_heaps[i]->current_bgc_state = bgc_mark_handles;
25713             }
25714 #else
25715             current_bgc_state = bgc_mark_handles;
25716 #endif //MULTIPLE_HEAPS
25717
25718             current_c_gc_state = c_gc_state_marking;
25719
25720             enable_preemptive ();
25721
25722 #ifdef MULTIPLE_HEAPS
25723             dprintf(3, ("Joining BGC threads after resetting writewatch"));
25724             bgc_t_join.restart();
25725 #endif //MULTIPLE_HEAPS
25726         }
25727
25728         disable_preemptive (true);
25729
25730         if (num_sizedrefs > 0)
25731         {
25732             GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
25733
25734             enable_preemptive ();
25735
25736 #ifdef MULTIPLE_HEAPS
25737             bgc_t_join.join(this, gc_join_scan_sizedref_done);
25738             if (bgc_t_join.joined())
25739             {
25740                 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
25741                 bgc_t_join.restart();
25742             }
25743 #endif //MULTIPLE_HEAPS
25744
25745             disable_preemptive (true);
25746         }
25747
25748         dprintf (3,("BGC: handle table marking"));
25749         GCScan::GcScanHandles(background_promote,
25750                                   max_generation, max_generation,
25751                                   &sc);
25752         //concurrent_print_time_delta ("concurrent marking handle table");
25753         concurrent_print_time_delta ("CRH");
25754
25755         current_bgc_state = bgc_mark_stack;
25756         dprintf (2,("concurrent draining mark list"));
25757         background_drain_mark_list (thread);
25758         //concurrent_print_time_delta ("concurrent marking stack roots");
25759         concurrent_print_time_delta ("CRS");
25760
25761         dprintf (2,("concurrent revisiting dirtied pages"));
25762         revisit_written_pages (TRUE);
25763         revisit_written_pages (TRUE);
25764         //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
25765         concurrent_print_time_delta ("CRre");
25766
25767         enable_preemptive ();
25768
25769 #ifdef MULTIPLE_HEAPS
25770         bgc_t_join.join(this, gc_join_concurrent_overflow);
25771         if (bgc_t_join.joined())
25772         {
25773             uint8_t* all_heaps_max = 0;
25774             uint8_t* all_heaps_min = MAX_PTR;
25775             int i;
25776             for (i = 0; i < n_heaps; i++)
25777             {
25778                 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix", 
25779                     i,
25780                     g_heaps[i]->background_max_overflow_address,
25781                     g_heaps[i]->background_min_overflow_address));
25782                 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25783                     all_heaps_max = g_heaps[i]->background_max_overflow_address;
25784                 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25785                     all_heaps_min = g_heaps[i]->background_min_overflow_address;
25786             }
25787             for (i = 0; i < n_heaps; i++)
25788             {
25789                 g_heaps[i]->background_max_overflow_address = all_heaps_max;
25790                 g_heaps[i]->background_min_overflow_address = all_heaps_min;
25791             }
25792             dprintf(3, ("Starting all bgc threads after updating the overflow info"));
25793             bgc_t_join.restart();
25794         }
25795 #endif //MULTIPLE_HEAPS
25796
25797         disable_preemptive (true);
25798
25799         dprintf (2, ("before CRov count: %d", bgc_overflow_count));
25800         bgc_overflow_count = 0;
25801         background_process_mark_overflow (TRUE);
25802         dprintf (2, ("after CRov count: %d", bgc_overflow_count));
25803         bgc_overflow_count = 0;
25804         //concurrent_print_time_delta ("concurrent processing mark overflow");
25805         concurrent_print_time_delta ("CRov");
25806
25807         // Stop all threads, crawl all stacks and revisit changed pages.
25808         FIRE_EVENT(BGC1stConEnd);
25809
25810         dprintf (2, ("Stopping the EE"));
25811
25812         enable_preemptive ();
25813
25814 #ifdef MULTIPLE_HEAPS
25815         bgc_t_join.join(this, gc_join_suspend_ee);
25816         if (bgc_t_join.joined())
25817         {
25818             bgc_threads_sync_event.Reset();
25819
25820             dprintf(3, ("Joining BGC threads for non concurrent final marking"));
25821             bgc_t_join.restart();
25822         }
25823 #endif //MULTIPLE_HEAPS
25824
25825         if (heap_number == 0)
25826         {
25827             enter_spin_lock (&gc_lock);
25828
25829             bgc_suspend_EE ();
25830             //suspend_EE ();
25831             bgc_threads_sync_event.Set();
25832         }
25833         else
25834         {
25835             bgc_threads_sync_event.Wait(INFINITE, FALSE);
25836             dprintf (2, ("bgc_threads_sync_event is signalled"));
25837         }
25838
25839         assert (settings.concurrent);
25840         assert (settings.condemned_generation == max_generation);
25841
25842         dprintf (2, ("clearing cm_in_progress"));
25843         c_write (cm_in_progress, FALSE);
25844
25845         bgc_alloc_lock->check();
25846
25847         current_bgc_state = bgc_final_marking;
25848
25849         //concurrent_print_time_delta ("concurrent marking ended");
25850         concurrent_print_time_delta ("CR");
25851
25852         FIRE_EVENT(BGC2ndNonConBegin);
25853
25854         mark_absorb_new_alloc();
25855
25856         // We need a join here 'cause find_object would complain if the gen0
25857         // bricks of another heap haven't been fixed up. So we need to make sure
25858         // that every heap's gen0 bricks are fixed up before we proceed.
25859 #ifdef MULTIPLE_HEAPS
25860         bgc_t_join.join(this, gc_join_after_absorb);
25861         if (bgc_t_join.joined())
25862         {
25863             dprintf(3, ("Joining BGC threads after absorb"));
25864             bgc_t_join.restart();
25865         }
25866 #endif //MULTIPLE_HEAPS
25867
25868         // give VM a chance to do work
25869         GCToEEInterface::GcBeforeBGCSweepWork();
25870
25871         //reset the flag, indicating that the EE no longer expect concurrent
25872         //marking
25873         sc.concurrent = FALSE;
25874
25875         total_loh_size = generation_size (max_generation + 1);
25876         total_soh_size = generation_sizes (generation_of (max_generation));
25877
25878         dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25879
25880         dprintf (2, ("nonconcurrent marking stack roots"));
25881         GCScan::GcScanRoots(background_promote,
25882                                 max_generation, max_generation,
25883                                 &sc);
25884         //concurrent_print_time_delta ("nonconcurrent marking stack roots");
25885         concurrent_print_time_delta ("NRS");
25886
25887 //        finalize_queue->EnterFinalizeLock();
25888         finalize_queue->GcScanRoots(background_promote, heap_number, 0);
25889 //        finalize_queue->LeaveFinalizeLock();
25890
25891         dprintf (2, ("nonconcurrent marking handle table"));
25892         GCScan::GcScanHandles(background_promote,
25893                                   max_generation, max_generation,
25894                                   &sc);
25895         //concurrent_print_time_delta ("nonconcurrent marking handle table");
25896         concurrent_print_time_delta ("NRH");
25897
25898         dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
25899         revisit_written_pages (FALSE);
25900         //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
25901         concurrent_print_time_delta ("NRre LOH");
25902
25903 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25904 #ifdef MULTIPLE_HEAPS
25905         bgc_t_join.join(this, gc_join_disable_software_write_watch);
25906         if (bgc_t_join.joined())
25907 #endif // MULTIPLE_HEAPS
25908         {
25909             // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to
25910             // avoid further perf penalty after the runtime is restarted
25911             SoftwareWriteWatch::DisableForGCHeap();
25912
25913 #ifdef MULTIPLE_HEAPS
25914             dprintf(3, ("Restarting BGC threads after disabling software write watch"));
25915             bgc_t_join.restart();
25916 #endif // MULTIPLE_HEAPS
25917         }
25918 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25919
25920         dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
25921         bgc_overflow_count = 0;
25922
25923         // Dependent handles need to be scanned with a special algorithm (see the header comment on
25924         // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
25925         // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
25926         // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
25927         // The call to background_scan_dependent_handles is what will cycle through more iterations if
25928         // required and will also perform processing of any mark stack overflow once the dependent handle
25929         // table has been fully promoted.
25930         dprintf (2, ("1st dependent handle scan and process mark overflow"));
25931         GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
25932         background_scan_dependent_handles (&sc);
25933         //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
25934         concurrent_print_time_delta ("NR 1st Hov");
25935
25936         dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
25937         bgc_overflow_count = 0;
25938
25939 #ifdef MULTIPLE_HEAPS
25940         bgc_t_join.join(this, gc_join_null_dead_short_weak);
25941         if (bgc_t_join.joined())
25942 #endif //MULTIPLE_HEAPS
25943         {
25944             GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
25945
25946 #ifdef MULTIPLE_HEAPS
25947             dprintf(3, ("Joining BGC threads for short weak handle scan"));
25948             bgc_t_join.restart();
25949 #endif //MULTIPLE_HEAPS
25950         }
25951
25952         // null out the target of short weakref that were not promoted.
25953         GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
25954
25955         //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
25956         concurrent_print_time_delta ("NR GcShortWeakPtrScan");
25957     }
25958
25959     {
25960 #ifdef MULTIPLE_HEAPS
25961         bgc_t_join.join(this, gc_join_scan_finalization);
25962         if (bgc_t_join.joined())
25963         {
25964             dprintf(3, ("Joining BGC threads for finalization"));
25965             bgc_t_join.restart();
25966         }
25967 #endif //MULTIPLE_HEAPS
25968
25969         //Handle finalization.
25970         dprintf(3,("Marking finalization data"));
25971         //concurrent_print_time_delta ("bgc joined to mark finalization");
25972         concurrent_print_time_delta ("NRj");
25973
25974 //        finalize_queue->EnterFinalizeLock();
25975         finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
25976 //        finalize_queue->LeaveFinalizeLock();
25977
25978         concurrent_print_time_delta ("NRF");
25979     }
25980
25981     dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
25982     bgc_overflow_count = 0;
25983
25984     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
25985     // for finalization. As before background_scan_dependent_handles will also process any mark stack
25986     // overflow.
25987     dprintf (2, ("2nd dependent handle scan and process mark overflow"));
25988     background_scan_dependent_handles (&sc);
25989     //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
25990     concurrent_print_time_delta ("NR 2nd Hov");
25991
25992 #ifdef MULTIPLE_HEAPS
25993     bgc_t_join.join(this, gc_join_null_dead_long_weak);
25994     if (bgc_t_join.joined())
25995     {
25996         dprintf(2, ("Joining BGC threads for weak pointer deletion"));
25997         bgc_t_join.restart();
25998     }
25999 #endif //MULTIPLE_HEAPS
26000
26001     // null out the target of long weakref that were not promoted.
26002     GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
26003     concurrent_print_time_delta ("NR GcWeakPtrScan");
26004
26005 #ifdef MULTIPLE_HEAPS
26006     bgc_t_join.join(this, gc_join_null_dead_syncblk);
26007     if (bgc_t_join.joined())
26008 #endif //MULTIPLE_HEAPS
26009     {
26010         dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
26011         // scan for deleted entries in the syncblk cache
26012         GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
26013         concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
26014 #ifdef MULTIPLE_HEAPS
26015         dprintf(2, ("Starting BGC threads for end of background mark phase"));
26016         bgc_t_join.restart();
26017 #endif //MULTIPLE_HEAPS
26018     }
26019
26020     gen0_bricks_cleared = FALSE;
26021
26022     dprintf (2, ("end of bgc mark: loh: %d, soh: %d", 
26023                  generation_size (max_generation + 1), 
26024                  generation_sizes (generation_of (max_generation))));
26025
26026     for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
26027     {
26028         generation* gen = generation_of (gen_idx);
26029         dynamic_data* dd = dynamic_data_of (gen_idx);
26030         dd_begin_data_size (dd) = generation_size (gen_idx) - 
26031                                    (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
26032                                    Align (size (generation_allocation_start (gen)));
26033         dd_survived_size (dd) = 0;
26034         dd_pinned_survived_size (dd) = 0;
26035         dd_artificial_pinned_survived_size (dd) = 0;
26036         dd_added_pinned_size (dd) = 0;
26037     }
26038
26039     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26040     PREFIX_ASSUME(seg != NULL);
26041
26042     while (seg)
26043     {
26044         seg->flags &= ~heap_segment_flags_swept;
26045
26046         if (heap_segment_allocated (seg) == heap_segment_mem (seg))
26047         {
26048             // This can't happen...
26049             FATAL_GC_ERROR();
26050         }
26051
26052         if (seg == ephemeral_heap_segment)
26053         {
26054             heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
26055         }
26056         else
26057         {
26058             heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
26059         }
26060
26061         dprintf (2, ("seg %Ix background allocated is %Ix", 
26062                       heap_segment_mem (seg), 
26063                       heap_segment_background_allocated (seg)));
26064         seg = heap_segment_next_rw (seg);
26065     }
26066
26067     // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
26068     // we can't let the user code consume the left over parts in these alloc contexts.
26069     repair_allocation_contexts (FALSE);
26070
26071 #ifdef TIME_GC
26072         finish = GetCycleCount32();
26073         mark_time = finish - start;
26074 #endif //TIME_GC
26075
26076     dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d", 
26077         generation_free_list_space (generation_of (max_generation)), 
26078         generation_free_obj_space (generation_of (max_generation))));
26079
26080     dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
26081 }
26082
26083 void
26084 gc_heap::suspend_EE ()
26085 {
26086     dprintf (2, ("suspend_EE"));
26087 #ifdef MULTIPLE_HEAPS
26088     gc_heap* hp = gc_heap::g_heaps[0];
26089     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26090 #else
26091     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26092 #endif //MULTIPLE_HEAPS
26093 }
26094
26095 #ifdef MULTIPLE_HEAPS
26096 void
26097 gc_heap::bgc_suspend_EE ()
26098 {
26099     for (int i = 0; i < n_heaps; i++)
26100     {
26101         gc_heap::g_heaps[i]->reset_gc_done();
26102     }
26103     gc_started = TRUE;
26104     dprintf (2, ("bgc_suspend_EE"));
26105     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26106
26107     gc_started = FALSE;
26108     for (int i = 0; i < n_heaps; i++)
26109     {
26110         gc_heap::g_heaps[i]->set_gc_done();
26111     }
26112 }
26113 #else
26114 void
26115 gc_heap::bgc_suspend_EE ()
26116 {
26117     reset_gc_done();
26118     gc_started = TRUE;
26119     dprintf (2, ("bgc_suspend_EE"));
26120     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26121     gc_started = FALSE;
26122     set_gc_done();
26123 }
26124 #endif //MULTIPLE_HEAPS
26125
26126 void
26127 gc_heap::restart_EE ()
26128 {
26129     dprintf (2, ("restart_EE"));
26130 #ifdef MULTIPLE_HEAPS
26131     GCToEEInterface::RestartEE(FALSE);
26132 #else
26133     GCToEEInterface::RestartEE(FALSE);
26134 #endif //MULTIPLE_HEAPS
26135 }
26136
26137 inline uint8_t* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
26138 {
26139     if (concurrent_p)
26140     {
26141         uint8_t* end = ((seg == ephemeral_heap_segment) ?
26142                      generation_allocation_start (generation_of (max_generation-1)) :
26143                      heap_segment_allocated (seg));
26144         return align_lower_page (end);
26145     }
26146     else 
26147     {
26148         return heap_segment_allocated (seg);
26149     }
26150 }
26151
26152 void gc_heap::revisit_written_page (uint8_t* page,
26153                                     uint8_t* end,
26154                                     BOOL concurrent_p,
26155                                     heap_segment* seg,
26156                                     uint8_t*& last_page,
26157                                     uint8_t*& last_object,
26158                                     BOOL large_objects_p,
26159                                     size_t& num_marked_objects)
26160 {
26161     UNREFERENCED_PARAMETER(seg);
26162
26163     uint8_t*   start_address = page;
26164     uint8_t*   o             = 0;
26165     int align_const = get_alignment_constant (!large_objects_p);
26166     uint8_t* high_address = end;
26167     uint8_t* current_lowest_address = background_saved_lowest_address;
26168     uint8_t* current_highest_address = background_saved_highest_address;
26169     BOOL no_more_loop_p = FALSE;
26170
26171     THREAD_FROM_HEAP;
26172 #ifndef MULTIPLE_HEAPS
26173     const int thread = heap_number;
26174 #endif //!MULTIPLE_HEAPS
26175
26176     if (large_objects_p)
26177     {
26178         o = last_object;
26179     }
26180     else
26181     {
26182         if (((last_page + WRITE_WATCH_UNIT_SIZE) == page)
26183             || (start_address <= last_object))
26184         {
26185             o = last_object;
26186         }
26187         else
26188         {
26189             o = find_first_object (start_address, last_object);
26190             // We can visit the same object again, but on a different page.
26191             assert (o >= last_object);
26192         }
26193     }
26194
26195     dprintf (3,("page %Ix start: %Ix, %Ix[ ",
26196                (size_t)page, (size_t)o,
26197                (size_t)(min (high_address, page + WRITE_WATCH_UNIT_SIZE))));
26198
26199     while (o < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26200     {
26201         size_t s;
26202
26203         if (concurrent_p && large_objects_p)
26204         {
26205             bgc_alloc_lock->bgc_mark_set (o);
26206
26207             if (((CObjectHeader*)o)->IsFree())
26208             {
26209                 s = unused_array_size (o);
26210             }
26211             else
26212             {
26213                 s = size (o);
26214             }
26215         }
26216         else
26217         {
26218             s = size (o);
26219         }
26220
26221         dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
26222
26223         assert (Align (s) >= Align (min_obj_size));
26224
26225         uint8_t* next_o =  o + Align (s, align_const);
26226
26227         if (next_o >= start_address) 
26228         {
26229 #ifdef MULTIPLE_HEAPS
26230             if (concurrent_p)
26231             {
26232                 // We set last_object here for SVR BGC here because SVR BGC has more than 
26233                 // one GC thread. When we have more than one GC thread we would run into this 
26234                 // situation if we skipped unmarked objects:
26235                 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it 
26236                 // for revisit. 
26237                 // bgc thread 2 marks X and all its current children.
26238                 // user thread comes along and dirties more (and later) pages in X.
26239                 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
26240                 // on them because it had already skipped X. We need to detect that this object is now
26241                 // marked and mark the children on the dirtied pages.
26242                 // In the future if we have less BGC threads than we have heaps we should add
26243                 // the check to the number of BGC threads.
26244                 last_object = o;
26245             }
26246 #endif //MULTIPLE_HEAPS
26247
26248             if (contain_pointers (o) &&
26249                 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
26250                 background_marked (o)))
26251             {
26252                 dprintf (3, ("going through %Ix", (size_t)o));
26253                 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
26254                                     if ((uint8_t*)poo >= min (high_address, page + WRITE_WATCH_UNIT_SIZE))
26255                                     {
26256                                         no_more_loop_p = TRUE;
26257                                         goto end_limit;
26258                                     }
26259                                     uint8_t* oo = *poo;
26260
26261                                     num_marked_objects++;
26262                                     background_mark_object (oo THREAD_NUMBER_ARG);
26263                                 );
26264             }
26265             else if (
26266                 concurrent_p &&
26267 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // see comment below
26268                 large_objects_p &&
26269 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26270                 ((CObjectHeader*)o)->IsFree() &&
26271                 (next_o > min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26272             {
26273                 // We need to not skip the object here because of this corner scenario:
26274                 // A large object was being allocated during BGC mark so we first made it 
26275                 // into a free object, then cleared its memory. In this loop we would detect
26276                 // that it's a free object which normally we would skip. But by the next time
26277                 // we call GetWriteWatch we could still be on this object and the object had
26278                 // been made into a valid object and some of its memory was changed. We need
26279                 // to be sure to process those written pages so we can't skip the object just
26280                 // yet.
26281                 //
26282                 // Similarly, when using software write watch, don't advance last_object when
26283                 // the current object is a free object that spans beyond the current page or
26284                 // high_address. Software write watch acquires gc_lock before the concurrent
26285                 // GetWriteWatch() call during revisit_written_pages(). A foreground GC may
26286                 // happen at that point and allocate from this free region, so when
26287                 // revisit_written_pages() continues, it cannot skip now-valid objects in this
26288                 // region.
26289                 no_more_loop_p = TRUE;
26290                 goto end_limit;                
26291             }
26292         }
26293 end_limit:
26294         if (concurrent_p && large_objects_p)
26295         {
26296             bgc_alloc_lock->bgc_mark_done ();
26297         }
26298         if (no_more_loop_p)
26299         {
26300             break;
26301         }
26302         o = next_o;
26303     }
26304
26305 #ifdef MULTIPLE_HEAPS
26306     if (concurrent_p)
26307     {
26308         assert (last_object < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)));
26309     }
26310     else
26311 #endif //MULTIPLE_HEAPS
26312     {
26313         last_object = o;
26314     }
26315
26316     dprintf (3,("Last object: %Ix", (size_t)last_object));
26317     last_page = align_write_watch_lower_page (o);
26318 }
26319
26320 // When reset_only_p is TRUE, we should only reset pages that are in range
26321 // because we need to consider the segments or part of segments that were
26322 // allocated out of range all live.
26323 void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
26324 {
26325 #ifdef WRITE_WATCH
26326     if (concurrent_p && !reset_only_p)
26327     {
26328         current_bgc_state = bgc_revisit_soh;
26329     }
26330
26331     size_t total_dirtied_pages = 0;
26332     size_t total_marked_objects = 0;
26333
26334     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26335
26336     PREFIX_ASSUME(seg != NULL);
26337
26338     bool reset_watch_state = !!concurrent_p;
26339     bool is_runtime_suspended = !concurrent_p;
26340     BOOL small_object_segments = TRUE;
26341     int align_const = get_alignment_constant (small_object_segments);
26342
26343     while (1)
26344     {
26345         if (seg == 0)
26346         {
26347             if (small_object_segments)
26348             {
26349                 //switch to large segment
26350                 if (concurrent_p && !reset_only_p)
26351                 {
26352                     current_bgc_state = bgc_revisit_loh;
26353                 }
26354
26355                 if (!reset_only_p)
26356                 {
26357                     dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26358                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26359                     concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
26360                     total_dirtied_pages = 0;
26361                     total_marked_objects = 0;
26362                 }
26363
26364                 small_object_segments = FALSE;
26365                 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
26366
26367                 dprintf (3, ("now revisiting large object segments"));
26368                 align_const = get_alignment_constant (small_object_segments);
26369                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
26370
26371                 PREFIX_ASSUME(seg != NULL);
26372
26373                 continue;
26374             }
26375             else
26376             {
26377                 if (reset_only_p)
26378                 {
26379                     dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
26380                 } 
26381                 else
26382                 {
26383                     dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26384                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26385                 }
26386                 break;
26387             }
26388         }
26389         uint8_t* base_address = (uint8_t*)heap_segment_mem (seg);
26390         //we need to truncate to the base of the page because
26391         //some newly allocated could exist beyond heap_segment_allocated
26392         //and if we reset the last page write watch status,
26393         // they wouldn't be guaranteed to be visited -> gc hole.
26394         uintptr_t bcount = array_size;
26395         uint8_t* last_page = 0;
26396         uint8_t* last_object = heap_segment_mem (seg);
26397         uint8_t* high_address = 0;
26398
26399         BOOL skip_seg_p = FALSE;
26400
26401         if (reset_only_p)
26402         {
26403             if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
26404                 (heap_segment_reserved (seg) <= background_saved_highest_address))
26405             {
26406                 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number, 
26407                     heap_segment_mem (seg), heap_segment_reserved (seg)));
26408                 skip_seg_p = TRUE;
26409             }
26410         }
26411
26412         if (!skip_seg_p)
26413         {
26414             dprintf (3, ("looking at seg %Ix", (size_t)last_object));
26415
26416             if (reset_only_p)
26417             {
26418                 base_address = max (base_address, background_saved_lowest_address);
26419                 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
26420             }
26421
26422             dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address, 
26423                 heap_segment_mem (seg), heap_segment_reserved (seg)));
26424
26425
26426             while (1)
26427             {
26428                 if (reset_only_p)
26429                 {
26430                     high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
26431                     high_address = min (high_address, background_saved_highest_address);
26432                 }
26433                 else
26434                 {
26435                     high_address = high_page (seg, concurrent_p);
26436                 }
26437
26438                 if ((base_address < high_address) &&
26439                     (bcount >= array_size))
26440                 {
26441                     ptrdiff_t region_size = high_address - base_address;
26442                     dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
26443
26444 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26445                     // When the runtime is not suspended, it's possible for the table to be resized concurrently with the scan
26446                     // for dirty pages below. Prevent that by synchronizing with grow_brick_card_tables(). When the runtime is
26447                     // suspended, it's ok to scan for dirty pages concurrently from multiple background GC threads for disjoint
26448                     // memory regions.
26449                     if (!is_runtime_suspended)
26450                     {
26451                         enter_spin_lock(&gc_lock);
26452                     }
26453 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26454
26455                     get_write_watch_for_gc_heap (reset_watch_state, base_address, region_size,
26456                                                  (void**)background_written_addresses,
26457                                                  &bcount, is_runtime_suspended);
26458
26459 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26460                     if (!is_runtime_suspended)
26461                     {
26462                         leave_spin_lock(&gc_lock);
26463                     }
26464 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26465
26466                     if (bcount != 0)
26467                     {
26468                         total_dirtied_pages += bcount;
26469
26470                         dprintf (3, ("Found %d pages [%Ix, %Ix[", 
26471                                         bcount, (size_t)base_address, (size_t)high_address));
26472                     }
26473
26474                     if (!reset_only_p)
26475                     {
26476                         for (unsigned i = 0; i < bcount; i++)
26477                         {
26478                             uint8_t* page = (uint8_t*)background_written_addresses[i];
26479                             dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i, 
26480                                 (size_t)page, (size_t)high_address));
26481                             if (page < high_address)
26482                             {
26483                                 //search for marked objects in the page
26484                                 revisit_written_page (page, high_address, concurrent_p,
26485                                                     seg, last_page, last_object,
26486                                                     !small_object_segments,
26487                                                     total_marked_objects);
26488                             }
26489                             else
26490                             {
26491                                 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
26492                                 assert (!"page shouldn't have exceeded limit");
26493                             }
26494                         }
26495                     }
26496
26497                     if (bcount >= array_size){
26498                         base_address = background_written_addresses [array_size-1] + WRITE_WATCH_UNIT_SIZE;
26499                         bcount = array_size;
26500                     }
26501                 }
26502                 else
26503                 {
26504                     break;
26505                 }
26506             }
26507         }
26508
26509         seg = heap_segment_next_rw (seg);
26510     }
26511
26512 #endif //WRITE_WATCH
26513 }
26514
26515 void gc_heap::background_grow_c_mark_list()
26516 {
26517     assert (c_mark_list_index >= c_mark_list_length);
26518     BOOL should_drain_p = FALSE;
26519     THREAD_FROM_HEAP;
26520 #ifndef MULTIPLE_HEAPS
26521     const int thread = heap_number;
26522 #endif //!MULTIPLE_HEAPS
26523
26524     dprintf (2, ("stack copy buffer overflow"));
26525     uint8_t** new_c_mark_list = 0;
26526     {
26527         FAULT_NOT_FATAL();
26528         if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (uint8_t*))))
26529         {
26530             should_drain_p = TRUE;
26531         }
26532         else
26533         {
26534             new_c_mark_list = new (nothrow) uint8_t*[c_mark_list_length*2];
26535             if (new_c_mark_list == 0)
26536             {
26537                 should_drain_p = TRUE;
26538             }
26539         }
26540     }
26541     if (should_drain_p)
26542
26543     {
26544         dprintf (2, ("No more memory for the stacks copy, draining.."));
26545         //drain the list by marking its elements
26546         background_drain_mark_list (thread);
26547     }
26548     else
26549     {
26550         assert (new_c_mark_list);
26551         memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(uint8_t*));
26552         c_mark_list_length = c_mark_list_length*2;
26553         delete c_mark_list;
26554         c_mark_list = new_c_mark_list;
26555     }
26556 }
26557
26558 void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
26559                                   uint32_t flags)
26560 {
26561     UNREFERENCED_PARAMETER(sc);
26562     //in order to save space on the array, mark the object,
26563     //knowing that it will be visited later
26564     assert (settings.concurrent);
26565
26566     THREAD_NUMBER_FROM_CONTEXT;
26567 #ifndef MULTIPLE_HEAPS
26568     const int thread = 0;
26569 #endif //!MULTIPLE_HEAPS
26570
26571     uint8_t* o = (uint8_t*)*ppObject;
26572
26573     if (o == 0)
26574         return;
26575
26576     HEAP_FROM_THREAD;
26577
26578     gc_heap* hp = gc_heap::heap_of (o);
26579
26580     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
26581     {
26582         return;
26583     }
26584
26585 #ifdef INTERIOR_POINTERS
26586     if (flags & GC_CALL_INTERIOR)
26587     {
26588         o = hp->find_object (o, hp->background_saved_lowest_address);
26589         if (o == 0)
26590             return;
26591     }
26592 #endif //INTERIOR_POINTERS
26593
26594 #ifdef FEATURE_CONSERVATIVE_GC
26595     // For conservative GC, a value on stack may point to middle of a free object.
26596     // In this case, we don't need to promote the pointer.
26597     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
26598     {
26599         return;
26600     }
26601 #endif //FEATURE_CONSERVATIVE_GC
26602
26603 #ifdef _DEBUG
26604     ((CObjectHeader*)o)->Validate();
26605 #endif //_DEBUG
26606
26607     dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
26608     if (o && (size (o) > LARGE_OBJECT_SIZE))
26609     {
26610         dprintf (3, ("Brc %Ix", (size_t)o));
26611     }
26612
26613     if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
26614     {
26615         hpt->background_grow_c_mark_list();
26616     }
26617     dprintf (3, ("pushing %08x into mark_list", (size_t)o));
26618     hpt->c_mark_list [hpt->c_mark_list_index++] = o;
26619
26620     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);
26621 }
26622
26623 void gc_heap::mark_absorb_new_alloc()
26624 {
26625     fix_allocation_contexts (FALSE);
26626     
26627     gen0_bricks_cleared = FALSE;
26628
26629     clear_gen0_bricks();
26630 }
26631
26632 BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
26633 {
26634     BOOL success = FALSE;
26635     BOOL thread_created = FALSE;
26636     dprintf (2, ("Preparing gc thread"));
26637     gh->bgc_threads_timeout_cs.Enter();
26638     if (!(gh->bgc_thread_running))
26639     {
26640         dprintf (2, ("GC thread not runnning"));
26641         if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
26642         {
26643             success = TRUE;
26644             thread_created = TRUE;
26645         }
26646     }
26647     else
26648     {
26649         dprintf (3, ("GC thread already running"));
26650         success = TRUE;
26651     }
26652     gh->bgc_threads_timeout_cs.Leave();
26653
26654     if(thread_created)
26655         FIRE_EVENT(GCCreateConcurrentThread_V1);
26656
26657     return success;
26658 }
26659
26660 BOOL gc_heap::create_bgc_thread(gc_heap* gh)
26661 {
26662     assert (background_gc_done_event.IsValid());
26663
26664     //dprintf (2, ("Creating BGC thread"));
26665
26666     gh->bgc_thread_running = GCToEEInterface::CreateThread(gh->bgc_thread_stub, gh, true, ".NET Background GC");
26667     return gh->bgc_thread_running;
26668 }
26669
26670 BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
26671 {
26672     BOOL ret = FALSE;
26673     dprintf (3, ("Creating concurrent GC thread for the first time"));
26674     if (!background_gc_done_event.CreateManualEventNoThrow(TRUE))
26675     {
26676         goto cleanup;
26677     }
26678     if (!bgc_threads_sync_event.CreateManualEventNoThrow(FALSE))
26679     {
26680         goto cleanup;
26681     }
26682     if (!ee_proceed_event.CreateAutoEventNoThrow(FALSE))
26683     {
26684         goto cleanup;
26685     }
26686     if (!bgc_start_event.CreateManualEventNoThrow(FALSE))
26687     {
26688         goto cleanup;
26689     }
26690
26691 #ifdef MULTIPLE_HEAPS
26692     bgc_t_join.init (number_of_heaps, join_flavor_bgc);
26693 #else
26694     UNREFERENCED_PARAMETER(number_of_heaps);
26695 #endif //MULTIPLE_HEAPS
26696
26697     ret = TRUE;
26698
26699 cleanup:
26700
26701     if (!ret)
26702     {
26703         if (background_gc_done_event.IsValid())
26704         {
26705             background_gc_done_event.CloseEvent();
26706         }
26707         if (bgc_threads_sync_event.IsValid())
26708         {
26709             bgc_threads_sync_event.CloseEvent();
26710         }
26711         if (ee_proceed_event.IsValid())
26712         {
26713             ee_proceed_event.CloseEvent();
26714         }
26715         if (bgc_start_event.IsValid())
26716         {
26717             bgc_start_event.CloseEvent();
26718         }
26719     }
26720
26721     return ret;
26722 }
26723
26724 BOOL gc_heap::create_bgc_thread_support()
26725 {
26726     BOOL ret = FALSE;
26727     uint8_t** parr;
26728     
26729     if (!gc_lh_block_event.CreateManualEventNoThrow(FALSE))
26730     {
26731         goto cleanup;
26732     }
26733
26734     //needs to have room for enough smallest objects fitting on a page
26735     parr = new (nothrow) uint8_t*[1 + OS_PAGE_SIZE / MIN_OBJECT_SIZE];
26736     if (!parr)
26737     {
26738         goto cleanup;
26739     }
26740
26741     make_c_mark_list (parr);
26742
26743     ret = TRUE;
26744
26745 cleanup:
26746
26747     if (!ret)
26748     {
26749         if (gc_lh_block_event.IsValid())
26750         {
26751             gc_lh_block_event.CloseEvent();
26752         }
26753     }
26754
26755     return ret;
26756 }
26757
26758 int gc_heap::check_for_ephemeral_alloc()
26759 {
26760     int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
26761
26762     if (gen == -1)
26763     {
26764 #ifdef MULTIPLE_HEAPS
26765         for (int heap_index = 0; heap_index < n_heaps; heap_index++)
26766 #endif //MULTIPLE_HEAPS
26767         {
26768             for (int i = 0; i <= (max_generation - 1); i++)
26769             {
26770 #ifdef MULTIPLE_HEAPS
26771                 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
26772 #else
26773                 if (get_new_allocation (i) <= 0)
26774 #endif //MULTIPLE_HEAPS
26775                 {
26776                     gen = max (gen, i);
26777                 }
26778                 else
26779                     break;
26780             }
26781         }
26782     }
26783
26784     return gen;
26785 }
26786
26787 // Wait for gc to finish sequential part
26788 void gc_heap::wait_to_proceed()
26789 {
26790     assert (background_gc_done_event.IsValid());
26791     assert (bgc_start_event.IsValid());
26792
26793     user_thread_wait(&ee_proceed_event, FALSE);
26794 }
26795
26796 // Start a new concurrent gc
26797 void gc_heap::start_c_gc()
26798 {
26799     assert (background_gc_done_event.IsValid());
26800     assert (bgc_start_event.IsValid());
26801
26802 //Need to make sure that the gc thread is in the right place.
26803     background_gc_done_event.Wait(INFINITE, FALSE);
26804     background_gc_done_event.Reset();
26805     bgc_start_event.Set();
26806 }
26807
26808 void gc_heap::do_background_gc()
26809 {
26810     dprintf (2, ("starting a BGC"));
26811 #ifdef MULTIPLE_HEAPS
26812     for (int i = 0; i < n_heaps; i++)
26813     {
26814         g_heaps[i]->init_background_gc();
26815     }
26816 #else
26817     init_background_gc();
26818 #endif //MULTIPLE_HEAPS
26819     //start the background gc
26820     start_c_gc ();
26821
26822     //wait until we get restarted by the BGC.
26823     wait_to_proceed();
26824 }
26825
26826 void gc_heap::kill_gc_thread()
26827 {
26828     //assert (settings.concurrent == FALSE);
26829
26830     // We are doing a two-stage shutdown now.
26831     // In the first stage, we do minimum work, and call ExitProcess at the end.
26832     // In the secodn stage, we have the Loader lock and only one thread is
26833     // alive.  Hence we do not need to kill gc thread.
26834     background_gc_done_event.CloseEvent();
26835     gc_lh_block_event.CloseEvent();
26836     bgc_start_event.CloseEvent();
26837     bgc_threads_timeout_cs.Destroy();
26838     bgc_thread = 0;
26839     recursive_gc_sync::shutdown();
26840 }
26841
26842 void gc_heap::bgc_thread_function()
26843 {
26844     assert (background_gc_done_event.IsValid());
26845     assert (bgc_start_event.IsValid());
26846
26847     dprintf (3, ("gc_thread thread starting..."));
26848
26849     BOOL do_exit = FALSE;
26850
26851     bool cooperative_mode = true;
26852     bgc_thread_id.SetToCurrentThread();
26853     dprintf (1, ("bgc_thread_id is set to %x", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()));
26854     while (1)
26855     {
26856         // Wait for work to do...
26857         dprintf (3, ("bgc thread: waiting..."));
26858
26859         cooperative_mode = enable_preemptive ();
26860         //current_thread->m_fPreemptiveGCDisabled = 0;
26861
26862         uint32_t result = bgc_start_event.Wait(
26863 #ifdef _DEBUG
26864 #ifdef MULTIPLE_HEAPS
26865                                              INFINITE,
26866 #else
26867                                              2000,
26868 #endif //MULTIPLE_HEAPS
26869 #else //_DEBUG
26870 #ifdef MULTIPLE_HEAPS
26871                                              INFINITE,
26872 #else
26873                                              20000,
26874 #endif //MULTIPLE_HEAPS
26875 #endif //_DEBUG
26876             FALSE);
26877         dprintf (2, ("gc thread: finished waiting"));
26878
26879         // not calling disable_preemptive here 'cause we 
26880         // can't wait for GC complete here - RestartEE will be called 
26881         // when we've done the init work.
26882
26883         if (result == WAIT_TIMEOUT)
26884         {
26885             // Should join the bgc threads and terminate all of them
26886             // at once.
26887             dprintf (1, ("GC thread timeout"));
26888             bgc_threads_timeout_cs.Enter();
26889             if (!keep_bgc_threads_p)
26890             {
26891                 dprintf (2, ("GC thread exiting"));
26892                 bgc_thread_running = FALSE;
26893                 bgc_thread = 0;
26894                 bgc_thread_id.Clear();
26895                 do_exit = TRUE;
26896             }
26897             bgc_threads_timeout_cs.Leave();
26898             if (do_exit)
26899                 break;
26900             else
26901             {
26902                 dprintf (3, ("GC thread needed, not exiting"));
26903                 continue;
26904             }
26905         }
26906         // if we signal the thread with no concurrent work to do -> exit
26907         if (!settings.concurrent)
26908         {
26909             dprintf (3, ("no concurrent GC needed, exiting"));
26910             break;
26911         }
26912 #ifdef TRACE_GC
26913         //trace_gc = TRUE;
26914 #endif //TRACE_GC
26915         recursive_gc_sync::begin_background();
26916         dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d", 
26917             generation_free_list_space (generation_of (max_generation)),
26918             generation_free_obj_space (generation_of (max_generation)),
26919             dd_fragmentation (dynamic_data_of (max_generation))));
26920
26921         gc1();
26922
26923         current_bgc_state = bgc_not_in_process;
26924
26925 #ifdef TRACE_GC
26926         //trace_gc = FALSE;
26927 #endif //TRACE_GC
26928
26929         enable_preemptive ();
26930 #ifdef MULTIPLE_HEAPS
26931         bgc_t_join.join(this, gc_join_done);
26932         if (bgc_t_join.joined())
26933 #endif //MULTIPLE_HEAPS
26934         {
26935             enter_spin_lock (&gc_lock);
26936             dprintf (SPINLOCK_LOG, ("bgc Egc"));
26937             
26938             bgc_start_event.Reset();
26939             do_post_gc();
26940 #ifdef MULTIPLE_HEAPS
26941             for (int gen = max_generation; gen <= (max_generation + 1); gen++)
26942             {
26943                 size_t desired_per_heap = 0;
26944                 size_t total_desired = 0;
26945                 gc_heap* hp = 0;
26946                 dynamic_data* dd;
26947                 for (int i = 0; i < n_heaps; i++)
26948                 {
26949                     hp = g_heaps[i];
26950                     dd = hp->dynamic_data_of (gen);
26951                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
26952                     if (temp_total_desired < total_desired)
26953                     {
26954                         // we overflowed.
26955                         total_desired = (size_t)MAX_PTR;
26956                         break;
26957                     }
26958                     total_desired = temp_total_desired;
26959                 }
26960
26961                 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
26962
26963                 for (int i = 0; i < n_heaps; i++)
26964                 {
26965                     hp = gc_heap::g_heaps[i];
26966                     dd = hp->dynamic_data_of (gen);
26967                     dd_desired_allocation (dd) = desired_per_heap;
26968                     dd_gc_new_allocation (dd) = desired_per_heap;
26969                     dd_new_allocation (dd) = desired_per_heap;
26970                 }
26971             }
26972 #endif //MULTIPLE_HEAPS
26973 #ifdef MULTIPLE_HEAPS
26974             fire_pevents();
26975 #endif //MULTIPLE_HEAPS
26976
26977             c_write (settings.concurrent, FALSE);
26978             recursive_gc_sync::end_background();
26979             keep_bgc_threads_p = FALSE;
26980             background_gc_done_event.Set();
26981
26982             dprintf (SPINLOCK_LOG, ("bgc Lgc"));
26983             leave_spin_lock (&gc_lock);
26984 #ifdef MULTIPLE_HEAPS
26985             dprintf(1, ("End of BGC - starting all BGC threads"));
26986             bgc_t_join.restart();
26987 #endif //MULTIPLE_HEAPS
26988         }
26989         // We can't disable preempt here because there might've been a GC already
26990         // started and decided to do a BGC and waiting for a BGC thread to restart 
26991         // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
26992         // to restart the VM so we deadlock.
26993         //gc_heap::disable_preemptive (current_thread, TRUE);
26994     }
26995
26996     FIRE_EVENT(GCTerminateConcurrentThread_V1);
26997
26998     dprintf (3, ("bgc_thread thread exiting"));
26999     return;
27000 }
27001
27002 #endif //BACKGROUND_GC
27003
27004 //Clear the cards [start_card, end_card[
27005 void gc_heap::clear_cards (size_t start_card, size_t end_card)
27006 {
27007     if (start_card < end_card)
27008     {
27009         size_t start_word = card_word (start_card);
27010         size_t end_word = card_word (end_card);
27011         if (start_word < end_word)
27012         {
27013             // Figure out the bit positions of the cards within their words
27014             unsigned bits = card_bit (start_card);
27015             card_table [start_word] &= lowbits (~0, bits);
27016             for (size_t i = start_word+1; i < end_word; i++)
27017                 card_table [i] = 0;
27018             bits = card_bit (end_card);
27019             // Don't write beyond end_card (and possibly uncommitted card table space).
27020             if (bits != 0)
27021             {
27022                 card_table [end_word] &= highbits (~0, bits);
27023             }
27024         }
27025         else
27026         {
27027             // If the start and end cards are in the same word, just clear the appropriate card
27028             // bits in that word.
27029             card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
27030                                         highbits (~0, card_bit (end_card)));
27031         }
27032 #ifdef VERYSLOWDEBUG
27033         size_t  card = start_card;
27034         while (card < end_card)
27035         {
27036             assert (! (card_set_p (card)));
27037             card++;
27038         }
27039 #endif //VERYSLOWDEBUG
27040         dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
27041                   start_card, (size_t)card_address (start_card),
27042                   end_card, (size_t)card_address (end_card)));
27043     }
27044 }
27045
27046 void gc_heap::clear_card_for_addresses (uint8_t* start_address, uint8_t* end_address)
27047 {
27048     size_t   start_card = card_of (align_on_card (start_address));
27049     size_t   end_card = card_of (align_lower_card (end_address));
27050     clear_cards (start_card, end_card);
27051 }
27052
27053 // copy [srccard, ...[ to [dst_card, end_card[
27054 // This will set the same bit twice. Can be optimized.
27055 inline
27056 void gc_heap::copy_cards (size_t dst_card,
27057                           size_t src_card,
27058                           size_t end_card, 
27059                           BOOL nextp)
27060 {
27061     // If the range is empty, this function is a no-op - with the subtlety that
27062     // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
27063     // outside the committed region.  To avoid the access, leave early.
27064     if (!(dst_card < end_card))
27065         return;
27066
27067     unsigned int srcbit = card_bit (src_card);
27068     unsigned int dstbit = card_bit (dst_card);
27069     size_t srcwrd = card_word (src_card);
27070     size_t dstwrd = card_word (dst_card);
27071     unsigned int srctmp = card_table[srcwrd];
27072     unsigned int dsttmp = card_table[dstwrd];
27073
27074     for (size_t card = dst_card; card < end_card; card++)
27075     {
27076         if (srctmp & (1 << srcbit))
27077             dsttmp |= 1 << dstbit;
27078         else
27079             dsttmp &= ~(1 << dstbit);
27080
27081         if (!(++srcbit % 32))
27082         {
27083             srctmp = card_table[++srcwrd];
27084             srcbit = 0;
27085         }
27086
27087         if (nextp)
27088         {
27089             if (srctmp & (1 << srcbit))
27090                 dsttmp |= 1 << dstbit;
27091         }
27092
27093         if (!(++dstbit % 32))
27094         {
27095             card_table[dstwrd] = dsttmp;
27096
27097 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27098             if (dsttmp != 0)
27099             {
27100                 card_bundle_set(cardw_card_bundle(dstwrd));
27101             }
27102 #endif
27103
27104             dstwrd++;
27105             dsttmp = card_table[dstwrd];
27106             dstbit = 0;
27107         }
27108     }
27109
27110     card_table[dstwrd] = dsttmp;
27111
27112 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27113     if (dsttmp != 0)
27114     {
27115         card_bundle_set(cardw_card_bundle(dstwrd));
27116     }
27117 #endif
27118 }
27119
27120 void gc_heap::copy_cards_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27121 {
27122     ptrdiff_t relocation_distance = src - dest;
27123     size_t start_dest_card = card_of (align_on_card (dest));
27124     size_t end_dest_card = card_of (dest + len - 1);
27125     size_t dest_card = start_dest_card;
27126     size_t src_card = card_of (card_address (dest_card)+relocation_distance);
27127     dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
27128                  src_card, (size_t)src, dest_card, (size_t)dest));
27129     dprintf (3,(" %Ix->%Ix:%Ix[",
27130               (size_t)src+len, end_dest_card, (size_t)dest+len));
27131
27132     dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
27133         dest, src, len, relocation_distance, (align_on_card (dest))));
27134
27135     dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
27136         start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
27137
27138     //First card has two boundaries
27139     if (start_dest_card != card_of (dest))
27140     {
27141         if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
27142             card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
27143         {
27144             dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
27145                     (card_address (start_dest_card) + relocation_distance),
27146                     card_of (card_address (start_dest_card) + relocation_distance),
27147                     (src + len - 1),
27148                     card_of (src + len - 1)));
27149
27150             dprintf (3, ("setting card: %Ix", card_of (dest)));
27151             set_card (card_of (dest));
27152         }
27153     }
27154
27155     if (card_set_p (card_of (src)))
27156         set_card (card_of (dest));
27157
27158
27159     copy_cards (dest_card, src_card, end_dest_card,
27160                 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
27161
27162     //Last card has two boundaries.
27163     if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
27164         card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
27165     {
27166         dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
27167                 (card_address (end_dest_card) + relocation_distance),
27168                 card_of (card_address (end_dest_card) + relocation_distance),
27169                 src,
27170                 card_of (src)));
27171
27172         dprintf (3, ("setting card: %Ix", end_dest_card));
27173         set_card (end_dest_card);
27174     }
27175
27176     if (card_set_p (card_of (src + len - 1)))
27177         set_card (end_dest_card);
27178
27179 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27180     card_bundles_set(cardw_card_bundle(card_word(card_of(dest))), cardw_card_bundle(align_cardw_on_bundle(card_word(end_dest_card))));
27181 #endif
27182 }
27183
27184 #ifdef BACKGROUND_GC
27185 // this does not need the Interlocked version of mark_array_set_marked.
27186 void gc_heap::copy_mark_bits_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27187 {
27188     dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
27189                  (size_t)src, (size_t)dest,
27190                  (size_t)src+len, (size_t)dest+len));
27191
27192     uint8_t* src_o = src;
27193     uint8_t* dest_o;
27194     uint8_t* src_end = src + len;
27195     int align_const = get_alignment_constant (TRUE);
27196     ptrdiff_t reloc = dest - src;
27197
27198     while (src_o < src_end)
27199     {
27200         uint8_t*  next_o = src_o + Align (size (src_o), align_const);
27201
27202         if (background_object_marked (src_o, TRUE))
27203         {
27204             dest_o = src_o + reloc;
27205
27206             //if (background_object_marked (dest_o, FALSE))
27207             //{
27208             //    dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
27209             //    FATAL_GC_ERROR();
27210             //}
27211
27212             background_mark (dest_o, 
27213                              background_saved_lowest_address, 
27214                              background_saved_highest_address);
27215             dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
27216         }
27217
27218         src_o = next_o;
27219     }
27220 }
27221 #endif //BACKGROUND_GC
27222
27223 void gc_heap::fix_brick_to_highest (uint8_t* o, uint8_t* next_o)
27224 {
27225     size_t new_current_brick = brick_of (o);
27226     set_brick (new_current_brick,
27227                (o - brick_address (new_current_brick)));
27228     size_t b = 1 + new_current_brick;
27229     size_t limit = brick_of (next_o);
27230     //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
27231     dprintf(3,("b:%Ix->%Ix-%Ix", 
27232                new_current_brick, (size_t)o, (size_t)next_o));
27233     while (b < limit)
27234     {
27235         set_brick (b,(new_current_brick - b));
27236         b++;
27237     }
27238 }
27239
27240 // start can not be >= heap_segment_allocated for the segment.
27241 uint8_t* gc_heap::find_first_object (uint8_t* start, uint8_t* first_object)
27242 {
27243     size_t brick = brick_of (start);
27244     uint8_t* o = 0;
27245     //last_object == null -> no search shortcut needed
27246     if ((brick == brick_of (first_object) || (start <= first_object)))
27247     {
27248         o = first_object;
27249     }
27250     else
27251     {
27252         ptrdiff_t  min_brick = (ptrdiff_t)brick_of (first_object);
27253         ptrdiff_t  prev_brick = (ptrdiff_t)brick - 1;
27254         int         brick_entry = 0;
27255         while (1)
27256         {
27257             if (prev_brick < min_brick)
27258             {
27259                 break;
27260             }
27261             if ((brick_entry = get_brick_entry(prev_brick)) >= 0)
27262             {
27263                 break;
27264             }
27265             assert (! ((brick_entry == 0)));
27266             prev_brick = (brick_entry + prev_brick);
27267
27268         }
27269         o = ((prev_brick < min_brick) ? first_object :
27270                       brick_address (prev_brick) + brick_entry - 1);
27271         assert (o <= start);
27272     }
27273
27274     assert (Align (size (o)) >= Align (min_obj_size));
27275     uint8_t*  next_o = o + Align (size (o));
27276     size_t curr_cl = (size_t)next_o / brick_size;
27277     size_t min_cl = (size_t)first_object / brick_size;
27278
27279     //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
27280 #ifdef TRACE_GC
27281     unsigned int n_o = 1;
27282 #endif //TRACE_GC
27283
27284     uint8_t* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27285
27286     while (next_o <= start)
27287     {
27288         do
27289         {
27290 #ifdef TRACE_GC
27291             n_o++;
27292 #endif //TRACE_GC
27293             o = next_o;
27294             assert (Align (size (o)) >= Align (min_obj_size));
27295             next_o = o + Align (size (o));
27296             Prefetch (next_o);
27297         }while (next_o < next_b);
27298
27299         if (((size_t)next_o / brick_size) != curr_cl)
27300         {
27301             if (curr_cl >= min_cl)
27302             {
27303                 fix_brick_to_highest (o, next_o);
27304             }
27305             curr_cl = (size_t) next_o / brick_size;
27306         }
27307         next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27308     }
27309
27310     size_t bo = brick_of (o);
27311     //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix", 
27312     dprintf (3, ("%Id o, [%Ix-[%Ix", 
27313         n_o, bo, brick));
27314     if (bo < brick)
27315     {
27316         set_brick (bo, (o - brick_address(bo)));
27317         size_t b = 1 + bo;
27318         int x = -1;
27319         while (b < brick)
27320         {
27321             set_brick (b,x--);
27322             b++;
27323         }
27324     }
27325
27326     return o;
27327 }
27328
27329 #ifdef CARD_BUNDLE
27330
27331 // Find the first non-zero card word between cardw and cardw_end.
27332 // The index of the word we find is returned in cardw.
27333 BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
27334 {
27335     dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
27336                  dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
27337
27338     if (card_bundles_enabled())
27339     {
27340         size_t cardb = cardw_card_bundle (cardw);
27341         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
27342         while (1)
27343         {
27344             // Find a non-zero bundle
27345             while ((cardb < end_cardb) && (card_bundle_set_p (cardb) == 0))
27346             {
27347                 cardb++;
27348             }
27349
27350             if (cardb == end_cardb)
27351                 return FALSE;
27352
27353             // We found a bundle, so go through its words and find a non-zero card word
27354             uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
27355             uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
27356             while ((card_word < card_word_end) && !(*card_word))
27357             {
27358                 card_word++;
27359             }
27360
27361             if (card_word != card_word_end)
27362             {
27363                 cardw = (card_word - &card_table[0]);
27364                 return TRUE;
27365             }
27366             else if ((cardw <= card_bundle_cardw (cardb)) &&
27367                      (card_word == &card_table [card_bundle_cardw (cardb+1)]))
27368             {
27369                 // a whole bundle was explored and is empty
27370                 dprintf  (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
27371                         dd_collection_count (dynamic_data_of (0)), 
27372                         cardb, card_bundle_cardw (cardb),
27373                         card_bundle_cardw (cardb+1)));
27374                 card_bundle_clear (cardb);
27375             }
27376
27377             cardb++;
27378         }
27379     }
27380     else
27381     {
27382         uint32_t* card_word = &card_table[cardw];
27383         uint32_t* card_word_end = &card_table [cardw_end];
27384
27385         while (card_word < card_word_end)
27386         {
27387             if (*card_word != 0)
27388             {
27389                 cardw = (card_word - &card_table [0]);
27390                 return TRUE;
27391             }
27392
27393             card_word++;
27394         }
27395
27396         return FALSE;
27397     }
27398 }
27399
27400 #endif //CARD_BUNDLE
27401
27402 // Find cards that are set between two points in a card table.
27403 // Parameters
27404 //     card_table    : The card table.
27405 //     card          : [in/out] As input, the card to start searching from.
27406 //                              As output, the first card that's set.
27407 //     card_word_end : The card word at which to stop looking.
27408 //     end_card      : [out] The last card which is set.
27409 BOOL gc_heap::find_card(uint32_t* card_table,
27410                         size_t&   card,
27411                         size_t    card_word_end,
27412                         size_t&   end_card)
27413 {
27414     uint32_t* last_card_word;
27415     uint32_t card_word_value;
27416     uint32_t bit_position;
27417     
27418     // Find the first card which is set
27419     last_card_word = &card_table [card_word (card)];
27420     bit_position = card_bit (card);
27421     card_word_value = (*last_card_word) >> bit_position;
27422     if (!card_word_value)
27423     {
27424         bit_position = 0;
27425 #ifdef CARD_BUNDLE
27426         // Using the card bundle, go through the remaining card words between here and 
27427         // card_word_end until we find one that is non-zero.
27428         size_t lcw = card_word(card) + 1;
27429         if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
27430         {
27431             return FALSE;
27432         }
27433         else
27434         {
27435             last_card_word = &card_table [lcw];
27436             card_word_value = *last_card_word;
27437         }
27438
27439 #else //CARD_BUNDLE
27440         // Go through the remaining card words between here and card_word_end until we find
27441         // one that is non-zero.
27442         do
27443         {
27444             ++last_card_word;
27445         }
27446         while ((last_card_word < &card_table [card_word_end]) && !(*last_card_word));
27447
27448         if (last_card_word < &card_table [card_word_end])
27449         {
27450             card_word_value = *last_card_word;
27451         }
27452         else
27453         {
27454             // We failed to find any non-zero card words before we got to card_word_end
27455             return FALSE;
27456         }
27457 #endif //CARD_BUNDLE
27458     }
27459
27460     // Look for the lowest bit set
27461     if (card_word_value)
27462     {
27463         while (!(card_word_value & 1))
27464         {
27465             bit_position++;
27466             card_word_value = card_word_value / 2;
27467         }
27468     }
27469     
27470     // card is the card word index * card size + the bit index within the card
27471     card = (last_card_word - &card_table[0]) * card_word_width + bit_position;
27472
27473     do
27474     {
27475         // Keep going until we get to an un-set card.
27476         bit_position++;
27477         card_word_value = card_word_value / 2;
27478
27479         // If we reach the end of the card word and haven't hit a 0 yet, start going
27480         // card word by card word until we get to one that's not fully set (0xFFFF...)
27481         // or we reach card_word_end.
27482         if ((bit_position == card_word_width) && (last_card_word < &card_table [card_word_end]))
27483         {
27484             do
27485             {
27486                 card_word_value = *(++last_card_word);
27487             } while ((last_card_word < &card_table [card_word_end]) &&
27488
27489 #ifdef _MSC_VER
27490                      (card_word_value == (1 << card_word_width)-1)
27491 #else
27492                      // if left shift count >= width of type,
27493                      // gcc reports error.
27494                      (card_word_value == ~0u)
27495 #endif // _MSC_VER
27496                 );
27497             bit_position = 0;
27498         }
27499     } while (card_word_value & 1);
27500
27501     end_card = (last_card_word - &card_table [0])* card_word_width + bit_position;
27502     
27503     //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
27504     dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
27505     return TRUE;
27506 }
27507
27508
27509     //because of heap expansion, computing end is complicated.
27510 uint8_t* compute_next_end (heap_segment* seg, uint8_t* low)
27511 {
27512     if ((low >=  heap_segment_mem (seg)) &&
27513         (low < heap_segment_allocated (seg)))
27514         return low;
27515     else
27516         return heap_segment_allocated (seg);
27517 }
27518
27519 uint8_t*
27520 gc_heap::compute_next_boundary (uint8_t* low, int gen_number,
27521                                 BOOL relocating)
27522 {
27523     UNREFERENCED_PARAMETER(low);
27524
27525     //when relocating, the fault line is the plan start of the younger
27526     //generation because the generation is promoted.
27527     if (relocating && (gen_number == (settings.condemned_generation + 1)))
27528     {
27529         generation* gen = generation_of (gen_number - 1);
27530         uint8_t* gen_alloc = generation_plan_allocation_start (gen);
27531         assert (gen_alloc);
27532         return gen_alloc;
27533     }
27534     else
27535     {
27536         assert (gen_number > settings.condemned_generation);
27537         return generation_allocation_start (generation_of (gen_number - 1 ));
27538     }
27539
27540 }
27541
27542 inline void
27543 gc_heap::keep_card_live (uint8_t* o, size_t& n_gen,
27544                          size_t& cg_pointers_found)
27545 {
27546     THREAD_FROM_HEAP;
27547     if ((gc_low <= o) && (gc_high > o))
27548     {
27549         n_gen++;
27550     }
27551 #ifdef MULTIPLE_HEAPS
27552     else if (o)
27553     {
27554         gc_heap* hp = heap_of (o);
27555         if (hp != this)
27556         {
27557             if ((hp->gc_low <= o) &&
27558                 (hp->gc_high > o))
27559             {
27560                 n_gen++;
27561             }
27562         }
27563     }
27564 #endif //MULTIPLE_HEAPS
27565     cg_pointers_found ++;
27566     dprintf (4, ("keep card live for %Ix", o));
27567 }
27568
27569 inline void
27570 gc_heap::mark_through_cards_helper (uint8_t** poo, size_t& n_gen,
27571                                     size_t& cg_pointers_found,
27572                                     card_fn fn, uint8_t* nhigh,
27573                                     uint8_t* next_boundary)
27574 {
27575     THREAD_FROM_HEAP;
27576     if ((gc_low <= *poo) && (gc_high > *poo))
27577     {
27578         n_gen++;
27579         call_fn(fn) (poo THREAD_NUMBER_ARG);
27580     }
27581 #ifdef MULTIPLE_HEAPS
27582     else if (*poo)
27583     {
27584         gc_heap* hp = heap_of_gc (*poo);
27585         if (hp != this)
27586         {
27587             if ((hp->gc_low <= *poo) &&
27588                 (hp->gc_high > *poo))
27589             {
27590                 n_gen++;
27591                 call_fn(fn) (poo THREAD_NUMBER_ARG);
27592             }
27593             if ((fn == &gc_heap::relocate_address) ||
27594                 ((hp->ephemeral_low <= *poo) &&
27595                  (hp->ephemeral_high > *poo)))
27596             {
27597                 cg_pointers_found++;
27598             }
27599         }
27600     }
27601 #endif //MULTIPLE_HEAPS
27602     if ((next_boundary <= *poo) && (nhigh > *poo))
27603     {
27604         cg_pointers_found ++;
27605         dprintf (4, ("cg pointer %Ix found, %Id so far",
27606                      (size_t)*poo, cg_pointers_found ));
27607
27608     }
27609 }
27610
27611 BOOL gc_heap::card_transition (uint8_t* po, uint8_t* end, size_t card_word_end,
27612                                size_t& cg_pointers_found, 
27613                                size_t& n_eph, size_t& n_card_set,
27614                                size_t& card, size_t& end_card,
27615                                BOOL& foundp, uint8_t*& start_address,
27616                                uint8_t*& limit, size_t& n_cards_cleared)
27617 {
27618     dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
27619     dprintf (3, ("ct: %Id cg", cg_pointers_found));
27620     BOOL passed_end_card_p = FALSE;
27621     foundp = FALSE;
27622
27623     if (cg_pointers_found == 0)
27624     {
27625         //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
27626         dprintf(3,(" CC [%Ix, %Ix[ ",
27627                 (size_t)card_address(card), (size_t)po));
27628         clear_cards (card, card_of(po));
27629         n_card_set -= (card_of (po) - card);
27630         n_cards_cleared += (card_of (po) - card);
27631
27632     }
27633     n_eph +=cg_pointers_found;
27634     cg_pointers_found = 0;
27635     card = card_of (po);
27636     if (card >= end_card)
27637     {
27638         passed_end_card_p = TRUE;
27639         dprintf (3, ("card %Ix exceeding end_card %Ix",
27640                     (size_t)card, (size_t)end_card));
27641         foundp = find_card (card_table, card, card_word_end, end_card);
27642         if (foundp)
27643         {
27644             n_card_set+= end_card - card;
27645             start_address = card_address (card);
27646             dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
27647                         (size_t)card, (size_t)start_address,
27648                         (size_t)card_address (end_card)));
27649         }
27650         limit = min (end, card_address (end_card));
27651
27652         assert (!((limit == card_address (end_card))&&
27653                 card_set_p (end_card)));
27654     }
27655
27656     return passed_end_card_p;
27657 }
27658
27659 void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
27660 {
27661 #ifdef BACKGROUND_GC
27662     dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
27663                  current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
27664
27665     heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
27666     PREFIX_ASSUME(soh_seg != NULL);
27667
27668     while (soh_seg)
27669     {
27670         dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix", 
27671             soh_seg, 
27672             heap_segment_background_allocated (soh_seg),
27673             heap_segment_allocated (soh_seg)));
27674
27675         soh_seg = heap_segment_next_rw (soh_seg);
27676     }
27677 #endif //BACKGROUND_GC
27678
27679     uint8_t* low = gc_low;
27680     uint8_t* high = gc_high;
27681     size_t end_card = 0;
27682
27683     generation*   oldest_gen        = generation_of (max_generation);
27684     int           curr_gen_number   = max_generation;
27685     uint8_t*      gen_boundary      = generation_allocation_start(generation_of(curr_gen_number - 1));
27686     uint8_t*      next_boundary     = compute_next_boundary(gc_low, curr_gen_number, relocating);
27687     
27688     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
27689     PREFIX_ASSUME(seg != NULL);
27690
27691     uint8_t*      beg               = generation_allocation_start (oldest_gen);
27692     uint8_t*      end               = compute_next_end (seg, low);
27693     uint8_t*      last_object       = beg;
27694
27695     size_t  cg_pointers_found = 0;
27696
27697     size_t  card_word_end = (card_of (align_on_card_word (end)) / card_word_width);
27698
27699     size_t        n_eph             = 0;
27700     size_t        n_gen             = 0;
27701     size_t        n_card_set        = 0;
27702     uint8_t*      nhigh             = (relocating ? heap_segment_plan_allocated (ephemeral_heap_segment) : high);
27703
27704     BOOL          foundp            = FALSE;
27705     uint8_t*      start_address     = 0;
27706     uint8_t*      limit             = 0;
27707     size_t        card              = card_of (beg);
27708 #ifdef BACKGROUND_GC
27709     BOOL consider_bgc_mark_p        = FALSE;
27710     BOOL check_current_sweep_p      = FALSE;
27711     BOOL check_saved_sweep_p        = FALSE;
27712     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
27713 #endif //BACKGROUND_GC
27714
27715     dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
27716     size_t total_cards_cleared = 0;
27717
27718     while (1)
27719     {
27720         if (card_of(last_object) > card)
27721         {
27722             // cg means cross-generational
27723             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
27724             if (cg_pointers_found == 0)
27725             {
27726                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
27727                 clear_cards (card, card_of(last_object));
27728                 n_card_set -= (card_of (last_object) - card);
27729                 total_cards_cleared += (card_of (last_object) - card);
27730             }
27731
27732             n_eph += cg_pointers_found;
27733             cg_pointers_found = 0;
27734             card = card_of (last_object);
27735         }
27736
27737         if (card >= end_card)
27738         {
27739             // Find the first card that's set (between card and card_word_end)
27740             foundp = find_card(card_table, card, card_word_end, end_card);
27741             if (foundp)
27742             {
27743                 // We found card(s) set. 
27744                 n_card_set += end_card - card;
27745                 start_address = max (beg, card_address (card));
27746             }
27747
27748             limit = min (end, card_address (end_card));
27749         }
27750
27751         if (!foundp || (last_object >= end) || (card_address (card) >= end))
27752         {
27753             if (foundp && (cg_pointers_found == 0))
27754             {
27755                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
27756                            (size_t)end));
27757                 clear_cards (card, card_of (end));
27758                 n_card_set -= (card_of (end) - card);
27759                 total_cards_cleared += (card_of (end) - card);
27760             }
27761
27762             n_eph += cg_pointers_found;
27763             cg_pointers_found = 0;
27764
27765             if ((seg = heap_segment_next_in_range (seg)) != 0)
27766             {
27767 #ifdef BACKGROUND_GC
27768                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
27769 #endif //BACKGROUND_GC
27770                 beg = heap_segment_mem (seg);
27771                 end = compute_next_end (seg, low);
27772                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
27773                 card = card_of (beg);
27774                 last_object = beg;
27775                 end_card = 0;
27776                 continue;
27777             }
27778             else
27779             {
27780                 break;
27781             }
27782         }
27783
27784         // We've found a card and will now go through the objects in it.
27785         assert (card_set_p (card));
27786         {
27787             uint8_t* o = last_object;
27788             o = find_first_object (start_address, last_object);
27789             // Never visit an object twice.
27790             assert (o >= last_object);
27791
27792             //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
27793             dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
27794                    card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
27795
27796             while (o < limit)
27797             {
27798                 assert (Align (size (o)) >= Align (min_obj_size));
27799                 size_t s = size (o);
27800
27801                 uint8_t* next_o =  o + Align (s);
27802                 Prefetch (next_o);
27803
27804                 if ((o >= gen_boundary) &&
27805                     (seg == ephemeral_heap_segment))
27806                 {
27807                     dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
27808                     curr_gen_number--;
27809                     assert ((curr_gen_number > 0));
27810                     gen_boundary = generation_allocation_start
27811                         (generation_of (curr_gen_number - 1));
27812                     next_boundary = (compute_next_boundary
27813                                      (low, curr_gen_number, relocating));
27814                 }
27815
27816                 dprintf (4, ("|%Ix|", (size_t)o));
27817
27818                 if (next_o < start_address)
27819                 {
27820                     goto end_object;
27821                 }
27822
27823 #ifdef BACKGROUND_GC
27824                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
27825                 {
27826                     goto end_object;
27827                 }
27828 #endif //BACKGROUND_GC
27829
27830 #ifdef COLLECTIBLE_CLASS
27831                 if (is_collectible(o))
27832                 {
27833                     BOOL passed_end_card_p = FALSE;
27834
27835                     if (card_of (o) > card)
27836                     {
27837                         passed_end_card_p = card_transition (o, end, card_word_end,
27838                             cg_pointers_found, 
27839                             n_eph, n_card_set,
27840                             card, end_card,
27841                             foundp, start_address,
27842                             limit, total_cards_cleared);
27843                     }
27844
27845                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
27846                     {
27847                         // card is valid and it covers the head of the object
27848                         if (fn == &gc_heap::relocate_address)
27849                         {
27850                             keep_card_live (o, n_gen, cg_pointers_found);
27851                         }
27852                         else
27853                         {
27854                             uint8_t* class_obj = get_class_object (o);
27855                             mark_through_cards_helper (&class_obj, n_gen,
27856                                                     cg_pointers_found, fn,
27857                                                     nhigh, next_boundary);
27858                         }
27859                     }
27860
27861                     if (passed_end_card_p)
27862                     {
27863                         if (foundp && (card_address (card) < next_o))
27864                         {
27865                             goto go_through_refs;
27866                         }
27867                         else if (foundp && (start_address < limit))
27868                         {
27869                             next_o = find_first_object (start_address, o);
27870                             goto end_object;
27871                         }
27872                         else
27873                             goto end_limit;                            
27874                     }
27875                 }
27876
27877 go_through_refs:
27878 #endif //COLLECTIBLE_CLASS
27879
27880                 if (contain_pointers (o))
27881                 {
27882                     dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
27883
27884                     {
27885                         dprintf (4, ("normal object path"));
27886                         go_through_object
27887                             (method_table(o), o, s, poo,
27888                              start_address, use_start, (o + s),
27889                              {
27890                                  dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
27891                                  if (card_of ((uint8_t*)poo) > card)
27892                                  {
27893                                     BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
27894                                             card_word_end,
27895                                             cg_pointers_found, 
27896                                             n_eph, n_card_set,
27897                                             card, end_card,
27898                                             foundp, start_address,
27899                                             limit, total_cards_cleared);
27900
27901                                      if (passed_end_card_p)
27902                                      {
27903                                         if (foundp && (card_address (card) < next_o))
27904                                         {
27905                                              //new_start();
27906                                              {
27907                                                  if (ppstop <= (uint8_t**)start_address)
27908                                                      {break;}
27909                                                  else if (poo < (uint8_t**)start_address)
27910                                                      {poo = (uint8_t**)start_address;}
27911                                              }
27912                                         }
27913                                         else if (foundp && (start_address < limit))
27914                                         {
27915                                             next_o = find_first_object (start_address, o);
27916                                             goto end_object;
27917                                         }
27918                                          else
27919                                             goto end_limit;
27920                                      }
27921                                  }
27922
27923                                  mark_through_cards_helper (poo, n_gen,
27924                                                             cg_pointers_found, fn,
27925                                                             nhigh, next_boundary);
27926                              }
27927                             );
27928                     }
27929                 }
27930
27931             end_object:
27932                 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
27933                 {
27934                     if (brick_table [brick_of (o)] <0)
27935                         fix_brick_to_highest (o, next_o);
27936                 }
27937                 o = next_o;
27938             }
27939         end_limit:
27940             last_object = o;
27941         }
27942     }
27943     // compute the efficiency ratio of the card table
27944     if (!relocating)
27945     {
27946         generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
27947         dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
27948             n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
27949     }
27950     else
27951     {
27952         dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
27953             n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
27954     }
27955 }
27956
27957 #ifdef SEG_REUSE_STATS
27958 size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
27959 {
27960     size_t total_items = 0;
27961     *total_size = 0;
27962     for (int i = 0; i < count; i++)
27963     {
27964         total_items += ordered_indices[i];
27965         *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
27966         dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
27967     } 
27968     dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
27969     return total_items;
27970 }
27971 #endif // SEG_REUSE_STATS
27972
27973 void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug)
27974 {
27975     // detect pinned plugs
27976     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
27977     {
27978         deque_pinned_plug();
27979         update_oldest_pinned_plug();
27980         dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
27981     }
27982     else
27983     {
27984         size_t plug_size = last_plug_size + Align(min_obj_size);
27985         BOOL is_padded = FALSE;
27986
27987 #ifdef SHORT_PLUGS
27988         plug_size += Align (min_obj_size);
27989         is_padded = TRUE;
27990 #endif //SHORT_PLUGS
27991
27992 #ifdef RESPECT_LARGE_ALIGNMENT
27993         plug_size += switch_alignment_size (is_padded);
27994 #endif //RESPECT_LARGE_ALIGNMENT
27995
27996         total_ephemeral_plugs += plug_size;
27997         size_t plug_size_power2 = round_up_power2 (plug_size);
27998         ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
27999         dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array", 
28000             heap_number, 
28001             last_plug, 
28002             plug_size, 
28003             (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
28004     }
28005 }
28006
28007 void gc_heap::count_plugs_in_brick (uint8_t* tree, uint8_t*& last_plug)
28008 {
28009     assert ((tree != NULL));
28010     if (node_left_child (tree))
28011     {
28012         count_plugs_in_brick (tree + node_left_child (tree), last_plug);
28013     }
28014
28015     if (last_plug != 0)
28016     {
28017         uint8_t*  plug = tree;
28018         size_t gap_size = node_gap_size (plug);
28019         uint8_t*   gap = (plug - gap_size);
28020         uint8_t*  last_plug_end = gap;
28021         size_t  last_plug_size = (last_plug_end - last_plug);
28022         dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
28023             tree, last_plug, gap_size, gap, last_plug_size));
28024
28025         if (tree == oldest_pinned_plug)
28026         {
28027             dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
28028                 tree, last_plug, last_plug_size));
28029             mark* m = oldest_pin();
28030             if (m->has_pre_plug_info())
28031             {
28032                 last_plug_size += sizeof (gap_reloc_pair);
28033                 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
28034             }
28035         }
28036         // Can't assert here - if it's a pinned plug it can be less.
28037         //assert (last_plug_size >= Align (min_obj_size));
28038
28039         count_plug (last_plug_size, last_plug);
28040     }
28041
28042     last_plug = tree;
28043
28044     if (node_right_child (tree))
28045     {
28046         count_plugs_in_brick (tree + node_right_child (tree), last_plug);
28047     }
28048 }
28049
28050 void gc_heap::build_ordered_plug_indices ()
28051 {
28052     memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
28053     memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
28054
28055     uint8_t*  start_address = generation_limit (max_generation);
28056     uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment);
28057     size_t  current_brick = brick_of (start_address);
28058     size_t  end_brick = brick_of (end_address - 1);
28059     uint8_t* last_plug = 0;
28060
28061     //Look for the right pinned plug to start from.
28062     reset_pinned_queue_bos();
28063     while (!pinned_plug_que_empty_p())
28064     {
28065         mark* m = oldest_pin();
28066         if ((m->first >= start_address) && (m->first < end_address))
28067         {
28068             dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
28069
28070             break;
28071         }
28072         else
28073             deque_pinned_plug();
28074     }
28075     
28076     update_oldest_pinned_plug();
28077
28078     while (current_brick <= end_brick)
28079     {
28080         int brick_entry =  brick_table [ current_brick ];
28081         if (brick_entry >= 0)
28082         {
28083             count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
28084         }
28085
28086         current_brick++;
28087     }
28088
28089     if (last_plug !=0)
28090     {
28091         count_plug (end_address - last_plug, last_plug);
28092     }
28093
28094     // we need to make sure that after fitting all the existing plugs, we
28095     // have big enough free space left to guarantee that the next allocation
28096     // will succeed.
28097     size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
28098     total_ephemeral_plugs += extra_size;
28099     dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
28100     ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
28101     
28102     memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
28103
28104 #ifdef SEG_REUSE_STATS
28105     dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
28106     size_t total_plug_power2 = 0;
28107     dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
28108     dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))", 
28109                 total_ephemeral_plugs, 
28110                 total_plug_power2, 
28111                 (total_ephemeral_plugs ? 
28112                     (total_plug_power2 * 100 / total_ephemeral_plugs) :
28113                     0)));
28114     dprintf (SEG_REUSE_LOG_0, ("-------------------"));
28115 #endif // SEG_REUSE_STATS
28116 }
28117
28118 void gc_heap::init_ordered_free_space_indices ()
28119 {
28120     memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
28121     memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
28122 }
28123
28124 void gc_heap::trim_free_spaces_indices ()
28125 {
28126     trimmed_free_space_index = -1;
28127     size_t max_count = max_free_space_items - 1;
28128     size_t count = 0;
28129     int i = 0;
28130     for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
28131     {
28132         count += ordered_free_space_indices[i];
28133
28134         if (count >= max_count)
28135         {
28136             break;
28137         }
28138     }
28139
28140     ptrdiff_t extra_free_space_items = count - max_count;
28141
28142     if (extra_free_space_items > 0)
28143     {
28144         ordered_free_space_indices[i] -= extra_free_space_items;
28145         free_space_items = max_count;
28146         trimmed_free_space_index = i;
28147     }
28148     else
28149     {
28150         free_space_items = count;
28151     }
28152
28153     if (i == -1)
28154     {
28155         i = 0;
28156     }
28157
28158     free_space_buckets = MAX_NUM_BUCKETS - i;
28159
28160     for (--i; i >= 0; i--)
28161     {
28162         ordered_free_space_indices[i] = 0;
28163     }
28164
28165     memcpy (saved_ordered_free_space_indices, 
28166             ordered_free_space_indices,
28167             sizeof(ordered_free_space_indices));
28168 }
28169
28170 // We fit as many plugs as we can and update the number of plugs left and the number
28171 // of free spaces left.
28172 BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
28173 {
28174     assert (small_index <= big_index);
28175     assert (big_index < MAX_NUM_BUCKETS);
28176
28177     size_t small_blocks = ordered_blocks[small_index];
28178
28179     if (small_blocks == 0)
28180     {
28181         return TRUE;
28182     }
28183
28184     size_t big_spaces = ordered_spaces[big_index];
28185
28186     if (big_spaces == 0)
28187     {
28188         return FALSE;
28189     }
28190
28191     dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces", 
28192         heap_number,
28193         small_blocks, (small_index + MIN_INDEX_POWER2),
28194         big_spaces, (big_index + MIN_INDEX_POWER2)));
28195
28196     size_t big_to_small = big_spaces << (big_index - small_index);
28197
28198     ptrdiff_t extra_small_spaces = big_to_small - small_blocks;
28199     dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks", 
28200         heap_number,
28201         big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
28202     BOOL can_fit = (extra_small_spaces >= 0);
28203
28204     if (can_fit) 
28205     {
28206         dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks", 
28207             heap_number,
28208             extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
28209     }
28210
28211     int i = 0;
28212
28213     dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
28214     ordered_spaces[big_index] = 0;
28215     if (extra_small_spaces > 0)
28216     {
28217         dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
28218         ordered_blocks[small_index] = 0;
28219         for (i = small_index; i < big_index; i++)
28220         {
28221             if (extra_small_spaces & 1)
28222             {
28223                 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d", 
28224                     heap_number,
28225                     (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
28226                 ordered_spaces[i] += 1;
28227             }
28228             extra_small_spaces >>= 1;
28229         }
28230
28231         dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d", 
28232             heap_number,
28233             (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
28234         ordered_spaces[i] += extra_small_spaces;
28235     }
28236     else
28237     {
28238         dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d", 
28239             heap_number,
28240             (small_index + MIN_INDEX_POWER2), 
28241             ordered_blocks[small_index], 
28242             (ordered_blocks[small_index] - big_to_small)));
28243         ordered_blocks[small_index] -= big_to_small;
28244     }
28245
28246 #ifdef SEG_REUSE_STATS
28247     size_t temp;
28248     dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
28249     dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
28250
28251     dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
28252     dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
28253 #endif //SEG_REUSE_STATS
28254
28255     return can_fit;
28256 }
28257
28258 // space_index gets updated to the biggest available space index.
28259 BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
28260 {
28261     assert (*space_index >= block_index);
28262
28263     while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
28264     {
28265         (*space_index)--;
28266         if (*space_index < block_index)
28267         {
28268             return FALSE;
28269         }
28270     }
28271
28272     return TRUE;
28273 }
28274
28275 BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
28276 {
28277 #ifdef FEATURE_STRUCTALIGN
28278     // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
28279     return FALSE;
28280 #endif // FEATURE_STRUCTALIGN
28281     int space_index = count - 1;
28282     for (int block_index = (count - 1); block_index >= 0; block_index--)
28283     {
28284         if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
28285         {
28286             return FALSE;
28287         }
28288     }
28289
28290     return TRUE;
28291 }
28292
28293 void gc_heap::build_ordered_free_spaces (heap_segment* seg)
28294 {
28295     assert (bestfit_seg);
28296
28297     //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2, 
28298     //                    ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets), 
28299     //                    free_space_buckets, 
28300     //                    free_space_items);
28301
28302     bestfit_seg->add_buckets (MIN_INDEX_POWER2, 
28303                         ordered_free_space_indices, 
28304                         MAX_NUM_BUCKETS, 
28305                         free_space_items);
28306
28307     assert (settings.condemned_generation == max_generation);
28308
28309     uint8_t* first_address = heap_segment_mem (seg);
28310     uint8_t* end_address   = heap_segment_reserved (seg);
28311     //look through the pinned plugs for relevant ones.
28312     //Look for the right pinned plug to start from.
28313     reset_pinned_queue_bos();
28314     mark* m = 0;
28315     // See comment in can_expand_into_p why we need (max_generation + 1).
28316     size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
28317     BOOL has_fit_gen_starts = FALSE;
28318
28319     while (!pinned_plug_que_empty_p())
28320     {
28321         m = oldest_pin();
28322         if ((pinned_plug (m) >= first_address) && 
28323             (pinned_plug (m) < end_address) &&
28324             (pinned_len (m) >= eph_gen_starts))
28325         {
28326
28327             assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
28328             break;
28329         }
28330         else
28331         {
28332             deque_pinned_plug();
28333         }
28334     }
28335
28336     if (!pinned_plug_que_empty_p())
28337     {
28338         bestfit_seg->add ((void*)m, TRUE, TRUE);
28339         deque_pinned_plug();
28340         m = oldest_pin();
28341         has_fit_gen_starts = TRUE;
28342     }
28343
28344     while (!pinned_plug_que_empty_p() &&
28345             ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28346     {
28347         bestfit_seg->add ((void*)m, TRUE, FALSE);
28348         deque_pinned_plug();
28349         m = oldest_pin();
28350     }
28351
28352     if (commit_end_of_seg)
28353     {
28354         if (!has_fit_gen_starts)
28355         {
28356             assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
28357         }
28358         bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
28359     }
28360
28361 #ifdef _DEBUG
28362     bestfit_seg->check();
28363 #endif //_DEBUG
28364 }
28365
28366 BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
28367 {
28368     if (!end_of_segment_p)
28369     {
28370         trim_free_spaces_indices ();
28371     }
28372
28373     BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices, 
28374                                              ordered_free_space_indices, 
28375                                              MAX_NUM_BUCKETS);
28376
28377     return can_bestfit;
28378 }
28379
28380 BOOL gc_heap::best_fit (size_t free_space, 
28381                         size_t largest_free_space, 
28382                         size_t additional_space, 
28383                         BOOL* use_additional_space)
28384 {
28385     dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
28386
28387     assert (!additional_space || (additional_space && use_additional_space));
28388     if (use_additional_space)
28389     {
28390         *use_additional_space = FALSE;
28391     }
28392
28393     if (ordered_plug_indices_init == FALSE)
28394     {
28395         total_ephemeral_plugs = 0;
28396         build_ordered_plug_indices();
28397         ordered_plug_indices_init = TRUE;
28398     }
28399     else
28400     {
28401         memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
28402     }
28403
28404     if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
28405     {
28406         dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
28407         size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
28408         BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
28409         if (!can_fit_empty_eph)
28410         {
28411             can_fit_empty_eph = (additional_space >= empty_eph);
28412
28413             if (can_fit_empty_eph)
28414             {
28415                 *use_additional_space = TRUE;
28416             }
28417         }
28418
28419         return can_fit_empty_eph;
28420     }
28421
28422     if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
28423     {
28424         dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
28425         return FALSE;
28426     }
28427
28428     if ((free_space + additional_space) == 0)
28429     {
28430         dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
28431         return FALSE;
28432     }
28433
28434 #ifdef SEG_REUSE_STATS
28435     dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
28436     size_t total_free_space_power2 = 0;
28437     size_t total_free_space_items = 
28438         dump_buckets (ordered_free_space_indices, 
28439                       MAX_NUM_BUCKETS,
28440                       &total_free_space_power2);
28441     dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
28442
28443     dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
28444                 total_ephemeral_plugs, 
28445                 free_space, 
28446                 total_free_space_power2, 
28447                 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
28448                 additional_space));
28449
28450     size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
28451     memcpy (saved_all_free_space_indices, 
28452             ordered_free_space_indices, 
28453             sizeof(saved_all_free_space_indices));
28454
28455 #endif // SEG_REUSE_STATS
28456
28457     if (total_ephemeral_plugs > (free_space + additional_space))
28458     {
28459         return FALSE;
28460     }
28461
28462     use_bestfit = try_best_fit(FALSE);
28463
28464     if (!use_bestfit && additional_space)
28465     {
28466         int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
28467
28468         if (relative_free_space_index != -1)
28469         {
28470             int relative_plug_index = 0;
28471             size_t plugs_to_fit = 0;
28472
28473             for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
28474             {
28475                 plugs_to_fit = ordered_plug_indices[relative_plug_index];
28476                 if (plugs_to_fit != 0)
28477                 {
28478                     break;
28479                 }
28480             }
28481
28482             if ((relative_plug_index > relative_free_space_index) ||
28483                 ((relative_plug_index == relative_free_space_index) &&
28484                 (plugs_to_fit > 1)))
28485             {
28486 #ifdef SEG_REUSE_STATS
28487                 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
28488                             (relative_free_space_index + MIN_INDEX_POWER2),
28489                             plugs_to_fit,
28490                             (relative_plug_index + MIN_INDEX_POWER2)));
28491 #endif // SEG_REUSE_STATS
28492                 goto adjust;
28493             }
28494             
28495             dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
28496             ordered_free_space_indices[relative_free_space_index]++;
28497             use_bestfit = try_best_fit(TRUE);
28498             if (use_bestfit)
28499             {
28500                 free_space_items++;
28501                 // Since we might've trimmed away some of the free spaces we had, we should see
28502                 // if we really need to use end of seg space - if it's the same or smaller than
28503                 // the largest space we trimmed we can just add that one back instead of 
28504                 // using end of seg.
28505                 if (relative_free_space_index > trimmed_free_space_index)
28506                 {
28507                     *use_additional_space = TRUE;
28508                 }
28509                 else 
28510                 {
28511                     // If the addition space is <= than the last trimmed space, we
28512                     // should just use that last trimmed space instead.
28513                     saved_ordered_free_space_indices[trimmed_free_space_index]++;
28514                 }
28515             }
28516         }
28517     }
28518
28519 adjust:
28520
28521     if (!use_bestfit)
28522     {
28523         dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
28524
28525 #ifdef SEG_REUSE_STATS
28526         size_t saved_max = max_free_space_items;
28527         BOOL temp_bestfit = FALSE;
28528
28529         dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
28530         dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
28531
28532         // TODO: need to take the end of segment into consideration.
28533         while (max_free_space_items <= total_free_space_items)
28534         {
28535             max_free_space_items += max_free_space_items / 2;
28536             dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
28537             memcpy (ordered_free_space_indices, 
28538                     saved_all_free_space_indices,
28539                     sizeof(ordered_free_space_indices));
28540             if (try_best_fit(FALSE))
28541             {
28542                 temp_bestfit = TRUE;
28543                 break;
28544             }
28545         }
28546
28547         if (temp_bestfit)
28548         {
28549             dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
28550         }
28551         else
28552         {
28553             dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
28554         }
28555
28556         dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
28557         max_free_space_items = saved_max;
28558 #endif // SEG_REUSE_STATS
28559         if (free_space_items)
28560         {
28561             max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
28562             max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
28563         }
28564         else
28565         {
28566             max_free_space_items = MAX_NUM_FREE_SPACES;
28567         }
28568     }
28569
28570     dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
28571     dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
28572
28573     return use_bestfit;
28574 }
28575
28576 BOOL gc_heap::process_free_space (heap_segment* seg, 
28577                          size_t free_space,
28578                          size_t min_free_size, 
28579                          size_t min_cont_size,
28580                          size_t* total_free_space,
28581                          size_t* largest_free_space)
28582 {
28583     *total_free_space += free_space;
28584     *largest_free_space = max (*largest_free_space, free_space);
28585
28586 #ifdef SIMPLE_DPRINTF
28587     dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix", 
28588                 free_space, *total_free_space, *largest_free_space));
28589 #endif //SIMPLE_DPRINTF
28590
28591     if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
28592     {
28593 #ifdef SIMPLE_DPRINTF
28594         dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit", 
28595             settings.condemned_generation,
28596             *total_free_space, min_free_size, *largest_free_space, min_cont_size,
28597             (size_t)seg));
28598 #else
28599         UNREFERENCED_PARAMETER(seg);
28600 #endif //SIMPLE_DPRINTF
28601         return TRUE;
28602     }
28603
28604     int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
28605     if (free_space_index != -1)
28606     {
28607         ordered_free_space_indices[free_space_index]++;
28608     }
28609     return FALSE;
28610 }
28611
28612 BOOL gc_heap::expand_reused_seg_p()
28613 {
28614     BOOL reused_seg = FALSE;
28615     int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
28616     if ((heap_expand_mechanism == expand_reuse_bestfit) || 
28617         (heap_expand_mechanism == expand_reuse_normal))
28618     {
28619         reused_seg = TRUE;
28620     }
28621
28622     return reused_seg;
28623 }
28624
28625 BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
28626                                  allocator* gen_allocator)
28627 {
28628     min_cont_size += END_SPACE_AFTER_GC;
28629     use_bestfit = FALSE;
28630     commit_end_of_seg = FALSE;
28631     bestfit_first_pin = 0;
28632     uint8_t* first_address = heap_segment_mem (seg);
28633     uint8_t* end_address   = heap_segment_reserved (seg);
28634     size_t end_extra_space = end_space_after_gc();
28635
28636     if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
28637     {
28638         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
28639                                    first_address, end_address, end_extra_space));
28640         return FALSE;
28641     }
28642
28643     end_address -= end_extra_space;
28644
28645     dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix", 
28646         settings.condemned_generation, min_free_size, min_cont_size));
28647     size_t eph_gen_starts = eph_gen_starts_size;
28648
28649     if (settings.condemned_generation == max_generation)
28650     {
28651         size_t free_space = 0;
28652         size_t largest_free_space = free_space;
28653         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
28654         //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from. 
28655         //We are going to allocate the generation starts in the 1st free space,
28656         //so start from the first free space that's big enough for gen starts and a min object size.
28657         // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it - 
28658         // we could use it by allocating the last generation start a bit bigger but 
28659         // the complexity isn't worth the effort (those plugs are from gen2 
28660         // already anyway).
28661         reset_pinned_queue_bos();
28662         mark* m = 0;
28663         BOOL has_fit_gen_starts = FALSE;
28664
28665         init_ordered_free_space_indices ();
28666         while (!pinned_plug_que_empty_p())
28667         {
28668             m = oldest_pin();
28669             if ((pinned_plug (m) >= first_address) && 
28670                 (pinned_plug (m) < end_address) &&
28671                 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
28672             {
28673                 break;
28674             }
28675             else
28676             {
28677                 deque_pinned_plug();
28678             }
28679         }
28680
28681         if (!pinned_plug_que_empty_p())
28682         {
28683             bestfit_first_pin = pinned_plug (m) - pinned_len (m);
28684
28685             if (process_free_space (seg, 
28686                                     pinned_len (m) - eph_gen_starts, 
28687                                     min_free_size, min_cont_size, 
28688                                     &free_space, &largest_free_space))
28689             {
28690                 return TRUE;
28691             }
28692
28693             deque_pinned_plug();
28694             m = oldest_pin();
28695             has_fit_gen_starts = TRUE;
28696         }
28697
28698         dprintf (3, ("first pin is %Ix", pinned_plug (m)));
28699
28700         //tally up free space
28701         while (!pinned_plug_que_empty_p() &&
28702                ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28703         {
28704             dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
28705             if (process_free_space (seg, 
28706                                     pinned_len (m), 
28707                                     min_free_size, min_cont_size, 
28708                                     &free_space, &largest_free_space))
28709             {
28710                 return TRUE;
28711             }
28712
28713             deque_pinned_plug();
28714             m = oldest_pin();
28715         }
28716
28717         //try to find space at the end of the segment. 
28718         size_t end_space = (end_address - heap_segment_plan_allocated (seg)); 
28719         size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0); 
28720         dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
28721         if (end_space >= additional_space)
28722         {
28723             BOOL can_fit = TRUE;
28724             commit_end_of_seg = TRUE;
28725
28726             if (largest_free_space < min_cont_size)
28727             {
28728                 if (end_space >= min_cont_size)
28729                 {
28730                     additional_space = max (min_cont_size, additional_space);
28731                     dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph", 
28732                         seg));
28733                 }
28734                 else 
28735                 {
28736                     if (settings.concurrent)
28737                     {
28738                         can_fit = FALSE;
28739                         commit_end_of_seg = FALSE;
28740                     }
28741                     else
28742                     {
28743                         size_t additional_space_bestfit = additional_space;
28744                         if (!has_fit_gen_starts)
28745                         {
28746                             if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
28747                             {
28748                                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
28749                                         additional_space_bestfit));
28750                                 return FALSE;
28751                             }
28752
28753                             bestfit_first_pin = heap_segment_plan_allocated (seg);
28754                             additional_space_bestfit -= eph_gen_starts;
28755                         }
28756
28757                         can_fit = best_fit (free_space, 
28758                                             largest_free_space,
28759                                             additional_space_bestfit, 
28760                                             &commit_end_of_seg);
28761
28762                         if (can_fit)
28763                         {
28764                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg", 
28765                                 seg, (commit_end_of_seg ? "with" : "without")));
28766                         }
28767                         else
28768                         {
28769                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
28770                         }
28771                     }
28772                 }
28773             }
28774             else
28775             {
28776                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
28777             }
28778
28779             assert (additional_space <= end_space);
28780             if (commit_end_of_seg)
28781             {
28782                 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
28783                 {
28784                     dprintf (2, ("Couldn't commit end of segment?!"));
28785                     use_bestfit = FALSE;
28786  
28787                     return FALSE;
28788                 }
28789
28790                 if (use_bestfit)
28791                 {
28792                     // We increase the index here because growing heap segment could create a discrepency with 
28793                     // the additional space we used (could be bigger).
28794                     size_t free_space_end_of_seg = 
28795                         heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
28796                     int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
28797                     saved_ordered_free_space_indices[relative_free_space_index]++;
28798                 }
28799             }
28800         
28801             if (use_bestfit)
28802             {
28803                 memcpy (ordered_free_space_indices, 
28804                         saved_ordered_free_space_indices, 
28805                         sizeof(ordered_free_space_indices));
28806                 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
28807                 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
28808                 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
28809             }
28810
28811             return can_fit;
28812         }
28813
28814         dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
28815         return FALSE;
28816     }
28817     else
28818     {
28819         assert (settings.condemned_generation == (max_generation-1));
28820         size_t free_space = (end_address - heap_segment_plan_allocated (seg));
28821         size_t largest_free_space = free_space;
28822         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
28823         //find the first free list in range of the current segment
28824         size_t sz_list = gen_allocator->first_bucket_size();
28825         unsigned int a_l_idx = 0;
28826         uint8_t* free_list = 0;
28827         for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
28828         {
28829             if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
28830             {
28831                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
28832                 while (free_list)
28833                 {
28834                     if ((free_list >= first_address) && 
28835                         (free_list < end_address) && 
28836                         (unused_array_size (free_list) >= eph_gen_starts))
28837                     {
28838                         goto next;
28839                     }
28840                     else
28841                     {
28842                         free_list = free_list_slot (free_list);
28843                     }
28844                 }
28845             }
28846         }
28847 next:
28848         if (free_list)
28849         {
28850             init_ordered_free_space_indices ();
28851             if (process_free_space (seg, 
28852                                     unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size), 
28853                                     min_free_size, min_cont_size, 
28854                                     &free_space, &largest_free_space))
28855             {
28856                 return TRUE;
28857             }
28858
28859             free_list = free_list_slot (free_list);
28860         }
28861         else
28862         {
28863             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
28864             return FALSE;
28865         }
28866
28867        //tally up free space
28868
28869         while (1)
28870         {
28871             while (free_list)
28872             {
28873                 if ((free_list >= first_address) && (free_list < end_address) &&
28874                     process_free_space (seg, 
28875                                         unused_array_size (free_list), 
28876                                         min_free_size, min_cont_size, 
28877                                         &free_space, &largest_free_space))
28878                 {
28879                     return TRUE;
28880                 }
28881
28882                 free_list = free_list_slot (free_list);
28883             }
28884             a_l_idx++;
28885             if (a_l_idx < gen_allocator->number_of_buckets())
28886             {
28887                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
28888             }
28889             else
28890                 break;
28891         } 
28892
28893         dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
28894         return FALSE;
28895
28896         /*
28897         BOOL can_fit = best_fit (free_space, 0, NULL);
28898         if (can_fit)
28899         {
28900             dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
28901         }
28902         else
28903         {
28904             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
28905         }
28906
28907         return can_fit;
28908         */
28909     }
28910 }
28911
28912 void gc_heap::realloc_plug (size_t last_plug_size, uint8_t*& last_plug,
28913                             generation* gen, uint8_t* start_address,
28914                             unsigned int& active_new_gen_number,
28915                             uint8_t*& last_pinned_gap, BOOL& leftp,
28916                             BOOL shortened_p
28917 #ifdef SHORT_PLUGS
28918                             , mark* pinned_plug_entry
28919 #endif //SHORT_PLUGS
28920                             )
28921 {
28922     // detect generation boundaries
28923     // make sure that active_new_gen_number is not the youngest generation.
28924     // because the generation_limit wouldn't return the right thing in this case.
28925     if (!use_bestfit)
28926     {
28927         if ((active_new_gen_number > 1) &&
28928             (last_plug >= generation_limit (active_new_gen_number)))
28929         {
28930             assert (last_plug >= start_address);
28931             active_new_gen_number--;
28932             realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
28933             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
28934             leftp = FALSE;
28935         }
28936     }
28937
28938     // detect pinned plugs
28939     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
28940     {
28941         size_t  entry = deque_pinned_plug();
28942         mark*  m = pinned_plug_of (entry);
28943
28944         size_t saved_pinned_len = pinned_len(m);
28945         pinned_len(m) = last_plug - last_pinned_gap;
28946         //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
28947
28948         if (m->has_post_plug_info())
28949         {
28950             last_plug_size += sizeof (gap_reloc_pair);
28951             dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
28952         }
28953
28954         last_pinned_gap = last_plug + last_plug_size;
28955         dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
28956             pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
28957         leftp = FALSE;
28958
28959         //we are creating a generation fault. set the cards.
28960         {
28961             size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
28962             size_t card = card_of (last_plug);
28963             while (card != end_card)
28964             {
28965                 set_card (card);
28966                 card++;
28967             }
28968         }
28969     }
28970     else if (last_plug >= start_address)
28971     {
28972 #ifdef FEATURE_STRUCTALIGN
28973         int requiredAlignment;
28974         ptrdiff_t pad;
28975         node_aligninfo (last_plug, requiredAlignment, pad);
28976
28977         // from how we previously aligned the plug's destination address,
28978         // compute the actual alignment offset.
28979         uint8_t* reloc_plug = last_plug + node_relocation_distance (last_plug);
28980         ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
28981         if (!alignmentOffset)
28982         {
28983             // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
28984             alignmentOffset = requiredAlignment;
28985         }
28986
28987         //clear the alignment info because we are reallocating
28988         clear_node_aligninfo (last_plug);
28989 #else // FEATURE_STRUCTALIGN
28990         //clear the realignment flag because we are reallocating
28991         clear_node_realigned (last_plug);
28992 #endif // FEATURE_STRUCTALIGN
28993         BOOL adjacentp = FALSE;
28994         BOOL set_padding_on_saved_p = FALSE;
28995
28996         if (shortened_p)
28997         {
28998             last_plug_size += sizeof (gap_reloc_pair);
28999
29000 #ifdef SHORT_PLUGS
29001             assert (pinned_plug_entry != NULL);
29002             if (last_plug_size <= sizeof (plug_and_gap))
29003             {
29004                 set_padding_on_saved_p = TRUE;
29005             }
29006 #endif //SHORT_PLUGS
29007
29008             dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29009         }
29010
29011 #ifdef SHORT_PLUGS
29012         clear_padding_in_expand (last_plug, set_padding_on_saved_p, pinned_plug_entry);
29013 #endif //SHORT_PLUGS
29014
29015         uint8_t* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug,
29016 #ifdef SHORT_PLUGS
29017                                      set_padding_on_saved_p,
29018                                      pinned_plug_entry,
29019 #endif //SHORT_PLUGS
29020                                      TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
29021
29022         dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
29023         assert (new_address);
29024         set_node_relocation_distance (last_plug, new_address - last_plug);
29025 #ifdef FEATURE_STRUCTALIGN
29026         if (leftp && node_alignpad (last_plug) == 0)
29027 #else // FEATURE_STRUCTALIGN
29028         if (leftp && !node_realigned (last_plug))
29029 #endif // FEATURE_STRUCTALIGN
29030         {
29031             // TODO - temporarily disable L optimization because of a bug in it.
29032             //set_node_left (last_plug);
29033         }
29034         dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
29035         leftp = adjacentp;
29036     }
29037 }
29038
29039 void gc_heap::realloc_in_brick (uint8_t* tree, uint8_t*& last_plug,
29040                                 uint8_t* start_address,
29041                                 generation* gen,
29042                                 unsigned int& active_new_gen_number,
29043                                 uint8_t*& last_pinned_gap, BOOL& leftp)
29044 {
29045     assert (tree != NULL);
29046     int   left_node = node_left_child (tree);
29047     int   right_node = node_right_child (tree);
29048
29049     dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d", 
29050         tree, last_pinned_gap, last_plug, left_node, right_node));
29051
29052     if (left_node)
29053     {
29054         dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
29055         realloc_in_brick ((tree + left_node), last_plug, start_address,
29056                           gen, active_new_gen_number, last_pinned_gap,
29057                           leftp);
29058     }
29059
29060     if (last_plug != 0)
29061     {
29062         uint8_t*  plug = tree;
29063
29064         BOOL has_pre_plug_info_p = FALSE;
29065         BOOL has_post_plug_info_p = FALSE;
29066         mark* pinned_plug_entry = get_next_pinned_entry (tree, 
29067                                                          &has_pre_plug_info_p,
29068                                                          &has_post_plug_info_p, 
29069                                                          FALSE);
29070
29071         // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
29072         // The pinned plugs are handled in realloc_plug.
29073         size_t gap_size = node_gap_size (plug);
29074         uint8_t*   gap = (plug - gap_size);
29075         uint8_t*  last_plug_end = gap;
29076         size_t  last_plug_size = (last_plug_end - last_plug);
29077         // Cannot assert this - a plug could be less than that due to the shortened ones.
29078         //assert (last_plug_size >= Align (min_obj_size));
29079         dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
29080             plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
29081         realloc_plug (last_plug_size, last_plug, gen, start_address,
29082                       active_new_gen_number, last_pinned_gap,
29083                       leftp, has_pre_plug_info_p
29084 #ifdef SHORT_PLUGS
29085                       , pinned_plug_entry
29086 #endif //SHORT_PLUGS
29087                       );
29088     }
29089
29090     last_plug = tree;
29091
29092     if (right_node)
29093     {
29094         dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
29095         realloc_in_brick ((tree + right_node), last_plug, start_address,
29096                           gen, active_new_gen_number, last_pinned_gap,
29097                           leftp);
29098     }
29099 }
29100
29101 void
29102 gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
29103                         uint8_t* start_address, uint8_t* end_address,
29104                         unsigned active_new_gen_number)
29105 {
29106     dprintf (3, ("--- Reallocing ---"));
29107
29108     if (use_bestfit)
29109     {
29110         //make sure that every generation has a planned allocation start
29111         int  gen_number = max_generation - 1;
29112         while (gen_number >= 0)
29113         {
29114             generation* gen = generation_of (gen_number);
29115             if (0 == generation_plan_allocation_start (gen))
29116             {
29117                 generation_plan_allocation_start (gen) = 
29118                     bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
29119                 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
29120                 assert (generation_plan_allocation_start (gen));
29121             }
29122             gen_number--;
29123         }
29124     }
29125
29126     uint8_t* first_address = start_address;
29127     //Look for the right pinned plug to start from.
29128     reset_pinned_queue_bos();
29129     uint8_t* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
29130     while (!pinned_plug_que_empty_p())
29131     {
29132         mark* m = oldest_pin();
29133         if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
29134         {
29135             if (pinned_plug (m) < first_address)
29136             {
29137                 first_address = pinned_plug (m);
29138             }
29139             break;
29140         }
29141         else
29142             deque_pinned_plug();
29143     }
29144
29145     size_t  current_brick = brick_of (first_address);
29146     size_t  end_brick = brick_of (end_address-1);
29147     uint8_t*  last_plug = 0;
29148
29149     uint8_t* last_pinned_gap = heap_segment_plan_allocated (seg);
29150     BOOL leftp = FALSE;
29151
29152     dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
29153         start_address, first_address, pinned_plug (oldest_pin())));
29154
29155     while (current_brick <= end_brick)
29156     {
29157         int   brick_entry =  brick_table [ current_brick ];
29158         if (brick_entry >= 0)
29159         {
29160             realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
29161                               last_plug, start_address, consing_gen,
29162                               active_new_gen_number, last_pinned_gap,
29163                               leftp);
29164         }
29165         current_brick++;
29166     }
29167
29168     if (last_plug != 0)
29169     {
29170         realloc_plug (end_address - last_plug, last_plug, consing_gen,
29171                       start_address,
29172                       active_new_gen_number, last_pinned_gap,
29173                       leftp, FALSE
29174 #ifdef SHORT_PLUGS
29175                       , NULL
29176 #endif //SHORT_PLUGS
29177                       );
29178     }
29179
29180     //Fix the old segment allocated size
29181     assert (last_pinned_gap >= heap_segment_mem (seg));
29182     assert (last_pinned_gap <= heap_segment_committed (seg));
29183     heap_segment_plan_allocated (seg) = last_pinned_gap;
29184 }
29185
29186 void gc_heap::verify_no_pins (uint8_t* start, uint8_t* end)
29187 {
29188 #ifdef VERIFY_HEAP
29189     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
29190     {
29191         BOOL contains_pinned_plugs = FALSE;
29192         size_t mi = 0;
29193         mark* m = 0;
29194         while (mi != mark_stack_tos)
29195         {
29196             m = pinned_plug_of (mi);
29197             if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
29198             {
29199                 contains_pinned_plugs = TRUE;
29200                 break;
29201             }
29202             else
29203                 mi++;
29204         }
29205
29206         if (contains_pinned_plugs)
29207         {
29208             FATAL_GC_ERROR();
29209         }
29210     }
29211 #endif //VERIFY_HEAP
29212 }
29213
29214 void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
29215 {
29216     if (!should_expand_in_full_gc)
29217     {
29218         if ((condemned_gen_number != max_generation) && 
29219             (settings.pause_mode != pause_low_latency) &&
29220             (settings.pause_mode != pause_sustained_low_latency))
29221         {
29222             should_expand_in_full_gc = TRUE;
29223         }
29224     }
29225 }
29226
29227 void gc_heap::save_ephemeral_generation_starts()
29228 {
29229     for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
29230     {
29231         saved_ephemeral_plan_start[ephemeral_generation] = 
29232             generation_plan_allocation_start (generation_of (ephemeral_generation));
29233         saved_ephemeral_plan_start_size[ephemeral_generation] = 
29234             generation_plan_allocation_start_size (generation_of (ephemeral_generation));
29235     }
29236 }
29237
29238 generation* gc_heap::expand_heap (int condemned_generation,
29239                                   generation* consing_gen,
29240                                   heap_segment* new_heap_segment)
29241 {
29242     UNREFERENCED_PARAMETER(condemned_generation);
29243     assert (condemned_generation >= (max_generation -1));
29244     unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
29245     uint8_t*  start_address = generation_limit (max_generation);
29246     uint8_t*  end_address = heap_segment_allocated (ephemeral_heap_segment);
29247     BOOL should_promote_ephemeral = FALSE;
29248     ptrdiff_t eph_size = total_ephemeral_size;
29249 #ifdef BACKGROUND_GC
29250     dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
29251 #endif //BACKGROUND_GC
29252     settings.heap_expansion = TRUE;
29253
29254 #ifdef BACKGROUND_GC
29255     if (cm_in_progress)
29256     {
29257         if (!expanded_in_fgc)
29258         {
29259             expanded_in_fgc = TRUE;
29260         }
29261     }
29262 #endif //BACKGROUND_GC
29263
29264     //reset the elevation state for next time.
29265     dprintf (2, ("Elevation: elevation = el_none"));
29266     if (settings.should_lock_elevation && !expand_reused_seg_p())
29267         settings.should_lock_elevation = FALSE;
29268
29269     heap_segment* new_seg = new_heap_segment;
29270
29271     if (!new_seg)
29272         return consing_gen;
29273
29274     //copy the card and brick tables
29275     if (g_gc_card_table!= card_table)
29276         copy_brick_card_table();
29277
29278     BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
29279     dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
29280
29281     assert (generation_plan_allocation_start (generation_of (max_generation-1)));
29282     assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
29283             heap_segment_mem (ephemeral_heap_segment));
29284     assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
29285             heap_segment_committed (ephemeral_heap_segment));
29286
29287     assert (generation_plan_allocation_start (youngest_generation));
29288     assert (generation_plan_allocation_start (youngest_generation) <
29289             heap_segment_plan_allocated (ephemeral_heap_segment));
29290
29291     if (settings.pause_mode == pause_no_gc)
29292     {
29293         // We don't reuse for no gc, so the size used on the new eph seg is eph_size.
29294         if ((size_t)(heap_segment_reserved (new_seg) - heap_segment_mem (new_seg)) < (eph_size + soh_allocation_no_gc))
29295             should_promote_ephemeral = TRUE;
29296     }
29297     else
29298     {
29299         if (!use_bestfit)
29300         {
29301             should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
29302         }
29303     }
29304
29305     if (should_promote_ephemeral)
29306     {
29307         ephemeral_promotion = TRUE;
29308         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_new_seg_ep);
29309         dprintf (2, ("promoting ephemeral"));
29310         save_ephemeral_generation_starts();
29311     }
29312     else
29313     {
29314         // commit the new ephemeral segment all at once if it is a new one.
29315         if ((eph_size > 0) && new_segment_p)
29316         {
29317 #ifdef FEATURE_STRUCTALIGN
29318             // The destination may require a larger alignment padding than the source.
29319             // Assume the worst possible alignment padding.
29320             eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
29321 #endif // FEATURE_STRUCTALIGN
29322 #ifdef RESPECT_LARGE_ALIGNMENT
29323             //Since the generation start can be larger than min_obj_size
29324             //The alignment could be switched. 
29325             eph_size += switch_alignment_size(FALSE);
29326 #endif //RESPECT_LARGE_ALIGNMENT
29327             //Since the generation start can be larger than min_obj_size
29328             //Compare the alignment of the first object in gen1 
29329             if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
29330             {
29331                 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
29332                 return consing_gen;
29333             }
29334             heap_segment_used (new_seg) = heap_segment_committed (new_seg);
29335         }
29336
29337         //Fix the end of the old ephemeral heap segment
29338         heap_segment_plan_allocated (ephemeral_heap_segment) =
29339             generation_plan_allocation_start (generation_of (max_generation-1));
29340
29341         dprintf (3, ("Old ephemeral allocated set to %Ix",
29342                     (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
29343     }
29344
29345     if (new_segment_p)
29346     {
29347         // TODO - Is this really necessary? We should think about it.
29348         //initialize the first brick
29349         size_t first_brick = brick_of (heap_segment_mem (new_seg));
29350         set_brick (first_brick,
29351                 heap_segment_mem (new_seg) - brick_address (first_brick));
29352     }
29353
29354     //From this point on, we cannot run out of memory
29355
29356     //reset the allocation of the consing generation back to the end of the
29357     //old ephemeral segment
29358     generation_allocation_limit (consing_gen) =
29359         heap_segment_plan_allocated (ephemeral_heap_segment);
29360     generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
29361     generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
29362
29363     //clear the generation gap for all of the ephemeral generations
29364     {
29365         int generation_num = max_generation-1;
29366         while (generation_num >= 0)
29367         {
29368             generation* gen = generation_of (generation_num);
29369             generation_plan_allocation_start (gen) = 0;
29370             generation_num--;
29371         }
29372     }
29373
29374     heap_segment* old_seg = ephemeral_heap_segment;
29375     ephemeral_heap_segment = new_seg;
29376
29377     //Note: the ephemeral segment shouldn't be threaded onto the segment chain
29378     //because the relocation and compact phases shouldn't see it
29379
29380     // set the generation members used by allocate_in_expanded_heap
29381     // and switch to ephemeral generation
29382     consing_gen = ensure_ephemeral_heap_segment (consing_gen);
29383
29384     if (!should_promote_ephemeral)
29385     {
29386         realloc_plugs (consing_gen, old_seg, start_address, end_address,
29387                     active_new_gen_number);
29388     }
29389
29390     if (!use_bestfit)
29391     {
29392         repair_allocation_in_expanded_heap (consing_gen);
29393     }
29394
29395     // assert that the generation gap for all of the ephemeral generations were allocated.
29396 #ifdef _DEBUG
29397     {
29398         int generation_num = max_generation-1;
29399         while (generation_num >= 0)
29400         {
29401             generation* gen = generation_of (generation_num);
29402             assert (generation_plan_allocation_start (gen));
29403             generation_num--;
29404         }
29405     }
29406 #endif // _DEBUG
29407
29408     if (!new_segment_p)
29409     {
29410         dprintf (2, ("Demoting ephemeral segment"));
29411         //demote the entire segment.
29412         settings.demotion = TRUE;
29413         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
29414         demotion_low = heap_segment_mem (ephemeral_heap_segment);
29415         demotion_high = heap_segment_reserved (ephemeral_heap_segment);
29416     }
29417     else
29418     {
29419         demotion_low = MAX_PTR;
29420         demotion_high = 0;
29421 #ifndef MULTIPLE_HEAPS
29422         settings.demotion = FALSE;
29423         get_gc_data_per_heap()->clear_mechanism_bit (gc_demotion_bit);
29424 #endif //!MULTIPLE_HEAPS
29425     }
29426     ptrdiff_t eph_size1 = total_ephemeral_size;
29427     MAYBE_UNUSED_VAR(eph_size1);
29428
29429     if (!should_promote_ephemeral && new_segment_p)
29430     {
29431         assert (eph_size1 <= eph_size);
29432     }
29433
29434     if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
29435     {
29436         // This is to catch when we accidently delete a segment that has pins.
29437         verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
29438     }
29439
29440     verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
29441
29442     dprintf(2,("---- End of Heap Expansion ----"));
29443     return consing_gen;
29444 }
29445
29446 void gc_heap::set_static_data()
29447 {
29448     static_data* pause_mode_sdata = static_data_table[latency_level];
29449     for (int i = 0; i < NUMBERGENERATIONS; i++)
29450     {
29451         dynamic_data* dd = dynamic_data_of (i);
29452         static_data* sdata = &pause_mode_sdata[i];
29453
29454         dd->sdata = sdata;
29455         dd->min_size = sdata->min_size;
29456
29457         dprintf (GTC_LOG, ("PM: %d - min: %Id, max: %Id, fr_l: %Id, fr_b: %d%%",
29458             settings.pause_mode,
29459             dd->min_size, dd_max_size, 
29460             dd->fragmentation_limit, (int)(dd->fragmentation_burden_limit * 100)));
29461     }
29462 }
29463
29464 // Initialize the values that are not const.
29465 void gc_heap::init_static_data()
29466 {
29467     size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
29468     size_t gen0_min_size = Align(gen0size / 8 * 5);
29469
29470     size_t gen0_max_size =
29471 #ifdef MULTIPLE_HEAPS
29472         max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024));
29473 #else //MULTIPLE_HEAPS
29474         (gc_can_use_concurrent ?
29475             6*1024*1024 :
29476             max (6*1024*1024,  min ( Align(soh_segment_size/2), 200*1024*1024)));
29477 #endif //MULTIPLE_HEAPS
29478
29479     // TODO: gen0_max_size has a 200mb cap; gen1_max_size should also have a cap.
29480     size_t gen1_max_size = 
29481 #ifdef MULTIPLE_HEAPS
29482         max (6*1024*1024, Align(soh_segment_size/2));
29483 #else //MULTIPLE_HEAPS
29484         (gc_can_use_concurrent ?
29485             6*1024*1024 :
29486             max (6*1024*1024, Align(soh_segment_size/2)));
29487 #endif //MULTIPLE_HEAPS
29488
29489     dprintf (GTC_LOG, ("gen0size: %Id, gen0 min: %Id, max: %Id, gen1 max: %Id",
29490         gen0size, gen0_min_size, gen0_max_size, gen1_max_size));
29491
29492     for (int i = latency_level_first; i <= latency_level_last; i++)
29493     {
29494         static_data_table[i][0].min_size = gen0_min_size;
29495         static_data_table[i][0].max_size = gen0_max_size;
29496         static_data_table[i][1].max_size = gen1_max_size;
29497     }
29498 }
29499
29500 bool gc_heap::init_dynamic_data()
29501 {
29502     qpf = GCToOSInterface::QueryPerformanceFrequency();
29503
29504     uint32_t now = (uint32_t)GetHighPrecisionTimeStamp();
29505
29506     set_static_data();
29507
29508     for (int i = 0; i <= max_generation+1; i++)
29509     {
29510         dynamic_data* dd = dynamic_data_of (i);
29511         dd->gc_clock = 0;
29512         dd->time_clock = now;
29513         dd->current_size = 0;
29514         dd->promoted_size = 0;
29515         dd->collection_count = 0;
29516         dd->new_allocation = dd->min_size;
29517         dd->gc_new_allocation = dd->new_allocation;
29518         dd->desired_allocation = dd->new_allocation;
29519         dd->fragmentation = 0;
29520     }
29521
29522 #ifdef GC_CONFIG_DRIVEN
29523     if (heap_number == 0)
29524         time_init = now;
29525 #endif //GC_CONFIG_DRIVEN
29526
29527     return true;
29528 }
29529
29530 float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
29531 {
29532     if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
29533         return ((limit - limit*cst) / (1.0f - (cst * limit)));
29534     else
29535         return max_limit;
29536 }
29537
29538
29539 //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may 
29540 //not be correct (collection happened too soon). Correct with a linear estimation based on the previous 
29541 //value of the budget 
29542 static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation, 
29543                                        size_t previous_desired_allocation, size_t collection_count)
29544 {
29545     if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
29546     {
29547         dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0)));
29548         new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
29549     }
29550 #if 0 
29551     size_t smoothing = 3; // exponential smoothing factor
29552     if (smoothing  > collection_count)
29553         smoothing  = collection_count;
29554     new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
29555 #else
29556     UNREFERENCED_PARAMETER(collection_count);
29557 #endif //0
29558     return new_allocation;
29559 }
29560
29561 size_t gc_heap::desired_new_allocation (dynamic_data* dd,
29562                                         size_t out, int gen_number,
29563                                         int pass)
29564 {
29565     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29566
29567     if (dd_begin_data_size (dd) == 0)
29568     {
29569         size_t new_allocation = dd_min_size (dd);
29570         current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;        
29571         return new_allocation;
29572     }
29573     else
29574     {
29575         float     cst;
29576         size_t    previous_desired_allocation = dd_desired_allocation (dd);
29577         size_t    current_size = dd_current_size (dd);
29578         float     max_limit = dd_max_limit (dd);
29579         float     limit = dd_limit (dd);
29580         size_t    min_gc_size = dd_min_size (dd);
29581         float     f = 0;
29582         size_t    max_size = dd_max_size (dd);
29583         size_t    new_allocation = 0;
29584         float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
29585         if (gen_number >= max_generation)
29586         {
29587             size_t    new_size = 0;
29588
29589             cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
29590
29591             f = surv_to_growth (cst, limit, max_limit);
29592             size_t max_growth_size = (size_t)(max_size / f);
29593             if (current_size >= max_growth_size)
29594             {
29595                 new_size = max_size;
29596             }
29597             else
29598             {
29599                 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
29600             }
29601
29602             assert ((new_size >= current_size) || (new_size == max_size));
29603
29604             if (gen_number == max_generation)
29605             {
29606                 new_allocation  =  max((new_size - current_size), min_gc_size);
29607
29608                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
29609                                                           dd_desired_allocation (dd), dd_collection_count (dd));
29610
29611                 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
29612                 {
29613                     //reducing allocation in case of fragmentation
29614                     size_t new_allocation1 = max (min_gc_size,
29615                                                   // CAN OVERFLOW
29616                                                   (size_t)((float)new_allocation * current_size /
29617                                                            ((float)current_size + 2*dd_fragmentation (dd))));
29618                     dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
29619                                  new_allocation, new_allocation1));
29620                     new_allocation = new_allocation1;
29621                 }
29622             }
29623             else //large object heap
29624             {
29625                 uint32_t memory_load = 0;
29626                 uint64_t available_physical = 0;
29627                 get_memory_info (&memory_load, &available_physical);
29628                 if (heap_number == 0)
29629                     settings.exit_memory_load = memory_load;
29630                 if (available_physical > 1024*1024)
29631                     available_physical -= 1024*1024;
29632
29633                 uint64_t available_free = available_physical + (uint64_t)generation_free_list_space (generation_of (gen_number));
29634                 if (available_free > (uint64_t)MAX_PTR)
29635                 {
29636                     available_free = (uint64_t)MAX_PTR;
29637                 }
29638
29639                 //try to avoid OOM during large object allocation
29640                 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))), 
29641                                           (size_t)available_free), 
29642                                       max ((current_size/4), min_gc_size));
29643
29644                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29645                                                           dd_desired_allocation (dd), dd_collection_count (dd));
29646
29647             }
29648         }
29649         else
29650         {
29651             size_t survivors = out;
29652             cst = float (survivors) / float (dd_begin_data_size (dd));
29653             f = surv_to_growth (cst, limit, max_limit);
29654             new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
29655
29656             new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
29657                                                       dd_desired_allocation (dd), dd_collection_count (dd));
29658
29659             if (gen_number == 0)
29660             {
29661                 if (pass == 0)
29662                 {
29663
29664                     //printf ("%f, %Id\n", cst, new_allocation);
29665                     size_t free_space = generation_free_list_space (generation_of (gen_number));
29666                     // DTREVIEW - is min_gc_size really a good choice? 
29667                     // on 64-bit this will almost always be true.
29668                     dprintf (GTC_LOG, ("frag: %Id, min: %Id", free_space, min_gc_size));
29669                     if (free_space > min_gc_size)
29670                     {
29671                         settings.gen0_reduction_count = 2;
29672                     }
29673                     else
29674                     {
29675                         if (settings.gen0_reduction_count > 0)
29676                             settings.gen0_reduction_count--;
29677                     }
29678                 }
29679                 if (settings.gen0_reduction_count > 0)
29680                 {
29681                     dprintf (2, ("Reducing new allocation based on fragmentation"));
29682                     new_allocation = min (new_allocation,
29683                                           max (min_gc_size, (max_size/3)));
29684                 }
29685             }
29686         }
29687
29688         size_t new_allocation_ret = 
29689             Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
29690         int gen_data_index = gen_number;
29691         gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
29692         gen_data->new_allocation = new_allocation_ret;
29693
29694         dd_surv (dd) = cst;
29695
29696 #ifdef SIMPLE_DPRINTF
29697         dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
29698                      heap_number, gen_number, out, current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)),
29699                      (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
29700 #else
29701         dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
29702         dprintf (1,("current: %Id alloc: %Id ", current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd))));
29703         dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
29704                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
29705 #endif //SIMPLE_DPRINTF
29706
29707         return new_allocation_ret;
29708     }
29709 }
29710
29711 //returns the planned size of a generation (including free list element)
29712 size_t gc_heap::generation_plan_size (int gen_number)
29713 {
29714     if (0 == gen_number)
29715         return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
29716                     generation_plan_allocation_start (generation_of (gen_number))),
29717                    (int)Align (min_obj_size));
29718     else
29719     {
29720         generation* gen = generation_of (gen_number);
29721         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
29722             return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
29723                     generation_plan_allocation_start (generation_of (gen_number)));
29724         else
29725         {
29726             size_t gensize = 0;
29727             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
29728
29729             PREFIX_ASSUME(seg != NULL);
29730
29731             while (seg && (seg != ephemeral_heap_segment))
29732             {
29733                 gensize += heap_segment_plan_allocated (seg) -
29734                            heap_segment_mem (seg);
29735                 seg = heap_segment_next_rw (seg);
29736             }
29737             if (seg)
29738             {
29739                 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
29740                             heap_segment_mem (ephemeral_heap_segment));
29741             }
29742             return gensize;
29743         }
29744     }
29745
29746 }
29747
29748 //returns the size of a generation (including free list element)
29749 size_t gc_heap::generation_size (int gen_number)
29750 {
29751     if (0 == gen_number)
29752         return max((heap_segment_allocated (ephemeral_heap_segment) -
29753                     generation_allocation_start (generation_of (gen_number))),
29754                    (int)Align (min_obj_size));
29755     else
29756     {
29757         generation* gen = generation_of (gen_number);
29758         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
29759             return (generation_allocation_start (generation_of (gen_number - 1)) -
29760                     generation_allocation_start (generation_of (gen_number)));
29761         else
29762         {
29763             size_t gensize = 0;
29764             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
29765
29766             PREFIX_ASSUME(seg != NULL);
29767
29768             while (seg && (seg != ephemeral_heap_segment))
29769             {
29770                 gensize += heap_segment_allocated (seg) -
29771                            heap_segment_mem (seg);
29772                 seg = heap_segment_next_rw (seg);
29773             }
29774             if (seg)
29775             {
29776                 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
29777                             heap_segment_mem (ephemeral_heap_segment));
29778             }
29779
29780             return gensize;
29781         }
29782     }
29783
29784 }
29785
29786 size_t  gc_heap::compute_in (int gen_number)
29787 {
29788     assert (gen_number != 0);
29789     dynamic_data* dd = dynamic_data_of (gen_number);
29790
29791     size_t in = generation_allocation_size (generation_of (gen_number));
29792
29793     if (gen_number == max_generation && ephemeral_promotion)
29794     {
29795         in = 0;
29796         for (int i = 0; i <= max_generation; i++)
29797         {
29798             dynamic_data* dd = dynamic_data_of (i);
29799             in += dd_survived_size (dd);
29800             if (i != max_generation)
29801             {
29802                 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
29803             }
29804         }
29805     }
29806
29807     dd_gc_new_allocation (dd) -= in;
29808     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
29809
29810     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29811     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
29812     gen_data->in = in;
29813
29814     generation_allocation_size (generation_of (gen_number)) = 0;
29815     return in;
29816 }
29817
29818 void  gc_heap::compute_promoted_allocation (int gen_number)
29819 {
29820     compute_in (gen_number);
29821 }
29822
29823 #ifdef BIT64
29824 inline
29825 size_t gc_heap::trim_youngest_desired (uint32_t memory_load,
29826                                        size_t total_new_allocation,
29827                                        size_t total_min_allocation)
29828 {
29829     if (memory_load < MAX_ALLOWED_MEM_LOAD)
29830     {
29831         // If the total of memory load and gen0 budget exceeds 
29832         // our max memory load limit, trim the gen0 budget so the total 
29833         // is the max memory load limit.
29834         size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
29835         return min (total_new_allocation, remain_memory_load);
29836     }
29837     else
29838     {
29839         return max (mem_one_percent, total_min_allocation);
29840     }
29841 }
29842
29843 size_t gc_heap::joined_youngest_desired (size_t new_allocation)
29844 {
29845     dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
29846
29847     size_t final_new_allocation = new_allocation;
29848     if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
29849     {
29850         uint32_t num_heaps = 1;
29851
29852 #ifdef MULTIPLE_HEAPS
29853         num_heaps = gc_heap::n_heaps;
29854 #endif //MULTIPLE_HEAPS
29855
29856         size_t total_new_allocation = new_allocation * num_heaps;
29857         size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
29858
29859         if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
29860             (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
29861         {
29862             uint32_t memory_load = 0;
29863             get_memory_info (&memory_load);
29864             settings.exit_memory_load = memory_load;
29865             dprintf (2, ("Current emory load: %d", memory_load));
29866
29867             size_t final_total = 
29868                 trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation);
29869             size_t max_new_allocation = 
29870 #ifdef MULTIPLE_HEAPS
29871                                          dd_max_size (g_heaps[0]->dynamic_data_of (0));
29872 #else //MULTIPLE_HEAPS
29873                                          dd_max_size (dynamic_data_of (0));
29874 #endif //MULTIPLE_HEAPS
29875
29876             final_new_allocation  = min (Align ((final_total / num_heaps), get_alignment_constant (TRUE)), max_new_allocation);
29877         }
29878     }
29879
29880     if (final_new_allocation < new_allocation)
29881     {
29882         settings.gen0_reduction_count = 2;
29883     }
29884
29885     return final_new_allocation;
29886 }
29887 #endif // BIT64 
29888
29889 inline
29890 gc_history_per_heap* gc_heap::get_gc_data_per_heap()
29891 {
29892 #ifdef BACKGROUND_GC
29893     return (settings.concurrent ? &bgc_data_per_heap : &gc_data_per_heap);
29894 #else
29895     return &gc_data_per_heap;
29896 #endif //BACKGROUND_GC
29897 }
29898
29899 void gc_heap::compute_new_dynamic_data (int gen_number)
29900 {
29901     PREFIX_ASSUME(gen_number >= 0);
29902     PREFIX_ASSUME(gen_number <= max_generation);
29903
29904     dynamic_data* dd = dynamic_data_of (gen_number);
29905     generation*   gen = generation_of (gen_number);
29906     size_t        in = (gen_number==0) ? 0 : compute_in (gen_number);
29907
29908     size_t total_gen_size = generation_size (gen_number);
29909     //keep track of fragmentation
29910     dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
29911     dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
29912
29913     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29914
29915     size_t out = dd_survived_size (dd);
29916
29917     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
29918     gen_data->size_after = total_gen_size;
29919     gen_data->free_list_space_after = generation_free_list_space (gen);
29920     gen_data->free_obj_space_after = generation_free_obj_space (gen);
29921
29922     if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
29923     {
29924         // When we are in the low latency mode, we can still be
29925         // condemning more than gen1's 'cause of induced GCs.
29926         dd_desired_allocation (dd) = low_latency_alloc;
29927     }
29928     else
29929     {
29930         if (gen_number == 0)
29931         {
29932             //compensate for dead finalizable objects promotion.
29933             //they shoudn't be counted for growth.
29934             size_t final_promoted = 0;
29935             final_promoted = min (promoted_bytes (heap_number), out);
29936             // Prefast: this is clear from above but prefast needs to be told explicitly
29937             PREFIX_ASSUME(final_promoted <= out);
29938
29939             dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
29940             dd_freach_previous_promotion (dd) = final_promoted;
29941             size_t lower_bound = desired_new_allocation  (dd, out-final_promoted, gen_number, 0);
29942
29943             if (settings.condemned_generation == 0)
29944             {
29945                 //there is no noise.
29946                 dd_desired_allocation (dd) = lower_bound;
29947             }
29948             else
29949             {
29950                 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
29951
29952                 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
29953                 //assert ( lower_bound <= higher_bound);
29954
29955                 //discount the noise. Change the desired allocation
29956                 //only if the previous value is outside of the range.
29957                 if (dd_desired_allocation (dd) < lower_bound)
29958                 {
29959                     dd_desired_allocation (dd) = lower_bound;
29960                 }
29961                 else if (dd_desired_allocation (dd) > higher_bound)
29962                 {
29963                     dd_desired_allocation (dd) = higher_bound;
29964                 }
29965 #if defined (BIT64) && !defined (MULTIPLE_HEAPS)
29966                 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
29967 #endif // BIT64 && !MULTIPLE_HEAPS
29968                 trim_youngest_desired_low_memory();
29969                 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
29970             }
29971         }
29972         else
29973         {
29974             dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
29975         }
29976     }
29977
29978     gen_data->pinned_surv = dd_pinned_survived_size (dd);
29979     gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd);
29980
29981     dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
29982     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
29983
29984     //update counter
29985     dd_promoted_size (dd) = out;
29986     if (gen_number == max_generation)
29987     {
29988         dd = dynamic_data_of (max_generation+1);
29989         total_gen_size = generation_size (max_generation + 1);
29990         dd_fragmentation (dd) = generation_free_list_space (large_object_generation) + 
29991                                 generation_free_obj_space (large_object_generation);
29992         dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
29993         dd_survived_size (dd) = dd_current_size (dd);
29994         in = 0;
29995         out = dd_current_size (dd);
29996         dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
29997         dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
29998                                            get_alignment_constant (FALSE));
29999         dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30000
30001         gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
30002         gen_data->size_after = total_gen_size;
30003         gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
30004         gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
30005         gen_data->npinned_surv = out;
30006 #ifdef BACKGROUND_GC
30007         end_loh_size = total_gen_size;
30008 #endif //BACKGROUND_GC
30009         //update counter
30010         dd_promoted_size (dd) = out;
30011     }
30012 }
30013
30014 void gc_heap::trim_youngest_desired_low_memory()
30015 {
30016     if (g_low_memory_status)
30017     {
30018         size_t committed_mem = 0;
30019         heap_segment* seg = generation_start_segment (generation_of (max_generation));
30020         while (seg)
30021         {
30022             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30023             seg = heap_segment_next (seg);
30024         }
30025         seg = generation_start_segment (generation_of (max_generation + 1));
30026         while (seg)
30027         {
30028             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30029             seg = heap_segment_next (seg);
30030         }
30031
30032         dynamic_data* dd = dynamic_data_of (0);
30033         size_t current = dd_desired_allocation (dd);
30034         size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_size (dd));
30035
30036         dd_desired_allocation (dd) = min (current, candidate);
30037     }
30038 }
30039
30040 void gc_heap::decommit_ephemeral_segment_pages()
30041 {
30042     if (settings.concurrent)
30043     {
30044         return;
30045     }
30046
30047     size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30048     dynamic_data* dd = dynamic_data_of (0);
30049
30050 #ifndef MULTIPLE_HEAPS
30051     size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
30052     size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
30053     size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
30054
30055     if (dd_desired_allocation (dd) > gc_gen0_desired_high)
30056     {
30057         gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space;
30058     }
30059
30060     if (ephemeral_elapsed >= decommit_timeout)
30061     {
30062         slack_space = min (slack_space, gc_gen0_desired_high);
30063
30064         gc_last_ephemeral_decommit_time = dd_time_clock(dd);
30065         gc_gen0_desired_high = 0;
30066     }
30067 #endif //!MULTIPLE_HEAPS
30068
30069     if (settings.condemned_generation >= (max_generation-1))
30070     {
30071         size_t new_slack_space = 
30072 #ifdef BIT64
30073                     max(min(min(soh_segment_size/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
30074 #else
30075 #ifdef FEATURE_CORECLR
30076                     dd_desired_allocation (dd);
30077 #else
30078                     dd_max_size (dd);
30079 #endif //FEATURE_CORECLR                                    
30080 #endif // BIT64
30081
30082         slack_space = min (slack_space, new_slack_space);
30083     }
30084
30085     decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);    
30086
30087     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30088     current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30089 }
30090
30091 size_t gc_heap::new_allocation_limit (size_t size, size_t free_size, int gen_number)
30092 {
30093     dynamic_data* dd        = dynamic_data_of (gen_number);
30094     ptrdiff_t           new_alloc = dd_new_allocation (dd);
30095     assert (new_alloc == (ptrdiff_t)Align (new_alloc,
30096                                            get_alignment_constant (!(gen_number == (max_generation+1)))));
30097     size_t        limit     = min (max (new_alloc, (ptrdiff_t)size), (ptrdiff_t)free_size);
30098     assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
30099     dd_new_allocation (dd) = (new_alloc - limit );
30100     return limit;
30101 }
30102
30103 //This is meant to be called by decide_on_compacting.
30104
30105 size_t gc_heap::generation_fragmentation (generation* gen,
30106                                           generation* consing_gen,
30107                                           uint8_t* end)
30108 {
30109     size_t frag;
30110     uint8_t* alloc = generation_allocation_pointer (consing_gen);
30111     // If the allocation pointer has reached the ephemeral segment
30112     // fine, otherwise the whole ephemeral segment is considered
30113     // fragmentation
30114     if (in_range_for_segment (alloc, ephemeral_heap_segment))
30115         {
30116             if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
30117                 frag = end - alloc;
30118             else
30119             {
30120                 // case when no survivors, allocated set to beginning
30121                 frag = 0;
30122             }
30123             dprintf (3, ("ephemeral frag: %Id", frag));
30124         }
30125     else
30126         frag = (heap_segment_allocated (ephemeral_heap_segment) -
30127                 heap_segment_mem (ephemeral_heap_segment));
30128     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30129
30130     PREFIX_ASSUME(seg != NULL);
30131
30132     while (seg != ephemeral_heap_segment)
30133     {
30134         frag += (heap_segment_allocated (seg) -
30135                  heap_segment_plan_allocated (seg));
30136         dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
30137                      (heap_segment_allocated (seg) -
30138                       heap_segment_plan_allocated (seg))));
30139
30140         seg = heap_segment_next_rw (seg);
30141         assert (seg);
30142     }
30143     dprintf (3, ("frag: %Id discounting pinned plugs", frag));
30144     //add the length of the dequeued plug free space
30145     size_t bos = 0;
30146     while (bos < mark_stack_bos)
30147     {
30148         frag += (pinned_len (pinned_plug_of (bos)));
30149         bos++;
30150     }
30151
30152     return frag;
30153 }
30154
30155 // for SOH this returns the total sizes of the generation and its 
30156 // younger generation(s).
30157 // for LOH this returns just LOH size.
30158 size_t gc_heap::generation_sizes (generation* gen)
30159 {
30160     size_t result = 0;
30161     if (generation_start_segment (gen ) == ephemeral_heap_segment)
30162         result = (heap_segment_allocated (ephemeral_heap_segment) -
30163                   generation_allocation_start (gen));
30164     else
30165     {
30166         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
30167
30168         PREFIX_ASSUME(seg != NULL);
30169
30170         while (seg)
30171         {
30172             result += (heap_segment_allocated (seg) -
30173                        heap_segment_mem (seg));
30174             seg = heap_segment_next_in_range (seg);
30175         }
30176     }
30177
30178     return result;
30179 }
30180
30181 BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
30182                                     size_t fragmentation,
30183                                     BOOL& should_expand)
30184 {
30185     BOOL should_compact = FALSE;
30186     should_expand = FALSE;
30187     generation*   gen = generation_of (condemned_gen_number);
30188     dynamic_data* dd = dynamic_data_of (condemned_gen_number);
30189     size_t gen_sizes     = generation_sizes(gen);
30190     float  fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
30191                                     (float (fragmentation) / gen_sizes) );
30192
30193     dprintf (GTC_LOG, ("fragmentation: %Id (%d%%)", fragmentation, (int)(fragmentation_burden * 100.0)));
30194
30195 #ifdef STRESS_HEAP
30196     // for pure GC stress runs we need compaction, for GC stress "mix"
30197     // we need to ensure a better mix of compacting and sweeping collections
30198     if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
30199         && !g_pConfig->IsGCStressMix())
30200         should_compact = TRUE;
30201
30202 #ifdef GC_STATS
30203     // in GC stress "mix" mode, for stress induced collections make sure we 
30204     // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
30205     // against the GC's determination, as it may lead to premature OOMs.
30206     if (g_pConfig->IsGCStressMix() && settings.stress_induced)
30207     {
30208         int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
30209         int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
30210         if (compactions < sweeps / 10)
30211         {
30212             should_compact = TRUE;
30213         }
30214     }
30215 #endif // GC_STATS
30216 #endif //STRESS_HEAP
30217
30218     if (GCConfig::GetForceCompact())
30219         should_compact = TRUE;
30220
30221     if ((condemned_gen_number == max_generation) && last_gc_before_oom)
30222     {
30223         should_compact = TRUE;
30224         last_gc_before_oom = FALSE;
30225         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc);
30226     }
30227
30228     if (settings.reason == reason_induced_compacting)
30229     {
30230         dprintf (2, ("induced compacting GC"));
30231         should_compact = TRUE;
30232         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
30233     }
30234
30235     dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
30236                 fragmentation, (int) (100*fragmentation_burden)));
30237
30238     if (!should_compact)
30239     {
30240         if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
30241         {
30242             dprintf(GTC_LOG, ("compacting due to low ephemeral"));
30243             should_compact = TRUE;
30244             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral);
30245         }
30246     }
30247
30248     if (should_compact)
30249     {
30250         if ((condemned_gen_number >= (max_generation - 1)))
30251         {
30252             if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
30253             {
30254                 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
30255                 should_expand = TRUE;
30256             }
30257         }
30258     }
30259
30260 #ifdef BIT64
30261     BOOL high_memory = FALSE;
30262 #endif // BIT64
30263
30264     if (!should_compact)
30265     {
30266         // We are not putting this in dt_high_frag_p because it's not exactly
30267         // high fragmentation - it's just enough planned fragmentation for us to 
30268         // want to compact. Also the "fragmentation" we are talking about here
30269         // is different from anywhere else.
30270         BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
30271                                 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
30272
30273         if (frag_exceeded)
30274         {
30275 #ifdef BACKGROUND_GC
30276             // do not force compaction if this was a stress-induced GC
30277             IN_STRESS_HEAP(if (!settings.stress_induced))
30278             {
30279 #endif // BACKGROUND_GC
30280             assert (settings.concurrent == FALSE);
30281             should_compact = TRUE;
30282             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag);
30283 #ifdef BACKGROUND_GC
30284             }
30285 #endif // BACKGROUND_GC
30286         }
30287
30288 #ifdef BIT64
30289         // check for high memory situation
30290         if(!should_compact)
30291         {
30292             uint32_t num_heaps = 1;
30293 #ifdef MULTIPLE_HEAPS
30294             num_heaps = gc_heap::n_heaps;
30295 #endif // MULTIPLE_HEAPS
30296             
30297             ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
30298             if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th))
30299             {
30300                 if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps)))
30301                 {
30302                     dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
30303                     should_compact = TRUE;
30304                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag);
30305                 }
30306                 high_memory = TRUE;
30307             }
30308             else if(settings.entry_memory_load >= v_high_memory_load_th)
30309             {
30310                 if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps)))
30311                 {
30312                     dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
30313                     should_compact = TRUE;
30314                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag);
30315                 }
30316                 high_memory = TRUE;
30317             }
30318         }
30319 #endif // BIT64
30320     }
30321
30322     // The purpose of calling ensure_gap_allocation here is to make sure
30323     // that we actually are able to commit the memory to allocate generation
30324     // starts.
30325     if ((should_compact == FALSE) &&
30326         (ensure_gap_allocation (condemned_gen_number) == FALSE))
30327     {
30328         should_compact = TRUE;
30329         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps);
30330     }
30331
30332     if (settings.condemned_generation == max_generation)
30333     {
30334         //check the progress
30335         if (
30336 #ifdef BIT64
30337             (high_memory && !should_compact) ||
30338 #endif // BIT64
30339             (generation_plan_allocation_start (generation_of (max_generation - 1)) >= 
30340                 generation_allocation_start (generation_of (max_generation - 1))))
30341         {
30342             dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked",
30343                      generation_size (max_generation),
30344                      generation_plan_size (max_generation)));
30345             //no progress -> lock
30346             settings.should_lock_elevation = TRUE;
30347         }
30348     }
30349
30350     if (settings.pause_mode == pause_no_gc)
30351     {
30352         should_compact = TRUE;
30353         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment))
30354             < soh_allocation_no_gc)
30355         {
30356             should_expand = TRUE;
30357         }
30358     }
30359
30360     dprintf (2, ("will %s", (should_compact ? "compact" : "sweep")));
30361     return should_compact;
30362 }
30363
30364 size_t align_lower_good_size_allocation (size_t size)
30365 {
30366     return (size/64)*64;
30367 }
30368
30369 size_t gc_heap::approximate_new_allocation()
30370 {
30371     dynamic_data* dd0 = dynamic_data_of (0);
30372     return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
30373 }
30374
30375 // After we did a GC we expect to have at least this 
30376 // much space at the end of the segment to satisfy
30377 // a reasonable amount of allocation requests.
30378 size_t gc_heap::end_space_after_gc()
30379 {
30380     return max ((dd_min_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
30381 }
30382
30383 BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
30384 {
30385     uint8_t* start = 0;
30386     
30387     if ((tp == tuning_deciding_condemned_gen) ||
30388         (tp == tuning_deciding_compaction))
30389     {
30390         start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
30391         if (settings.concurrent)
30392         {
30393             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)", 
30394                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30395         }
30396         else
30397         {
30398             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)", 
30399                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
30400         }
30401     }
30402     else if (tp == tuning_deciding_expansion)
30403     {
30404         start = heap_segment_plan_allocated (ephemeral_heap_segment);
30405         dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan", 
30406             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
30407     }
30408     else
30409     {
30410         assert (tp == tuning_deciding_full_gc);
30411         dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)", 
30412             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30413         start = alloc_allocated;
30414     }
30415     
30416     if (start == 0) // empty ephemeral generations
30417     {
30418         assert (tp == tuning_deciding_expansion);
30419         // if there are no survivors in the ephemeral segment, 
30420         // this should be the beginning of ephemeral segment.
30421         start = generation_allocation_pointer (generation_of (max_generation));
30422         assert (start == heap_segment_mem (ephemeral_heap_segment));
30423     }
30424
30425     if (tp == tuning_deciding_expansion)
30426     {
30427         assert (settings.condemned_generation >= (max_generation-1));
30428         size_t gen0size = approximate_new_allocation();
30429         size_t eph_size = gen0size;
30430
30431         for (int j = 1; j <= max_generation-1; j++)
30432         {
30433             eph_size += 2*dd_min_size (dynamic_data_of(j));
30434         }
30435         
30436         // We must find room for one large object and enough room for gen0size
30437         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
30438         {
30439             dprintf (3, ("Enough room before end of segment"));
30440             return TRUE;
30441         }
30442         else
30443         {
30444             size_t room = align_lower_good_size_allocation
30445                 (heap_segment_reserved (ephemeral_heap_segment) - start);
30446             size_t end_seg = room;
30447
30448             //look at the plug free space
30449             size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
30450             bool large_chunk_found = FALSE;
30451             size_t bos = 0;
30452             uint8_t* gen0start = generation_plan_allocation_start (youngest_generation);
30453             dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
30454             if (gen0start == 0)
30455                 return FALSE;
30456             dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
30457                          room, gen0size));
30458             while ((bos < mark_stack_bos) &&
30459                    !((room >= gen0size) && large_chunk_found))
30460             {
30461                 uint8_t* plug = pinned_plug (pinned_plug_of (bos));
30462                 if (in_range_for_segment (plug, ephemeral_heap_segment))
30463                 {
30464                     if (plug >= gen0start)
30465                     {
30466                         size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
30467                         room += chunk;
30468                         if (!large_chunk_found)
30469                         {
30470                             large_chunk_found = (chunk >= largest_alloc);
30471                         }
30472                         dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
30473                                      room, large_chunk_found));
30474                     }
30475                 }
30476                 bos++;
30477             }
30478
30479             if (room >= gen0size)
30480             {
30481                 if (large_chunk_found)
30482                 {
30483                     dprintf (3, ("Enough room"));
30484                     return TRUE;
30485                 }
30486                 else
30487                 {
30488                     // now we need to find largest_alloc at the end of the segment.
30489                     if (end_seg >= end_space_after_gc())
30490                     {
30491                         dprintf (3, ("Enough room (may need end of seg)"));
30492                         return TRUE;
30493                     }
30494                 }
30495             }
30496
30497             dprintf (3, ("Not enough room"));
30498                 return FALSE;
30499         }
30500     }
30501     else
30502     {
30503         size_t end_space = 0;
30504         dynamic_data* dd = dynamic_data_of (0);
30505         if ((tp == tuning_deciding_condemned_gen) ||
30506             (tp == tuning_deciding_full_gc))
30507         {
30508             end_space = 2*dd_min_size (dd);
30509         }
30510         else
30511         {
30512             assert (tp == tuning_deciding_compaction);
30513             end_space = approximate_new_allocation();
30514         }
30515
30516         if (!((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space))
30517         {
30518             dprintf (GTC_LOG, ("ephemeral_gen_fit_p: does not fit without compaction"));
30519         }
30520         return ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space);
30521     }
30522 }
30523
30524 CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_bytes)
30525 {
30526     //create a new alloc context because gen3context is shared.
30527     alloc_context acontext;
30528     acontext.alloc_ptr = 0;
30529     acontext.alloc_limit = 0;
30530     acontext.alloc_bytes = 0;
30531 #ifdef MULTIPLE_HEAPS
30532     acontext.set_alloc_heap(vm_heap);
30533 #endif //MULTIPLE_HEAPS
30534
30535 #ifdef MARK_ARRAY
30536     uint8_t* current_lowest_address = lowest_address;
30537     uint8_t* current_highest_address = highest_address;
30538 #ifdef BACKGROUND_GC
30539     if (recursive_gc_sync::background_running_p())
30540     {
30541         current_lowest_address = background_saved_lowest_address;
30542         current_highest_address = background_saved_highest_address;
30543     }
30544 #endif //BACKGROUND_GC
30545 #endif // MARK_ARRAY
30546
30547     #if BIT64
30548     size_t maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
30549     #else
30550     size_t maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
30551     #endif
30552
30553     if (jsize >= maxObjectSize)
30554     {
30555         if (GCConfig::GetBreakOnOOM())
30556         {
30557             GCToOSInterface::DebugBreak();
30558         }
30559         return NULL;
30560     }
30561
30562     size_t size = AlignQword (jsize);
30563     int align_const = get_alignment_constant (FALSE);
30564 #ifdef FEATURE_LOH_COMPACTION
30565     size_t pad = Align (loh_padding_obj_size, align_const);
30566 #else
30567     size_t pad = 0;
30568 #endif //FEATURE_LOH_COMPACTION
30569
30570     assert (size >= Align (min_obj_size, align_const));
30571 #ifdef _MSC_VER
30572 #pragma inline_depth(0)
30573 #endif //_MSC_VER
30574     if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
30575     {
30576         return 0;
30577     }
30578
30579 #ifdef _MSC_VER
30580 #pragma inline_depth(20)
30581 #endif //_MSC_VER
30582
30583 #ifdef FEATURE_LOH_COMPACTION
30584     // The GC allocator made a free object already in this alloc context and
30585     // adjusted the alloc_ptr accordingly.
30586 #endif //FEATURE_LOH_COMPACTION
30587
30588     uint8_t*  result = acontext.alloc_ptr;
30589
30590     assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
30591     alloc_bytes += size;
30592
30593     CObjectHeader* obj = (CObjectHeader*)result;
30594
30595 #ifdef MARK_ARRAY
30596     if (recursive_gc_sync::background_running_p())
30597     {
30598         if ((result < current_highest_address) && (result >= current_lowest_address))
30599         {
30600             dprintf (3, ("Clearing mark bit at address %Ix",
30601                      (size_t)(&mark_array [mark_word_of (result)])));
30602
30603             mark_array_clear_marked (result);
30604         }
30605 #ifdef BACKGROUND_GC
30606         //the object has to cover one full mark uint32_t
30607         assert (size > mark_word_size);
30608         if (current_c_gc_state == c_gc_state_marking)
30609         {
30610             dprintf (3, ("Concurrent allocation of a large object %Ix",
30611                         (size_t)obj));
30612             //mark the new block specially so we know it is a new object
30613             if ((result < current_highest_address) && (result >= current_lowest_address))
30614             {
30615                 dprintf (3, ("Setting mark bit at address %Ix",
30616                             (size_t)(&mark_array [mark_word_of (result)])));
30617     
30618                 mark_array_set_marked (result);
30619             }
30620         }
30621 #endif //BACKGROUND_GC
30622     }
30623 #endif //MARK_ARRAY
30624
30625     assert (obj != 0);
30626     assert ((size_t)obj == Align ((size_t)obj, align_const));
30627
30628     return obj;
30629 }
30630
30631 void reset_memory (uint8_t* o, size_t sizeo)
30632 {
30633     if (sizeo > 128 * 1024)
30634     {
30635         // We cannot reset the memory for the useful part of a free object.
30636         size_t size_to_skip = min_free_list - plug_skew;
30637
30638         size_t page_start = align_on_page ((size_t)(o + size_to_skip));
30639         size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
30640         // Note we need to compensate for an OS bug here. This bug would cause the MEM_RESET to fail
30641         // on write watched memory.
30642         if (reset_mm_p)
30643         {
30644 #ifdef MULTIPLE_HEAPS
30645             bool unlock_p = true;
30646 #else
30647             // We don't do unlock because there could be many processes using workstation GC and it's
30648             // bad perf to have many threads doing unlock at the same time.
30649             bool unlock_p = false;
30650 #endif // MULTIPLE_HEAPS
30651
30652             reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, unlock_p);
30653         }
30654     }
30655 }
30656
30657 void gc_heap::reset_large_object (uint8_t* o)
30658 {
30659     // If it's a large object, allow the O/S to discard the backing store for these pages.
30660     reset_memory (o, size(o));
30661 }
30662
30663 BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp)
30664 {
30665     BOOL m = FALSE;
30666     // It shouldn't be necessary to do these comparisons because this is only used for blocking
30667     // GCs and LOH segments cannot be out of range.
30668     if ((o >= lowest_address) && (o < highest_address))
30669     {
30670         if (marked (o))
30671         {
30672             if (clearp)
30673             {
30674                 clear_marked (o);
30675                 if (pinned (o))
30676                     clear_pinned(o);
30677             }
30678             m = TRUE;
30679         }
30680         else
30681             m = FALSE;
30682     }
30683     else
30684         m = TRUE;
30685     return m;
30686 }
30687
30688 void gc_heap::walk_survivors_relocation (void* profiling_context, record_surv_fn fn)
30689 {
30690     // Now walk the portion of memory that is actually being relocated.
30691     walk_relocation (profiling_context, fn);
30692
30693 #ifdef FEATURE_LOH_COMPACTION
30694     if (loh_compacted_p)
30695     {
30696         walk_relocation_for_loh (profiling_context, fn);
30697     }
30698 #endif //FEATURE_LOH_COMPACTION
30699 }
30700
30701 void gc_heap::walk_survivors_for_loh (void* profiling_context, record_surv_fn fn)
30702 {
30703     generation* gen        = large_object_generation;
30704     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));;
30705
30706     PREFIX_ASSUME(seg != NULL);
30707
30708     uint8_t* o                = generation_allocation_start (gen);
30709     uint8_t* plug_end         = o;
30710     uint8_t* plug_start       = o;
30711
30712     while (1)
30713     {
30714         if (o >= heap_segment_allocated (seg))
30715         {
30716             seg = heap_segment_next (seg);
30717             if (seg == 0)
30718                 break;
30719             else
30720                 o = heap_segment_mem (seg);
30721         }
30722         if (large_object_marked(o, FALSE))
30723         {
30724             plug_start = o;
30725
30726             BOOL m = TRUE;
30727             while (m)
30728             {
30729                 o = o + AlignQword (size (o));
30730                 if (o >= heap_segment_allocated (seg))
30731                 {
30732                     break;
30733                 }
30734                 m = large_object_marked (o, FALSE);
30735             }
30736
30737             plug_end = o;
30738
30739             fn (plug_start, plug_end, 0, profiling_context, false, false);
30740         }
30741         else
30742         {
30743             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
30744             {
30745                 o = o + AlignQword (size (o));
30746             }
30747         }
30748     }
30749 }
30750
30751 #ifdef BACKGROUND_GC
30752
30753 BOOL gc_heap::background_object_marked (uint8_t* o, BOOL clearp)
30754 {
30755     BOOL m = FALSE;
30756     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
30757     {
30758         if (mark_array_marked (o))
30759         {
30760             if (clearp)
30761             {
30762                 mark_array_clear_marked (o);
30763                 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
30764                 dprintf (3, ("CM: %Ix", o));
30765             }
30766             m = TRUE;
30767         }
30768         else
30769             m = FALSE;
30770     }
30771     else
30772         m = TRUE;
30773
30774     dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
30775     return m;
30776 }
30777
30778 uint8_t* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
30779 {
30780     return
30781         (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
30782 }
30783
30784 void gc_heap::set_mem_verify (uint8_t* start, uint8_t* end, uint8_t b)
30785 {
30786 #ifdef VERIFY_HEAP
30787     if (end > start)
30788     {
30789         if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
30790            !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_MEM_FILL))
30791         {
30792             dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
30793             memset (start, b, (end - start));
30794         }
30795     }
30796 #endif //VERIFY_HEAP
30797 }
30798
30799 void gc_heap::generation_delete_heap_segment (generation* gen, 
30800                                               heap_segment* seg,
30801                                               heap_segment* prev_seg,
30802                                               heap_segment* next_seg)
30803 {
30804     dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
30805     if (gen == large_object_generation)
30806     {
30807         heap_segment_next (prev_seg) = next_seg;
30808
30809         dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
30810
30811         heap_segment_next (seg) = freeable_large_heap_segment;
30812         freeable_large_heap_segment = seg;
30813     }
30814     else
30815     {
30816         if (seg == ephemeral_heap_segment)
30817         {
30818             FATAL_GC_ERROR();
30819         }
30820
30821         heap_segment_next (next_seg) = prev_seg;
30822
30823         dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
30824         heap_segment_next (seg) = freeable_small_heap_segment;
30825         freeable_small_heap_segment = seg;
30826     }
30827
30828     decommit_heap_segment (seg);
30829     seg->flags |= heap_segment_flags_decommitted;
30830
30831     set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
30832 }
30833
30834 void gc_heap::process_background_segment_end (heap_segment* seg, 
30835                                           generation* gen,
30836                                           uint8_t* last_plug_end,
30837                                           heap_segment* start_seg,
30838                                           BOOL* delete_p)
30839 {
30840     *delete_p = FALSE;
30841     uint8_t* allocated = heap_segment_allocated (seg);
30842     uint8_t* background_allocated = heap_segment_background_allocated (seg);
30843
30844     dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)", 
30845                 (size_t)heap_segment_mem (seg), background_allocated, allocated));
30846
30847
30848     if (allocated != background_allocated)
30849     {
30850         if (gen == large_object_generation)
30851         {
30852             FATAL_GC_ERROR();
30853         }
30854
30855         dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[", 
30856                     (size_t)last_plug_end, background_allocated));
30857         thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
30858
30859         fix_brick_to_highest (last_plug_end, background_allocated);
30860
30861         // When we allowed fgc's during going through gaps, we could have erased the brick
30862         // that corresponds to bgc_allocated 'cause we had to update the brick there, 
30863         // recover it here.
30864         fix_brick_to_highest (background_allocated, background_allocated);
30865     }
30866     else
30867     {
30868         // by default, if allocated == background_allocated, it can't
30869         // be the ephemeral segment.
30870         if (seg == ephemeral_heap_segment)
30871         {
30872             FATAL_GC_ERROR();
30873         }
30874
30875         if (allocated == heap_segment_mem (seg))
30876         {
30877             // this can happen with LOH segments when multiple threads
30878             // allocate new segments and not all of them were needed to
30879             // satisfy allocation requests.
30880             assert (gen == large_object_generation);
30881         }
30882
30883         if (last_plug_end == heap_segment_mem (seg))
30884         {
30885             dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
30886                         (size_t)allocated, (*delete_p ? "should" : "should not")));
30887
30888             if (seg != start_seg)
30889             {
30890                 *delete_p = TRUE;
30891             }
30892         }
30893         else
30894         {
30895             dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
30896             heap_segment_allocated (seg) = last_plug_end;
30897             set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
30898
30899             decommit_heap_segment_pages (seg, 0);
30900         }
30901     }
30902
30903     dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
30904     bgc_verify_mark_array_cleared (seg);
30905 }
30906
30907 void gc_heap::process_n_background_segments (heap_segment* seg, 
30908                                              heap_segment* prev_seg,
30909                                              generation* gen)
30910 {
30911     assert (gen != large_object_generation);
30912
30913     while (seg)
30914     {
30915         dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
30916         heap_segment* next_seg = heap_segment_next (seg);
30917
30918         if (heap_segment_read_only_p (seg))
30919         {
30920             prev_seg = seg;
30921         }
30922         else
30923         {
30924             if (heap_segment_allocated (seg) == heap_segment_mem (seg))
30925             {
30926                 // This can happen - if we have a LOH segment where nothing survived
30927                 // or a SOH segment allocated by a gen1 GC when BGC was going where 
30928                 // nothing survived last time we did a gen1 GC.
30929                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
30930             }
30931             else
30932             {
30933                 prev_seg = seg;
30934             }
30935         }
30936
30937         verify_soh_segment_list();
30938         seg = next_seg;
30939     }
30940 }
30941
30942 inline
30943 BOOL gc_heap::fgc_should_consider_object (uint8_t* o,
30944                                           heap_segment* seg,
30945                                           BOOL consider_bgc_mark_p, 
30946                                           BOOL check_current_sweep_p, 
30947                                           BOOL check_saved_sweep_p)
30948 {
30949     // the logic for this function must be kept in sync with the analogous function
30950     // in ToolBox\SOS\Strike\gc.cpp
30951
30952     // TRUE means we don't need to check the bgc mark bit
30953     // FALSE means we do.
30954     BOOL no_bgc_mark_p = FALSE;
30955
30956     if (consider_bgc_mark_p)
30957     {
30958         if (check_current_sweep_p && (o < current_sweep_pos))
30959         {
30960             dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
30961             no_bgc_mark_p = TRUE;
30962         }
30963
30964         if (!no_bgc_mark_p)
30965         {
30966             if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
30967             {
30968                 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
30969                 no_bgc_mark_p = TRUE;
30970             }
30971
30972             if (!check_saved_sweep_p)
30973             {
30974                 uint8_t* background_allocated = heap_segment_background_allocated (seg);
30975                 // if this was the saved ephemeral segment, check_saved_sweep_p 
30976                 // would've been true.
30977                 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
30978                 // background_allocated could be 0 for the new segments acquired during bgc
30979                 // sweep and we still want no_bgc_mark_p to be true.
30980                 if (o >= background_allocated)
30981                 {
30982                     dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
30983                     no_bgc_mark_p = TRUE;
30984                 }
30985             }
30986         }
30987     }
30988     else
30989     {
30990         no_bgc_mark_p = TRUE;
30991     }
30992
30993     dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
30994     return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
30995 }
30996
30997 // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
30998 // if it's TRUE, check_current_sweep_p tells you if you should consider the
30999 // current sweep position or not.
31000 void gc_heap::should_check_bgc_mark (heap_segment* seg, 
31001                                      BOOL* consider_bgc_mark_p, 
31002                                      BOOL* check_current_sweep_p,
31003                                      BOOL* check_saved_sweep_p)
31004 {
31005     // the logic for this function must be kept in sync with the analogous function
31006     // in ToolBox\SOS\Strike\gc.cpp
31007     *consider_bgc_mark_p = FALSE;
31008     *check_current_sweep_p = FALSE;
31009     *check_saved_sweep_p = FALSE;
31010
31011     if (current_c_gc_state == c_gc_state_planning)
31012     {
31013         // We are doing the current_sweep_pos comparison here because we have yet to 
31014         // turn on the swept flag for the segment but in_range_for_segment will return
31015         // FALSE if the address is the same as reserved.
31016         if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
31017         {
31018             dprintf (3, ("seg %Ix is already swept by bgc", seg));
31019         }
31020         else
31021         {
31022             *consider_bgc_mark_p = TRUE;
31023
31024             dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
31025
31026             if (seg == saved_sweep_ephemeral_seg)
31027             {
31028                 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
31029                 *check_saved_sweep_p = TRUE;
31030             }
31031
31032             if (in_range_for_segment (current_sweep_pos, seg))
31033             {
31034                 dprintf (3, ("current sweep pos is %Ix and within seg %Ix", 
31035                               current_sweep_pos, seg));
31036                 *check_current_sweep_p = TRUE;
31037             }
31038         }
31039     }
31040 }
31041
31042 void gc_heap::background_ephemeral_sweep()
31043 {
31044     dprintf (3, ("bgc ephemeral sweep"));
31045
31046     int align_const = get_alignment_constant (TRUE);
31047
31048     saved_sweep_ephemeral_seg = ephemeral_heap_segment;
31049     saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
31050
31051     // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
31052     // we thread onto a list first then publish it when we are done.
31053     allocator youngest_free_list;
31054     size_t youngest_free_list_space = 0;
31055     size_t youngest_free_obj_space = 0;
31056
31057     youngest_free_list.clear();
31058
31059     for (int i = 0; i <= (max_generation - 1); i++)
31060     {
31061         generation* gen_to_reset = generation_of (i);
31062         assert (generation_free_list_space (gen_to_reset) == 0);
31063         // Can only assert free_list_space is 0, not free_obj_space as the allocator could have added 
31064         // something there.
31065     }
31066
31067     for (int i = (max_generation - 1); i >= 0; i--)
31068     {
31069         generation* current_gen = generation_of (i);
31070         uint8_t* o = generation_allocation_start (current_gen);
31071         //Skip the generation gap object
31072         o = o + Align(size (o), align_const);
31073         uint8_t* end = ((i > 0) ?
31074                      generation_allocation_start (generation_of (i - 1)) : 
31075                      heap_segment_allocated (ephemeral_heap_segment));
31076
31077         uint8_t* plug_end = o;
31078         uint8_t* plug_start = o;
31079         BOOL marked_p = FALSE;
31080
31081         while (o < end)
31082         {
31083             marked_p = background_object_marked (o, TRUE);
31084             if (marked_p)
31085             {
31086                 plug_start = o;
31087                 size_t plug_size = plug_start - plug_end;
31088
31089                 if (i >= 1)
31090                 {
31091                     thread_gap (plug_end, plug_size, current_gen);
31092                 }
31093                 else
31094                 {
31095                     if (plug_size > 0)
31096                     {
31097                         make_unused_array (plug_end, plug_size);
31098                         if (plug_size >= min_free_list)
31099                         {
31100                             youngest_free_list_space += plug_size;
31101                             youngest_free_list.thread_item (plug_end, plug_size);
31102                         }
31103                         else
31104                         {
31105                             youngest_free_obj_space += plug_size;
31106                         }
31107                     }
31108                 }
31109
31110                 fix_brick_to_highest (plug_end, plug_start);
31111                 fix_brick_to_highest (plug_start, plug_start);
31112
31113                 BOOL m = TRUE;
31114                 while (m)
31115                 {
31116                     o = o + Align (size (o), align_const);
31117                     if (o >= end)
31118                     {
31119                         break;
31120                     }
31121
31122                     m = background_object_marked (o, TRUE);
31123                 }
31124                 plug_end = o;
31125                 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31126             }
31127             else
31128             {
31129                 while ((o < end) && !background_object_marked (o, FALSE))
31130                 {
31131                     o = o + Align (size (o), align_const);
31132                 }
31133             }
31134         }
31135
31136         if (plug_end != end)
31137         {
31138             if (i >= 1)
31139             {
31140                 thread_gap (plug_end, end - plug_end, current_gen);
31141                 fix_brick_to_highest (plug_end, end);
31142             }
31143             else
31144             {
31145                 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
31146                 // the following line is temporary.
31147                 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
31148 #ifdef VERIFY_HEAP
31149                 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
31150                 {
31151                     make_unused_array (plug_end, (end - plug_end));
31152                 }
31153 #endif //VERIFY_HEAP
31154             }
31155         }
31156
31157         dd_fragmentation (dynamic_data_of (i)) = 
31158             generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
31159     }
31160
31161     generation* youngest_gen = generation_of (0);
31162     generation_free_list_space (youngest_gen) = youngest_free_list_space;
31163     generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
31164     dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
31165     generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
31166 }
31167
31168 void gc_heap::background_sweep()
31169 {
31170     generation* gen         = generation_of (max_generation);
31171     dynamic_data* dd        = dynamic_data_of (max_generation);
31172     // For SOH segments we go backwards.
31173     heap_segment* start_seg = ephemeral_heap_segment;
31174     PREFIX_ASSUME(start_seg != NULL);
31175     heap_segment* fseg      = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31176     heap_segment* seg       = start_seg;
31177     uint8_t* o                 = heap_segment_mem (seg);
31178
31179     heap_segment* prev_seg = heap_segment_next (seg);
31180     int align_const        = get_alignment_constant (TRUE);
31181     if (seg == fseg)
31182     {
31183         assert (o == generation_allocation_start (generation_of (max_generation)));
31184         o = o + Align(size (o), align_const);
31185     }
31186
31187     uint8_t* plug_end      = o;
31188     uint8_t* plug_start    = o;
31189     next_sweep_obj         = o;
31190     current_sweep_pos      = o;
31191
31192     //uint8_t* end              = background_next_end (seg, (gen == large_object_generation));
31193     uint8_t* end              = heap_segment_background_allocated (seg);
31194     BOOL delete_p          = FALSE;
31195
31196     //concurrent_print_time_delta ("finished with mark and start with sweep");
31197     concurrent_print_time_delta ("Sw");
31198     dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31199
31200     //block concurrent allocation for large objects
31201     dprintf (3, ("lh state: planning"));
31202     if (gc_lh_block_event.IsValid())
31203     {
31204         gc_lh_block_event.Reset();
31205     }
31206
31207     for (int i = 0; i <= (max_generation + 1); i++)
31208     {
31209         generation* gen_to_reset = generation_of (i);
31210         generation_allocator (gen_to_reset)->clear();
31211         generation_free_list_space (gen_to_reset) = 0;
31212         generation_free_obj_space (gen_to_reset) = 0;
31213         generation_free_list_allocated (gen_to_reset) = 0;
31214         generation_end_seg_allocated (gen_to_reset) = 0;
31215         generation_condemned_allocated (gen_to_reset) = 0; 
31216         //reset the allocation so foreground gc can allocate into older generation
31217         generation_allocation_pointer (gen_to_reset)= 0;
31218         generation_allocation_limit (gen_to_reset) = 0;
31219         generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
31220     }
31221
31222     FIRE_EVENT(BGC2ndNonConEnd);
31223
31224     current_bgc_state = bgc_sweep_soh;
31225     verify_soh_segment_list();
31226
31227 #ifdef FEATURE_BASICFREEZE
31228     if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
31229         ro_segments_in_range)
31230     {
31231         sweep_ro_segments (generation_start_segment (gen));
31232     }
31233 #endif // FEATURE_BASICFREEZE
31234
31235     //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
31236     if (current_c_gc_state != c_gc_state_planning)
31237     {
31238         current_c_gc_state = c_gc_state_planning;
31239     }
31240
31241     concurrent_print_time_delta ("Swe");
31242
31243     heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
31244     PREFIX_ASSUME(loh_seg  != NULL);
31245     while (loh_seg )
31246     {
31247         loh_seg->flags &= ~heap_segment_flags_swept;
31248         heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
31249         loh_seg = heap_segment_next_rw (loh_seg);
31250     }
31251
31252 #ifdef MULTIPLE_HEAPS
31253     bgc_t_join.join(this, gc_join_restart_ee);
31254     if (bgc_t_join.joined())
31255 #endif //MULTIPLE_HEAPS 
31256     {
31257 #ifdef MULTIPLE_HEAPS
31258         dprintf(2, ("Starting BGC threads for resuming EE"));
31259         bgc_t_join.restart();
31260 #endif //MULTIPLE_HEAPS
31261     }
31262
31263     if (heap_number == 0)
31264     {
31265         restart_EE ();
31266     }
31267
31268     FIRE_EVENT(BGC2ndConBegin);
31269
31270     background_ephemeral_sweep();
31271
31272 #ifdef MULTIPLE_HEAPS
31273     bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
31274     if (bgc_t_join.joined())
31275 #endif //MULTIPLE_HEAPS
31276     {
31277 #ifdef FEATURE_EVENT_TRACE
31278         bgc_heap_walk_for_etw_p = GCEventStatus::IsEnabled(GCEventProvider_Default, 
31279                                                            GCEventKeyword_GCHeapSurvivalAndMovement, 
31280                                                            GCEventLevel_Information);
31281 #endif //FEATURE_EVENT_TRACE
31282
31283         leave_spin_lock (&gc_lock);
31284
31285 #ifdef MULTIPLE_HEAPS
31286         dprintf(2, ("Starting BGC threads for BGC sweeping"));
31287         bgc_t_join.restart();
31288 #endif //MULTIPLE_HEAPS
31289     }
31290
31291     disable_preemptive (true);
31292
31293     dprintf (2, ("bgs: sweeping gen2 objects"));
31294     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31295                     (size_t)heap_segment_mem (seg),
31296                     (size_t)heap_segment_allocated (seg),
31297                     (size_t)heap_segment_background_allocated (seg)));
31298
31299     int num_objs = 256;
31300     int current_num_objs = 0;
31301     heap_segment* next_seg = 0;
31302
31303     while (1)
31304     {
31305         if (o >= end)
31306         {
31307             if (gen == large_object_generation)
31308             {
31309                 next_seg = heap_segment_next (seg);
31310             }
31311             else
31312             {
31313                 next_seg = heap_segment_prev (fseg, seg);
31314             }
31315
31316             delete_p = FALSE;
31317
31318             if (!heap_segment_read_only_p (seg))
31319             {
31320                 if (gen == large_object_generation)
31321                 {
31322                     // we can treat all LOH segments as in the bgc domain
31323                     // regardless of whether we saw in bgc mark or not
31324                     // because we don't allow LOH allocations during bgc
31325                     // sweep anyway - the LOH segments can't change.
31326                     process_background_segment_end (seg, gen, plug_end, 
31327                                                     start_seg, &delete_p);
31328                 }
31329                 else
31330                 {
31331                     assert (heap_segment_background_allocated (seg) != 0);
31332                     process_background_segment_end (seg, gen, plug_end, 
31333                                                     start_seg, &delete_p);
31334
31335                     assert (next_seg || !delete_p);
31336                 }
31337             }
31338
31339             if (delete_p)
31340             {
31341                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
31342             }
31343             else
31344             {
31345                 prev_seg = seg;
31346                 dprintf (2, ("seg %Ix has been swept", seg));
31347                 seg->flags |= heap_segment_flags_swept;
31348             }
31349
31350             verify_soh_segment_list();
31351
31352             seg = next_seg;
31353
31354             dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
31355             
31356             if (seg == 0)
31357             {
31358                 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31359
31360                 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31361
31362                 if (gen != large_object_generation)
31363                 {
31364                     dprintf (2, ("bgs: sweeping gen3 objects"));
31365                     current_bgc_state = bgc_sweep_loh;
31366                     gen = generation_of (max_generation+1);
31367                     start_seg = heap_segment_rw (generation_start_segment (gen));
31368
31369                     PREFIX_ASSUME(start_seg != NULL);
31370
31371                     seg = start_seg;
31372                     prev_seg = 0;
31373                     o = generation_allocation_start (gen);
31374                     assert (method_table (o) == g_gc_pFreeObjectMethodTable);
31375                     align_const = get_alignment_constant (FALSE);
31376                     o = o + Align(size (o), align_const);
31377                     plug_end = o;
31378                     end = heap_segment_allocated (seg);
31379                     dprintf (2, ("sweeping gen3 objects"));
31380                     generation_free_obj_space (gen) = 0;
31381                     generation_allocator (gen)->clear();
31382                     generation_free_list_space (gen) = 0;
31383
31384                     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31385                                     (size_t)heap_segment_mem (seg),
31386                                     (size_t)heap_segment_allocated (seg),
31387                                     (size_t)heap_segment_background_allocated (seg)));
31388                 }
31389                 else
31390                     break;
31391             }
31392             else
31393             {
31394                 o = heap_segment_mem (seg);
31395                 if (seg == fseg)
31396                 {
31397                     assert (gen != large_object_generation);
31398                     assert (o == generation_allocation_start (generation_of (max_generation)));
31399                     align_const = get_alignment_constant (TRUE);
31400                     o = o + Align(size (o), align_const);
31401                 }
31402
31403                 plug_end = o;
31404                 current_sweep_pos = o;
31405                 next_sweep_obj = o;
31406                 
31407                 allow_fgc();
31408                 end = background_next_end (seg, (gen == large_object_generation));
31409                 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31410                                 (size_t)heap_segment_mem (seg),
31411                                 (size_t)heap_segment_allocated (seg),
31412                                 (size_t)heap_segment_background_allocated (seg)));
31413             }
31414         }
31415
31416         if ((o < end) && background_object_marked (o, TRUE))
31417         {
31418             plug_start = o;
31419             if (gen == large_object_generation)
31420             {
31421                 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
31422             }
31423
31424             thread_gap (plug_end, plug_start-plug_end, gen);
31425             if (gen != large_object_generation)
31426             {
31427                 add_gen_free (max_generation, plug_start-plug_end);
31428                 fix_brick_to_highest (plug_end, plug_start);
31429                 // we need to fix the brick for the next plug here 'cause an FGC can
31430                 // happen and can't read a stale brick.
31431                 fix_brick_to_highest (plug_start, plug_start);
31432             }
31433
31434             BOOL m = TRUE;
31435
31436             while (m)
31437             {
31438                 next_sweep_obj = o + Align(size (o), align_const);
31439                 current_num_objs++;
31440                 if (current_num_objs >= num_objs)
31441                 {
31442                     current_sweep_pos = next_sweep_obj;
31443
31444                     allow_fgc();
31445                     current_num_objs = 0;
31446                 }
31447
31448                 o = next_sweep_obj;
31449                 if (o >= end)
31450                 {
31451                     break;
31452                 }
31453
31454                 m = background_object_marked (o, TRUE);
31455             }
31456             plug_end = o;
31457             if (gen != large_object_generation)
31458             {
31459                 add_gen_plug (max_generation, plug_end-plug_start);
31460                 dd_survived_size (dd) += (plug_end - plug_start);
31461             }
31462             dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31463         }
31464         else
31465         {
31466             while ((o < end) && !background_object_marked (o, FALSE))
31467             {
31468                 next_sweep_obj = o + Align(size (o), align_const);;
31469                 current_num_objs++;
31470                 if (current_num_objs >= num_objs)
31471                 {
31472                     current_sweep_pos = plug_end;
31473                     dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
31474                     allow_fgc();
31475                     current_num_objs = 0;
31476                 }
31477
31478                 o = next_sweep_obj;
31479             }
31480         }
31481     }
31482
31483     size_t total_loh_size = generation_size (max_generation + 1);
31484     size_t total_soh_size = generation_sizes (generation_of (max_generation));
31485
31486     dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
31487
31488     dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id", 
31489         generation_free_list_space (generation_of (max_generation)),
31490         generation_free_obj_space (generation_of (max_generation))));
31491     dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id", 
31492         heap_number,
31493         generation_free_list_space (generation_of (max_generation + 1)),
31494         generation_free_obj_space (generation_of (max_generation + 1))));
31495
31496     FIRE_EVENT(BGC2ndConEnd);
31497     concurrent_print_time_delta ("background sweep");
31498     
31499     heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31500     PREFIX_ASSUME(reset_seg != NULL);
31501
31502     while (reset_seg)
31503     {
31504         heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
31505         heap_segment_background_allocated (reset_seg) = 0;
31506         reset_seg = heap_segment_next_rw (reset_seg);
31507     }
31508
31509     // We calculate dynamic data here because if we wait till we signal the lh event, 
31510     // the allocation thread can change the fragmentation and we may read an intermediate
31511     // value (which can be greater than the generation size). Plus by that time it won't 
31512     // be accurate.
31513     compute_new_dynamic_data (max_generation);
31514
31515     enable_preemptive ();
31516
31517 #ifdef MULTIPLE_HEAPS
31518     bgc_t_join.join(this, gc_join_set_state_free);
31519     if (bgc_t_join.joined())
31520 #endif //MULTIPLE_HEAPS
31521     {
31522         // TODO: We are using this join just to set the state. Should
31523         // look into eliminating it - check to make sure things that use 
31524         // this state can live with per heap state like should_check_bgc_mark.
31525         current_c_gc_state = c_gc_state_free;
31526
31527 #ifdef MULTIPLE_HEAPS
31528         dprintf(2, ("Starting BGC threads after background sweep phase"));
31529         bgc_t_join.restart();
31530 #endif //MULTIPLE_HEAPS
31531     }
31532
31533     disable_preemptive (true);
31534
31535     if (gc_lh_block_event.IsValid())
31536     {
31537         gc_lh_block_event.Set();
31538     }
31539
31540     //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31541     dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
31542 }
31543 #endif //BACKGROUND_GC
31544
31545 void gc_heap::sweep_large_objects ()
31546 {
31547     //this min value is for the sake of the dynamic tuning.
31548     //so we know that we are not starting even if we have no
31549     //survivors.
31550     generation* gen        = large_object_generation;
31551     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
31552
31553     PREFIX_ASSUME(start_seg != NULL);
31554
31555     heap_segment* seg      = start_seg;
31556     heap_segment* prev_seg = 0;
31557     uint8_t* o             = generation_allocation_start (gen);
31558     int align_const        = get_alignment_constant (FALSE);
31559
31560     //Skip the generation gap object
31561     o = o + Align(size (o), align_const);
31562
31563     uint8_t* plug_end         = o;
31564     uint8_t* plug_start       = o;
31565
31566     generation_allocator (gen)->clear();
31567     generation_free_list_space (gen) = 0;
31568     generation_free_obj_space (gen) = 0;
31569
31570
31571     dprintf (3, ("sweeping large objects"));
31572     dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix", 
31573                  (size_t)seg,
31574                  (size_t)heap_segment_mem (seg),
31575                  (size_t)heap_segment_allocated (seg),
31576                  o));
31577
31578     while (1)
31579     {
31580         if (o >= heap_segment_allocated (seg))
31581         {
31582             heap_segment* next_seg = heap_segment_next (seg);
31583             //delete the empty segment if not the only one
31584             if ((plug_end == heap_segment_mem (seg)) &&
31585                 (seg != start_seg) && !heap_segment_read_only_p (seg))
31586             {
31587                 //prepare for deletion
31588                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
31589                 assert (prev_seg);
31590                 heap_segment_next (prev_seg) = next_seg;
31591                 heap_segment_next (seg) = freeable_large_heap_segment;
31592                 freeable_large_heap_segment = seg;
31593             }
31594             else
31595             {
31596                 if (!heap_segment_read_only_p (seg))
31597                 {
31598                     dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
31599                     heap_segment_allocated (seg) = plug_end;
31600                     decommit_heap_segment_pages (seg, 0);
31601                 }
31602                 prev_seg = seg;
31603             }
31604             seg = next_seg;
31605             if (seg == 0)
31606                 break;
31607             else
31608             {
31609                 o = heap_segment_mem (seg);
31610                 plug_end = o;
31611                 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
31612                              (size_t)heap_segment_mem (seg),
31613                              (size_t)heap_segment_allocated (seg)));
31614             }
31615         }
31616         if (large_object_marked(o, TRUE))
31617         {
31618             plug_start = o;
31619             //everything between plug_end and plug_start is free
31620             thread_gap (plug_end, plug_start-plug_end, gen);
31621
31622             BOOL m = TRUE;
31623             while (m)
31624             {
31625                 o = o + AlignQword (size (o));
31626                 if (o >= heap_segment_allocated (seg))
31627                 {
31628                     break;
31629                 }
31630                 m = large_object_marked (o, TRUE);
31631             }
31632             plug_end = o;
31633             dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31634         }
31635         else
31636         {
31637             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
31638             {
31639                 o = o + AlignQword (size (o));
31640             }
31641         }
31642     }
31643
31644     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31645
31646     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31647 }
31648
31649 void gc_heap::relocate_in_large_objects ()
31650 {
31651     relocate_args args;
31652     args.low = gc_low;
31653     args.high = gc_high;
31654     args.last_plug = 0;
31655
31656     generation* gen = large_object_generation;
31657
31658     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31659
31660     PREFIX_ASSUME(seg != NULL);
31661
31662     uint8_t* o = generation_allocation_start (gen);
31663
31664     while (1)
31665     {
31666         if (o >= heap_segment_allocated (seg))
31667         {
31668             seg = heap_segment_next_rw (seg);
31669             if (seg == 0)
31670                 break;
31671             else
31672             {
31673                 o = heap_segment_mem (seg);
31674             }
31675         }
31676         while (o < heap_segment_allocated (seg))
31677         {
31678             check_class_object_demotion (o);
31679             if (contain_pointers (o))
31680             {
31681                 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
31682                 go_through_object_nostart (method_table (o), o, size(o), pval,
31683                         {
31684                             reloc_survivor_helper (pval);
31685                         });
31686             }
31687             o = o + AlignQword (size (o));
31688         }
31689     }
31690 }
31691
31692 void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
31693                                                     BOOL relocating)
31694 {
31695     uint8_t*      low               = gc_low;
31696     size_t        end_card          = 0;
31697     generation*   oldest_gen        = generation_of (max_generation+1);
31698     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
31699
31700     PREFIX_ASSUME(seg != NULL);
31701
31702     uint8_t*      beg               = generation_allocation_start (oldest_gen);
31703     uint8_t*      end               = heap_segment_allocated (seg);
31704
31705     size_t  cg_pointers_found = 0;
31706
31707     size_t  card_word_end = (card_of (align_on_card_word (end)) /
31708                              card_word_width);
31709
31710     size_t      n_eph             = 0;
31711     size_t      n_gen             = 0;
31712     size_t      n_card_set        = 0;
31713     uint8_t*    next_boundary = (relocating ?
31714                               generation_plan_allocation_start (generation_of (max_generation -1)) :
31715                               ephemeral_low);
31716
31717     uint8_t*    nhigh         = (relocating ?
31718                               heap_segment_plan_allocated (ephemeral_heap_segment) :
31719                               ephemeral_high);
31720
31721     BOOL          foundp            = FALSE;
31722     uint8_t*      start_address     = 0;
31723     uint8_t*      limit             = 0;
31724     size_t        card              = card_of (beg);
31725     uint8_t*      o                 = beg;
31726 #ifdef BACKGROUND_GC
31727     BOOL consider_bgc_mark_p        = FALSE;
31728     BOOL check_current_sweep_p      = FALSE;
31729     BOOL check_saved_sweep_p        = FALSE;
31730     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31731 #endif //BACKGROUND_GC
31732
31733     size_t total_cards_cleared = 0;
31734
31735     //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
31736     dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
31737     while (1)
31738     {
31739         if ((o < end) && (card_of(o) > card))
31740         {
31741             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
31742             if (cg_pointers_found == 0)
31743             {
31744                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
31745                 clear_cards (card, card_of((uint8_t*)o));
31746                 total_cards_cleared += (card_of((uint8_t*)o) - card);
31747             }
31748             n_eph +=cg_pointers_found;
31749             cg_pointers_found = 0;
31750             card = card_of ((uint8_t*)o);
31751         }
31752         if ((o < end) &&(card >= end_card))
31753         {
31754             foundp = find_card (card_table, card, card_word_end, end_card);
31755             if (foundp)
31756             {
31757                 n_card_set+= end_card - card;
31758                 start_address = max (beg, card_address (card));
31759             }
31760             limit = min (end, card_address (end_card));
31761         }
31762         if ((!foundp) || (o >= end) || (card_address (card) >= end))
31763         {
31764             if ((foundp) && (cg_pointers_found == 0))
31765             {
31766                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
31767                            (size_t)card_address(card+1)));
31768                 clear_cards (card, card+1);
31769                 total_cards_cleared += 1;
31770             }
31771             n_eph +=cg_pointers_found;
31772             cg_pointers_found = 0;
31773             if ((seg = heap_segment_next_rw (seg)) != 0)
31774             {
31775 #ifdef BACKGROUND_GC
31776                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31777 #endif //BACKGROUND_GC
31778                 beg = heap_segment_mem (seg);
31779                 end = compute_next_end (seg, low);
31780                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
31781                 card = card_of (beg);
31782                 o  = beg;
31783                 end_card = 0;
31784                 continue;
31785             }
31786             else
31787             {
31788                 break;
31789             }
31790         }
31791
31792         assert (card_set_p (card));
31793         {
31794             dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
31795                        card, (size_t)o, (size_t)limit));
31796
31797             assert (Align (size (o)) >= Align (min_obj_size));
31798             size_t s = size (o);
31799             uint8_t* next_o =  o + AlignQword (s);
31800             Prefetch (next_o);
31801
31802             while (o < limit)
31803             {
31804                 s = size (o);
31805                 assert (Align (s) >= Align (min_obj_size));
31806                 next_o =  o + AlignQword (s);
31807                 Prefetch (next_o);
31808
31809                 dprintf (4, ("|%Ix|", (size_t)o));
31810                 if (next_o < start_address)
31811                 {
31812                     goto end_object;
31813                 }
31814
31815 #ifdef BACKGROUND_GC
31816                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
31817                 {
31818                     goto end_object;
31819                 }
31820 #endif //BACKGROUND_GC
31821
31822 #ifdef COLLECTIBLE_CLASS
31823                 if (is_collectible(o))
31824                 {
31825                     BOOL passed_end_card_p = FALSE;
31826
31827                     if (card_of (o) > card)
31828                     {
31829                         passed_end_card_p = card_transition (o, end, card_word_end,
31830                             cg_pointers_found, 
31831                             n_eph, n_card_set,
31832                             card, end_card,
31833                             foundp, start_address,
31834                             limit, total_cards_cleared);
31835                     }
31836
31837                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
31838                     {
31839                         // card is valid and it covers the head of the object
31840                         if (fn == &gc_heap::relocate_address)
31841                         {
31842                             keep_card_live (o, n_gen, cg_pointers_found);
31843                         }
31844                         else
31845                         {
31846                             uint8_t* class_obj = get_class_object (o);
31847                             mark_through_cards_helper (&class_obj, n_gen,
31848                                                     cg_pointers_found, fn,
31849                                                     nhigh, next_boundary);
31850                         }
31851                     }
31852
31853                     if (passed_end_card_p)
31854                     {
31855                         if (foundp && (card_address (card) < next_o))
31856                         {
31857                             goto go_through_refs;
31858                         }
31859                         else 
31860                         {
31861                             goto end_object;
31862                         }
31863                     }
31864                 }
31865
31866 go_through_refs:
31867 #endif //COLLECTIBLE_CLASS
31868
31869                 if (contain_pointers (o))
31870                 {
31871                     dprintf(3,("Going through %Ix", (size_t)o));
31872
31873                     go_through_object (method_table(o), o, s, poo,
31874                                        start_address, use_start, (o + s),
31875                        {
31876                            if (card_of ((uint8_t*)poo) > card)
31877                            {
31878                                 BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
31879                                         card_word_end,
31880                                         cg_pointers_found, 
31881                                         n_eph, n_card_set,
31882                                         card, end_card,
31883                                         foundp, start_address,
31884                                         limit, total_cards_cleared);
31885
31886                                 if (passed_end_card_p)
31887                                 {
31888                                     if (foundp && (card_address (card) < next_o))
31889                                     {
31890                                         //new_start();
31891                                         {
31892                                             if (ppstop <= (uint8_t**)start_address)
31893                                             {break;}
31894                                             else if (poo < (uint8_t**)start_address)
31895                                             {poo = (uint8_t**)start_address;}
31896                                         }
31897                                     }
31898                                     else
31899                                     {
31900                                         goto end_object;
31901                                     }
31902                                 }
31903                             }
31904
31905                            mark_through_cards_helper (poo, n_gen,
31906                                                       cg_pointers_found, fn,
31907                                                       nhigh, next_boundary);
31908                        }
31909                         );
31910                 }
31911
31912             end_object:
31913                 o = next_o;
31914             }
31915
31916         }
31917     }
31918
31919     // compute the efficiency ratio of the card table
31920     if (!relocating)
31921     {
31922         generation_skip_ratio = min (((n_eph > 800) ?
31923                                       (int)(((float)n_gen / (float)n_eph) * 100) : 100),
31924                                      generation_skip_ratio);
31925
31926         dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d", 
31927              n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
31928     }
31929     else
31930     {
31931         dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d", 
31932              n_eph, n_gen, n_card_set, generation_skip_ratio));
31933     }
31934 }
31935
31936 void gc_heap::descr_segment (heap_segment* seg )
31937 {
31938 #ifdef TRACE_GC
31939     uint8_t*  x = heap_segment_mem (seg);
31940     while (x < heap_segment_allocated (seg))
31941     {
31942         dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
31943         x = x + Align(size (x));
31944     }
31945 #else // TRACE_GC
31946     UNREFERENCED_PARAMETER(seg);
31947 #endif // TRACE_GC
31948 }
31949
31950 void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
31951 {
31952 #ifdef MULTIPLE_HEAPS
31953     int n_heaps = g_theGCHeap->GetNumberOfHeaps ();
31954     for (int i = 0; i < n_heaps; i++)
31955     {
31956         gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
31957 #else //MULTIPLE_HEAPS
31958     {
31959         gc_heap* hp = NULL;
31960 #ifdef _PREFAST_
31961         // prefix complains about us dereferencing hp in wks build even though we only access static members
31962         // this way. not sure how to shut it up except for this ugly workaround:
31963         PREFIX_ASSUME(hp != NULL);
31964 #endif // _PREFAST_
31965 #endif //MULTIPLE_HEAPS
31966
31967         int curr_gen_number0 = max_generation+1;
31968         while (curr_gen_number0 >= 0)
31969         {
31970             generation* gen = hp->generation_of (curr_gen_number0);
31971             heap_segment* seg = generation_start_segment (gen);
31972             while (seg && (seg != hp->ephemeral_heap_segment))
31973             {
31974                 assert (curr_gen_number0 > 0);
31975
31976                 // report bounds from heap_segment_mem (seg) to
31977                 // heap_segment_allocated (seg);
31978                 // for generation # curr_gen_number0
31979                 // for heap # heap_no
31980
31981                 fn(context, curr_gen_number0, heap_segment_mem (seg),
31982                                               heap_segment_allocated (seg),
31983                                               curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
31984
31985                 seg = heap_segment_next (seg);
31986             }
31987             if (seg)
31988             {
31989                 assert (seg == hp->ephemeral_heap_segment);
31990                 assert (curr_gen_number0 <= max_generation);
31991                 //
31992                 if (curr_gen_number0 == max_generation)
31993                 {
31994                     if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
31995                     {
31996                         // report bounds from heap_segment_mem (seg) to
31997                         // generation_allocation_start (generation_of (max_generation-1))
31998                         // for heap # heap_number
31999
32000                         fn(context, curr_gen_number0, heap_segment_mem (seg),
32001                                                       generation_allocation_start (hp->generation_of (max_generation-1)),
32002                                                       generation_allocation_start (hp->generation_of (max_generation-1)) );
32003                     }
32004                 }
32005                 else if (curr_gen_number0 != 0)
32006                 {
32007                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32008                     // to generation_allocation_start (generation_of (curr_gen_number0-1))
32009                     // for heap # heap_number
32010
32011                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32012                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
32013                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
32014                 }
32015                 else
32016                 {
32017                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32018                     // to heap_segment_allocated (ephemeral_heap_segment);
32019                     // for heap # heap_number
32020
32021                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32022                                                   heap_segment_allocated (hp->ephemeral_heap_segment),
32023                                                   heap_segment_reserved (hp->ephemeral_heap_segment) );
32024                 }
32025             }
32026             curr_gen_number0--;
32027         }
32028     }
32029 }
32030
32031 #ifdef TRACE_GC
32032 // Note that when logging is on it can take a long time to go through the free items.
32033 void gc_heap::print_free_list (int gen, heap_segment* seg)
32034 {
32035     UNREFERENCED_PARAMETER(gen);
32036     UNREFERENCED_PARAMETER(seg);
32037 /*
32038     if (settings.concurrent == FALSE)
32039     {
32040         uint8_t* seg_start = heap_segment_mem (seg);
32041         uint8_t* seg_end = heap_segment_allocated (seg);
32042
32043         dprintf (3, ("Free list in seg %Ix:", seg_start));
32044
32045         size_t total_free_item = 0;
32046
32047         allocator* gen_allocator = generation_allocator (generation_of (gen));
32048         for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
32049         {
32050             uint8_t* fo = gen_allocator->alloc_list_head_of (b);
32051             while (fo)
32052             {
32053                 if (fo >= seg_start && fo < seg_end)
32054                 {
32055                     total_free_item++;
32056
32057                     size_t free_item_len = size(fo);
32058
32059                     dprintf (3, ("[%Ix, %Ix[:%Id",
32060                                  (size_t)fo,
32061                                  (size_t)(fo + free_item_len),
32062                                  free_item_len));
32063                 }
32064
32065                 fo = free_list_slot (fo);
32066             }
32067         }
32068
32069         dprintf (3, ("total %Id free items", total_free_item));
32070     }
32071 */
32072 }
32073 #endif //TRACE_GC
32074
32075 void gc_heap::descr_generations (BOOL begin_gc_p)
32076 {
32077     UNREFERENCED_PARAMETER(begin_gc_p);
32078 #ifdef STRESS_LOG
32079     if (StressLog::StressLogOn(LF_GC, LL_INFO10))
32080     {
32081         gc_heap* hp = 0;
32082 #ifdef MULTIPLE_HEAPS
32083         hp= this;
32084 #endif //MULTIPLE_HEAPS
32085
32086         STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
32087         for (int n = max_generation; n >= 0; --n)
32088         {
32089             STRESS_LOG4(LF_GC, LL_INFO10, "    Generation %d [%p, %p] cur = %p\n",
32090                     n,
32091                     generation_allocation_start(generation_of(n)),
32092                     generation_allocation_limit(generation_of(n)),
32093                     generation_allocation_pointer(generation_of(n)));
32094
32095             heap_segment* seg = generation_start_segment(generation_of(n));
32096             while (seg)
32097             {
32098                 STRESS_LOG4(LF_GC, LL_INFO10, "        Segment mem %p alloc = %p used %p committed %p\n",
32099                         heap_segment_mem(seg),
32100                         heap_segment_allocated(seg),
32101                         heap_segment_used(seg),
32102                         heap_segment_committed(seg));
32103                 seg = heap_segment_next(seg);
32104             }
32105         }
32106     }
32107 #endif  // STRESS_LOG
32108
32109 #ifdef TRACE_GC
32110     dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
32111              (size_t) lowest_address, (size_t) highest_address));
32112 #ifdef BACKGROUND_GC
32113     dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
32114              (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
32115 #endif //BACKGROUND_GC
32116
32117     if (heap_number == 0)
32118     {
32119         dprintf (1, ("total heap size: %Id, commit size: %Id", get_total_heap_size(), get_total_committed_size()));
32120     }
32121
32122     int curr_gen_number = max_generation+1;
32123     while (curr_gen_number >= 0)
32124     {
32125         size_t total_gen_size = generation_size (curr_gen_number);
32126 #ifdef SIMPLE_DPRINTF
32127         dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
32128                       (begin_gc_p ? "BEG" : "END"),
32129                       settings.condemned_generation,
32130                       curr_gen_number,
32131                       total_gen_size,
32132                       dd_fragmentation (dynamic_data_of (curr_gen_number)),
32133                       generation_free_list_space (generation_of (curr_gen_number)),
32134                       generation_free_obj_space (generation_of (curr_gen_number)),
32135                       (total_gen_size ? 
32136                         (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
32137                         0),
32138                       (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
32139                       (settings.heap_expansion ? "(EX)" : " "),
32140                       (settings.promotion ? "Promotion" : "NoPromotion")));
32141 #else
32142         dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
32143                       curr_gen_number,
32144                       size (generation_allocation_start (generation_of (curr_gen_number))),
32145                       total_gen_size,
32146                       dd_fragmentation (dynamic_data_of (curr_gen_number))));
32147 #endif //SIMPLE_DPRINTF
32148
32149         generation* gen = generation_of (curr_gen_number);
32150         heap_segment* seg = generation_start_segment (gen);
32151         while (seg && (seg != ephemeral_heap_segment))
32152         {
32153             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
32154                         curr_gen_number,
32155                         (size_t)heap_segment_mem (seg),
32156                         (size_t)heap_segment_allocated (seg),
32157                         (size_t)heap_segment_committed (seg),
32158                         (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
32159                         (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
32160             print_free_list (curr_gen_number, seg);
32161             seg = heap_segment_next (seg);
32162         }
32163         if (seg && (seg != generation_start_segment (gen)))
32164         {
32165             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32166                          curr_gen_number,
32167                          (size_t)heap_segment_mem (seg),
32168                          (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
32169             print_free_list (curr_gen_number, seg);
32170
32171         }
32172         else if (seg)
32173         {
32174             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32175                          curr_gen_number,
32176                          (size_t)generation_allocation_start (generation_of (curr_gen_number)),
32177                          (size_t)(((curr_gen_number == 0)) ?
32178                                   (heap_segment_allocated
32179                                    (generation_start_segment
32180                                     (generation_of (curr_gen_number)))) :
32181                                   (generation_allocation_start
32182                                    (generation_of (curr_gen_number - 1))))
32183                          ));
32184             print_free_list (curr_gen_number, seg);
32185         }
32186         curr_gen_number--;
32187     }
32188
32189 #endif //TRACE_GC
32190 }
32191
32192 #undef TRACE_GC
32193
32194 //#define TRACE_GC
32195
32196 //-----------------------------------------------------------------------------
32197 //
32198 //                                  VM Specific support
32199 //
32200 //-----------------------------------------------------------------------------
32201
32202
32203 #ifdef TRACE_GC
32204
32205  unsigned int PromotedObjectCount  = 0;
32206  unsigned int CreatedObjectCount       = 0;
32207  unsigned int AllocDuration            = 0;
32208  unsigned int AllocCount               = 0;
32209  unsigned int AllocBigCount            = 0;
32210  unsigned int AllocSmallCount      = 0;
32211  unsigned int AllocStart             = 0;
32212 #endif //TRACE_GC
32213
32214 //Static member variables.
32215 VOLATILE(BOOL)    GCHeap::GcInProgress            = FALSE;
32216 //GCTODO
32217 //CMCSafeLock*      GCHeap::fGcLock;
32218 GCEvent            *GCHeap::WaitForGCEvent         = NULL;
32219 //GCTODO
32220 #ifdef TRACE_GC
32221 unsigned int       GCHeap::GcDuration;
32222 #endif //TRACE_GC
32223 unsigned            GCHeap::GcCondemnedGeneration   = 0;
32224 size_t              GCHeap::totalSurvivedSize       = 0;
32225 #ifdef FEATURE_PREMORTEM_FINALIZATION
32226 CFinalize*          GCHeap::m_Finalize              = 0;
32227 BOOL                GCHeap::GcCollectClasses        = FALSE;
32228 VOLATILE(int32_t)      GCHeap::m_GCFLock               = 0;
32229
32230 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32231 #ifdef STRESS_HEAP
32232 #ifdef BACKGROUND_GC
32233 int                 GCHeap::gc_stress_fgcs_in_bgc   = 0;
32234 #endif // BACKGROUND_GC
32235 #ifndef MULTIPLE_HEAPS
32236 OBJECTHANDLE        GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
32237 int                 GCHeap::m_CurStressObj          = 0;
32238 #endif // !MULTIPLE_HEAPS
32239 #endif // STRESS_HEAP
32240 #endif // FEATURE_REDHAWK
32241
32242 #endif //FEATURE_PREMORTEM_FINALIZATION
32243
32244 class NoGCRegionLockHolder
32245 {
32246 public:
32247     NoGCRegionLockHolder()
32248     {
32249         enter_spin_lock_noinstru(&g_no_gc_lock);
32250     }
32251
32252     ~NoGCRegionLockHolder()
32253     {
32254         leave_spin_lock_noinstru(&g_no_gc_lock);
32255     }
32256 };
32257
32258 // An explanation of locking for finalization:
32259 //
32260 // Multiple threads allocate objects.  During the allocation, they are serialized by
32261 // the AllocLock above.  But they release that lock before they register the object
32262 // for finalization.  That's because there is much contention for the alloc lock, but
32263 // finalization is presumed to be a rare case.
32264 //
32265 // So registering an object for finalization must be protected by the FinalizeLock.
32266 //
32267 // There is another logical queue that involves finalization.  When objects registered
32268 // for finalization become unreachable, they are moved from the "registered" queue to
32269 // the "unreachable" queue.  Note that this only happens inside a GC, so no other
32270 // threads can be manipulating either queue at that time.  Once the GC is over and
32271 // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
32272 // queue and call their finalizers.  This dequeue operation is also protected with
32273 // the finalize lock.
32274 //
32275 // At first, this seems unnecessary.  Only one thread is ever enqueuing or dequeuing
32276 // on the unreachable queue (either the GC thread during a GC or the finalizer thread
32277 // when a GC is not in progress).  The reason we share a lock with threads enqueuing
32278 // on the "registered" queue is that the "registered" and "unreachable" queues are
32279 // interrelated.
32280 //
32281 // They are actually two regions of a longer list, which can only grow at one end.
32282 // So to enqueue an object to the "registered" list, you actually rotate an unreachable
32283 // object at the boundary between the logical queues, out to the other end of the
32284 // unreachable queue -- where all growing takes place.  Then you move the boundary
32285 // pointer so that the gap we created at the boundary is now on the "registered"
32286 // side rather than the "unreachable" side.  Now the object can be placed into the
32287 // "registered" side at that point.  This is much more efficient than doing moves
32288 // of arbitrarily long regions, but it causes the two queues to require a shared lock.
32289 //
32290 // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock.  Instead, it relies
32291 // on the fact that the lock will only be taken for a brief period and that it will
32292 // never provoke or allow a GC while the lock is held.  This is critical.  If the
32293 // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
32294 // allow a GC), then the Alloc client would have to GC protect a finalizable object
32295 // to protect against that eventuality.  That is too slow!
32296
32297
32298
32299 BOOL IsValidObject99(uint8_t *pObject)
32300 {
32301 #ifdef VERIFY_HEAP
32302     if (!((CObjectHeader*)pObject)->IsFree())
32303         ((CObjectHeader *) pObject)->Validate();
32304 #endif //VERIFY_HEAP
32305     return(TRUE);
32306 }
32307
32308 #ifdef BACKGROUND_GC 
32309 BOOL gc_heap::bgc_mark_array_range (heap_segment* seg, 
32310                                     BOOL whole_seg_p,
32311                                     uint8_t** range_beg,
32312                                     uint8_t** range_end)
32313 {
32314     uint8_t* seg_start = heap_segment_mem (seg);
32315     uint8_t* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
32316
32317     if ((seg_start < background_saved_highest_address) &&
32318         (seg_end > background_saved_lowest_address))
32319     {
32320         *range_beg = max (seg_start, background_saved_lowest_address);
32321         *range_end = min (seg_end, background_saved_highest_address);
32322         return TRUE;
32323     }
32324     else
32325     {
32326         return FALSE;
32327     }
32328 }
32329
32330 void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
32331 {
32332 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32333     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32334     {
32335         uint8_t* range_beg = 0;
32336         uint8_t* range_end = 0;
32337
32338         if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
32339         {
32340             size_t  markw = mark_word_of (range_beg);
32341             size_t  markw_end = mark_word_of (range_end);
32342             while (markw < markw_end)
32343             {
32344                 if (mark_array [markw])
32345                 {
32346                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32347                                     markw, mark_array [markw], mark_word_address (markw)));
32348                     FATAL_GC_ERROR();
32349                 }
32350                 markw++;
32351             }
32352             uint8_t* p = mark_word_address (markw_end);
32353             while (p < range_end)
32354             {
32355                 assert (!(mark_array_marked (p)));
32356                 p++;
32357             }
32358         }
32359     }
32360 #endif //VERIFY_HEAP && MARK_ARRAY
32361 }
32362
32363 void gc_heap::verify_mark_bits_cleared (uint8_t* obj, size_t s)
32364 {
32365 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32366     size_t start_mark_bit = mark_bit_of (obj) + 1;
32367     size_t end_mark_bit = mark_bit_of (obj + s);
32368     unsigned int startbit = mark_bit_bit (start_mark_bit);
32369     unsigned int endbit = mark_bit_bit (end_mark_bit);
32370     size_t startwrd = mark_bit_word (start_mark_bit);
32371     size_t endwrd = mark_bit_word (end_mark_bit);
32372     unsigned int result = 0;
32373
32374     unsigned int firstwrd = ~(lowbits (~0, startbit));
32375     unsigned int lastwrd = ~(highbits (~0, endbit));
32376
32377     if (startwrd == endwrd)
32378     {
32379         unsigned int wrd = firstwrd & lastwrd;
32380         result = mark_array[startwrd] & wrd;
32381         if (result)
32382         {
32383             FATAL_GC_ERROR();
32384         }
32385         return;
32386     }
32387
32388     // verify the first mark word is cleared.
32389     if (startbit)
32390     {
32391         result = mark_array[startwrd] & firstwrd;
32392         if (result)
32393         {
32394             FATAL_GC_ERROR();
32395         }
32396         startwrd++;
32397     }
32398
32399     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
32400     {
32401         result = mark_array[wrdtmp];
32402         if (result)
32403         {
32404             FATAL_GC_ERROR();
32405         }
32406     }
32407
32408     // set the last mark word.
32409     if (endbit)
32410     {
32411         result = mark_array[endwrd] & lastwrd;
32412         if (result)
32413         {
32414             FATAL_GC_ERROR();
32415         }
32416     }
32417 #endif //VERIFY_HEAP && MARK_ARRAY
32418 }
32419
32420 void gc_heap::clear_all_mark_array()
32421 {
32422 #ifdef MARK_ARRAY
32423     //size_t num_dwords_written = 0;
32424     //size_t begin_time = GetHighPrecisionTimeStamp();
32425
32426     generation* gen = generation_of (max_generation);
32427     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32428     
32429     while (1)
32430     {
32431         if (seg == 0)
32432         {
32433             if (gen != large_object_generation)
32434             {
32435                 gen = generation_of (max_generation+1);
32436                 seg = heap_segment_rw (generation_start_segment (gen));
32437             }
32438             else
32439             {
32440                 break;
32441             }
32442         }
32443
32444         uint8_t* range_beg = 0;
32445         uint8_t* range_end = 0;
32446
32447         if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
32448         { 
32449             size_t markw = mark_word_of (range_beg);
32450             size_t markw_end = mark_word_of (range_end);
32451             size_t size_total = (markw_end - markw) * sizeof (uint32_t);
32452             //num_dwords_written = markw_end - markw;
32453             size_t size = 0;
32454             size_t size_left = 0;
32455
32456             assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
32457
32458             if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
32459             {
32460                 size = (size_total & ~(sizeof(PTR_PTR) - 1));
32461                 size_left = size_total - size;
32462                 assert ((size_left & (sizeof (uint32_t) - 1)) == 0);
32463             }
32464             else
32465             {
32466                 size = size_total;
32467             }
32468
32469             memclr ((uint8_t*)&mark_array[markw], size);
32470
32471             if (size_left != 0)
32472             {
32473                 uint32_t* markw_to_clear = &mark_array[markw + size / sizeof (uint32_t)];
32474                 for (size_t i = 0; i < (size_left / sizeof (uint32_t)); i++)
32475                 {
32476                     *markw_to_clear = 0;
32477                     markw_to_clear++;
32478                 }
32479             }
32480         }
32481
32482         seg = heap_segment_next_rw (seg);
32483     }
32484
32485     //size_t end_time = GetHighPrecisionTimeStamp() - begin_time; 
32486
32487     //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(uint32_t));
32488
32489 #endif //MARK_ARRAY
32490 }
32491
32492 #endif //BACKGROUND_GC 
32493
32494 void gc_heap::verify_mark_array_cleared (heap_segment* seg)
32495 {
32496 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32497     assert (card_table == g_gc_card_table);
32498     size_t  markw = mark_word_of (heap_segment_mem (seg));
32499     size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
32500
32501     while (markw < markw_end)
32502     {
32503         if (mark_array [markw])
32504         {
32505             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32506                             markw, mark_array [markw], mark_word_address (markw)));
32507             FATAL_GC_ERROR();
32508         }
32509         markw++;
32510     }
32511 #endif //VERIFY_HEAP && MARK_ARRAY
32512 }
32513
32514 void gc_heap::verify_mark_array_cleared ()
32515 {
32516 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32517     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32518     {
32519         generation* gen = generation_of (max_generation);
32520         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32521         
32522         while (1)
32523         {
32524             if (seg == 0)
32525             {
32526                 if (gen != large_object_generation)
32527                 {
32528                     gen = generation_of (max_generation+1);
32529                     seg = heap_segment_rw (generation_start_segment (gen));
32530                 }
32531                 else
32532                 {
32533                     break;
32534                 }
32535             }
32536
32537             bgc_verify_mark_array_cleared (seg);
32538             seg = heap_segment_next_rw (seg);
32539         }
32540     }
32541 #endif //VERIFY_HEAP && MARK_ARRAY
32542 }
32543
32544 void gc_heap::verify_seg_end_mark_array_cleared()
32545 {
32546 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32547     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32548     {
32549         generation* gen = generation_of (max_generation);
32550         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32551         
32552         while (1)
32553         {
32554             if (seg == 0)
32555             {
32556                 if (gen != large_object_generation)
32557                 {
32558                     gen = generation_of (max_generation+1);
32559                     seg = heap_segment_rw (generation_start_segment (gen));
32560                 }
32561                 else
32562                 {
32563                     break;
32564                 }
32565             }
32566
32567             // We already cleared all mark array bits for ephemeral generations
32568             // at the beginning of bgc sweep
32569             uint8_t* from = ((seg == ephemeral_heap_segment) ?
32570                           generation_allocation_start (generation_of (max_generation - 1)) :
32571                           heap_segment_allocated (seg));
32572             size_t  markw = mark_word_of (align_on_mark_word (from));
32573             size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
32574
32575             while (from < mark_word_address (markw))
32576             {
32577                 if (is_mark_bit_set (from))
32578                 {
32579                     dprintf (3, ("mark bit for %Ix was not cleared", from));
32580                     FATAL_GC_ERROR();
32581                 }
32582
32583                 from += mark_bit_pitch;
32584             }
32585
32586             while (markw < markw_end)
32587             {
32588                 if (mark_array [markw])
32589                 {
32590                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32591                                     markw, mark_array [markw], mark_word_address (markw)));
32592                     FATAL_GC_ERROR();
32593                 }
32594                 markw++;
32595             }
32596             seg = heap_segment_next_rw (seg);
32597         }
32598     }
32599 #endif //VERIFY_HEAP && MARK_ARRAY
32600 }
32601
32602 // This function is called to make sure we don't mess up the segment list
32603 // in SOH. It's called by:
32604 // 1) begin and end of ephemeral GCs
32605 // 2) during bgc sweep when we switch segments.
32606 void gc_heap::verify_soh_segment_list()
32607 {
32608 #ifdef VERIFY_HEAP
32609     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32610     {
32611         generation* gen = generation_of (max_generation);
32612         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32613         heap_segment* last_seg = 0;
32614         while (seg)
32615         {
32616             last_seg = seg;
32617             seg = heap_segment_next_rw (seg);
32618         }
32619         if (last_seg != ephemeral_heap_segment)
32620         {
32621             FATAL_GC_ERROR();
32622         }
32623     }
32624 #endif //VERIFY_HEAP
32625 }
32626
32627 // This function can be called at any foreground GCs or blocking GCs. For background GCs,
32628 // it can be called at the end of the final marking; and at any point during background
32629 // sweep.
32630 // NOTE - to be able to call this function during background sweep, we need to temporarily 
32631 // NOT clear the mark array bits as we go.
32632 void gc_heap::verify_partial ()
32633 {
32634 #ifdef BACKGROUND_GC
32635     //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
32636     //generation* gen = large_object_generation;
32637     generation* gen = generation_of (max_generation);
32638     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32639     int align_const = get_alignment_constant (gen != large_object_generation);
32640
32641     uint8_t* o = 0;
32642     uint8_t* end = 0;
32643     size_t s = 0;
32644
32645     // Different ways to fail.
32646     BOOL mark_missed_p = FALSE;
32647     BOOL bad_ref_p = FALSE;
32648     BOOL free_ref_p = FALSE;
32649
32650     while (1)
32651     {
32652         if (seg == 0)
32653         {
32654             if (gen != large_object_generation)
32655             {
32656                 //switch to LOH
32657                 gen = large_object_generation;
32658                 align_const = get_alignment_constant (gen != large_object_generation);
32659                 seg = heap_segment_rw (generation_start_segment (gen));
32660                 continue;
32661             }
32662             else
32663             {
32664                 break;
32665             }
32666         }
32667
32668         o = heap_segment_mem (seg);
32669         end  = heap_segment_allocated (seg);
32670         //printf ("validating [%Ix-[%Ix\n", o, end);
32671         while (o < end)
32672         {
32673             s = size (o);
32674
32675             BOOL marked_p = background_object_marked (o, FALSE);
32676
32677             if (marked_p)
32678             {
32679                 go_through_object_cl (method_table (o), o, s, oo,
32680                     {
32681                         if (*oo)
32682                         {
32683                             //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
32684                             MethodTable *pMT = method_table (*oo);
32685
32686                             if (pMT == g_gc_pFreeObjectMethodTable)
32687                             {
32688                                 free_ref_p = TRUE;
32689                                 FATAL_GC_ERROR();
32690                             }
32691
32692                             if (!pMT->SanityCheck()) 
32693                             {
32694                                 bad_ref_p = TRUE;
32695                                 dprintf (3, ("Bad member of %Ix %Ix",
32696                                             (size_t)oo, (size_t)*oo));
32697                                 FATAL_GC_ERROR();
32698                             }
32699
32700                             if (current_bgc_state == bgc_final_marking)
32701                             {
32702                                 if (marked_p && !background_object_marked (*oo, FALSE))
32703                                 {
32704                                     mark_missed_p = TRUE;
32705                                     FATAL_GC_ERROR();
32706                                 }
32707                             }
32708                         }
32709                     }
32710                                     );
32711             }
32712
32713             o = o + Align(s, align_const);
32714         }
32715         seg = heap_segment_next_rw (seg);
32716     }
32717
32718     //printf ("didn't find any large object large enough...\n");
32719     //printf ("finished verifying loh\n");
32720 #endif //BACKGROUND_GC 
32721 }
32722
32723 #ifdef VERIFY_HEAP
32724
32725 void 
32726 gc_heap::verify_free_lists ()
32727 {
32728     for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
32729     {
32730         dprintf (3, ("Verifying free list for gen:%d", gen_num));
32731         allocator* gen_alloc = generation_allocator (generation_of (gen_num));
32732         size_t sz = gen_alloc->first_bucket_size();
32733         bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
32734
32735         for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
32736         {
32737             uint8_t* free_list = gen_alloc->alloc_list_head_of (a_l_number);
32738             uint8_t* prev = 0;
32739             while (free_list)
32740             {
32741                 if (!((CObjectHeader*)free_list)->IsFree())
32742                 {
32743                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
32744                                  (size_t)free_list));
32745                     FATAL_GC_ERROR();
32746                 }
32747                 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
32748                     || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
32749                 {
32750                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
32751                                  (size_t)free_list));
32752                     FATAL_GC_ERROR();
32753                 }
32754                 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
32755                 {
32756                     dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
32757                                  (size_t)free_list));
32758                     FATAL_GC_ERROR();
32759                 }
32760                 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
32761                 {
32762                     dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
32763                                  (size_t)free_list));
32764                     FATAL_GC_ERROR();
32765                 }
32766                     
32767                 prev = free_list;
32768                 free_list = free_list_slot (free_list);
32769             }
32770             //verify the sanity of the tail 
32771             uint8_t* tail = gen_alloc->alloc_list_tail_of (a_l_number);
32772             if (!((tail == 0) || (tail == prev)))
32773             {
32774                 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
32775                 FATAL_GC_ERROR();
32776             }
32777             if (tail == 0)
32778             {
32779                 uint8_t* head = gen_alloc->alloc_list_head_of (a_l_number);
32780                 if ((head != 0) && (free_list_slot (head) != 0))
32781                 {
32782                     dprintf (3, ("Verifying Heap: tail of free list is not correct"));
32783                     FATAL_GC_ERROR();
32784                 }
32785             }
32786
32787             sz *=2;
32788         }
32789     }
32790 }
32791
32792 void
32793 gc_heap::verify_heap (BOOL begin_gc_p)
32794 {
32795     int             heap_verify_level = static_cast<int>(GCConfig::GetHeapVerifyLevel());
32796     size_t          last_valid_brick = 0;
32797     BOOL            bCurrentBrickInvalid = FALSE;
32798     BOOL            large_brick_p = TRUE;
32799     size_t          curr_brick = 0;
32800     size_t          prev_brick = (size_t)-1;
32801     int             curr_gen_num = max_generation+1;    
32802     heap_segment*   seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
32803
32804     PREFIX_ASSUME(seg != NULL);
32805
32806     uint8_t*        curr_object = heap_segment_mem (seg);
32807     uint8_t*        prev_object = 0;
32808     uint8_t*        begin_youngest = generation_allocation_start(generation_of(0));
32809     uint8_t*        end_youngest = heap_segment_allocated (ephemeral_heap_segment);
32810     uint8_t*        next_boundary = generation_allocation_start (generation_of (max_generation - 1));
32811     int             align_const = get_alignment_constant (FALSE);
32812     size_t          total_objects_verified = 0;
32813     size_t          total_objects_verified_deep = 0;
32814
32815 #ifdef BACKGROUND_GC
32816     BOOL consider_bgc_mark_p    = FALSE;
32817     BOOL check_current_sweep_p  = FALSE;
32818     BOOL check_saved_sweep_p    = FALSE;
32819     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32820 #endif //BACKGROUND_GC
32821
32822 #ifdef MULTIPLE_HEAPS
32823     t_join* current_join = &gc_t_join;
32824 #ifdef BACKGROUND_GC
32825     if (settings.concurrent && (bgc_thread_id.IsCurrentThread()))
32826     {
32827         // We always call verify_heap on entry of GC on the SVR GC threads.
32828         current_join = &bgc_t_join;
32829     }
32830 #endif //BACKGROUND_GC
32831 #endif //MULTIPLE_HEAPS
32832
32833     UNREFERENCED_PARAMETER(begin_gc_p);
32834 #ifdef BACKGROUND_GC 
32835     dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin", 
32836         (begin_gc_p ? "BEG" : "END"),
32837         VolatileLoad(&settings.gc_index), 
32838         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
32839 #else
32840     dprintf (2,("[%s]GC#%d: Verifying heap - begin", 
32841                 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
32842 #endif //BACKGROUND_GC 
32843
32844 #ifndef MULTIPLE_HEAPS
32845     if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
32846         (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
32847     {
32848         FATAL_GC_ERROR();
32849     }
32850 #endif //MULTIPLE_HEAPS
32851
32852 #ifdef BACKGROUND_GC
32853     //don't touch the memory because the program is allocating from it.
32854     if (!settings.concurrent)
32855 #endif //BACKGROUND_GC
32856     {
32857         if (!(heap_verify_level & GCConfig::HEAPVERIFY_NO_MEM_FILL))
32858         {
32859             //uninit the unused portions of segments.
32860             generation* gen1 = large_object_generation;
32861             heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
32862             PREFIX_ASSUME(seg1 != NULL);
32863
32864             while (1)
32865             {
32866                 if (seg1)
32867                 {
32868                     uint8_t* clear_start = heap_segment_allocated (seg1) - plug_skew;
32869                     if (heap_segment_used (seg1) > clear_start)
32870                     {
32871                         dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa", 
32872                                     heap_segment_mem (seg1),
32873                                     clear_start ,
32874                                     heap_segment_used (seg1)));
32875                         memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
32876                             (heap_segment_used (seg1) - clear_start));
32877                     }
32878                     seg1 = heap_segment_next_rw (seg1);
32879                 }
32880                 else
32881                 {
32882                     if (gen1 == large_object_generation)
32883                     {
32884                         gen1 = generation_of (max_generation);
32885                         seg1 = heap_segment_rw (generation_start_segment (gen1));
32886                         PREFIX_ASSUME(seg1 != NULL);
32887                     }
32888                     else
32889                     {
32890                         break;
32891                     }
32892                 }
32893             }
32894         }
32895     }
32896
32897 #ifdef MULTIPLE_HEAPS
32898     current_join->join(this, gc_join_verify_copy_table);
32899     if (current_join->joined())
32900     {
32901         // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
32902         for (int i = 0; i < n_heaps; i++)
32903         {
32904             //copy the card and brick tables
32905             if (g_gc_card_table != g_heaps[i]->card_table)
32906             {
32907                 g_heaps[i]->copy_brick_card_table();
32908             }
32909         }
32910
32911         current_join->restart();
32912     }
32913 #else
32914         if (g_gc_card_table != card_table)
32915             copy_brick_card_table();
32916 #endif //MULTIPLE_HEAPS
32917
32918     //verify that the generation structures makes sense
32919     {
32920         generation* gen = generation_of (max_generation);
32921
32922         assert (generation_allocation_start (gen) ==
32923                 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
32924         int gen_num = max_generation-1;
32925         generation* prev_gen = gen;
32926         while (gen_num >= 0)
32927         {
32928             gen = generation_of (gen_num);
32929             assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
32930             assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
32931             assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
32932
32933             if (generation_start_segment (prev_gen ) ==
32934                 generation_start_segment (gen))
32935             {
32936                 assert (generation_allocation_start (prev_gen) <
32937                         generation_allocation_start (gen));
32938             }
32939             prev_gen = gen;
32940             gen_num--;
32941         }
32942     }
32943
32944     while (1)
32945     {
32946         // Handle segment transitions
32947         if (curr_object >= heap_segment_allocated (seg))
32948         {
32949             if (curr_object > heap_segment_allocated(seg))
32950             {
32951                 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
32952                         (size_t)curr_object, (size_t)seg));
32953                 FATAL_GC_ERROR();
32954             }
32955             seg = heap_segment_next_in_range (seg);
32956             if (seg)
32957             {
32958 #ifdef BACKGROUND_GC
32959                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32960 #endif //BACKGROUND_GC
32961                 curr_object = heap_segment_mem(seg);
32962                 prev_object = 0;
32963                 continue;
32964             }
32965             else
32966             {
32967                 if (curr_gen_num == (max_generation+1))
32968                 {
32969                     curr_gen_num--;
32970                     seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
32971
32972                     PREFIX_ASSUME(seg != NULL);
32973
32974 #ifdef BACKGROUND_GC
32975                     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32976 #endif //BACKGROUND_GC
32977                     curr_object = heap_segment_mem (seg);
32978                     prev_object = 0;
32979                     large_brick_p = FALSE;
32980                     align_const = get_alignment_constant (TRUE);
32981                 }
32982                 else
32983                     break;  // Done Verifying Heap -- no more segments
32984             }
32985         }
32986
32987         // Are we at the end of the youngest_generation?
32988         if (seg == ephemeral_heap_segment)
32989         {
32990             if (curr_object >= end_youngest)
32991             {
32992                 // prev_object length is too long if we hit this int3
32993                 if (curr_object > end_youngest)
32994                 {
32995                     dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
32996                             (size_t)curr_object, (size_t)end_youngest));
32997                     FATAL_GC_ERROR();
32998                 }
32999                 break;
33000             }
33001             
33002             if ((curr_object >= next_boundary) && (curr_gen_num > 0))
33003             {
33004                 curr_gen_num--;
33005                 if (curr_gen_num > 0)
33006                 {
33007                     next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
33008                 }
33009             }
33010         }
33011
33012          //if (is_mark_set (curr_object))
33013          //{
33014          //        printf ("curr_object: %Ix is marked!",(size_t)curr_object);
33015          //        FATAL_GC_ERROR();
33016          //}
33017
33018         size_t s = size (curr_object);
33019         dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
33020         if (s == 0)
33021         {
33022             dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
33023             FATAL_GC_ERROR();
33024         }
33025
33026         // If object is not in the youngest generation, then lets
33027         // verify that the brick table is correct....
33028         if (((seg != ephemeral_heap_segment) ||
33029              (brick_of(curr_object) < brick_of(begin_youngest))))
33030         {
33031             curr_brick = brick_of(curr_object);
33032
33033             // Brick Table Verification...
33034             //
33035             // On brick transition
33036             //     if brick is negative
33037             //          verify that brick indirects to previous valid brick
33038             //     else
33039             //          set current brick invalid flag to be flipped if we
33040             //          encounter an object at the correct place
33041             //
33042             if (curr_brick != prev_brick)
33043             {
33044                 // If the last brick we were examining had positive
33045                 // entry but we never found the matching object, then
33046                 // we have a problem
33047                 // If prev_brick was the last one of the segment
33048                 // it's ok for it to be invalid because it is never looked at
33049                 if (bCurrentBrickInvalid &&
33050                     (curr_brick != brick_of (heap_segment_mem (seg))) &&
33051                     !heap_segment_read_only_p (seg))
33052                 {
33053                     dprintf (3, ("curr brick %Ix invalid", curr_brick));
33054                     FATAL_GC_ERROR();
33055                 }
33056
33057                 if (large_brick_p)
33058                 {
33059                     //large objects verify the table only if they are in
33060                     //range.
33061                     if ((heap_segment_reserved (seg) <= highest_address) &&
33062                         (heap_segment_mem (seg) >= lowest_address) &&
33063                         brick_table [curr_brick] != 0)
33064                     {
33065                         dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
33066                                 curr_brick, (size_t)curr_object));
33067                         FATAL_GC_ERROR();
33068                     }
33069                     else
33070                     {
33071                         bCurrentBrickInvalid = FALSE;
33072                     }
33073                 }
33074                 else
33075                 {
33076                     // If the current brick contains a negative value make sure
33077                     // that the indirection terminates at the last  valid brick
33078                     if (brick_table [curr_brick] <= 0)
33079                     {
33080                         if (brick_table [curr_brick] == 0)
33081                         {
33082                             dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
33083                                     curr_brick, (size_t)curr_object));
33084                             FATAL_GC_ERROR();
33085                         }
33086                         ptrdiff_t i = curr_brick;
33087                         while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
33088                                (brick_table[i] < 0))
33089                         {
33090                             i = i + brick_table[i];
33091                         }
33092                         if (i <  ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
33093                         {
33094                             dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
33095                                     i, brick_of (heap_segment_mem (seg)),
33096                                     curr_brick));
33097                             FATAL_GC_ERROR();
33098                         }
33099                         // if (i != last_valid_brick)
33100                         //  FATAL_GC_ERROR();
33101                         bCurrentBrickInvalid = FALSE;
33102                     }
33103                     else if (!heap_segment_read_only_p (seg))
33104                     {
33105                         bCurrentBrickInvalid = TRUE;
33106                     }
33107                 }
33108             }
33109
33110             if (bCurrentBrickInvalid)
33111             {
33112                 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
33113                 {
33114                     bCurrentBrickInvalid = FALSE;
33115                     last_valid_brick = curr_brick;
33116                 }
33117             }
33118         }
33119
33120         if (*((uint8_t**)curr_object) != (uint8_t *) g_gc_pFreeObjectMethodTable)
33121         {
33122 #ifdef FEATURE_LOH_COMPACTION
33123             if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
33124             {
33125                 assert (method_table (prev_object) == g_gc_pFreeObjectMethodTable);
33126             }
33127 #endif //FEATURE_LOH_COMPACTION
33128
33129             total_objects_verified++;
33130
33131             BOOL can_verify_deep = TRUE;
33132 #ifdef BACKGROUND_GC
33133             can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
33134 #endif //BACKGROUND_GC
33135
33136             BOOL deep_verify_obj = can_verify_deep;
33137             if ((heap_verify_level & GCConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
33138                 deep_verify_obj = FALSE;
33139
33140             ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
33141
33142             if (can_verify_deep)
33143             {
33144                 if (curr_gen_num > 0)
33145                 {
33146                     BOOL need_card_p = FALSE;
33147                     if (contain_pointers_or_collectible (curr_object))
33148                     {
33149                         dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
33150                         size_t crd = card_of (curr_object);
33151                         BOOL found_card_p = card_set_p (crd);
33152
33153 #ifdef COLLECTIBLE_CLASS
33154                         if (is_collectible(curr_object))
33155                         {
33156                             uint8_t* class_obj = get_class_object (curr_object);
33157                             if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
33158                             {
33159                                 if (!found_card_p)
33160                                 {
33161                                     dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
33162                                                 card_of (curr_object), (size_t)curr_object, class_obj));
33163
33164                                     FATAL_GC_ERROR();
33165                                 }
33166                             }
33167                         }
33168 #endif //COLLECTIBLE_CLASS
33169
33170                         if (contain_pointers(curr_object))
33171                         {
33172                             go_through_object_nostart
33173                                 (method_table(curr_object), curr_object, s, oo,
33174                                 {
33175                                     if ((crd != card_of ((uint8_t*)oo)) && !found_card_p)
33176                                     {
33177                                         crd = card_of ((uint8_t*)oo);
33178                                         found_card_p = card_set_p (crd);
33179                                         need_card_p = FALSE;
33180                                     }
33181                                     if ((*oo < ephemeral_high) && (*oo >= next_boundary))
33182                                     {
33183                                         need_card_p = TRUE;
33184                                     }
33185
33186                                 if (need_card_p && !found_card_p)
33187                                 {
33188
33189                                         dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33190                                                     card_of (curr_object), (size_t)curr_object,
33191                                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33192                                         FATAL_GC_ERROR();
33193                                     }
33194                                 }
33195                                     );
33196                         }
33197                         if (need_card_p && !found_card_p)
33198                         {
33199                             dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33200                                     card_of (curr_object), (size_t)curr_object,
33201                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33202                             FATAL_GC_ERROR();
33203                         }
33204                     }
33205                 }
33206                 total_objects_verified_deep++;
33207             }
33208         }
33209
33210         prev_object = curr_object;
33211         prev_brick = curr_brick;
33212         curr_object = curr_object + Align(s, align_const);
33213         if (curr_object < prev_object)
33214         {
33215             dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
33216             FATAL_GC_ERROR();
33217         }
33218     }
33219
33220 #ifdef BACKGROUND_GC
33221     dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id", 
33222                  (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
33223                  (begin_gc_p ? "BEG" : "END"),
33224                  ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
33225                  total_objects_verified, total_objects_verified_deep));
33226     if (current_c_gc_state != c_gc_state_planning)
33227     {
33228         assert (total_objects_verified == total_objects_verified_deep);
33229     }
33230 #endif //BACKGROUND_GC
33231     
33232     verify_free_lists();
33233
33234 #ifdef FEATURE_PREMORTEM_FINALIZATION
33235     finalize_queue->CheckFinalizerObjects();
33236 #endif // FEATURE_PREMORTEM_FINALIZATION
33237
33238     {
33239         // to be consistent with handle table APIs pass a ScanContext*
33240         // to provide the heap number.  the SC isn't complete though so
33241         // limit its scope to handle table verification.
33242         ScanContext sc;
33243         sc.thread_number = heap_number;
33244         GCScan::VerifyHandleTable(max_generation, max_generation, &sc);
33245     }
33246
33247 #ifdef MULTIPLE_HEAPS
33248     current_join->join(this, gc_join_verify_objects_done);
33249     if (current_join->joined())
33250 #endif //MULTIPLE_HEAPS
33251     {
33252         SyncBlockCache::GetSyncBlockCache()->VerifySyncTableEntry();
33253 #ifdef MULTIPLE_HEAPS
33254         current_join->restart();
33255 #endif //MULTIPLE_HEAPS
33256     }
33257
33258 #ifdef BACKGROUND_GC 
33259     if (!settings.concurrent)
33260     {
33261         if (current_c_gc_state == c_gc_state_planning)
33262         {
33263             // temporarily commenting this out 'cause an FGC
33264             // could be triggered before we sweep ephemeral.
33265             //verify_seg_end_mark_array_cleared();
33266         }
33267     }
33268
33269     if (settings.concurrent)
33270     {
33271         verify_mark_array_cleared();
33272     }
33273     dprintf (2,("GC%d(%s): Verifying heap - end", 
33274         VolatileLoad(&settings.gc_index), 
33275         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
33276 #else
33277     dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
33278 #endif //BACKGROUND_GC 
33279 }
33280
33281 #endif  //VERIFY_HEAP
33282
33283
33284 void GCHeap::ValidateObjectMember (Object* obj)
33285 {
33286 #ifdef VERIFY_HEAP
33287     size_t s = size (obj);
33288     uint8_t* o = (uint8_t*)obj;
33289
33290     go_through_object_cl (method_table (obj), o, s, oo,
33291                                 {
33292                                     uint8_t* child_o = *oo;
33293                                     if (child_o)
33294                                     {
33295                                         dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
33296                                         MethodTable *pMT = method_table (child_o);
33297                                         assert(pMT);
33298                                         if (!pMT->SanityCheck()) {
33299                                             dprintf (3, ("Bad member of %Ix %Ix",
33300                                                         (size_t)oo, (size_t)child_o));
33301                                             FATAL_GC_ERROR();
33302                                         }
33303                                     }
33304                                 } );
33305 #endif // VERIFY_HEAP
33306 }
33307
33308 void DestructObject (CObjectHeader* hdr)
33309 {
33310     UNREFERENCED_PARAMETER(hdr); // compiler bug? -- this *is*, indeed, referenced
33311     hdr->~CObjectHeader();
33312 }
33313
33314 HRESULT GCHeap::Shutdown ()
33315 {
33316     deleteGCShadow();
33317
33318     GCScan::GcRuntimeStructuresValid (FALSE);
33319
33320     // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
33321     // threads except the one performing the shutdown.
33322     // ASSERT( !GcInProgress );
33323
33324     // Guard against any more GC occurring and against any threads blocking
33325     // for GC to complete when the GC heap is gone.  This fixes a race condition
33326     // where a thread in GC is destroyed as part of process destruction and
33327     // the remaining threads block for GC complete.
33328
33329     //GCTODO
33330     //EnterAllocLock();
33331     //Enter();
33332     //EnterFinalizeLock();
33333     //SetGCDone();
33334
33335     // during shutdown lot of threads are suspended
33336     // on this even, we don't want to wake them up just yet
33337     //CloseHandle (WaitForGCEvent);
33338
33339     //find out if the global card table hasn't been used yet
33340     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
33341     if (card_table_refcount (ct) == 0)
33342     {
33343         destroy_card_table (ct);
33344         g_gc_card_table = nullptr;
33345
33346 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
33347         g_gc_card_bundle_table = nullptr;
33348 #endif
33349 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33350         SoftwareWriteWatch::StaticClose();
33351 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33352     }
33353
33354     //destroy all segments on the standby list
33355     while(gc_heap::segment_standby_list != 0)
33356     {
33357         heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
33358 #ifdef MULTIPLE_HEAPS
33359         (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33360 #else //MULTIPLE_HEAPS
33361         pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33362 #endif //MULTIPLE_HEAPS
33363         gc_heap::segment_standby_list = next_seg;
33364     }
33365
33366
33367 #ifdef MULTIPLE_HEAPS
33368
33369     for (int i = 0; i < gc_heap::n_heaps; i ++)
33370     {
33371         delete gc_heap::g_heaps[i]->vm_heap;
33372         //destroy pure GC stuff
33373         gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
33374     }
33375 #else
33376     gc_heap::destroy_gc_heap (pGenGCHeap);
33377
33378 #endif //MULTIPLE_HEAPS
33379     gc_heap::shutdown_gc();
33380
33381     return S_OK;
33382 }
33383
33384 // Wait until a garbage collection is complete
33385 // returns NOERROR if wait was OK, other error code if failure.
33386 // WARNING: This will not undo the must complete state. If you are
33387 // in a must complete when you call this, you'd better know what you're
33388 // doing.
33389
33390 #ifdef FEATURE_PREMORTEM_FINALIZATION
33391 static
33392 HRESULT AllocateCFinalize(CFinalize **pCFinalize)
33393 {
33394     *pCFinalize = new (nothrow) CFinalize();
33395     if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
33396         return E_OUTOFMEMORY;
33397
33398     return S_OK;
33399 }
33400 #endif // FEATURE_PREMORTEM_FINALIZATION
33401
33402 // init the instance heap
33403 HRESULT GCHeap::Init(size_t hn)
33404 {
33405     HRESULT hres = S_OK;
33406
33407 #ifdef MULTIPLE_HEAPS
33408     if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
33409         hres = E_OUTOFMEMORY;
33410 #else
33411     UNREFERENCED_PARAMETER(hn);
33412     if (!gc_heap::make_gc_heap())
33413         hres = E_OUTOFMEMORY;
33414 #endif //MULTIPLE_HEAPS
33415
33416     // Failed.
33417     return hres;
33418 }
33419
33420 //System wide initialization
33421 HRESULT GCHeap::Initialize ()
33422 {
33423     HRESULT hr = S_OK;
33424
33425     g_gc_pFreeObjectMethodTable = GCToEEInterface::GetFreeObjectMethodTable();
33426     g_num_processors = GCToOSInterface::GetTotalProcessorCount();
33427     assert(g_num_processors != 0);
33428
33429 //Initialize the static members.
33430 #ifdef TRACE_GC
33431     GcDuration = 0;
33432     CreatedObjectCount = 0;
33433 #endif //TRACE_GC
33434
33435     size_t seg_size = get_valid_segment_size();
33436     gc_heap::soh_segment_size = seg_size;
33437     size_t large_seg_size = get_valid_segment_size(TRUE);
33438     gc_heap::min_loh_segment_size = large_seg_size;
33439     gc_heap::min_segment_size = min (seg_size, large_seg_size);
33440 #ifdef SEG_MAPPING_TABLE
33441     gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_heap::min_segment_size);
33442 #endif //SEG_MAPPING_TABLE
33443
33444 #ifdef MULTIPLE_HEAPS
33445     if (GCConfig::GetNoAffinitize())
33446         gc_heap::gc_thread_no_affinitize_p = true;
33447
33448     uint32_t nhp_from_config = static_cast<uint32_t>(GCConfig::GetHeapCount());
33449     
33450     uint32_t nhp_from_process = GCToOSInterface::GetCurrentProcessCpuCount();
33451
33452     uint32_t nhp = ((nhp_from_config == 0) ? nhp_from_process :
33453                                              (min (nhp_from_config, nhp_from_process)));
33454
33455     nhp = min (nhp, MAX_SUPPORTED_CPUS);
33456
33457     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
33458 #else
33459     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
33460 #endif //MULTIPLE_HEAPS
33461
33462     if (hr != S_OK)
33463         return hr;
33464
33465     gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit();
33466
33467     gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
33468 #ifndef MULTIPLE_HEAPS
33469     gc_heap::mem_one_percent /= g_num_processors;
33470 #endif //!MULTIPLE_HEAPS
33471
33472     // We should only use this if we are in the "many process" mode which really is only applicable
33473     // to very powerful machines - before that's implemented, temporarily I am only enabling this for 80GB+ memory. 
33474     // For now I am using an estimate to calculate these numbers but this should really be obtained 
33475     // programmatically going forward.
33476     // I am assuming 47 processes using WKS GC and 3 using SVR GC.
33477     // I am assuming 3 in part due to the "very high memory load" is 97%.
33478     int available_mem_th = 10;
33479     if (gc_heap::total_physical_mem >= ((uint64_t)80 * 1024 * 1024 * 1024))
33480     {
33481         int adjusted_available_mem_th = 3 + (int)((float)47 / (float)(g_num_processors));
33482         available_mem_th = min (available_mem_th, adjusted_available_mem_th);
33483     }
33484
33485     gc_heap::high_memory_load_th = 100 - available_mem_th;
33486
33487 #if defined(BIT64) 
33488     gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
33489 #endif // BIT64
33490
33491     WaitForGCEvent = new (nothrow) GCEvent;
33492
33493     if (!WaitForGCEvent)
33494     {
33495         return E_OUTOFMEMORY;
33496     }
33497
33498     if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE))
33499     {
33500         return E_FAIL;
33501     }
33502
33503 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33504 #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
33505     if (GCStress<cfg_any>::IsEnabled())  {
33506         for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
33507             m_StressObjs[i] = CreateGlobalHandle(0);
33508         m_CurStressObj = 0;
33509     }
33510 #endif //STRESS_HEAP && !MULTIPLE_HEAPS
33511 #endif // FEATURE_REDHAWK
33512
33513     initGCShadow();         // If we are debugging write barriers, initialize heap shadow
33514
33515 #ifdef MULTIPLE_HEAPS
33516
33517     for (unsigned i = 0; i < nhp; i++)
33518     {
33519         GCHeap* Hp = new (nothrow) GCHeap();
33520         if (!Hp)
33521             return E_OUTOFMEMORY;
33522
33523         if ((hr = Hp->Init (i))!= S_OK)
33524         {
33525             return hr;
33526         }
33527     }
33528     // initialize numa node to heap map
33529     heap_select::init_numa_node_to_heap_map(nhp);
33530 #else
33531     hr = Init (0);
33532 #endif //MULTIPLE_HEAPS
33533
33534     if (hr == S_OK)
33535     {
33536         GCScan::GcRuntimeStructuresValid (TRUE);
33537
33538         GCToEEInterface::DiagUpdateGenerationBounds();
33539     }
33540
33541     return hr;
33542 };
33543
33544 ////
33545 // GC callback functions
33546 bool GCHeap::IsPromoted(Object* object)
33547 {
33548 #ifdef _DEBUG
33549     ((CObjectHeader*)object)->Validate();
33550 #endif //_DEBUG
33551
33552     uint8_t* o = (uint8_t*)object;
33553
33554     if (gc_heap::settings.condemned_generation == max_generation)
33555     {
33556 #ifdef MULTIPLE_HEAPS
33557         gc_heap* hp = gc_heap::g_heaps[0];
33558 #else
33559         gc_heap* hp = pGenGCHeap;
33560 #endif //MULTIPLE_HEAPS
33561
33562 #ifdef BACKGROUND_GC
33563         if (gc_heap::settings.concurrent)
33564         {
33565             bool is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
33566                             hp->background_marked (o));
33567             return is_marked;
33568         }
33569         else
33570 #endif //BACKGROUND_GC
33571         {
33572             return (!((o < hp->highest_address) && (o >= hp->lowest_address))
33573                     || hp->is_mark_set (o));
33574         }
33575     }
33576     else
33577     {
33578         gc_heap* hp = gc_heap::heap_of (o);
33579         return (!((o < hp->gc_high) && (o >= hp->gc_low))
33580                 || hp->is_mark_set (o));
33581     }
33582 }
33583
33584 size_t GCHeap::GetPromotedBytes(int heap_index)
33585 {
33586 #ifdef BACKGROUND_GC
33587     if (gc_heap::settings.concurrent)
33588     {
33589         return gc_heap::bpromoted_bytes (heap_index);
33590     }
33591     else
33592 #endif //BACKGROUND_GC
33593     {
33594         return gc_heap::promoted_bytes (heap_index);
33595     }
33596 }
33597
33598 unsigned int GCHeap::WhichGeneration (Object* object)
33599 {
33600     gc_heap* hp = gc_heap::heap_of ((uint8_t*)object);
33601     unsigned int g = hp->object_gennum ((uint8_t*)object);
33602     dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
33603     return g;
33604 }
33605
33606 bool GCHeap::IsEphemeral (Object* object)
33607 {
33608     uint8_t* o = (uint8_t*)object;
33609     gc_heap* hp = gc_heap::heap_of (o);
33610     return !!hp->ephemeral_pointer_p (o);
33611 }
33612
33613 // Return NULL if can't find next object. When EE is not suspended,
33614 // the result is not accurate: if the input arg is in gen0, the function could 
33615 // return zeroed out memory as next object
33616 Object * GCHeap::NextObj (Object * object)
33617 {
33618 #ifdef VERIFY_HEAP
33619     uint8_t* o = (uint8_t*)object;
33620
33621 #ifndef FEATURE_BASICFREEZE
33622     if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)))
33623     {
33624         return NULL;
33625     }
33626 #endif //!FEATURE_BASICFREEZE
33627
33628     heap_segment * hs = gc_heap::find_segment (o, FALSE);
33629     if (!hs)
33630     {
33631         return NULL;
33632     }
33633
33634     BOOL large_object_p = heap_segment_loh_p (hs);
33635     if (large_object_p)
33636         return NULL; //could be racing with another core allocating. 
33637 #ifdef MULTIPLE_HEAPS
33638     gc_heap* hp = heap_segment_heap (hs);
33639 #else //MULTIPLE_HEAPS
33640     gc_heap* hp = 0;
33641 #endif //MULTIPLE_HEAPS
33642     unsigned int g = hp->object_gennum ((uint8_t*)object);
33643     if ((g == 0) && hp->settings.demotion)
33644         return NULL;//could be racing with another core allocating. 
33645     int align_const = get_alignment_constant (!large_object_p);
33646     uint8_t* nextobj = o + Align (size (o), align_const);
33647     if (nextobj <= o) // either overflow or 0 sized object.
33648     {
33649         return NULL;
33650     }
33651
33652     if ((nextobj < heap_segment_mem(hs)) || 
33653         (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) || 
33654         (nextobj >= hp->alloc_allocated))
33655     {
33656         return NULL;
33657     }
33658
33659     return (Object *)nextobj;
33660 #else
33661     return nullptr;
33662 #endif // VERIFY_HEAP
33663 }
33664
33665 #ifdef VERIFY_HEAP
33666
33667 #ifdef FEATURE_BASICFREEZE
33668 BOOL GCHeap::IsInFrozenSegment (Object * object)
33669 {
33670     uint8_t* o = (uint8_t*)object;
33671     heap_segment * hs = gc_heap::find_segment (o, FALSE);
33672     //We create a frozen object for each frozen segment before the segment is inserted
33673     //to segment list; during ngen, we could also create frozen objects in segments which
33674     //don't belong to current GC heap.
33675     //So we return true if hs is NULL. It might create a hole about detecting invalidate 
33676     //object. But given all other checks present, the hole should be very small
33677     return !hs || heap_segment_read_only_p (hs);
33678 }
33679 #endif //FEATURE_BASICFREEZE
33680
33681 #endif //VERIFY_HEAP
33682
33683 // returns TRUE if the pointer is in one of the GC heaps.
33684 bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only)
33685 {
33686     STATIC_CONTRACT_SO_TOLERANT;
33687
33688     // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment 
33689     // no longer calls GCEvent::Wait which eventually takes a lock.
33690
33691     uint8_t* object = (uint8_t*) vpObject;
33692 #ifndef FEATURE_BASICFREEZE
33693     if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address)))
33694         return FALSE;
33695 #endif //!FEATURE_BASICFREEZE
33696
33697     heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
33698     return !!hs;
33699 }
33700
33701 #ifdef STRESS_PINNING
33702 static n_promote = 0;
33703 #endif //STRESS_PINNING
33704 // promote an object
33705 void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
33706 {
33707     THREAD_NUMBER_FROM_CONTEXT;
33708 #ifndef MULTIPLE_HEAPS
33709     const int thread = 0;
33710 #endif //!MULTIPLE_HEAPS
33711
33712     uint8_t* o = (uint8_t*)*ppObject;
33713
33714     if (o == 0)
33715         return;
33716
33717 #ifdef DEBUG_DestroyedHandleValue
33718     // we can race with destroy handle during concurrent scan
33719     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
33720         return;
33721 #endif //DEBUG_DestroyedHandleValue
33722
33723     HEAP_FROM_THREAD;
33724
33725     gc_heap* hp = gc_heap::heap_of (o);
33726
33727     dprintf (3, ("Promote %Ix", (size_t)o));
33728
33729 #ifdef INTERIOR_POINTERS
33730     if (flags & GC_CALL_INTERIOR)
33731     {
33732         if ((o < hp->gc_low) || (o >= hp->gc_high))
33733         {
33734             return;
33735         }
33736         if ( (o = hp->find_object (o, hp->gc_low)) == 0)
33737         {
33738             return;
33739         }
33740
33741     }
33742 #endif //INTERIOR_POINTERS
33743
33744 #ifdef FEATURE_CONSERVATIVE_GC
33745     // For conservative GC, a value on stack may point to middle of a free object.
33746     // In this case, we don't need to promote the pointer.
33747     if (GCConfig::GetConservativeGC()
33748         && ((CObjectHeader*)o)->IsFree())
33749     {
33750         return;
33751     }
33752 #endif
33753
33754 #ifdef _DEBUG
33755     ((CObjectHeader*)o)->ValidatePromote(sc, flags);
33756 #else 
33757     UNREFERENCED_PARAMETER(sc);
33758 #endif //_DEBUG
33759
33760     if (flags & GC_CALL_PINNED)
33761         hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
33762
33763 #ifdef STRESS_PINNING
33764     if ((++n_promote % 20) == 1)
33765             hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
33766 #endif //STRESS_PINNING
33767
33768 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33769     size_t promoted_size_begin = hp->promoted_bytes (thread);
33770 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33771
33772     if ((o >= hp->gc_low) && (o < hp->gc_high))
33773     {
33774         hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
33775     }
33776
33777 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33778     size_t promoted_size_end = hp->promoted_bytes (thread);
33779     if (g_fEnableARM)
33780     {
33781         if (sc->pCurrentDomain)
33782         {
33783             sc->pCurrentDomain->RecordSurvivedBytes ((promoted_size_end - promoted_size_begin), thread);
33784         }
33785     }
33786 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33787
33788     STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
33789 }
33790
33791 void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
33792                        uint32_t flags)
33793 {
33794     UNREFERENCED_PARAMETER(sc);
33795
33796     uint8_t* object = (uint8_t*)(Object*)(*ppObject);
33797     
33798     THREAD_NUMBER_FROM_CONTEXT;
33799
33800     //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
33801     dprintf (3, ("R: %Ix", (size_t)ppObject));
33802     
33803     if (object == 0)
33804         return;
33805
33806     gc_heap* hp = gc_heap::heap_of (object);
33807
33808 #ifdef _DEBUG
33809     if (!(flags & GC_CALL_INTERIOR))
33810     {
33811         // We cannot validate this object if it's in the condemned gen because it could 
33812         // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
33813         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
33814         {
33815             ((CObjectHeader*)object)->Validate(FALSE);
33816         }
33817     }
33818 #endif //_DEBUG
33819
33820     dprintf (3, ("Relocate %Ix\n", (size_t)object));
33821
33822     uint8_t* pheader;
33823
33824     if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
33825     {
33826         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
33827         {
33828             return;
33829         }
33830
33831         if (gc_heap::loh_object_p (object))
33832         {
33833             pheader = hp->find_object (object, 0);
33834             if (pheader == 0)
33835             {
33836                 return;
33837             }
33838
33839             ptrdiff_t ref_offset = object - pheader;
33840             hp->relocate_address(&pheader THREAD_NUMBER_ARG);
33841             *ppObject = (Object*)(pheader + ref_offset);
33842             return;
33843         }
33844     }
33845
33846     {
33847         pheader = object;
33848         hp->relocate_address(&pheader THREAD_NUMBER_ARG);
33849         *ppObject = (Object*)pheader;
33850     }
33851
33852     STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
33853 }
33854
33855 /*static*/ bool GCHeap::IsObjectInFixedHeap(Object *pObj)
33856 {
33857     // For now we simply look at the size of the object to determine if it in the
33858     // fixed heap or not. If the bit indicating this gets set at some point
33859     // we should key off that instead.
33860     return size( pObj ) >= LARGE_OBJECT_SIZE;
33861 }
33862
33863 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33864 #ifdef STRESS_HEAP
33865
33866 void StressHeapDummy ();
33867
33868 static int32_t GCStressStartCount = -1;
33869 static int32_t GCStressCurCount = 0;
33870 static int32_t GCStressStartAtJit = -1;
33871
33872 // the maximum number of foreground GCs we'll induce during one BGC
33873 // (this number does not include "naturally" occuring GCs).
33874 static int32_t GCStressMaxFGCsPerBGC = -1;
33875
33876 // CLRRandom implementation can produce FPU exceptions if 
33877 // the test/application run by CLR is enabling any FPU exceptions. 
33878 // We want to avoid any unexpected exception coming from stress 
33879 // infrastructure, so CLRRandom is not an option.
33880 // The code below is a replicate of CRT rand() implementation.
33881 // Using CRT rand() is not an option because we will interfere with the user application
33882 // that may also use it. 
33883 int StressRNG(int iMaxValue)
33884 {
33885     static BOOL bisRandInit = FALSE;
33886     static int lHoldrand = 1L;
33887
33888     if (!bisRandInit)
33889     {
33890         lHoldrand = (int)time(NULL);
33891         bisRandInit = TRUE;
33892     }
33893     int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
33894     return randValue % iMaxValue;
33895 }
33896 #endif // STRESS_HEAP
33897 #endif // !FEATURE_REDHAWK
33898
33899 // free up object so that things will move and then do a GC
33900 //return TRUE if GC actually happens, otherwise FALSE
33901 bool GCHeap::StressHeap(gc_alloc_context * context)
33902 {
33903 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
33904     alloc_context* acontext = static_cast<alloc_context*>(context);
33905     assert(context != nullptr);
33906
33907     // if GC stress was dynamically disabled during this run we return FALSE
33908     if (!GCStressPolicy::IsEnabled())
33909         return FALSE;
33910
33911 #ifdef _DEBUG
33912     if (g_pConfig->FastGCStressLevel() && !GCToEEInterface::GetThread()->StressHeapIsEnabled()) {
33913         return FALSE;
33914     }
33915
33916 #endif //_DEBUG
33917
33918     if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
33919 #ifdef _DEBUG
33920         || g_pConfig->FastGCStressLevel() > 1
33921 #endif //_DEBUG
33922         ) {
33923         if (!Thread::UniqueStack(&acontext)) {
33924             return FALSE;
33925         }
33926     }
33927
33928 #ifdef BACKGROUND_GC
33929         // don't trigger a GC from the GC threads but still trigger GCs from user threads.
33930         if (GCToEEInterface::WasCurrentThreadCreatedByGC())
33931         {
33932             return FALSE;
33933         }
33934 #endif //BACKGROUND_GC
33935
33936         if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
33937         {
33938             GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
33939             GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
33940         }
33941
33942         if (GCStressMaxFGCsPerBGC == -1)
33943         {
33944             GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
33945             if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
33946                 GCStressMaxFGCsPerBGC = 6;
33947         }
33948
33949 #ifdef _DEBUG
33950         if (g_JitCount < GCStressStartAtJit)
33951             return FALSE;
33952 #endif //_DEBUG
33953
33954         // Allow programmer to skip the first N Stress GCs so that you can
33955         // get to the interesting ones faster.
33956         Interlocked::Increment(&GCStressCurCount);
33957         if (GCStressCurCount < GCStressStartCount)
33958             return FALSE;
33959
33960         // throttle the number of stress-induced GCs by a factor given by GCStressStep
33961         if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
33962         {
33963             return FALSE;
33964         }
33965
33966 #ifdef BACKGROUND_GC
33967         if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
33968         {
33969             // allow a maximum number of stress induced FGCs during one BGC
33970             if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
33971                 return FALSE;
33972             ++gc_stress_fgcs_in_bgc;
33973         }
33974 #endif // BACKGROUND_GC
33975
33976     if (g_pStringClass == 0)
33977     {
33978         // If the String class has not been loaded, dont do any stressing. This should
33979         // be kept to a minimum to get as complete coverage as possible.
33980         _ASSERTE(g_fEEInit);
33981         return FALSE;
33982     }
33983
33984 #ifndef MULTIPLE_HEAPS
33985     static int32_t OneAtATime = -1;
33986
33987     // Only bother with this if the stress level is big enough and if nobody else is
33988     // doing it right now.  Note that some callers are inside the AllocLock and are
33989     // guaranteed synchronized.  But others are using AllocationContexts and have no
33990     // particular synchronization.
33991     //
33992     // For this latter case, we want a very high-speed way of limiting this to one
33993     // at a time.  A secondary advantage is that we release part of our StressObjs
33994     // buffer sparingly but just as effectively.
33995
33996     if (Interlocked::Increment(&OneAtATime) == 0 &&
33997         !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
33998     {
33999         StringObject* str;
34000
34001         // If the current string is used up
34002         if (HndFetchHandle(m_StressObjs[m_CurStressObj]) == 0)
34003         {
34004             // Populate handles with strings
34005             int i = m_CurStressObj;
34006             while(HndFetchHandle(m_StressObjs[i]) == 0)
34007             {
34008                 _ASSERTE(m_StressObjs[i] != 0);
34009                 unsigned strLen = (LARGE_OBJECT_SIZE - 32) / sizeof(WCHAR);
34010                 unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
34011                 
34012                 // update the cached type handle before allocating
34013                 SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
34014                 str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
34015                 if (str)
34016                 {
34017                     str->SetMethodTable (g_pStringClass);
34018                     str->SetStringLength (strLen);
34019
34020                     HndAssignHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
34021                 }
34022                 i = (i + 1) % NUM_HEAP_STRESS_OBJS;
34023                 if (i == m_CurStressObj) break;
34024             }
34025
34026             // advance the current handle to the next string
34027             m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
34028         }
34029
34030         // Get the current string
34031         str = (StringObject*) OBJECTREFToObject(HndFetchHandle(m_StressObjs[m_CurStressObj]));
34032         if (str)
34033         {
34034             // Chop off the end of the string and form a new object out of it.
34035             // This will 'free' an object at the begining of the heap, which will
34036             // force data movement.  Note that we can only do this so many times.
34037             // before we have to move on to the next string.
34038             unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
34039             if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
34040             {
34041                 unsigned sizeToNextObj = (unsigned)Align(size(str));
34042                 uint8_t* freeObj = ((uint8_t*) str) + sizeToNextObj - sizeOfNewObj;
34043                 pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);                    
34044                 str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
34045             }
34046             else
34047             {
34048                 // Let the string itself become garbage.
34049                 // will be realloced next time around
34050                 HndAssignHandle(m_StressObjs[m_CurStressObj], 0);
34051             }
34052         }
34053     }
34054     Interlocked::Decrement(&OneAtATime);
34055 #endif // !MULTIPLE_HEAPS
34056     if (IsConcurrentGCEnabled())
34057     {
34058         int rgen = StressRNG(10);
34059
34060         // gen0:gen1:gen2 distribution: 40:40:20
34061         if (rgen >= 8)
34062             rgen = 2;
34063         else if (rgen >= 4)
34064             rgen = 1;
34065     else
34066             rgen = 0;
34067
34068         GarbageCollectTry (rgen, FALSE, collection_gcstress);
34069     }
34070     else
34071     {
34072         GarbageCollect(max_generation, FALSE, collection_gcstress);
34073     }
34074
34075     return TRUE;
34076 #else
34077     UNREFERENCED_PARAMETER(context);
34078     return FALSE;
34079 #endif // defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34080 }
34081
34082
34083 #ifdef FEATURE_PREMORTEM_FINALIZATION
34084 #define REGISTER_FOR_FINALIZATION(_object, _size) \
34085     hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
34086 #else // FEATURE_PREMORTEM_FINALIZATION
34087 #define REGISTER_FOR_FINALIZATION(_object, _size) true
34088 #endif // FEATURE_PREMORTEM_FINALIZATION
34089
34090 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {  \
34091     if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size)))   \
34092     {                                                                                       \
34093         STRESS_LOG_OOM_STACK(_size);                                                        \
34094         return NULL;                                                                        \
34095     }                                                                                       \
34096 } while (false)
34097
34098 //
34099 // Small Object Allocator
34100 //
34101 //
34102 // Allocate small object with an alignment requirement of 8-bytes.
34103 Object*
34104 GCHeap::AllocAlign8(gc_alloc_context* ctx, size_t size, uint32_t flags )
34105 {
34106 #ifdef FEATURE_64BIT_ALIGNMENT
34107     CONTRACTL {
34108         NOTHROW;
34109         GC_TRIGGERS;
34110     } CONTRACTL_END;
34111
34112     alloc_context* acontext = static_cast<alloc_context*>(ctx);
34113
34114 #ifdef MULTIPLE_HEAPS
34115     if (acontext->get_alloc_heap() == 0)
34116     {
34117         AssignHeap (acontext);
34118         assert (acontext->get_alloc_heap());
34119     }
34120
34121     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34122 #else
34123     gc_heap* hp = pGenGCHeap;
34124 #endif //MULTIPLE_HEAPS
34125
34126     return AllocAlign8Common(hp, acontext, size, flags);
34127 #else
34128     UNREFERENCED_PARAMETER(ctx);
34129     UNREFERENCED_PARAMETER(size);
34130     UNREFERENCED_PARAMETER(flags);
34131     assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!");
34132     return nullptr;
34133 #endif  //FEATURE_64BIT_ALIGNMENT
34134 }
34135
34136 // Common code used by both variants of AllocAlign8 above.
34137 Object*
34138 GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, uint32_t flags)
34139 {
34140 #ifdef FEATURE_64BIT_ALIGNMENT
34141     CONTRACTL {
34142         NOTHROW;
34143         GC_TRIGGERS;
34144     } CONTRACTL_END;
34145
34146     gc_heap* hp = (gc_heap*)_hp;
34147
34148     TRIGGERSGC();
34149
34150     Object* newAlloc = NULL;
34151
34152 #ifdef TRACE_GC
34153 #ifdef COUNT_CYCLES
34154     AllocStart = GetCycleCount32();
34155     unsigned finish;
34156 #elif defined(ENABLE_INSTRUMENTATION)
34157     unsigned AllocStart = GetInstLogTime();
34158     unsigned finish;
34159 #endif //COUNT_CYCLES
34160 #endif //TRACE_GC
34161
34162     if (size < LARGE_OBJECT_SIZE)
34163     {
34164 #ifdef TRACE_GC
34165         AllocSmallCount++;
34166 #endif //TRACE_GC
34167
34168         // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
34169         // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
34170         // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
34171         size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
34172
34173         // Retrieve the address of the next allocation from the context (note that we're inside the alloc
34174         // lock at this point).
34175         uint8_t*  result = acontext->alloc_ptr;
34176
34177         // Will an allocation at this point yield the correct alignment and fit into the remainder of the
34178         // context?
34179         if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
34180         {
34181             // Yes, we can just go ahead and make the allocation.
34182             newAlloc = (Object*) hp->allocate (size, acontext);
34183             ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34184         }
34185         else
34186         {
34187             // No, either the next available address is not aligned in the way we require it or there's
34188             // not enough space to allocate an object of the required size. In both cases we allocate a
34189             // padding object (marked as a free object). This object's size is such that it will reverse
34190             // the alignment of the next header (asserted below).
34191             //
34192             // We allocate both together then decide based on the result whether we'll format the space as
34193             // free object + real object or real object + free object.
34194             ASSERT((Align(min_obj_size) & 7) == 4);
34195             CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
34196             if (freeobj)
34197             {
34198                 if (((size_t)freeobj & 7) == desiredAlignment)
34199                 {
34200                     // New allocation has desired alignment, return this one and place the free object at the
34201                     // end of the allocated space.
34202                     newAlloc = (Object*)freeobj;
34203                     freeobj = (CObjectHeader*)((uint8_t*)freeobj + Align(size));
34204                 }
34205                 else
34206                 {
34207                     // New allocation is still mis-aligned, format the initial space as a free object and the
34208                     // rest of the space should be correctly aligned for the real object.
34209                     newAlloc = (Object*)((uint8_t*)freeobj + Align(min_obj_size));
34210                     ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34211                 }
34212                 freeobj->SetFree(min_obj_size);
34213             }
34214         }
34215     }
34216     else
34217     {
34218         // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
34219         // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
34220         // we've managed to arrange things so the only case where we see a bias is for boxed value types and
34221         // these can never get large enough to be allocated on the LOH.
34222         ASSERT(65536 < LARGE_OBJECT_SIZE);
34223         ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
34224
34225         alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34226
34227         newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
34228         ASSERT(((size_t)newAlloc & 7) == 0);
34229     }
34230
34231     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34232
34233 #ifdef TRACE_GC
34234 #ifdef COUNT_CYCLES
34235     finish = GetCycleCount32();
34236 #elif defined(ENABLE_INSTRUMENTATION)
34237     finish = GetInstLogTime();
34238 #endif //COUNT_CYCLES
34239     AllocDuration += finish - AllocStart;
34240     AllocCount++;
34241 #endif //TRACE_GC
34242     return newAlloc;
34243 #else
34244     UNREFERENCED_PARAMETER(_hp);
34245     UNREFERENCED_PARAMETER(acontext);
34246     UNREFERENCED_PARAMETER(size);
34247     UNREFERENCED_PARAMETER(flags);
34248     assert(!"Should not call GCHeap::AllocAlign8Common without FEATURE_64BIT_ALIGNMENT defined!");
34249     return nullptr;
34250 #endif // FEATURE_64BIT_ALIGNMENT
34251 }
34252
34253 Object *
34254 GCHeap::AllocLHeap( size_t size, uint32_t flags REQD_ALIGN_DCL)
34255 {
34256     CONTRACTL {
34257         NOTHROW;
34258         GC_TRIGGERS;
34259     } CONTRACTL_END;
34260
34261     TRIGGERSGC();
34262
34263     Object* newAlloc = NULL;
34264
34265 #ifdef TRACE_GC
34266 #ifdef COUNT_CYCLES
34267     AllocStart = GetCycleCount32();
34268     unsigned finish;
34269 #elif defined(ENABLE_INSTRUMENTATION)
34270     unsigned AllocStart = GetInstLogTime();
34271     unsigned finish;
34272 #endif //COUNT_CYCLES
34273 #endif //TRACE_GC
34274
34275 #ifdef MULTIPLE_HEAPS
34276     //take the first heap....
34277     gc_heap* hp = gc_heap::g_heaps[0];
34278 #else
34279     gc_heap* hp = pGenGCHeap;
34280 #ifdef _PREFAST_
34281     // prefix complains about us dereferencing hp in wks build even though we only access static members
34282     // this way. not sure how to shut it up except for this ugly workaround:
34283     PREFIX_ASSUME(hp != NULL);
34284 #endif //_PREFAST_
34285 #endif //MULTIPLE_HEAPS
34286
34287     alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34288
34289     newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34290 #ifdef FEATURE_STRUCTALIGN
34291     newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34292 #endif // FEATURE_STRUCTALIGN
34293     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34294
34295 #ifdef TRACE_GC
34296 #ifdef COUNT_CYCLES
34297     finish = GetCycleCount32();
34298 #elif defined(ENABLE_INSTRUMENTATION)
34299     finish = GetInstLogTime();
34300 #endif //COUNT_CYCLES
34301     AllocDuration += finish - AllocStart;
34302     AllocCount++;
34303 #endif //TRACE_GC
34304     return newAlloc;
34305 }
34306
34307 Object*
34308 GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_DCL)
34309 {
34310     CONTRACTL {
34311         NOTHROW;
34312         GC_TRIGGERS;
34313     } CONTRACTL_END;
34314
34315     TRIGGERSGC();
34316
34317     Object* newAlloc = NULL;
34318     alloc_context* acontext = static_cast<alloc_context*>(context);
34319
34320 #ifdef TRACE_GC
34321 #ifdef COUNT_CYCLES
34322     AllocStart = GetCycleCount32();
34323     unsigned finish;
34324 #elif defined(ENABLE_INSTRUMENTATION)
34325     unsigned AllocStart = GetInstLogTime();
34326     unsigned finish;
34327 #endif //COUNT_CYCLES
34328 #endif //TRACE_GC
34329
34330 #ifdef MULTIPLE_HEAPS
34331     if (acontext->get_alloc_heap() == 0)
34332     {
34333         AssignHeap (acontext);
34334         assert (acontext->get_alloc_heap());
34335     }
34336 #endif //MULTIPLE_HEAPS
34337
34338 #ifdef MULTIPLE_HEAPS
34339     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34340 #else
34341     gc_heap* hp = pGenGCHeap;
34342 #ifdef _PREFAST_
34343     // prefix complains about us dereferencing hp in wks build even though we only access static members
34344     // this way. not sure how to shut it up except for this ugly workaround:
34345     PREFIX_ASSUME(hp != NULL);
34346 #endif //_PREFAST_
34347 #endif //MULTIPLE_HEAPS
34348
34349     if (size < LARGE_OBJECT_SIZE)
34350     {
34351
34352 #ifdef TRACE_GC
34353         AllocSmallCount++;
34354 #endif //TRACE_GC
34355         newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
34356 #ifdef FEATURE_STRUCTALIGN
34357         newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
34358 #endif // FEATURE_STRUCTALIGN
34359 //        ASSERT (newAlloc);
34360     }
34361     else 
34362     {
34363         newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34364 #ifdef FEATURE_STRUCTALIGN
34365         newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34366 #endif // FEATURE_STRUCTALIGN
34367     }
34368
34369     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34370
34371 #ifdef TRACE_GC
34372 #ifdef COUNT_CYCLES
34373     finish = GetCycleCount32();
34374 #elif defined(ENABLE_INSTRUMENTATION)
34375     finish = GetInstLogTime();
34376 #endif //COUNT_CYCLES
34377     AllocDuration += finish - AllocStart;
34378     AllocCount++;
34379 #endif //TRACE_GC
34380     return newAlloc;
34381 }
34382
34383 void
34384 GCHeap::FixAllocContext (gc_alloc_context* context, bool lockp, void* arg, void *heap)
34385 {
34386     alloc_context* acontext = static_cast<alloc_context*>(context);
34387 #ifdef MULTIPLE_HEAPS
34388
34389     if (arg != 0)
34390         acontext->alloc_count = 0;
34391
34392     uint8_t * alloc_ptr = acontext->alloc_ptr;
34393
34394     if (!alloc_ptr)
34395         return;
34396
34397     // The acontext->alloc_heap can be out of sync with the ptrs because
34398     // of heap re-assignment in allocate
34399     gc_heap* hp = gc_heap::heap_of (alloc_ptr);
34400 #else
34401     gc_heap* hp = pGenGCHeap;
34402 #endif //MULTIPLE_HEAPS
34403
34404     if (heap == NULL || heap == hp)
34405     {
34406         if (lockp)
34407         {
34408             enter_spin_lock (&hp->more_space_lock);
34409         }
34410         hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
34411                                 get_alignment_constant(TRUE));
34412         if (lockp)
34413         {
34414             leave_spin_lock (&hp->more_space_lock);
34415         }
34416     }
34417 }
34418
34419 Object*
34420 GCHeap::GetContainingObject (void *pInteriorPtr, bool fCollectedGenOnly)
34421 {
34422     uint8_t *o = (uint8_t*)pInteriorPtr;
34423
34424     gc_heap* hp = gc_heap::heap_of (o);
34425
34426     uint8_t* lowest = (fCollectedGenOnly ? hp->gc_low : hp->lowest_address);
34427     uint8_t* highest = (fCollectedGenOnly ? hp->gc_high : hp->highest_address);
34428
34429     if (o >= lowest && o < highest)
34430     {
34431         o = hp->find_object (o, lowest);
34432     }
34433     else
34434     {
34435         o = NULL;
34436     }
34437     
34438     return (Object *)o;
34439 }
34440
34441 BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
34442 {
34443     if (dd_new_allocation (dd) < 0)
34444     {
34445         return TRUE;
34446     }
34447
34448     if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
34449     {
34450         return TRUE;
34451     }
34452
34453     return FALSE;
34454 }
34455
34456 //----------------------------------------------------------------------------
34457 // #GarbageCollector
34458 //
34459 //  API to ensure that a complete new garbage collection takes place
34460 //
34461 HRESULT
34462 GCHeap::GarbageCollect (int generation, bool low_memory_p, int mode)
34463 {
34464 #if defined(BIT64) 
34465     if (low_memory_p)
34466     {
34467         size_t total_allocated = 0;
34468         size_t total_desired = 0;
34469 #ifdef MULTIPLE_HEAPS
34470         int hn = 0;
34471         for (hn = 0; hn < gc_heap::n_heaps; hn++)
34472         {
34473             gc_heap* hp = gc_heap::g_heaps [hn];
34474             total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
34475             total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
34476                 dd_new_allocation (hp->dynamic_data_of (0));
34477         }
34478 #else
34479         gc_heap* hp = pGenGCHeap;
34480         total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
34481         total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
34482             dd_new_allocation (hp->dynamic_data_of (0));
34483 #endif //MULTIPLE_HEAPS
34484
34485         if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
34486         {
34487             dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
34488                          total_allocated, total_desired));
34489
34490             return S_OK;
34491         }
34492     }
34493 #endif // BIT64 
34494
34495 #ifdef MULTIPLE_HEAPS
34496     gc_heap* hpt = gc_heap::g_heaps[0];
34497 #else
34498     gc_heap* hpt = 0;
34499 #endif //MULTIPLE_HEAPS
34500
34501     generation = (generation < 0) ? max_generation : min (generation, max_generation);
34502     dynamic_data* dd = hpt->dynamic_data_of (generation);
34503
34504 #ifdef BACKGROUND_GC
34505     if (recursive_gc_sync::background_running_p())
34506     {
34507         if ((mode == collection_optimized) || (mode & collection_non_blocking))
34508         {
34509             return S_OK;
34510         }
34511         if (mode & collection_blocking)
34512         {
34513             pGenGCHeap->background_gc_wait();
34514             if (mode & collection_optimized)
34515             {
34516                 return S_OK;
34517             }
34518         }
34519     }
34520 #endif //BACKGROUND_GC
34521
34522     if (mode & collection_optimized)
34523     {
34524         if (pGenGCHeap->gc_started)
34525         {
34526             return S_OK;
34527         }
34528         else 
34529         {
34530             BOOL should_collect = FALSE;
34531             BOOL should_check_loh = (generation == max_generation);
34532 #ifdef MULTIPLE_HEAPS
34533             for (int i = 0; i < gc_heap::n_heaps; i++)
34534             {
34535                 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
34536                 dynamic_data* dd2 = (should_check_loh ? 
34537                                      (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
34538                                      0);
34539
34540                 if (should_collect_optimized (dd1, low_memory_p))
34541                 {
34542                     should_collect = TRUE;
34543                     break;
34544                 }
34545                 if (dd2 && should_collect_optimized (dd2, low_memory_p))
34546                 {
34547                     should_collect = TRUE;
34548                     break;
34549                 }
34550             }
34551 #else
34552             should_collect = should_collect_optimized (dd, low_memory_p);
34553             if (!should_collect && should_check_loh)
34554             {
34555                 should_collect = 
34556                     should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
34557             }
34558 #endif //MULTIPLE_HEAPS
34559             if (!should_collect)
34560             {
34561                 return S_OK;
34562             }
34563         }
34564     }
34565
34566     size_t CollectionCountAtEntry = dd_collection_count (dd);
34567     size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
34568     size_t CurrentCollectionCount = 0;
34569
34570 retry:
34571
34572     CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
34573     
34574     if ((mode & collection_blocking) && 
34575         (generation == max_generation) && 
34576         (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
34577     {
34578 #ifdef BACKGROUND_GC
34579         if (recursive_gc_sync::background_running_p())
34580         {
34581             pGenGCHeap->background_gc_wait();
34582         }
34583 #endif //BACKGROUND_GC
34584
34585         goto retry;
34586     }
34587
34588     if (CollectionCountAtEntry == CurrentCollectionCount)
34589     {
34590         goto retry;
34591     }
34592
34593     return S_OK;
34594 }
34595
34596 size_t
34597 GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
34598 {
34599     int gen = (generation < 0) ? 
34600                max_generation : min (generation, max_generation);
34601
34602     gc_reason reason = reason_empty;
34603     
34604     if (low_memory_p) 
34605     {
34606         if (mode & collection_blocking)
34607             reason = reason_lowmemory_blocking;
34608         else
34609             reason = reason_lowmemory;
34610     }
34611     else
34612         reason = reason_induced;
34613
34614     if (reason == reason_induced)
34615     {
34616         if (mode & collection_compacting)
34617         {
34618             reason = reason_induced_compacting;
34619         }
34620         else if (mode & collection_non_blocking)
34621         {
34622             reason = reason_induced_noforce;
34623         }
34624 #ifdef STRESS_HEAP
34625         else if (mode & collection_gcstress)
34626         {
34627             reason = reason_gcstress;
34628         }
34629 #endif
34630     }
34631
34632     return GarbageCollectGeneration (gen, reason);
34633 }
34634
34635 void gc_heap::do_pre_gc()
34636 {
34637     STRESS_LOG_GC_STACK;
34638
34639 #ifdef STRESS_LOG
34640     STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
34641                         (uint32_t)settings.condemned_generation,
34642                         (uint32_t)settings.reason);
34643 #endif // STRESS_LOG
34644
34645 #ifdef MULTIPLE_HEAPS
34646     gc_heap* hp = g_heaps[0];
34647 #else
34648     gc_heap* hp = 0;
34649 #endif //MULTIPLE_HEAPS
34650
34651 #ifdef BACKGROUND_GC
34652     settings.b_state = hp->current_bgc_state;
34653 #endif //BACKGROUND_GC
34654
34655 #ifdef BACKGROUND_GC
34656     dprintf (1, ("*GC* %d(gen0:%d)(%d)(%s)(%d)", 
34657         VolatileLoad(&settings.gc_index), 
34658         dd_collection_count (hp->dynamic_data_of (0)),
34659         settings.condemned_generation,
34660         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
34661         settings.b_state));
34662 #else
34663     dprintf (1, ("*GC* %d(gen0:%d)(%d)", 
34664         VolatileLoad(&settings.gc_index), 
34665         dd_collection_count(hp->dynamic_data_of(0)),
34666         settings.condemned_generation));
34667 #endif //BACKGROUND_GC
34668
34669     // TODO: this can happen...it's because of the way we are calling
34670     // do_pre_gc, will fix later.
34671     //if (last_gc_index > VolatileLoad(&settings.gc_index))
34672     //{
34673     //    FATAL_GC_ERROR();
34674     //}
34675
34676     last_gc_index = VolatileLoad(&settings.gc_index);
34677     GCHeap::UpdatePreGCCounters();
34678
34679     if (settings.concurrent)
34680     {
34681 #ifdef BACKGROUND_GC
34682         full_gc_counts[gc_type_background]++;
34683 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34684         GCHeap::gc_stress_fgcs_in_bgc = 0;
34685 #endif // STRESS_HEAP && !FEATURE_REDHAWK
34686 #endif // BACKGROUND_GC
34687     }
34688     else
34689     {
34690         if (settings.condemned_generation == max_generation)
34691         {
34692             full_gc_counts[gc_type_blocking]++;
34693         }
34694         else
34695         {
34696 #ifdef BACKGROUND_GC
34697             if (settings.background_p)
34698             {
34699                 ephemeral_fgc_counts[settings.condemned_generation]++;
34700             }
34701 #endif //BACKGROUND_GC
34702         }
34703     }
34704
34705 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34706     if (g_fEnableARM)
34707     {
34708         SystemDomain::ResetADSurvivedBytes();
34709     }
34710 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34711 }
34712
34713 #ifdef GC_CONFIG_DRIVEN
34714 void gc_heap::record_interesting_info_per_heap()
34715 {
34716     // datapoints are always from the last blocking GC so don't record again
34717     // for BGCs.
34718     if (!(settings.concurrent))
34719     {
34720         for (int i = 0; i < max_idp_count; i++)
34721         {
34722             interesting_data_per_heap[i] += interesting_data_per_gc[i];
34723         }
34724     }
34725
34726     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
34727     if (compact_reason >= 0)
34728         (compact_reasons_per_heap[compact_reason])++;
34729     int expand_mechanism = get_gc_data_per_heap()->get_mechanism (gc_heap_expand);
34730     if (expand_mechanism >= 0)
34731         (expand_mechanisms_per_heap[expand_mechanism])++;
34732
34733     for (int i = 0; i < max_gc_mechanism_bits_count; i++)
34734     {
34735         if (get_gc_data_per_heap()->is_mechanism_bit_set ((gc_mechanism_bit_per_heap)i))
34736             (interesting_mechanism_bits_per_heap[i])++;
34737     }
34738
34739     //         h#  | GC  | gen | C   | EX  | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
34740     cprintf (("%2d | %6d | %1d | %1s | %2s | %2s | %2s | %2s | %2s || %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id |",
34741             heap_number,
34742             (size_t)settings.gc_index,
34743             settings.condemned_generation,
34744             // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs.
34745             (settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W") : ""), // compaction
34746             ((expand_mechanism >= 0)? "X" : ""), // EX
34747             ((expand_mechanism == expand_reuse_normal) ? "X" : ""), // NF
34748             ((expand_mechanism == expand_reuse_bestfit) ? "X" : ""), // BF
34749             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_mark_list_bit) ? "X" : ""), // ML
34750             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_demotion_bit) ? "X" : ""), // DM
34751             interesting_data_per_gc[idp_pre_short],
34752             interesting_data_per_gc[idp_post_short],
34753             interesting_data_per_gc[idp_merged_pin],
34754             interesting_data_per_gc[idp_converted_pin],
34755             interesting_data_per_gc[idp_pre_pin],
34756             interesting_data_per_gc[idp_post_pin],
34757             interesting_data_per_gc[idp_pre_and_post_pin],
34758             interesting_data_per_gc[idp_pre_short_padded],
34759             interesting_data_per_gc[idp_post_short_padded]));
34760 }
34761
34762 void gc_heap::record_global_mechanisms()
34763 {
34764     for (int i = 0; i < max_global_mechanisms_count; i++)
34765     {
34766         if (gc_data_global.get_mechanism_p ((gc_global_mechanism_p)i))
34767         {
34768             ::record_global_mechanism (i);
34769         }
34770     }
34771 }
34772
34773 BOOL gc_heap::should_do_sweeping_gc (BOOL compact_p)
34774 {
34775     if (!compact_ratio)
34776         return (!compact_p);
34777
34778     size_t compact_count = compact_or_sweep_gcs[0];
34779     size_t sweep_count = compact_or_sweep_gcs[1];
34780
34781     size_t total_count = compact_count + sweep_count;
34782     BOOL should_compact = compact_p;
34783     if (total_count > 3)
34784     {
34785         if (compact_p)
34786         {
34787             int temp_ratio = (int)((compact_count + 1) * 100 / (total_count + 1));
34788             if (temp_ratio > compact_ratio)
34789             {
34790                 // cprintf (("compact would be: %d, total_count: %d, ratio would be %d%% > target\n",
34791                 //     (compact_count + 1), (total_count + 1), temp_ratio));
34792                 should_compact = FALSE;
34793             }
34794         }
34795         else
34796         {
34797             int temp_ratio = (int)((sweep_count + 1) * 100 / (total_count + 1));
34798             if (temp_ratio > (100 - compact_ratio))
34799             {
34800                 // cprintf (("sweep would be: %d, total_count: %d, ratio would be %d%% > target\n",
34801                 //     (sweep_count + 1), (total_count + 1), temp_ratio));
34802                 should_compact = TRUE;
34803             }
34804         }
34805     }
34806
34807     return !should_compact;
34808 }
34809 #endif //GC_CONFIG_DRIVEN
34810
34811 void gc_heap::do_post_gc()
34812 {
34813     if (!settings.concurrent)
34814     {
34815         initGCShadow();
34816     }
34817
34818 #ifdef TRACE_GC
34819 #ifdef COUNT_CYCLES
34820     AllocStart = GetCycleCount32();
34821 #else
34822     AllocStart = clock();
34823 #endif //COUNT_CYCLES
34824 #endif //TRACE_GC
34825
34826 #ifdef MULTIPLE_HEAPS
34827     gc_heap* hp = g_heaps[0];
34828 #else
34829     gc_heap* hp = 0;
34830 #endif //MULTIPLE_HEAPS
34831     
34832     GCToEEInterface::GcDone(settings.condemned_generation);
34833
34834     GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index),
34835                          (uint32_t)settings.condemned_generation,
34836                          (uint32_t)settings.reason,
34837                          !!settings.concurrent);
34838
34839     //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)", 
34840     dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)", 
34841         VolatileLoad(&settings.gc_index), 
34842         dd_collection_count(hp->dynamic_data_of(0)),
34843         settings.condemned_generation,
34844         (settings.concurrent ? "BGC" : "GC")));
34845
34846     if (settings.exit_memory_load != 0)
34847         last_gc_memory_load = settings.exit_memory_load;
34848     else if (settings.entry_memory_load != 0)
34849         last_gc_memory_load = settings.entry_memory_load;
34850
34851     last_gc_heap_size = get_total_heap_size();
34852     last_gc_fragmentation = get_total_fragmentation();
34853
34854     GCHeap::UpdatePostGCCounters();
34855 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34856     //if (g_fEnableARM)
34857     //{
34858     //    SystemDomain::GetADSurvivedBytes();
34859     //}
34860 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34861
34862 #ifdef STRESS_LOG
34863     STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
34864                       (uint32_t)settings.condemned_generation,
34865                       (uint32_t)settings.reason);
34866 #endif // STRESS_LOG
34867
34868 #ifdef GC_CONFIG_DRIVEN
34869     if (!settings.concurrent)
34870     {
34871         if (settings.compaction)
34872             (compact_or_sweep_gcs[0])++;
34873         else
34874             (compact_or_sweep_gcs[1])++;
34875     }
34876
34877 #ifdef MULTIPLE_HEAPS
34878     for (int i = 0; i < n_heaps; i++)
34879         g_heaps[i]->record_interesting_info_per_heap();
34880 #else
34881     record_interesting_info_per_heap();
34882 #endif //MULTIPLE_HEAPS
34883     record_global_mechanisms();
34884 #endif //GC_CONFIG_DRIVEN
34885 }
34886
34887 unsigned GCHeap::GetGcCount()
34888 {
34889     return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
34890 }
34891
34892 size_t
34893 GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
34894 {
34895     dprintf (2, ("triggered a GC!"));
34896
34897 #ifdef MULTIPLE_HEAPS
34898     gc_heap* hpt = gc_heap::g_heaps[0];
34899 #else
34900     gc_heap* hpt = 0;
34901 #endif //MULTIPLE_HEAPS
34902     bool cooperative_mode = true;
34903     dynamic_data* dd = hpt->dynamic_data_of (gen);
34904     size_t localCount = dd_collection_count (dd);
34905
34906     enter_spin_lock (&gc_heap::gc_lock);
34907     dprintf (SPINLOCK_LOG, ("GC Egc"));
34908     ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
34909
34910     //don't trigger another GC if one was already in progress
34911     //while waiting for the lock
34912     {
34913         size_t col_count = dd_collection_count (dd);
34914
34915         if (localCount != col_count)
34916         {
34917 #ifdef SYNCHRONIZATION_STATS
34918             gc_lock_contended++;
34919 #endif //SYNCHRONIZATION_STATS
34920             dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
34921             leave_spin_lock (&gc_heap::gc_lock);
34922
34923             // We don't need to release msl here 'cause this means a GC
34924             // has happened and would have release all msl's.
34925             return col_count;
34926          }
34927     }
34928
34929 #ifdef COUNT_CYCLES
34930     int gc_start = GetCycleCount32();
34931 #endif //COUNT_CYCLES
34932
34933 #ifdef TRACE_GC
34934 #ifdef COUNT_CYCLES
34935     AllocDuration += GetCycleCount32() - AllocStart;
34936 #else
34937     AllocDuration += clock() - AllocStart;
34938 #endif //COUNT_CYCLES
34939 #endif //TRACE_GC
34940
34941     gc_heap::g_low_memory_status = (reason == reason_lowmemory) || 
34942                                    (reason == reason_lowmemory_blocking) ||
34943                                    (gc_heap::latency_level == latency_level_memory_footprint);
34944
34945     gc_trigger_reason = reason;
34946
34947 #ifdef MULTIPLE_HEAPS
34948     for (int i = 0; i < gc_heap::n_heaps; i++)
34949     {
34950         gc_heap::g_heaps[i]->reset_gc_done();
34951     }
34952 #else
34953     gc_heap::reset_gc_done();
34954 #endif //MULTIPLE_HEAPS
34955
34956     gc_heap::gc_started = TRUE;
34957
34958     {
34959         init_sync_log_stats();
34960
34961 #ifndef MULTIPLE_HEAPS
34962         cooperative_mode = gc_heap::enable_preemptive ();
34963
34964         dprintf (2, ("Suspending EE"));
34965         BEGIN_TIMING(suspend_ee_during_log);
34966         GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
34967         END_TIMING(suspend_ee_during_log);
34968         gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();
34969         gc_heap::disable_preemptive (cooperative_mode);
34970         if (gc_heap::proceed_with_gc_p)
34971             pGenGCHeap->settings.init_mechanisms();
34972         else
34973             gc_heap::update_collection_counts_for_no_gc();
34974
34975 #endif //!MULTIPLE_HEAPS
34976     }
34977
34978 // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
34979
34980 #ifdef TRACE_GC
34981 #ifdef COUNT_CYCLES
34982     unsigned start;
34983     unsigned finish;
34984     start = GetCycleCount32();
34985 #else
34986     clock_t start;
34987     clock_t finish;
34988     start = clock();
34989 #endif //COUNT_CYCLES
34990     PromotedObjectCount = 0;
34991 #endif //TRACE_GC
34992
34993     unsigned int condemned_generation_number = gen;
34994
34995     // We want to get a stack from the user thread that triggered the GC
34996     // instead of on the GC thread which is the case for Server GC.
34997     // But we are doing it for Workstation GC as well to be uniform.
34998     FIRE_EVENT(GCTriggered, static_cast<uint32_t>(reason));
34999
35000 #ifdef MULTIPLE_HEAPS
35001     GcCondemnedGeneration = condemned_generation_number;
35002
35003     cooperative_mode = gc_heap::enable_preemptive ();
35004
35005     BEGIN_TIMING(gc_during_log);
35006     gc_heap::ee_suspend_event.Set();
35007     gc_heap::wait_for_gc_done();
35008     END_TIMING(gc_during_log);
35009
35010     gc_heap::disable_preemptive (cooperative_mode);
35011
35012     condemned_generation_number = GcCondemnedGeneration;
35013 #else
35014     if (gc_heap::proceed_with_gc_p)
35015     {
35016         BEGIN_TIMING(gc_during_log);
35017         pGenGCHeap->garbage_collect (condemned_generation_number);
35018         END_TIMING(gc_during_log);
35019     }
35020 #endif //MULTIPLE_HEAPS
35021
35022 #ifdef TRACE_GC
35023 #ifdef COUNT_CYCLES
35024     finish = GetCycleCount32();
35025 #else
35026     finish = clock();
35027 #endif //COUNT_CYCLES
35028     GcDuration += finish - start;
35029     dprintf (3,
35030              ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
35031               VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
35032               finish - start, GcDuration,
35033               AllocCount ? (AllocDuration / AllocCount) : 0,
35034               AllocSmallCount, AllocBigCount));
35035     AllocCount = 0;
35036     AllocDuration = 0;
35037 #endif // TRACE_GC
35038
35039 #ifdef BACKGROUND_GC
35040     // We are deciding whether we should fire the alloc wait end event here
35041     // because in begin_foreground we could be calling end_foreground 
35042     // if we need to retry.
35043     if (gc_heap::alloc_wait_event_p)
35044     {
35045         hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
35046         gc_heap::alloc_wait_event_p = FALSE;
35047     }
35048 #endif //BACKGROUND_GC
35049
35050 #ifndef MULTIPLE_HEAPS
35051 #ifdef BACKGROUND_GC
35052     if (!gc_heap::dont_restart_ee_p)
35053     {
35054 #endif //BACKGROUND_GC
35055         BEGIN_TIMING(restart_ee_during_log);
35056         GCToEEInterface::RestartEE(TRUE);
35057         END_TIMING(restart_ee_during_log);
35058 #ifdef BACKGROUND_GC
35059     }
35060 #endif //BACKGROUND_GC
35061 #endif //!MULTIPLE_HEAPS
35062
35063 #ifdef COUNT_CYCLES
35064     printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
35065             GetCycleCount32() - gc_start);
35066 #endif //COUNT_CYCLES
35067
35068 #ifndef MULTIPLE_HEAPS
35069     process_sync_log_stats();
35070     gc_heap::gc_started = FALSE;
35071     gc_heap::set_gc_done();
35072     dprintf (SPINLOCK_LOG, ("GC Lgc"));
35073     leave_spin_lock (&gc_heap::gc_lock);    
35074 #endif //!MULTIPLE_HEAPS
35075
35076 #ifdef FEATURE_PREMORTEM_FINALIZATION
35077     GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers);
35078 #endif // FEATURE_PREMORTEM_FINALIZATION
35079
35080     return dd_collection_count (dd);
35081 }
35082
35083 size_t      GCHeap::GetTotalBytesInUse ()
35084 {
35085 #ifdef MULTIPLE_HEAPS
35086     //enumarate all the heaps and get their size.
35087     size_t tot_size = 0;
35088     for (int i = 0; i < gc_heap::n_heaps; i++)
35089     {
35090         GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
35091         tot_size += Hp->ApproxTotalBytesInUse (FALSE);
35092     }
35093     return tot_size;
35094 #else
35095     return ApproxTotalBytesInUse ();
35096 #endif //MULTIPLE_HEAPS
35097 }
35098
35099 int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
35100 {
35101     if (get_bgc_fgc_count != 0)
35102     {
35103 #ifdef BACKGROUND_GC
35104         if (generation == max_generation)
35105         {
35106             return (int)(gc_heap::full_gc_counts[gc_type_background]);
35107         }
35108         else
35109         {
35110             return (int)(gc_heap::ephemeral_fgc_counts[generation]);
35111         }
35112 #else
35113         return 0;
35114 #endif //BACKGROUND_GC
35115     }
35116
35117 #ifdef MULTIPLE_HEAPS
35118     gc_heap* hp = gc_heap::g_heaps [0];
35119 #else  //MULTIPLE_HEAPS
35120     gc_heap* hp = pGenGCHeap;
35121 #endif //MULTIPLE_HEAPS
35122     if (generation > max_generation)
35123         return 0;
35124     else
35125         return (int)dd_collection_count (hp->dynamic_data_of (generation));
35126 }
35127
35128 size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
35129 {
35130     size_t totsize = 0;
35131     //GCTODO
35132     //ASSERT(InMustComplete());
35133     enter_spin_lock (&pGenGCHeap->gc_lock);
35134
35135     heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
35136     // Get small block heap size info
35137     totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
35138     heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
35139     while (seg1 != eph_seg)
35140     {
35141         totsize += heap_segment_allocated (seg1) -
35142             heap_segment_mem (seg1);
35143         seg1 = heap_segment_next (seg1);
35144     }
35145
35146     //discount the fragmentation
35147     for (int i = 0; i <= max_generation; i++)
35148     {
35149         generation* gen = pGenGCHeap->generation_of (i);
35150         totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
35151     }
35152
35153     if (!small_heap_only)
35154     {
35155         heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
35156
35157         while (seg2 != 0)
35158         {
35159             totsize += heap_segment_allocated (seg2) -
35160                 heap_segment_mem (seg2);
35161             seg2 = heap_segment_next (seg2);
35162         }
35163
35164         //discount the fragmentation
35165         generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
35166         size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
35167         totsize -= frag;
35168     }
35169     leave_spin_lock (&pGenGCHeap->gc_lock);
35170     return totsize;
35171 }
35172
35173 #ifdef MULTIPLE_HEAPS
35174 void GCHeap::AssignHeap (alloc_context* acontext)
35175 {
35176     // Assign heap based on processor
35177     acontext->set_alloc_heap(GetHeap(heap_select::select_heap(acontext, 0)));
35178     acontext->set_home_heap(acontext->get_alloc_heap());
35179 }
35180 GCHeap* GCHeap::GetHeap (int n)
35181 {
35182     assert (n < gc_heap::n_heaps);
35183     return gc_heap::g_heaps [n]->vm_heap;
35184 }
35185 #endif //MULTIPLE_HEAPS
35186
35187 bool GCHeap::IsThreadUsingAllocationContextHeap(gc_alloc_context* context, int thread_number)
35188 {
35189     alloc_context* acontext = static_cast<alloc_context*>(context);
35190 #ifdef MULTIPLE_HEAPS
35191     return ((acontext->get_home_heap() == GetHeap(thread_number)) ||
35192             ((acontext->get_home_heap() == 0) && (thread_number == 0)));
35193 #else
35194     UNREFERENCED_PARAMETER(acontext);
35195     UNREFERENCED_PARAMETER(thread_number);
35196     return true;
35197 #endif //MULTIPLE_HEAPS
35198 }
35199
35200 // Returns the number of processors required to trigger the use of thread based allocation contexts
35201 int GCHeap::GetNumberOfHeaps ()
35202 {
35203 #ifdef MULTIPLE_HEAPS
35204     return gc_heap::n_heaps;
35205 #else
35206     return 1;
35207 #endif //MULTIPLE_HEAPS
35208 }
35209
35210 /*
35211   in this way we spend extra time cycling through all the heaps while create the handle
35212   it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
35213 */
35214 int GCHeap::GetHomeHeapNumber ()
35215 {
35216 #ifdef MULTIPLE_HEAPS
35217     gc_alloc_context* ctx = GCToEEInterface::GetAllocContext();
35218     if (!ctx)
35219     {
35220         return 0;
35221     }
35222
35223     GCHeap *hp = static_cast<alloc_context*>(ctx)->get_home_heap();
35224     return (hp ? hp->pGenGCHeap->heap_number : 0);
35225 #else
35226     return 0;
35227 #endif //MULTIPLE_HEAPS
35228 }
35229
35230 unsigned int GCHeap::GetCondemnedGeneration()
35231
35232     return gc_heap::settings.condemned_generation;
35233 }
35234
35235 void GCHeap::GetMemoryInfo(uint32_t* highMemLoadThreshold, 
35236                            uint64_t* totalPhysicalMem, 
35237                            uint32_t* lastRecordedMemLoad,
35238                            size_t* lastRecordedHeapSize,
35239                            size_t* lastRecordedFragmentation)
35240 {
35241     *highMemLoadThreshold = gc_heap::high_memory_load_th;
35242     *totalPhysicalMem = gc_heap::total_physical_mem;
35243     *lastRecordedMemLoad = gc_heap::last_gc_memory_load;
35244     *lastRecordedHeapSize = gc_heap::last_gc_heap_size;
35245     *lastRecordedFragmentation = gc_heap::last_gc_fragmentation;
35246 }
35247
35248 int GCHeap::GetGcLatencyMode()
35249 {
35250     return (int)(pGenGCHeap->settings.pause_mode);
35251 }
35252
35253 int GCHeap::SetGcLatencyMode (int newLatencyMode)
35254 {
35255     if (gc_heap::settings.pause_mode == pause_no_gc)
35256         return (int)set_pause_mode_no_gc;
35257
35258     gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
35259
35260     if (new_mode == pause_low_latency)
35261     {
35262 #ifndef MULTIPLE_HEAPS
35263         pGenGCHeap->settings.pause_mode = new_mode;
35264 #endif //!MULTIPLE_HEAPS
35265     }
35266     else if (new_mode == pause_sustained_low_latency)
35267     {
35268 #ifdef BACKGROUND_GC
35269         if (gc_heap::gc_can_use_concurrent)
35270         {
35271             pGenGCHeap->settings.pause_mode = new_mode;
35272         }
35273 #endif //BACKGROUND_GC
35274     }
35275     else
35276     {
35277         pGenGCHeap->settings.pause_mode = new_mode;
35278     }
35279
35280 #ifdef BACKGROUND_GC
35281     if (recursive_gc_sync::background_running_p())
35282     {
35283         // If we get here, it means we are doing an FGC. If the pause
35284         // mode was altered we will need to save it in the BGC settings.
35285         if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
35286         {
35287             gc_heap::saved_bgc_settings.pause_mode = new_mode;
35288         }
35289     }
35290 #endif //BACKGROUND_GC
35291
35292     return (int)set_pause_mode_success;
35293 }
35294
35295 int GCHeap::GetLOHCompactionMode()
35296 {
35297     return pGenGCHeap->loh_compaction_mode;
35298 }
35299
35300 void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
35301 {
35302 #ifdef FEATURE_LOH_COMPACTION
35303     pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
35304 #endif //FEATURE_LOH_COMPACTION
35305 }
35306
35307 bool GCHeap::RegisterForFullGCNotification(uint32_t gen2Percentage,
35308                                            uint32_t lohPercentage)
35309 {
35310 #ifdef MULTIPLE_HEAPS
35311     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35312     {
35313         gc_heap* hp = gc_heap::g_heaps [hn];
35314         hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
35315     }
35316 #else //MULTIPLE_HEAPS
35317     pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
35318 #endif //MULTIPLE_HEAPS
35319
35320     pGenGCHeap->full_gc_approach_event.Reset();
35321     pGenGCHeap->full_gc_end_event.Reset();
35322     pGenGCHeap->full_gc_approach_event_set = false;
35323
35324     pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
35325     pGenGCHeap->fgn_loh_percent = lohPercentage;
35326
35327     return TRUE;
35328 }
35329
35330 bool GCHeap::CancelFullGCNotification()
35331 {
35332     pGenGCHeap->fgn_maxgen_percent = 0;
35333     pGenGCHeap->fgn_loh_percent = 0;
35334
35335     pGenGCHeap->full_gc_approach_event.Set();
35336     pGenGCHeap->full_gc_end_event.Set();
35337     
35338     return TRUE;
35339 }
35340
35341 int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
35342 {
35343     dprintf (2, ("WFGA: Begin wait"));
35344     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
35345     dprintf (2, ("WFGA: End wait"));
35346     return result;
35347 }
35348
35349 int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
35350 {
35351     dprintf (2, ("WFGE: Begin wait"));
35352     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
35353     dprintf (2, ("WFGE: End wait"));
35354     return result;
35355 }
35356
35357 int GCHeap::StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC)
35358 {
35359     NoGCRegionLockHolder lh;
35360
35361     dprintf (1, ("begin no gc called"));
35362     start_no_gc_region_status status = gc_heap::prepare_for_no_gc_region (totalSize, lohSizeKnown, lohSize, disallowFullBlockingGC);
35363     if (status == start_no_gc_success)
35364     {
35365         GarbageCollect (max_generation);
35366         status = gc_heap::get_start_no_gc_region_status();
35367     }
35368
35369     if (status != start_no_gc_success)
35370         gc_heap::handle_failure_for_no_gc();
35371
35372     return (int)status;
35373 }
35374
35375 int GCHeap::EndNoGCRegion()
35376 {
35377     NoGCRegionLockHolder lh;
35378     return (int)gc_heap::end_no_gc_region();
35379 }
35380
35381 void GCHeap::PublishObject (uint8_t* Obj)
35382 {
35383 #ifdef BACKGROUND_GC
35384     gc_heap* hp = gc_heap::heap_of (Obj);
35385     hp->bgc_alloc_lock->loh_alloc_done (Obj);
35386 #endif //BACKGROUND_GC
35387 }
35388
35389 // The spec for this one isn't clear. This function
35390 // returns the size that can be allocated without
35391 // triggering a GC of any kind.
35392 size_t GCHeap::ApproxFreeBytes()
35393 {
35394     //GCTODO
35395     //ASSERT(InMustComplete());
35396     enter_spin_lock (&pGenGCHeap->gc_lock);
35397
35398     generation* gen = pGenGCHeap->generation_of (0);
35399     size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
35400
35401     leave_spin_lock (&pGenGCHeap->gc_lock);
35402
35403     return res;
35404 }
35405
35406 HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
35407 {
35408     if ((gen < 0) || (gen > max_generation))
35409         return E_FAIL;
35410 #ifdef MULTIPLE_HEAPS
35411     counters->current_size = 0;
35412     counters->promoted_size = 0;
35413     counters->collection_count = 0;
35414
35415     //enumarate all the heaps and get their counters.
35416     for (int i = 0; i < gc_heap::n_heaps; i++)
35417     {
35418         dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
35419
35420         counters->current_size += dd_current_size (dd);
35421         counters->promoted_size += dd_promoted_size (dd);
35422         if (i == 0)
35423         counters->collection_count += dd_collection_count (dd);
35424     }
35425 #else
35426     dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
35427     counters->current_size = dd_current_size (dd);
35428     counters->promoted_size = dd_promoted_size (dd);
35429     counters->collection_count = dd_collection_count (dd);
35430 #endif //MULTIPLE_HEAPS
35431     return S_OK;
35432 }
35433
35434 // Get the segment size to use, making sure it conforms.
35435 size_t GCHeap::GetValidSegmentSize(bool large_seg)
35436 {
35437     return get_valid_segment_size (large_seg);
35438 }
35439
35440 // Get the max gen0 heap size, making sure it conforms.
35441 size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
35442 {
35443     size_t gen0size = static_cast<size_t>(GCConfig::GetGen0Size());
35444
35445     if ((gen0size == 0) || !g_theGCHeap->IsValidGen0MaxSize(gen0size))
35446     {
35447 #ifdef SERVER_GC
35448         // performance data seems to indicate halving the size results
35449         // in optimal perf.  Ask for adjusted gen0 size.
35450         gen0size = max(GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),(256*1024));
35451
35452         // if gen0 size is too large given the available memory, reduce it.
35453         // Get true cache size, as we don't want to reduce below this.
35454         size_t trueSize = max(GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE),(256*1024));
35455         dprintf (2, ("cache: %Id-%Id, cpu: %Id", 
35456             GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),
35457             GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)));
35458
35459         int n_heaps = gc_heap::n_heaps;
35460 #else //SERVER_GC
35461         size_t trueSize = GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE);
35462         gen0size = max((4*trueSize/5),(256*1024));
35463         trueSize = max(trueSize, (256*1024));
35464         int n_heaps = 1;
35465 #endif //SERVER_GC
35466
35467         // if the total min GC across heaps will exceed 1/6th of available memory,
35468         // then reduce the min GC size until it either fits or has been reduced to cache size.
35469         while ((gen0size * n_heaps) > GCToOSInterface::GetPhysicalMemoryLimit() / 6)
35470         {
35471             gen0size = gen0size / 2;
35472             if (gen0size <= trueSize)
35473             {
35474                 gen0size = trueSize;
35475                 break;
35476             }
35477         }
35478     }
35479
35480     // Generation 0 must never be more than 1/2 the segment size.
35481     if (gen0size >= (seg_size / 2))
35482         gen0size = seg_size / 2;
35483
35484     return (gen0size);
35485 }
35486
35487 void GCHeap::SetReservedVMLimit (size_t vmlimit)
35488 {
35489     gc_heap::reserved_memory_limit = vmlimit;
35490 }
35491
35492
35493 //versions of same method on each heap
35494
35495 #ifdef FEATURE_PREMORTEM_FINALIZATION
35496
35497 Object* GCHeap::GetNextFinalizableObject()
35498 {
35499
35500 #ifdef MULTIPLE_HEAPS
35501
35502     //return the first non critical one in the first queue.
35503     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35504     {
35505         gc_heap* hp = gc_heap::g_heaps [hn];
35506         Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
35507         if (O)
35508             return O;
35509     }
35510     //return the first non crtitical/critical one in the first queue.
35511     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35512     {
35513         gc_heap* hp = gc_heap::g_heaps [hn];
35514         Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
35515         if (O)
35516             return O;
35517     }
35518     return 0;
35519
35520
35521 #else //MULTIPLE_HEAPS
35522     return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
35523 #endif //MULTIPLE_HEAPS
35524
35525 }
35526
35527 size_t GCHeap::GetNumberFinalizableObjects()
35528 {
35529 #ifdef MULTIPLE_HEAPS
35530     size_t cnt = 0;
35531     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35532     {
35533         gc_heap* hp = gc_heap::g_heaps [hn];
35534         cnt += hp->finalize_queue->GetNumberFinalizableObjects();
35535     }
35536     return cnt;
35537
35538
35539 #else //MULTIPLE_HEAPS
35540     return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
35541 #endif //MULTIPLE_HEAPS
35542 }
35543
35544 size_t GCHeap::GetFinalizablePromotedCount()
35545 {
35546 #ifdef MULTIPLE_HEAPS
35547     size_t cnt = 0;
35548
35549     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35550     {
35551         gc_heap* hp = gc_heap::g_heaps [hn];
35552         cnt += hp->finalize_queue->GetPromotedCount();
35553     }
35554     return cnt;
35555
35556 #else //MULTIPLE_HEAPS
35557     return pGenGCHeap->finalize_queue->GetPromotedCount();
35558 #endif //MULTIPLE_HEAPS
35559 }
35560
35561 bool GCHeap::FinalizeAppDomain(void *pDomain, bool fRunFinalizers)
35562 {
35563 #ifdef MULTIPLE_HEAPS
35564     bool foundp = false;
35565     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35566     {
35567         gc_heap* hp = gc_heap::g_heaps [hn];
35568         if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
35569             foundp = true;
35570     }
35571     return foundp;
35572
35573 #else //MULTIPLE_HEAPS
35574     return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
35575 #endif //MULTIPLE_HEAPS
35576 }
35577
35578 bool GCHeap::ShouldRestartFinalizerWatchDog()
35579 {
35580     // This condition was historically used as part of the condition to detect finalizer thread timeouts
35581     return gc_heap::gc_lock.lock != -1;
35582 }
35583
35584 void GCHeap::SetFinalizeQueueForShutdown(bool fHasLock)
35585 {
35586 #ifdef MULTIPLE_HEAPS
35587     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35588     {
35589         gc_heap* hp = gc_heap::g_heaps [hn];
35590         hp->finalize_queue->SetSegForShutDown(fHasLock);
35591     }
35592
35593 #else //MULTIPLE_HEAPS
35594     pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
35595 #endif //MULTIPLE_HEAPS
35596 }
35597
35598 //---------------------------------------------------------------------------
35599 // Finalized class tracking
35600 //---------------------------------------------------------------------------
35601
35602 bool GCHeap::RegisterForFinalization (int gen, Object* obj)
35603 {
35604     if (gen == -1)
35605         gen = 0;
35606     if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
35607     {
35608         //just reset the bit
35609         ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
35610         return true;
35611     }
35612     else
35613     {
35614         gc_heap* hp = gc_heap::heap_of ((uint8_t*)obj);
35615         return hp->finalize_queue->RegisterForFinalization (gen, obj);
35616     }
35617 }
35618
35619 void GCHeap::SetFinalizationRun (Object* obj)
35620 {
35621     ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
35622 }
35623
35624
35625 //--------------------------------------------------------------------
35626 //
35627 //          Support for finalization
35628 //
35629 //--------------------------------------------------------------------
35630
35631 inline
35632 unsigned int gen_segment (int gen)
35633 {
35634     assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
35635     return (NUMBERGENERATIONS - gen - 1);
35636 }
35637
35638 bool CFinalize::Initialize()
35639 {
35640     CONTRACTL {
35641         NOTHROW;
35642         GC_NOTRIGGER;
35643     } CONTRACTL_END;
35644
35645     m_Array = new (nothrow)(Object*[100]);
35646
35647     if (!m_Array)
35648     {
35649         ASSERT (m_Array);
35650         STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
35651         if (GCConfig::GetBreakOnOOM())
35652         {
35653             GCToOSInterface::DebugBreak();
35654         }
35655         return false;
35656     }
35657     m_EndArray = &m_Array[100];
35658
35659     for (int i =0; i < FreeList; i++)
35660     {
35661         SegQueueLimit (i) = m_Array;
35662     }
35663     m_PromotedCount = 0;
35664     lock = -1;
35665 #ifdef _DEBUG
35666     lockowner_threadid.Clear();
35667 #endif // _DEBUG
35668
35669     return true;
35670 }
35671
35672 CFinalize::~CFinalize()
35673 {
35674     delete m_Array;
35675 }
35676
35677 size_t CFinalize::GetPromotedCount ()
35678 {
35679     return m_PromotedCount;
35680 }
35681
35682 inline
35683 void CFinalize::EnterFinalizeLock()
35684 {
35685     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
35686              GCToEEInterface::GetThread() == 0 ||
35687              GCToEEInterface::IsPreemptiveGCDisabled());
35688
35689 retry:
35690     if (Interlocked::CompareExchange(&lock, 0, -1) >= 0)
35691     {
35692         unsigned int i = 0;
35693         while (lock >= 0)
35694         {
35695             YieldProcessor();           // indicate to the processor that we are spining
35696             if (++i & 7)
35697                 GCToOSInterface::YieldThread (0);
35698             else
35699                 GCToOSInterface::Sleep (5);
35700         }
35701         goto retry;
35702     }
35703
35704 #ifdef _DEBUG
35705     lockowner_threadid.SetToCurrentThread();
35706 #endif // _DEBUG
35707 }
35708
35709 inline
35710 void CFinalize::LeaveFinalizeLock()
35711 {
35712     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
35713              GCToEEInterface::GetThread() == 0 ||
35714              GCToEEInterface::IsPreemptiveGCDisabled());
35715
35716 #ifdef _DEBUG
35717     lockowner_threadid.Clear();
35718 #endif // _DEBUG
35719     lock = -1;
35720 }
35721
35722 bool
35723 CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
35724 {
35725     CONTRACTL {
35726         NOTHROW;
35727         GC_NOTRIGGER;
35728     } CONTRACTL_END;
35729
35730     EnterFinalizeLock();
35731     // Adjust gen
35732     unsigned int dest = 0;
35733
35734     if (g_fFinalizerRunOnShutDown)
35735     {
35736         //no method table available yet,
35737         //put it in the finalizer queue and sort out when
35738         //dequeueing
35739         dest = FinalizerListSeg;
35740     }
35741
35742     else
35743         dest = gen_segment (gen);
35744
35745     // Adjust boundary for segments so that GC will keep objects alive.
35746     Object*** s_i = &SegQueue (FreeList);
35747     if ((*s_i) == m_EndArray)
35748     {
35749         if (!GrowArray())
35750         {
35751             LeaveFinalizeLock();
35752             if (method_table(obj) == NULL)
35753             {
35754                 // If the object is uninitialized, a valid size should have been passed.
35755                 assert (size >= Align (min_obj_size));
35756                 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
35757                 ((CObjectHeader*)obj)->SetFree(size);
35758             }
35759             STRESS_LOG_OOM_STACK(0);
35760             if (GCConfig::GetBreakOnOOM())
35761             {
35762                 GCToOSInterface::DebugBreak();
35763             }
35764             return false;
35765         }
35766     }
35767     Object*** end_si = &SegQueueLimit (dest);
35768     do
35769     {
35770         //is the segment empty?
35771         if (!(*s_i == *(s_i-1)))
35772         {
35773             //no, swap the end elements.
35774             *(*s_i) = *(*(s_i-1));
35775         }
35776         //increment the fill pointer
35777         (*s_i)++;
35778         //go to the next segment.
35779         s_i--;
35780     } while (s_i > end_si);
35781
35782     // We have reached the destination segment
35783     // store the object
35784     **s_i = obj;
35785     // increment the fill pointer
35786     (*s_i)++;
35787
35788     LeaveFinalizeLock();
35789
35790     return true;
35791 }
35792
35793 Object*
35794 CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
35795 {
35796     Object* obj = 0;
35797     //serialize
35798     EnterFinalizeLock();
35799
35800 retry:
35801     if (!IsSegEmpty(FinalizerListSeg))
35802     {
35803         if (g_fFinalizerRunOnShutDown)
35804         {
35805             obj = *(SegQueueLimit (FinalizerListSeg)-1);
35806             if (method_table(obj)->HasCriticalFinalizer())
35807             {
35808                 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
35809                           FinalizerListSeg, CriticalFinalizerListSeg);
35810                 goto retry;
35811             }
35812             else
35813                 --SegQueueLimit (FinalizerListSeg);
35814         }
35815         else
35816             obj =  *(--SegQueueLimit (FinalizerListSeg));
35817
35818     }
35819     else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
35820     {
35821         //the FinalizerList is empty, we can adjust both
35822         // limit instead of moving the object to the free list
35823         obj =  *(--SegQueueLimit (CriticalFinalizerListSeg));
35824         --SegQueueLimit (FinalizerListSeg);
35825     }
35826     if (obj)
35827     {
35828         dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
35829     }
35830     LeaveFinalizeLock();
35831     return obj;
35832 }
35833
35834 void
35835 CFinalize::SetSegForShutDown(BOOL fHasLock)
35836 {
35837     int i;
35838
35839     if (!fHasLock)
35840         EnterFinalizeLock();
35841     for (i = 0; i <= max_generation; i++)
35842     {
35843         unsigned int seg = gen_segment (i);
35844         Object** startIndex = SegQueueLimit (seg)-1;
35845         Object** stopIndex  = SegQueue (seg);
35846         for (Object** po = startIndex; po >= stopIndex; po--)
35847         {
35848             Object* obj = *po;
35849             if (method_table(obj)->HasCriticalFinalizer())
35850             {
35851                 MoveItem (po, seg, CriticalFinalizerListSeg);
35852             }
35853             else
35854             {
35855                 MoveItem (po, seg, FinalizerListSeg);
35856             }
35857         }
35858     }
35859     if (!fHasLock)
35860         LeaveFinalizeLock();
35861 }
35862
35863 void
35864 CFinalize::DiscardNonCriticalObjects()
35865 {
35866     //empty the finalization queue
35867     Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
35868     Object** stopIndex  = SegQueue (FinalizerListSeg);
35869     for (Object** po = startIndex; po >= stopIndex; po--)
35870     {
35871         MoveItem (po, FinalizerListSeg, FreeList);
35872     }
35873 }
35874
35875 size_t
35876 CFinalize::GetNumberFinalizableObjects()
35877 {
35878     return SegQueueLimit (FinalizerListSeg) -
35879         (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
35880 }
35881
35882 BOOL
35883 CFinalize::FinalizeSegForAppDomain (void *pDomain, 
35884                                     BOOL fRunFinalizers, 
35885                                     unsigned int Seg)
35886 {
35887     BOOL finalizedFound = FALSE;
35888     Object** endIndex = SegQueue (Seg);
35889     for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
35890     {
35891         CObjectHeader* obj = (CObjectHeader*)*i;
35892
35893         // Objects are put into the finalization queue before they are complete (ie their methodtable
35894         // may be null) so we must check that the object we found has a method table before checking
35895         // if it has the index we are looking for. If the methodtable is null, it can't be from the
35896         // unloading domain, so skip it.
35897         if (method_table(obj) == NULL)
35898         {
35899             continue;
35900         }
35901
35902         // does the EE actually want us to finalize this object?
35903         if (!GCToEEInterface::ShouldFinalizeObjectForUnload(pDomain, obj))
35904         {
35905             continue;
35906         }
35907
35908         if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
35909         {
35910             //remove the object because we don't want to
35911             //run the finalizer
35912             MoveItem (i, Seg, FreeList);
35913             //Reset the bit so it will be put back on the queue
35914             //if resurrected and re-registered.
35915             obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
35916         }
35917         else
35918         {
35919             if (method_table(obj)->HasCriticalFinalizer())
35920             {
35921                 finalizedFound = TRUE;
35922                 MoveItem (i, Seg, CriticalFinalizerListSeg);
35923             }
35924             else
35925             {
35926                 if (GCToEEInterface::AppDomainIsRudeUnload(pDomain))
35927                 {
35928                     MoveItem (i, Seg, FreeList);
35929                 }
35930                 else
35931                 {
35932                     finalizedFound = TRUE;
35933                     MoveItem (i, Seg, FinalizerListSeg);
35934                 }
35935             }
35936         }
35937     }
35938
35939     return finalizedFound;
35940 }
35941
35942 bool
35943 CFinalize::FinalizeAppDomain (void *pDomain, bool fRunFinalizers)
35944 {
35945     bool finalizedFound = false;
35946
35947     unsigned int startSeg = gen_segment (max_generation);
35948
35949     EnterFinalizeLock();
35950
35951     for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
35952     {
35953         if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
35954         {
35955             finalizedFound = true;
35956         }
35957     }
35958
35959     LeaveFinalizeLock();
35960
35961     return finalizedFound;
35962 }
35963
35964 void
35965 CFinalize::MoveItem (Object** fromIndex,
35966                      unsigned int fromSeg,
35967                      unsigned int toSeg)
35968 {
35969
35970     int step;
35971     ASSERT (fromSeg != toSeg);
35972     if (fromSeg > toSeg)
35973         step = -1;
35974     else
35975         step = +1;
35976     // Place the element at the boundary closest to dest
35977     Object** srcIndex = fromIndex;
35978     for (unsigned int i = fromSeg; i != toSeg; i+= step)
35979     {
35980         Object**& destFill = m_FillPointers[i+(step - 1 )/2];
35981         Object** destIndex = destFill - (step + 1)/2;
35982         if (srcIndex != destIndex)
35983         {
35984             Object* tmp = *srcIndex;
35985             *srcIndex = *destIndex;
35986             *destIndex = tmp;
35987         }
35988         destFill -= step;
35989         srcIndex = destIndex;
35990     }
35991 }
35992
35993 void
35994 CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
35995 {
35996     ScanContext sc;
35997     if (pSC == 0)
35998         pSC = &sc;
35999
36000     pSC->thread_number = hn;
36001
36002     //scan the finalization queue
36003     Object** startIndex  = SegQueue (CriticalFinalizerListSeg);
36004     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
36005
36006     for (Object** po = startIndex; po < stopIndex; po++)
36007     {
36008         Object* o = *po;
36009         //dprintf (3, ("scan freacheable %Ix", (size_t)o));
36010         dprintf (3, ("scan f %Ix", (size_t)o));
36011 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
36012         if (g_fEnableARM)
36013         {
36014             pSC->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(o->GetAppDomainIndex());
36015         }
36016 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
36017
36018         (*fn)(po, pSC, 0);
36019     }
36020 }
36021
36022 void CFinalize::WalkFReachableObjects (fq_walk_fn fn)
36023 {
36024     Object** startIndex = SegQueue (CriticalFinalizerListSeg);
36025     Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
36026     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
36027     for (Object** po = startIndex; po < stopIndex; po++)
36028     {
36029         //report *po
36030         fn(po < stopCriticalIndex, *po);
36031     }
36032 }
36033
36034 BOOL
36035 CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
36036                                 gc_heap* hp)
36037 {
36038     ScanContext sc;
36039     sc.promotion = TRUE;
36040 #ifdef MULTIPLE_HEAPS
36041     sc.thread_number = hp->heap_number;
36042 #else
36043     UNREFERENCED_PARAMETER(hp);
36044 #endif //MULTIPLE_HEAPS
36045
36046     BOOL finalizedFound = FALSE;
36047
36048     //start with gen and explore all the younger generations.
36049     unsigned int startSeg = gen_segment (gen);
36050     {
36051         m_PromotedCount = 0;
36052         for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
36053         {
36054             Object** endIndex = SegQueue (Seg);
36055             for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36056             {
36057                 CObjectHeader* obj = (CObjectHeader*)*i;
36058                 dprintf (3, ("scanning: %Ix", (size_t)obj));
36059                 if (!g_theGCHeap->IsPromoted (obj))
36060                 {
36061                     dprintf (3, ("freacheable: %Ix", (size_t)obj));
36062
36063                     assert (method_table(obj)->HasFinalizer());
36064
36065                     if (GCToEEInterface::EagerFinalized(obj))
36066                     {
36067                         MoveItem (i, Seg, FreeList);
36068                     }
36069                     else if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36070                     {
36071                         //remove the object because we don't want to
36072                         //run the finalizer
36073
36074                         MoveItem (i, Seg, FreeList);
36075
36076                         //Reset the bit so it will be put back on the queue
36077                         //if resurrected and re-registered.
36078                         obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36079
36080                     }
36081                     else
36082                     {
36083                         m_PromotedCount++;
36084
36085                         if (method_table(obj)->HasCriticalFinalizer())
36086                         {
36087                             MoveItem (i, Seg, CriticalFinalizerListSeg);
36088                         }
36089                         else
36090                         {
36091                             MoveItem (i, Seg, FinalizerListSeg);
36092                         }
36093                     }
36094                 }
36095 #ifdef BACKGROUND_GC
36096                 else
36097                 {
36098                     if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
36099                     {
36100                         // TODO - fix the following line.
36101                         //assert (gc_heap::background_object_marked ((uint8_t*)obj, FALSE));
36102                         dprintf (3, ("%Ix is marked", (size_t)obj));
36103                     }
36104                 }
36105 #endif //BACKGROUND_GC
36106             }
36107         }
36108     }
36109     finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
36110                      !IsSegEmpty(CriticalFinalizerListSeg);
36111                     
36112     if (finalizedFound)
36113     {
36114         //Promote the f-reachable objects
36115         GcScanRoots (pfn,
36116 #ifdef MULTIPLE_HEAPS
36117                      hp->heap_number
36118 #else
36119                      0
36120 #endif //MULTIPLE_HEAPS
36121                      , 0);
36122
36123         hp->settings.found_finalizers = TRUE;
36124
36125 #ifdef BACKGROUND_GC
36126         if (hp->settings.concurrent)
36127         {
36128             hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
36129         }
36130 #endif //BACKGROUND_GC
36131         if (hp->settings.concurrent && hp->settings.found_finalizers)
36132         {
36133             if (!mark_only_p)
36134                 GCToEEInterface::EnableFinalization(true);
36135         }
36136     }
36137
36138     return finalizedFound;
36139 }
36140
36141 //Relocates all of the objects in the finalization array
36142 void
36143 CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
36144 {
36145     ScanContext sc;
36146     sc.promotion = FALSE;
36147 #ifdef MULTIPLE_HEAPS
36148     sc.thread_number = hp->heap_number;
36149 #else
36150     UNREFERENCED_PARAMETER(hp);
36151 #endif //MULTIPLE_HEAPS
36152
36153     unsigned int Seg = gen_segment (gen);
36154
36155     Object** startIndex = SegQueue (Seg);
36156     for (Object** po = startIndex; po < SegQueue (FreeList);po++)
36157     {
36158         GCHeap::Relocate (po, &sc);
36159     }
36160 }
36161
36162 void
36163 CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
36164 {
36165     // update the generation fill pointers.
36166     // if gen_0_empty is FALSE, test each object to find out if
36167     // it was promoted or not
36168     if (gen_0_empty_p)
36169     {
36170         for (int i = min (gen+1, max_generation); i > 0; i--)
36171         {
36172             m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
36173         }
36174     }
36175     else
36176     {
36177         //Look for demoted or promoted plugs
36178
36179         for (int i = gen; i >= 0; i--)
36180         {
36181             unsigned int Seg = gen_segment (i);
36182             Object** startIndex = SegQueue (Seg);
36183
36184             for (Object** po = startIndex;
36185                  po < SegQueueLimit (gen_segment(i)); po++)
36186             {
36187                 int new_gen = g_theGCHeap->WhichGeneration (*po);
36188                 if (new_gen != i)
36189                 {
36190                     if (new_gen > i)
36191                     {
36192                         //promotion
36193                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
36194                     }
36195                     else
36196                     {
36197                         //demotion
36198                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
36199                         //back down in order to see all objects.
36200                         po--;
36201                     }
36202                 }
36203
36204             }
36205         }
36206     }
36207 }
36208
36209 BOOL
36210 CFinalize::GrowArray()
36211 {
36212     size_t oldArraySize = (m_EndArray - m_Array);
36213     size_t newArraySize =  (size_t)(((float)oldArraySize / 10) * 12);
36214
36215     Object** newArray = new (nothrow) Object*[newArraySize];
36216     if (!newArray)
36217     {
36218         // It's not safe to throw here, because of the FinalizeLock.  Tell our caller
36219         // to throw for us.
36220 //        ASSERT (newArray);
36221         return FALSE;
36222     }
36223     memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
36224
36225     //adjust the fill pointers
36226     for (int i = 0; i < FreeList; i++)
36227     {
36228         m_FillPointers [i] += (newArray - m_Array);
36229     }
36230     delete m_Array;
36231     m_Array = newArray;
36232     m_EndArray = &m_Array [newArraySize];
36233
36234     return TRUE;
36235 }
36236
36237 #ifdef VERIFY_HEAP
36238 void CFinalize::CheckFinalizerObjects()
36239 {
36240     for (int i = 0; i <= max_generation; i++)
36241     {
36242         Object **startIndex = SegQueue (gen_segment (i));
36243         Object **stopIndex  = SegQueueLimit (gen_segment (i));
36244
36245         for (Object **po = startIndex; po < stopIndex; po++)
36246         {
36247             if ((int)g_theGCHeap->WhichGeneration (*po) < i)
36248                 FATAL_GC_ERROR ();
36249             ((CObjectHeader*)*po)->Validate();
36250         }
36251     }
36252 }
36253 #endif //VERIFY_HEAP
36254
36255 #endif // FEATURE_PREMORTEM_FINALIZATION
36256
36257
36258 //------------------------------------------------------------------------------
36259 //
36260 //                      End of VM specific support
36261 //
36262 //------------------------------------------------------------------------------
36263 void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36264 {
36265     generation* gen = gc_heap::generation_of (gen_number);
36266     heap_segment*    seg = generation_start_segment (gen);
36267     uint8_t*       x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
36268                      generation_allocation_start (gen));
36269
36270     uint8_t*       end = heap_segment_allocated (seg);
36271     BOOL small_object_segments = TRUE;
36272     int align_const = get_alignment_constant (small_object_segments);
36273
36274     while (1)
36275
36276     {
36277         if (x >= end)
36278         {
36279             if ((seg = heap_segment_next (seg)) != 0)
36280             {
36281                 x = heap_segment_mem (seg);
36282                 end = heap_segment_allocated (seg);
36283                 continue;
36284             }
36285             else
36286             {
36287                 if (small_object_segments && walk_large_object_heap_p)
36288
36289                 {
36290                     small_object_segments = FALSE;
36291                     align_const = get_alignment_constant (small_object_segments);
36292                     seg = generation_start_segment (large_object_generation);
36293                     x = heap_segment_mem (seg);
36294                     end = heap_segment_allocated (seg);
36295                     continue;
36296                 }
36297                 else
36298                 {
36299                     break;
36300                 }
36301             }
36302         }
36303
36304         size_t s = size (x);
36305         CObjectHeader* o = (CObjectHeader*)x;
36306
36307         if (!o->IsFree())
36308
36309         {
36310             _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
36311
36312             if (!fn (o->GetObjectBase(), context))
36313                 return;
36314         }
36315         x = x + Align (s, align_const);
36316     }
36317 }
36318
36319 void gc_heap::walk_finalize_queue (fq_walk_fn fn)
36320 {
36321 #ifdef FEATURE_PREMORTEM_FINALIZATION
36322     finalize_queue->WalkFReachableObjects (fn);
36323 #endif //FEATURE_PREMORTEM_FINALIZATION
36324 }
36325
36326 void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36327 {
36328 #ifdef MULTIPLE_HEAPS
36329     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36330     {
36331         gc_heap* hp = gc_heap::g_heaps [hn];
36332
36333         hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p);
36334     }
36335 #else
36336     walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p);
36337 #endif //MULTIPLE_HEAPS
36338 }
36339
36340 void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context)
36341 {
36342     uint8_t* o = (uint8_t*)obj;
36343     if (o)
36344     {
36345         go_through_object_cl (method_table (o), o, size(o), oo,
36346                                     {
36347                                         if (*oo)
36348                                         {
36349                                             Object *oh = (Object*)*oo;
36350                                             if (!fn (oh, context))
36351                                                 return;
36352                                         }
36353                                     }
36354             );
36355     }
36356 }
36357
36358 void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type)
36359 {
36360     gc_heap* hp = (gc_heap*)gc_context;
36361     hp->walk_survivors (fn, diag_context, type);
36362 }
36363
36364 void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p)
36365 {
36366     gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
36367 }
36368
36369 void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
36370 {
36371     gc_heap* hp = (gc_heap*)gc_context;
36372     hp->walk_finalize_queue (fn);
36373 }
36374
36375 void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc)
36376 {
36377 #ifdef MULTIPLE_HEAPS
36378     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36379     {
36380         gc_heap* hp = gc_heap::g_heaps [hn];
36381         hp->finalize_queue->GcScanRoots(fn, hn, sc);
36382     }
36383 #else
36384         pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc);
36385 #endif //MULTIPLE_HEAPS
36386 }
36387
36388 void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36389 {
36390     UNREFERENCED_PARAMETER(gen_number);
36391     GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn);
36392 }
36393
36394 void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36395 {
36396     UNREFERENCED_PARAMETER(gen_number);
36397     GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn);
36398 }
36399
36400 // Go through and touch (read) each page straddled by a memory block.
36401 void TouchPages(void * pStart, size_t cb)
36402 {
36403     const uint32_t pagesize = OS_PAGE_SIZE;
36404     _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
36405     if (cb)
36406     {
36407         VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
36408         VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) -  (((size_t)pStart) & (pagesize-1)));
36409         while (p < pEnd)
36410         {
36411             char a;
36412             a = VolatileLoad(p);
36413             //printf("Touching page %lxh\n", (uint32_t)p);
36414             p += pagesize;
36415         }
36416     }
36417 }
36418
36419 #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
36420     // This code is designed to catch the failure to update the write barrier
36421     // The way it works is to copy the whole heap right after every GC.  The write
36422     // barrier code has been modified so that it updates the shadow as well as the
36423     // real GC heap.  Before doing the next GC, we walk the heap, looking for pointers
36424     // that were updated in the real heap, but not the shadow.  A mismatch indicates
36425     // an error.  The offending code can be found by breaking after the correct GC,
36426     // and then placing a data breakpoint on the Heap location that was updated without
36427     // going through the write barrier.
36428
36429     // Called at process shutdown
36430 void deleteGCShadow()
36431 {
36432     if (g_GCShadow != 0)
36433         GCToOSInterface::VirtualRelease (g_GCShadow, g_GCShadowEnd - g_GCShadow);
36434     g_GCShadow = 0;
36435     g_GCShadowEnd = 0;
36436 }
36437
36438     // Called at startup and right after a GC, get a snapshot of the GC Heap
36439 void initGCShadow()
36440 {
36441     if (!(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK))
36442         return;
36443
36444     size_t len = g_gc_highest_address - g_gc_lowest_address;
36445     if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) 
36446     {
36447         deleteGCShadow();
36448         g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None);
36449         if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len))
36450         {
36451             _ASSERTE(!"Not enough memory to run HeapVerify level 2");
36452             // If after the assert we decide to allow the program to continue 
36453             // running we need to be in a state that will not trigger any 
36454             // additional AVs while we fail to allocate a shadow segment, i.e. 
36455             // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
36456             deleteGCShadow();
36457             return;
36458         }
36459
36460         g_GCShadowEnd += len;
36461     }
36462
36463     // save the value of g_gc_lowest_address at this time.  If this value changes before
36464     // the next call to checkGCWriteBarrier() it means we extended the heap (with a
36465     // large object segment most probably), and the whole shadow segment is inconsistent.
36466     g_shadow_lowest_address = g_gc_lowest_address;
36467
36468         //****** Copy the whole GC heap ******
36469     //
36470     // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
36471     // can produce a NULL result.  This is because the initialization has not completed.
36472     //
36473     generation* gen = gc_heap::generation_of (max_generation);
36474     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36475
36476     ptrdiff_t delta = g_GCShadow - g_gc_lowest_address;
36477     BOOL small_object_segments = TRUE;
36478     while(1)
36479     {
36480         if (!seg)
36481         {
36482             if (small_object_segments)
36483             {
36484                 small_object_segments = FALSE;
36485                 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
36486                 continue;
36487             }
36488             else
36489                 break;
36490         }
36491             // Copy the segment
36492         uint8_t* start = heap_segment_mem(seg);
36493         uint8_t* end = heap_segment_allocated (seg);
36494         memcpy(start + delta, start, end - start);
36495         seg = heap_segment_next_rw (seg);
36496     }
36497 }
36498
36499 #define INVALIDGCVALUE (void*)((size_t)0xcccccccd)
36500
36501     // test to see if 'ptr' was only updated via the write barrier.
36502 inline void testGCShadow(Object** ptr)
36503 {
36504     Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)];
36505     if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow)
36506     {
36507
36508         // If you get this assertion, someone updated a GC pointer in the heap without
36509         // using the write barrier.  To find out who, check the value of 
36510         // dd_collection_count (dynamic_data_of (0)). Also
36511         // note the value of 'ptr'.  Rerun the App that the previous GC just occurred.
36512         // Then put a data breakpoint for the value of 'ptr'  Then check every write
36513         // to pointer between the two GCs.  The last one is not using the write barrier.
36514
36515         // If the memory of interest does not exist at system startup,
36516         // you need to set the data breakpoint right after the memory gets committed
36517         // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
36518         // in the memory window.  run until the memory gets mapped. Then you can set
36519         // your breakpoint
36520
36521         // Note a recent change, we've identified race conditions when updating the gc shadow.
36522         // Throughout the runtime, code will update an address in the gc heap, then erect the
36523         // write barrier, which calls updateGCShadow. With an app that pounds one heap location
36524         // from multiple threads, you can hit this assert even though all involved are using the
36525         // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
36526         // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
36527         // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
36528         // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
36529         // TODO: erroneous asserts in here.
36530
36531         if(*shadow!=INVALIDGCVALUE)
36532         {
36533 #ifdef FEATURE_BASICFREEZE
36534             // Write barriers for stores of references to frozen objects may be optimized away.
36535             if (!gc_heap::frozen_object_p(*ptr))
36536 #endif // FEATURE_BASICFREEZE
36537             {
36538                 _ASSERTE(!"Pointer updated without using write barrier");
36539             }
36540         }
36541         /*
36542         else
36543         {
36544              printf("saw a INVALIDGCVALUE. (just to let you know)\n");
36545         }
36546         */
36547     }
36548 }
36549
36550 void testGCShadowHelper (uint8_t* x)
36551 {
36552     size_t s = size (x);
36553     if (contain_pointers (x))
36554     {
36555         go_through_object_nostart (method_table(x), x, s, oo,
36556                            { testGCShadow((Object**) oo); });
36557     }
36558 }
36559
36560     // Walk the whole heap, looking for pointers that were not updated with the write barrier.
36561 void checkGCWriteBarrier()
36562 {
36563     // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment
36564     // and the GC shadow segment did not track that change!
36565     if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address)
36566     {
36567         // No shadow stack, nothing to check.
36568         return;
36569     }
36570
36571     {
36572         generation* gen = gc_heap::generation_of (max_generation);
36573         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36574
36575         PREFIX_ASSUME(seg != NULL);
36576
36577         while(seg)
36578         {
36579             uint8_t* x = heap_segment_mem(seg);
36580             while (x < heap_segment_allocated (seg))
36581             {
36582                 size_t s = size (x);
36583                 testGCShadowHelper (x);
36584                 x = x + Align (s);
36585             }
36586             seg = heap_segment_next_rw (seg);
36587         }
36588     }
36589
36590     {
36591         // go through large object heap
36592         int alignment = get_alignment_constant(FALSE);
36593         generation* gen = gc_heap::generation_of (max_generation+1);
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, alignment);
36606             }
36607             seg = heap_segment_next_rw (seg);
36608         }
36609     }
36610 }
36611 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
36612
36613 #endif // !DACCESS_COMPILE
36614
36615 #ifdef FEATURE_BASICFREEZE
36616 void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
36617 {
36618 #ifdef DACCESS_COMPILE
36619     UNREFERENCED_PARAMETER(seg);
36620     UNREFERENCED_PARAMETER(pvContext);
36621     UNREFERENCED_PARAMETER(pfnMethodTable);
36622     UNREFERENCED_PARAMETER(pfnObjRef);
36623 #else
36624     uint8_t *o = heap_segment_mem(seg);
36625
36626     // small heap alignment constant
36627     int alignment = get_alignment_constant(TRUE);
36628
36629     while (o < heap_segment_allocated(seg))
36630     {
36631         pfnMethodTable(pvContext, o);
36632
36633         if (contain_pointers (o))
36634         {
36635             go_through_object_nostart (method_table (o), o, size(o), oo,
36636                    {
36637                        if (*oo)
36638                            pfnObjRef(pvContext, oo);
36639                    }
36640             );
36641         }
36642
36643         o += Align(size(o), alignment);
36644     }
36645 #endif //!DACCESS_COMPILE
36646 }
36647 #endif // FEATURE_BASICFREEZE
36648
36649 #ifndef DACCESS_COMPILE
36650 HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
36651 {
36652 #ifdef BACKGROUND_GC
36653     if (recursive_gc_sync::background_running_p())
36654     {
36655         uint32_t dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
36656         if (dwRet == WAIT_OBJECT_0)
36657             return S_OK;
36658         else if (dwRet == WAIT_TIMEOUT)
36659             return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
36660         else
36661             return E_FAIL;      // It is not clear if what the last error would be if the wait failed,
36662                                 // as there are too many layers in between. The best we can do is to return E_FAIL;
36663     }
36664 #endif
36665
36666     return S_OK;
36667 }
36668 #endif // !DACCESS_COMPILE
36669
36670 void GCHeap::TemporaryEnableConcurrentGC()
36671 {
36672 #ifdef BACKGROUND_GC
36673     gc_heap::temp_disable_concurrent_p = false;
36674 #endif //BACKGROUND_GC
36675 }
36676
36677 void GCHeap::TemporaryDisableConcurrentGC()
36678 {
36679 #ifdef BACKGROUND_GC
36680     gc_heap::temp_disable_concurrent_p = true;
36681 #endif //BACKGROUND_GC
36682 }
36683
36684 bool GCHeap::IsConcurrentGCEnabled()
36685 {
36686 #ifdef BACKGROUND_GC
36687     return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
36688 #else
36689     return FALSE;
36690 #endif //BACKGROUND_GC
36691 }
36692
36693 void GCHeap::SetFinalizeRunOnShutdown(bool value)
36694 {
36695     g_fFinalizerRunOnShutDown = value;
36696 }
36697
36698 void PopulateDacVars(GcDacVars *gcDacVars)
36699 {
36700 #ifndef DACCESS_COMPILE
36701     assert(gcDacVars != nullptr);
36702     *gcDacVars = {};
36703     gcDacVars->major_version_number = 1;
36704     gcDacVars->minor_version_number = 0;
36705     gcDacVars->built_with_svr = &g_built_with_svr_gc;
36706     gcDacVars->build_variant = &g_build_variant;
36707     gcDacVars->gc_structures_invalid_cnt = const_cast<int32_t*>(&GCScan::m_GcStructuresInvalidCnt);
36708     gcDacVars->generation_size = sizeof(generation);
36709     gcDacVars->max_gen = &g_max_generation;
36710 #ifndef MULTIPLE_HEAPS
36711     gcDacVars->mark_array = &gc_heap::mark_array;
36712     gcDacVars->ephemeral_heap_segment = reinterpret_cast<dac_heap_segment**>(&gc_heap::ephemeral_heap_segment);
36713     gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state);
36714     gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast<dac_heap_segment**>(&gc_heap::saved_sweep_ephemeral_seg);
36715     gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start;
36716     gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address;
36717     gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address;
36718     gcDacVars->alloc_allocated = &gc_heap::alloc_allocated;
36719     gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj;
36720     gcDacVars->oom_info = &gc_heap::oom_info;
36721     gcDacVars->finalize_queue = reinterpret_cast<dac_finalize_queue**>(&gc_heap::finalize_queue);
36722     gcDacVars->generation_table = reinterpret_cast<dac_generation**>(&gc_heap::generation_table);
36723 #ifdef GC_CONFIG_DRIVEN
36724     gcDacVars->gc_global_mechanisms = reinterpret_cast<size_t**>(&gc_global_mechanisms);
36725     gcDacVars->interesting_data_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_data_per_heap);
36726     gcDacVars->compact_reasons_per_heap = reinterpret_cast<size_t**>(&gc_heap::compact_reasons_per_heap);
36727     gcDacVars->expand_mechanisms_per_heap = reinterpret_cast<size_t**>(&gc_heap::expand_mechanisms_per_heap);
36728     gcDacVars->interesting_mechanism_bits_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_mechanism_bits_per_heap);
36729 #endif // GC_CONFIG_DRIVEN
36730 #ifdef HEAP_ANALYZE
36731     gcDacVars->internal_root_array = &gc_heap::internal_root_array;
36732     gcDacVars->internal_root_array_index = &gc_heap::internal_root_array_index;
36733     gcDacVars->heap_analyze_success = &gc_heap::heap_analyze_success;
36734 #endif // HEAP_ANALYZE
36735 #else
36736     gcDacVars->n_heaps = &gc_heap::n_heaps;
36737     gcDacVars->g_heaps = reinterpret_cast<dac_gc_heap***>(&gc_heap::g_heaps);
36738 #endif // MULTIPLE_HEAPS
36739 #endif // DACCESS_COMPILE
36740 }