Merge pull request #19205 from vancem/EventSourceUIntFix
[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 #ifdef BIT64
312 #define TOTAL_TIMES_TO_SHIFT 6
313 #else
314 #define TOTAL_TIMES_TO_SHIFT 5
315 #endif // BIT64
316
317 size_t round_up_power2 (size_t size)
318 {
319     unsigned short shift = 1;
320     size_t shifted = 0;
321
322     size--;
323     for (unsigned short i = 0; i < TOTAL_TIMES_TO_SHIFT; i++)
324     {
325         shifted = size | (size >> shift);
326         if (shifted == size)
327         {
328             break;
329         }
330
331         size = shifted;
332         shift <<= 1;
333     }
334     shifted++;
335
336     return shifted;
337 }
338
339 inline
340 size_t round_down_power2 (size_t size)
341 {
342     size_t power2 = round_up_power2 (size);
343
344     if (power2 != size)
345     {
346         power2 >>= 1;
347     }
348
349     return power2;
350 }
351
352 // the index starts from 0.
353 int index_of_set_bit (size_t power2)
354 {
355     int low = 0;
356     int high = sizeof (size_t) * 8 - 1;
357     int mid; 
358     while (low <= high)
359     {
360         mid = ((low + high)/2);
361         size_t temp = (size_t)1 << mid;
362         if (power2 & temp)
363         {
364             return mid;
365         }
366         else if (power2 < temp)
367         {
368             high = mid - 1;
369         }
370         else
371         {
372             low = mid + 1;
373         }
374     }
375
376     return -1;
377 }
378
379 inline
380 int relative_index_power2_plug (size_t power2)
381 {
382     int index = index_of_set_bit (power2);
383     assert (index <= MAX_INDEX_POWER2);
384
385     return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
386 }
387
388 inline
389 int relative_index_power2_free_space (size_t power2)
390 {
391     int index = index_of_set_bit (power2);
392     assert (index <= MAX_INDEX_POWER2);
393
394     return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
395 }
396
397 #ifdef BACKGROUND_GC
398 uint32_t bgc_alloc_spin_count = 140;
399 uint32_t bgc_alloc_spin_count_loh = 16;
400 uint32_t bgc_alloc_spin = 2;
401
402
403 inline
404 void c_write (uint32_t& place, uint32_t value)
405 {
406     Interlocked::Exchange (&place, value);
407     //place = value;
408 }
409
410 #ifndef DACCESS_COMPILE
411 // If every heap's gen2 or gen3 size is less than this threshold we will do a blocking GC.
412 const size_t bgc_min_per_heap = 4*1024*1024;
413
414 int gc_heap::gchist_index = 0;
415 gc_mechanisms_store gc_heap::gchist[max_history_count];
416
417 #ifndef MULTIPLE_HEAPS
418 size_t gc_heap::total_promoted_bytes = 0;
419 VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process;
420 int gc_heap::gchist_index_per_heap = 0;
421 gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count];
422 #endif //MULTIPLE_HEAPS
423
424 void gc_heap::add_to_history_per_heap()
425 {
426 #ifdef GC_HISTORY
427     gc_history* current_hist = &gchist_per_heap[gchist_index_per_heap];
428     current_hist->gc_index = settings.gc_index;
429     current_hist->current_bgc_state = current_bgc_state;
430     size_t elapsed = dd_gc_elapsed_time (dynamic_data_of (0));
431     current_hist->gc_time_ms = (uint32_t)elapsed;
432     current_hist->gc_efficiency = (elapsed ? (total_promoted_bytes / elapsed) : total_promoted_bytes);
433     current_hist->eph_low = generation_allocation_start (generation_of (max_generation-1));
434     current_hist->gen0_start = generation_allocation_start (generation_of (0));
435     current_hist->eph_high = heap_segment_allocated (ephemeral_heap_segment);
436 #ifdef BACKGROUND_GC
437     current_hist->bgc_lowest = background_saved_lowest_address;
438     current_hist->bgc_highest = background_saved_highest_address;
439 #endif //BACKGROUND_GC
440     current_hist->fgc_lowest = lowest_address;
441     current_hist->fgc_highest = highest_address;
442     current_hist->g_lowest = g_gc_lowest_address;
443     current_hist->g_highest = g_gc_highest_address;
444
445     gchist_index_per_heap++;
446     if (gchist_index_per_heap == max_history_count)
447     {
448         gchist_index_per_heap = 0;
449     }
450 #endif //GC_HISTORY
451 }
452
453 void gc_heap::add_to_history()
454 {
455 #ifdef GC_HISTORY
456     gc_mechanisms_store* current_settings = &gchist[gchist_index];
457     current_settings->store (&settings);
458
459     gchist_index++;
460     if (gchist_index == max_history_count)
461     {
462         gchist_index = 0;
463     }
464 #endif //GC_HISTORY
465 }
466
467 #endif //DACCESS_COMPILE
468 #endif //BACKGROUND_GC
469
470 #if defined(TRACE_GC) && !defined(DACCESS_COMPILE)
471 BOOL   gc_log_on = TRUE;
472 FILE* gc_log = NULL;
473 size_t gc_log_file_size = 0;
474
475 size_t gc_buffer_index = 0;
476 size_t max_gc_buffers = 0;
477
478 static CLRCriticalSection gc_log_lock;
479
480 // we keep this much in a buffer and only flush when the buffer is full
481 #define gc_log_buffer_size (1024*1024)
482 uint8_t* gc_log_buffer = 0;
483 size_t gc_log_buffer_offset = 0;
484
485 void log_va_msg(const char *fmt, va_list args)
486 {
487     gc_log_lock.Enter();
488
489     const int BUFFERSIZE = 512;
490     static char rgchBuffer[BUFFERSIZE];
491     char *  pBuffer  = &rgchBuffer[0];
492
493     pBuffer[0] = '\n';
494     int buffer_start = 1;
495     int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging());
496     buffer_start += pid_len;
497     memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start);
498     int msg_len = _vsnprintf_s(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
499     if (msg_len == -1)
500     {
501         msg_len = BUFFERSIZE - buffer_start;
502     }
503
504     msg_len += buffer_start;
505
506     if ((gc_log_buffer_offset + msg_len) > (gc_log_buffer_size - 12))
507     {
508         char index_str[8];
509         memset (index_str, '-', 8);
510         sprintf_s (index_str, _countof(index_str), "%d", (int)gc_buffer_index);
511         gc_log_buffer[gc_log_buffer_offset] = '\n';
512         memcpy (gc_log_buffer + (gc_log_buffer_offset + 1), index_str, 8);
513
514         gc_buffer_index++;
515         if (gc_buffer_index > max_gc_buffers)
516         {
517             fseek (gc_log, 0, SEEK_SET);
518             gc_buffer_index = 0;
519         }
520         fwrite(gc_log_buffer, gc_log_buffer_size, 1, gc_log);
521         fflush(gc_log);
522         memset (gc_log_buffer, '*', gc_log_buffer_size);
523         gc_log_buffer_offset = 0;
524     }
525
526     memcpy (gc_log_buffer + gc_log_buffer_offset, pBuffer, msg_len);
527     gc_log_buffer_offset += msg_len;
528
529     gc_log_lock.Leave();
530 }
531
532 void GCLog (const char *fmt, ... )
533 {
534     if (gc_log_on && (gc_log != NULL))
535     {
536         va_list     args;
537         va_start(args, fmt);
538         log_va_msg (fmt, args);
539         va_end(args);
540     }
541 }
542 #endif // TRACE_GC && !DACCESS_COMPILE
543
544 #if defined(GC_CONFIG_DRIVEN) && !defined(DACCESS_COMPILE)
545
546 BOOL   gc_config_log_on = FALSE;
547 FILE* gc_config_log = NULL;
548
549 // we keep this much in a buffer and only flush when the buffer is full
550 #define gc_config_log_buffer_size (1*1024) // TEMP
551 uint8_t* gc_config_log_buffer = 0;
552 size_t gc_config_log_buffer_offset = 0;
553
554 // For config since we log so little we keep the whole history. Also it's only
555 // ever logged by one thread so no need to synchronize.
556 void log_va_msg_config(const char *fmt, va_list args)
557 {
558     const int BUFFERSIZE = 256;
559     static char rgchBuffer[BUFFERSIZE];
560     char *  pBuffer  = &rgchBuffer[0];
561
562     pBuffer[0] = '\n';
563     int buffer_start = 1;
564     int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
565     assert (msg_len != -1);
566     msg_len += buffer_start;
567
568     if ((gc_config_log_buffer_offset + msg_len) > gc_config_log_buffer_size)
569     {
570         fwrite(gc_config_log_buffer, gc_config_log_buffer_offset, 1, gc_config_log);
571         fflush(gc_config_log);
572         gc_config_log_buffer_offset = 0;
573     }
574
575     memcpy (gc_config_log_buffer + gc_config_log_buffer_offset, pBuffer, msg_len);
576     gc_config_log_buffer_offset += msg_len;
577 }
578
579 void GCLogConfig (const char *fmt, ... )
580 {
581     if (gc_config_log_on && (gc_config_log != NULL))
582     {
583         va_list     args;
584         va_start( args, fmt );
585         log_va_msg_config (fmt, args);
586     }
587 }
588 #endif // GC_CONFIG_DRIVEN && !DACCESS_COMPILE
589
590 #ifdef SYNCHRONIZATION_STATS
591
592 // Number of GCs have we done since we last logged.
593 static unsigned int         gc_count_during_log;
594  // In ms. This is how often we print out stats.
595 static const unsigned int   log_interval = 5000;
596 // Time (in ms) when we start a new log interval.
597 static unsigned int         log_start_tick;
598 static unsigned int         gc_lock_contended;
599 static int64_t              log_start_hires;
600 // Cycles accumulated in SuspendEE during log_interval.
601 static uint64_t             suspend_ee_during_log;
602 // Cycles accumulated in RestartEE during log_interval.
603 static uint64_t             restart_ee_during_log;
604 static uint64_t             gc_during_log;
605
606 #endif //SYNCHRONIZATION_STATS
607
608 void
609 init_sync_log_stats()
610 {
611 #ifdef SYNCHRONIZATION_STATS
612     if (gc_count_during_log == 0)
613     {
614         gc_heap::init_sync_stats();
615         suspend_ee_during_log = 0;
616         restart_ee_during_log = 0;
617         gc_during_log = 0;
618         gc_lock_contended = 0;
619
620         log_start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
621         log_start_hires = GCToOSInterface::QueryPerformanceCounter();
622     }
623     gc_count_during_log++;
624 #endif //SYNCHRONIZATION_STATS
625 }
626
627 void
628 process_sync_log_stats()
629 {
630 #ifdef SYNCHRONIZATION_STATS
631
632     unsigned int log_elapsed = GCToOSInterface::GetLowPrecisionTimeStamp() - log_start_tick;
633
634     if (log_elapsed > log_interval)
635     {
636         uint64_t total = GCToOSInterface::QueryPerformanceCounter() - log_start_hires;
637         // Print out the cycles we spent on average in each suspend and restart.
638         printf("\n_________________________________________________________________________________\n"
639             "Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n"
640             "SuspendEE: %8u; RestartEE: %8u GC %.3f%%\n",
641             log_interval / 1000,
642             gc_count_during_log,
643             gc_lock_contended,
644             (unsigned int)(gc_during_log / gc_count_during_log),
645             (unsigned int)(suspend_ee_during_log / gc_count_during_log),
646             (unsigned int)(restart_ee_during_log / gc_count_during_log),
647             (double)(100.0f * gc_during_log / total));
648         gc_heap::print_sync_stats(gc_count_during_log);
649
650         gc_count_during_log = 0;
651     }
652 #endif //SYNCHRONIZATION_STATS
653 }
654
655 #ifdef MULTIPLE_HEAPS
656
657 enum gc_join_stage
658 {
659     gc_join_init_cpu_mapping = 0,
660     gc_join_done = 1,
661     gc_join_generation_determined = 2,
662     gc_join_begin_mark_phase = 3,
663     gc_join_scan_dependent_handles = 4,
664     gc_join_rescan_dependent_handles = 5,
665     gc_join_scan_sizedref_done = 6,
666     gc_join_null_dead_short_weak = 7,
667     gc_join_scan_finalization = 8,
668     gc_join_null_dead_long_weak = 9, 
669     gc_join_null_dead_syncblk = 10, 
670     gc_join_decide_on_compaction = 11, 
671     gc_join_rearrange_segs_compaction = 12, 
672     gc_join_adjust_handle_age_compact = 13,
673     gc_join_adjust_handle_age_sweep = 14,
674     gc_join_begin_relocate_phase = 15,
675     gc_join_relocate_phase_done = 16,
676     gc_join_verify_objects_done = 17,
677     gc_join_start_bgc = 18,
678     gc_join_restart_ee = 19,
679     gc_join_concurrent_overflow = 20,
680     gc_join_suspend_ee = 21,
681     gc_join_bgc_after_ephemeral = 22,
682     gc_join_allow_fgc = 23,
683     gc_join_bgc_sweep = 24,
684     gc_join_suspend_ee_verify = 25,
685     gc_join_restart_ee_verify = 26,
686     gc_join_set_state_free = 27,
687     gc_r_join_update_card_bundle = 28,
688     gc_join_after_absorb = 29, 
689     gc_join_verify_copy_table = 30,
690     gc_join_after_reset = 31,
691     gc_join_after_ephemeral_sweep = 32,
692     gc_join_after_profiler_heap_walk = 33,
693     gc_join_minimal_gc = 34,
694     gc_join_after_commit_soh_no_gc = 35,
695     gc_join_expand_loh_no_gc = 36,
696     gc_join_final_no_gc = 37,
697     gc_join_disable_software_write_watch = 38,
698     gc_join_max = 39
699 };
700
701 enum gc_join_flavor
702 {
703     join_flavor_server_gc = 0,
704     join_flavor_bgc = 1
705 };
706
707 #define first_thread_arrived 2
708 struct DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE) join_structure
709 {
710     // Shared non volatile keep on separate line to prevent eviction
711     int n_threads;
712
713     // Keep polling/wait structures on separate line write once per join
714     DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
715     GCEvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
716     Volatile<int> lock_color;
717     VOLATILE(BOOL) wait_done;
718     VOLATILE(BOOL) joined_p;
719
720     // Keep volatile counted locks on separate cache line write many per join
721     DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
722     VOLATILE(int32_t) join_lock;
723     VOLATILE(int32_t) r_join_lock;
724
725 };
726
727 enum join_type 
728 {
729     type_last_join = 0, 
730     type_join = 1, 
731     type_restart = 2, 
732     type_first_r_join = 3, 
733     type_r_join = 4
734 };
735
736 enum join_time 
737 {
738     time_start = 0, 
739     time_end = 1
740 };
741
742 enum join_heap_index
743 {
744     join_heap_restart = 100,
745     join_heap_r_restart = 200
746 };
747
748 struct join_event
749 {
750     uint32_t heap;
751     join_time time;
752     join_type type;
753 };
754
755 class t_join
756 {
757     join_structure join_struct;
758
759     int id;
760     gc_join_flavor flavor;
761
762 #ifdef JOIN_STATS
763     uint64_t start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
764     // remember join id and last thread to arrive so restart can use these
765     int thd;
766     // we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval
767     uint32_t start_tick;
768     // counters for joins, in 1000's of clock cycles
769     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];
770 #endif //JOIN_STATS
771
772 public:
773     BOOL init (int n_th, gc_join_flavor f)
774     {
775         dprintf (JOIN_LOG, ("Initializing join structure"));
776         join_struct.n_threads = n_th;
777         join_struct.lock_color = 0;
778         for (int i = 0; i < 3; i++)
779         {
780             if (!join_struct.joined_event[i].IsValid())
781             {
782                 join_struct.joined_p = FALSE;
783                 dprintf (JOIN_LOG, ("Creating join event %d", i));
784                 // TODO - changing this to a non OS event
785                 // because this is also used by BGC threads which are 
786                 // managed threads and WaitEx does not allow you to wait
787                 // for an OS event on a managed thread.
788                 // But we are not sure if this plays well in the hosting 
789                 // environment.
790                 //join_struct.joined_event[i].CreateOSManualEventNoThrow(FALSE);
791                 if (!join_struct.joined_event[i].CreateManualEventNoThrow(FALSE))
792                     return FALSE;
793             }
794         }
795         join_struct.join_lock = join_struct.n_threads;
796         join_struct.r_join_lock = join_struct.n_threads;
797         join_struct.wait_done = FALSE;
798         flavor = f;
799
800 #ifdef JOIN_STATS
801         start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
802 #endif //JOIN_STATS
803
804         return TRUE;
805     }
806     
807     void destroy ()
808     {
809         dprintf (JOIN_LOG, ("Destroying join structure"));
810         for (int i = 0; i < 3; i++)
811         {
812             if (join_struct.joined_event[i].IsValid())
813                 join_struct.joined_event[i].CloseEvent();
814         }
815     }
816
817     inline void fire_event (int heap, join_time time, join_type type, int join_id)
818     {
819         FIRE_EVENT(GCJoin_V2, heap, time, type, join_id);
820     }
821
822     void join (gc_heap* gch, int join_id)
823     {
824 #ifdef JOIN_STATS
825         // parallel execution ends here
826         end[gch->heap_number] = get_ts();
827 #endif //JOIN_STATS
828
829         assert (!join_struct.joined_p);
830         int color = join_struct.lock_color.LoadWithoutBarrier();
831
832         if (Interlocked::Decrement(&join_struct.join_lock) != 0)
833         {
834             dprintf (JOIN_LOG, ("join%d(%d): Join() Waiting...join_lock is now %d", 
835                 flavor, join_id, (int32_t)(join_struct.join_lock)));
836
837             fire_event (gch->heap_number, time_start, type_join, join_id);
838
839             //busy wait around the color
840             if (color == join_struct.lock_color.LoadWithoutBarrier())
841             {
842 respin:
843                 int spin_count = 4096 * (gc_heap::n_heaps - 1);
844                 for (int j = 0; j < spin_count; j++)
845                 {
846                     if (color != join_struct.lock_color.LoadWithoutBarrier())
847                     {
848                         break;
849                     }
850                     YieldProcessor();           // indicate to the processor that we are spinning
851                 }
852
853                 // we've spun, and if color still hasn't changed, fall into hard wait
854                 if (color == join_struct.lock_color.LoadWithoutBarrier())
855                 {
856                     dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d", 
857                         flavor, join_id, color, (int32_t)(join_struct.join_lock)));
858
859                     //Thread* current_thread = GCToEEInterface::GetThread();
860                     //BOOL cooperative_mode = gc_heap::enable_preemptive (current_thread);
861                     uint32_t dwJoinWait = join_struct.joined_event[color].Wait(INFINITE, FALSE);
862                     //gc_heap::disable_preemptive (current_thread, cooperative_mode);
863
864                     if (dwJoinWait != WAIT_OBJECT_0)
865                     {
866                         STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
867                         FATAL_GC_ERROR ();
868                     }
869                 }
870
871                 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
872                 if (color == join_struct.lock_color.LoadWithoutBarrier())
873                 {
874                     goto respin;
875                 }
876
877                 dprintf (JOIN_LOG, ("join%d(%d): Join() done, join_lock is %d", 
878                     flavor, join_id, (int32_t)(join_struct.join_lock)));
879             }
880
881             fire_event (gch->heap_number, time_end, type_join, join_id);
882
883 #ifdef JOIN_STATS
884             // parallel execution starts here
885             start[gch->heap_number] = get_ts();
886             Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number]));
887 #endif //JOIN_STATS
888         }
889         else
890         {
891             fire_event (gch->heap_number, time_start, type_last_join, join_id);
892
893             join_struct.joined_p = TRUE;
894             dprintf (JOIN_LOG, ("join%d(%d): Last thread to complete the join, setting id", flavor, join_id));
895             join_struct.joined_event[!color].Reset();
896             id = join_id;
897             // this one is alone so it can proceed
898 #ifdef JOIN_STATS
899             // remember the join id, the last thread arriving, the start of the sequential phase,
900             // and keep track of the cycles spent waiting in the join
901             thd = gch->heap_number;
902             start_seq = get_ts();
903             Interlocked::ExchangeAdd(&in_join_total[join_id], (start_seq - end[gch->heap_number]));
904 #endif //JOIN_STATS
905         }
906     }
907
908     // Reverse join - first thread gets here does the work; other threads will only proceed
909     // after the work is done.
910     // Note that you cannot call this twice in a row on the same thread. Plus there's no 
911     // need to call it twice in row - you should just merge the work.
912     BOOL r_join (gc_heap* gch, int join_id)
913     {
914
915         if (join_struct.n_threads == 1)
916         {
917             return TRUE;
918         }
919
920         if (Interlocked::CompareExchange(&join_struct.r_join_lock, 0, join_struct.n_threads) == 0)
921         {
922             if (!join_struct.wait_done)
923             {
924                 dprintf (JOIN_LOG, ("r_join() Waiting..."));
925
926                 fire_event (gch->heap_number, time_start, type_join, join_id);
927
928                 //busy wait around the color
929                 if (!join_struct.wait_done)
930                 {
931         respin:
932                     int spin_count = 2 * 4096 * (gc_heap::n_heaps - 1);
933                     for (int j = 0; j < spin_count; j++)
934                     {
935                         if (join_struct.wait_done)
936                         {
937                             break;
938                         }
939                         YieldProcessor();           // indicate to the processor that we are spinning
940                     }
941
942                     // we've spun, and if color still hasn't changed, fall into hard wait
943                     if (!join_struct.wait_done)
944                     {
945                         dprintf (JOIN_LOG, ("Join() hard wait on reset event %d", first_thread_arrived));
946                         uint32_t dwJoinWait = join_struct.joined_event[first_thread_arrived].Wait(INFINITE, FALSE);
947                         if (dwJoinWait != WAIT_OBJECT_0)
948                         {
949                             STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
950                             FATAL_GC_ERROR ();
951                         }
952                     }
953
954                     // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
955                     if (!join_struct.wait_done)
956                     {
957                         goto respin;
958                     }
959
960                     dprintf (JOIN_LOG, ("r_join() done"));
961                 }
962
963                 fire_event (gch->heap_number, time_end, type_join, join_id);
964             }
965
966             return FALSE;
967         }
968         else
969         {
970             fire_event (gch->heap_number, time_start, type_first_r_join, join_id);
971             return TRUE;
972         }
973     }
974
975 #ifdef JOIN_STATS
976     uint64_t get_ts()
977     {
978         return GCToOSInterface::QueryPerformanceCounter();
979     }
980
981     void start_ts (gc_heap* gch)
982     {
983         // parallel execution ends here
984         start[gch->heap_number] = get_ts();
985     }
986 #endif //JOIN_STATS
987
988     void restart()
989     {
990 #ifdef JOIN_STATS
991         uint64_t elapsed_seq = get_ts() - start_seq;
992         uint64_t max = 0, sum = 0, wake = 0;
993         uint64_t min_ts = start[0];
994         for (int i = 1; i < join_struct.n_threads; i++)
995         {
996             if(min_ts > start[i]) min_ts = start[i];
997         }
998
999         for (int i = 0; i < join_struct.n_threads; i++)
1000         {
1001             uint64_t wake_delay = start[i] - min_ts;
1002             uint64_t elapsed = end[i] - start[i];
1003             if (max < elapsed)
1004                 max = elapsed;
1005             sum += elapsed;
1006             wake += wake_delay;
1007         }
1008         uint64_t seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
1009         uint64_t par_loss = join_struct.n_threads*max - sum;
1010         double efficiency = 0.0;
1011         if (max > 0)
1012             efficiency = sum*100.0/(join_struct.n_threads*max);
1013
1014         const double ts_scale = 1e-6;
1015
1016         // enable this printf to get statistics on each individual join as it occurs
1017 //      printf("join #%3d  seq_loss = %5g   par_loss = %5g  efficiency = %3.0f%%\n", join_id, ts_scale*seq_loss, ts_scale*par_loss, efficiency);
1018
1019         elapsed_total[id] += sum;
1020         wake_total[id] += wake;
1021         seq_loss_total[id] += seq_loss;
1022         par_loss_total[id] += par_loss;
1023
1024         // every 10 seconds, print a summary of the time spent in each type of join
1025         if (GCToOSInterface::GetLowPrecisionTimeStamp() - start_tick > 10*1000)
1026         {
1027             printf("**** summary *****\n");
1028             for (int i = 0; i < 16; i++)
1029             {
1030                 printf("join #%3d  elapsed_total = %8g wake_loss = %8g seq_loss = %8g  par_loss = %8g  in_join_total = %8g\n",
1031                    i,
1032                    ts_scale*elapsed_total[i],
1033                    ts_scale*wake_total[i],
1034                    ts_scale*seq_loss_total[i],
1035                    ts_scale*par_loss_total[i],
1036                    ts_scale*in_join_total[i]);
1037                 elapsed_total[i] = wake_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
1038             }
1039             start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
1040         }
1041 #endif //JOIN_STATS
1042
1043         fire_event (join_heap_restart, time_start, type_restart, -1);
1044         assert (join_struct.joined_p);
1045         join_struct.joined_p = FALSE;
1046         join_struct.join_lock = join_struct.n_threads;
1047         dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
1048 //        printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start);
1049         int color = join_struct.lock_color.LoadWithoutBarrier();
1050         join_struct.lock_color = !color;
1051         join_struct.joined_event[color].Set();
1052
1053 //        printf("Set joined_event %d\n", !join_struct.lock_color);
1054
1055         fire_event (join_heap_restart, time_end, type_restart, -1);
1056
1057 #ifdef JOIN_STATS
1058         start[thd] = get_ts();
1059 #endif //JOIN_STATS
1060     }
1061     
1062     BOOL joined()
1063     {
1064         dprintf (JOIN_LOG, ("join%d(%d): joined, join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
1065         return join_struct.joined_p;
1066     }
1067
1068     void r_restart()
1069     {
1070         if (join_struct.n_threads != 1)
1071         {
1072             fire_event (join_heap_r_restart, time_start, type_restart, -1);
1073             join_struct.wait_done = TRUE;
1074             join_struct.joined_event[first_thread_arrived].Set();
1075             fire_event (join_heap_r_restart, time_end, type_restart, -1);
1076         }
1077     }
1078
1079     void r_init()
1080     {
1081         if (join_struct.n_threads != 1)
1082         {
1083             join_struct.r_join_lock = join_struct.n_threads;
1084             join_struct.wait_done = FALSE;
1085             join_struct.joined_event[first_thread_arrived].Reset();
1086         }
1087     }
1088 };
1089
1090 t_join gc_t_join;
1091
1092 #ifdef BACKGROUND_GC
1093 t_join bgc_t_join;
1094 #endif //BACKGROUND_GC
1095
1096 #endif //MULTIPLE_HEAPS
1097
1098 #define spin_and_switch(count_to_spin, expr) \
1099 { \
1100     for (int j = 0; j < count_to_spin; j++) \
1101     { \
1102         if (expr) \
1103         { \
1104             break;\
1105         } \
1106         YieldProcessor(); \
1107     } \
1108     if (!(expr)) \
1109     { \
1110         GCToOSInterface::YieldThread(0); \
1111     } \
1112 }
1113
1114 #ifndef DACCESS_COMPILE
1115 #ifdef BACKGROUND_GC
1116
1117 #define max_pending_allocs 64
1118
1119 class exclusive_sync
1120 {
1121     // TODO - verify that this is the right syntax for Volatile.
1122     VOLATILE(uint8_t*) rwp_object;
1123     VOLATILE(int32_t) needs_checking;
1124     
1125     int spin_count;
1126
1127     uint8_t cache_separator[HS_CACHE_LINE_SIZE - sizeof (int) - sizeof (int32_t)];
1128
1129     // TODO - perhaps each object should be on its own cache line...
1130     VOLATILE(uint8_t*) alloc_objects[max_pending_allocs];
1131
1132     int find_free_index ()
1133     {
1134         for (int i = 0; i < max_pending_allocs; i++)
1135         {
1136             if (alloc_objects [i] == (uint8_t*)0)
1137             {
1138                 return i;
1139             }
1140         }
1141  
1142         return -1;
1143     }
1144
1145 public:
1146     void init()
1147     {
1148         spin_count = 32 * (g_num_processors - 1);
1149         rwp_object = 0;
1150         needs_checking = 0;
1151         for (int i = 0; i < max_pending_allocs; i++)
1152         {
1153             alloc_objects [i] = (uint8_t*)0;
1154         }
1155     }
1156
1157     void check()
1158     {
1159         for (int i = 0; i < max_pending_allocs; i++)
1160         {
1161             if (alloc_objects [i] != (uint8_t*)0)
1162             {
1163                 GCToOSInterface::DebugBreak();
1164             }
1165         }
1166     }
1167
1168     void bgc_mark_set (uint8_t* obj)
1169     {
1170         dprintf (3, ("cm: probing %Ix", obj));
1171 retry:
1172         if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
1173         {
1174             // If we spend too much time spending all the allocs,
1175             // consider adding a high water mark and scan up
1176             // to that; we'll need to interlock in done when
1177             // we update the high watermark.
1178             for (int i = 0; i < max_pending_allocs; i++)
1179             {
1180                 if (obj == alloc_objects[i])
1181                 {
1182                     needs_checking = 0;
1183                     dprintf (3, ("cm: will spin"));
1184                     spin_and_switch (spin_count, (obj != alloc_objects[i]));
1185                     goto retry;
1186                 }
1187             }
1188
1189             rwp_object = obj;
1190             needs_checking = 0;
1191             dprintf (3, ("cm: set %Ix", obj));
1192             return;
1193         }
1194         else
1195         {
1196             spin_and_switch (spin_count, (needs_checking == 0));
1197             goto retry;
1198         }
1199     }
1200
1201     int loh_alloc_set (uint8_t* obj)
1202     {
1203         if (!gc_heap::cm_in_progress)
1204         {
1205             return -1;
1206         }
1207
1208 retry:
1209         dprintf (3, ("loh alloc: probing %Ix", obj));
1210
1211         if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
1212         {
1213             if (obj == rwp_object)
1214             {
1215                 needs_checking = 0;
1216                 spin_and_switch (spin_count, (obj != rwp_object));
1217                 goto retry;
1218             }
1219             else
1220             {
1221                 int cookie = find_free_index();
1222
1223                 if (cookie != -1)
1224                 {
1225                     alloc_objects[cookie] = obj;
1226                     needs_checking = 0;
1227                     //if (cookie >= 4)
1228                     //{
1229                     //    GCToOSInterface::DebugBreak();
1230                     //}
1231
1232                     dprintf (3, ("loh alloc: set %Ix at %d", obj, cookie));
1233                     return cookie;
1234                 } 
1235                 else 
1236                 {
1237                     needs_checking = 0;
1238                     dprintf (3, ("loh alloc: setting %Ix will spin to acquire a free index", obj));
1239                     spin_and_switch (spin_count, (find_free_index () != -1));
1240                     goto retry;
1241                 }
1242             }
1243         }
1244         else
1245         {
1246             dprintf (3, ("loh alloc: will spin on checking %Ix", obj));
1247             spin_and_switch (spin_count, (needs_checking == 0));
1248             goto retry;
1249         }
1250     }
1251
1252     void bgc_mark_done ()
1253     {
1254         dprintf (3, ("cm: release lock on %Ix", (uint8_t *)rwp_object));
1255         rwp_object = 0;
1256     }
1257
1258     void loh_alloc_done_with_index (int index)
1259     {
1260         dprintf (3, ("loh alloc: release lock on %Ix based on %d", (uint8_t *)alloc_objects[index], index));
1261         assert ((index >= 0) && (index < max_pending_allocs)); 
1262         alloc_objects[index] = (uint8_t*)0;
1263     }
1264
1265     void loh_alloc_done (uint8_t* obj)
1266     {
1267 #ifdef BACKGROUND_GC
1268         if (!gc_heap::cm_in_progress)
1269         {
1270             return;
1271         }
1272
1273         for (int i = 0; i < max_pending_allocs; i++)
1274         {
1275             if (alloc_objects [i] == obj)
1276             {
1277                 dprintf (3, ("loh alloc: release lock on %Ix at %d", (uint8_t *)alloc_objects[i], i));
1278                 alloc_objects[i] = (uint8_t*)0;
1279                 return;
1280             }
1281         }
1282 #endif //BACKGROUND_GC
1283     }
1284 };
1285
1286 // Note that this class was written assuming just synchronization between
1287 // one background GC thread and multiple user threads that might request 
1288 // an FGC - it does not take into account what kind of locks the multiple
1289 // user threads might be holding at the time (eg, there could only be one
1290 // user thread requesting an FGC because it needs to take gc_lock first)
1291 // so you'll see checks that may not be necessary if you take those conditions
1292 // into consideration.
1293 //
1294 // With the introduction of Server Background GC we no longer use this
1295 // class to do synchronization between FGCs and BGC.
1296 class recursive_gc_sync
1297 {
1298     static VOLATILE(int32_t) foreground_request_count;//initial state 0
1299     static VOLATILE(BOOL) gc_background_running; //initial state FALSE
1300     static VOLATILE(int32_t) foreground_count; // initial state 0;
1301     static VOLATILE(uint32_t) foreground_gate; // initial state FALSE;
1302     static GCEvent foreground_complete;//Auto Reset
1303     static GCEvent foreground_allowed;//Auto Reset
1304 public:
1305     static void begin_background();
1306     static void end_background();
1307     static void begin_foreground();
1308     static void end_foreground();
1309     BOOL allow_foreground ();
1310     static BOOL init();
1311     static void shutdown();
1312     static BOOL background_running_p() {return gc_background_running;}
1313 };
1314
1315 VOLATILE(int32_t) recursive_gc_sync::foreground_request_count = 0;//initial state 0
1316 VOLATILE(int32_t) recursive_gc_sync::foreground_count = 0; // initial state 0;
1317 VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE
1318 VOLATILE(uint32_t) recursive_gc_sync::foreground_gate = 0;
1319 GCEvent recursive_gc_sync::foreground_complete;//Auto Reset
1320 GCEvent recursive_gc_sync::foreground_allowed;//Manual Reset
1321
1322 BOOL recursive_gc_sync::init ()
1323 {
1324     foreground_request_count = 0;
1325     foreground_count = 0;
1326     gc_background_running = FALSE;
1327     foreground_gate = 0;
1328
1329     if (!foreground_complete.CreateOSAutoEventNoThrow(FALSE))
1330     {
1331         goto error;
1332     }
1333     if (!foreground_allowed.CreateManualEventNoThrow(FALSE))
1334     {
1335         goto error;
1336     }
1337     return TRUE;
1338
1339 error:
1340     shutdown();
1341     return FALSE;
1342
1343 }
1344
1345 void recursive_gc_sync::shutdown()
1346 {
1347     if (foreground_complete.IsValid())
1348         foreground_complete.CloseEvent();
1349     if (foreground_allowed.IsValid())
1350         foreground_allowed.CloseEvent();
1351 }
1352
1353 void recursive_gc_sync::begin_background()
1354 {
1355     dprintf (2, ("begin background"));
1356     foreground_request_count = 1;
1357     foreground_count = 1;
1358     foreground_allowed.Reset();
1359     gc_background_running = TRUE;
1360 }
1361 void recursive_gc_sync::end_background()
1362 {
1363     dprintf (2, ("end background"));
1364     gc_background_running = FALSE;
1365     foreground_gate = 1;
1366     foreground_allowed.Set();
1367 }
1368
1369 void recursive_gc_sync::begin_foreground()
1370 {
1371     dprintf (2, ("begin_foreground"));
1372
1373     bool cooperative_mode = false;
1374     if (gc_background_running)
1375     {
1376         gc_heap::fire_alloc_wait_event_begin (awr_fgc_wait_for_bgc);
1377         gc_heap::alloc_wait_event_p = TRUE;
1378
1379 try_again_top:
1380
1381         Interlocked::Increment (&foreground_request_count);
1382
1383 try_again_no_inc:
1384         dprintf(2, ("Waiting sync gc point"));
1385         assert (foreground_allowed.IsValid());
1386         assert (foreground_complete.IsValid());
1387
1388         cooperative_mode = gc_heap::enable_preemptive ();
1389
1390         foreground_allowed.Wait(INFINITE, FALSE);
1391
1392         dprintf(2, ("Waiting sync gc point is done"));
1393
1394         gc_heap::disable_preemptive (cooperative_mode);
1395
1396         if (foreground_gate)
1397         {
1398             Interlocked::Increment (&foreground_count);
1399             dprintf (2, ("foreground_count: %d", (int32_t)foreground_count));
1400             if (foreground_gate)
1401             {
1402                 gc_heap::settings.concurrent = FALSE;
1403                 return;
1404             }
1405             else
1406             {
1407                 end_foreground();
1408                 goto try_again_top;
1409             }
1410         }
1411         else
1412         {
1413             goto try_again_no_inc;
1414         }
1415     }
1416 }
1417
1418 void recursive_gc_sync::end_foreground()
1419 {
1420     dprintf (2, ("end_foreground"));
1421     if (gc_background_running)
1422     {
1423         Interlocked::Decrement (&foreground_request_count);
1424         dprintf (2, ("foreground_count before decrement: %d", (int32_t)foreground_count));
1425         if (Interlocked::Decrement (&foreground_count) == 0)
1426         {
1427             //c_write ((BOOL*)&foreground_gate, 0);
1428             // TODO - couldn't make the syntax work with Volatile<T>
1429             foreground_gate = 0;
1430             if (foreground_count == 0)
1431             {
1432                 foreground_allowed.Reset ();
1433                 dprintf(2, ("setting foreground complete event"));
1434                 foreground_complete.Set();
1435             }
1436         }
1437     }
1438 }
1439
1440 inline
1441 BOOL recursive_gc_sync::allow_foreground()
1442 {
1443     assert (gc_heap::settings.concurrent);
1444     dprintf (100, ("enter allow_foreground, f_req_count: %d, f_count: %d",
1445                    (int32_t)foreground_request_count, (int32_t)foreground_count));
1446
1447     BOOL did_fgc = FALSE;
1448
1449     //if we have suspended the EE, just return because
1450     //some thread could be waiting on this to proceed.
1451     if (!GCHeap::GcInProgress)
1452     {
1453         //TODO BACKGROUND_GC This is to stress the concurrency between
1454         //background and foreground
1455 //        gc_heap::disallow_new_allocation (0);
1456
1457         //GCToOSInterface::YieldThread(0);
1458
1459         //END of TODO
1460         if (foreground_request_count != 0)
1461         {
1462             //foreground wants to run
1463             //save the important settings
1464             //TODO BACKGROUND_GC be more selective about the important settings.
1465             gc_mechanisms saved_settings = gc_heap::settings;
1466             do
1467             {
1468                 did_fgc = TRUE;
1469                 //c_write ((BOOL*)&foreground_gate, 1);
1470                 // TODO - couldn't make the syntax work with Volatile<T>
1471                 foreground_gate = 1;
1472                 foreground_allowed.Set ();
1473                 foreground_complete.Wait (INFINITE, FALSE);
1474             }while (/*foreground_request_count ||*/ foreground_gate);
1475
1476             assert (!foreground_gate);
1477
1478             //restore the important settings
1479             gc_heap::settings = saved_settings;
1480             GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
1481             //the background GC shouldn't be using gc_high and gc_low
1482             //gc_low = lowest_address;
1483             //gc_high = highest_address;
1484         }
1485
1486         //TODO BACKGROUND_GC This is to stress the concurrency between
1487         //background and foreground
1488 //        gc_heap::allow_new_allocation (0);
1489         //END of TODO
1490     }
1491
1492     dprintf (100, ("leave allow_foreground"));
1493     assert (gc_heap::settings.concurrent);
1494     return did_fgc;
1495 }
1496
1497 #endif //BACKGROUND_GC
1498 #endif //DACCESS_COMPILE
1499
1500
1501 #if  defined(COUNT_CYCLES)
1502 #ifdef _MSC_VER
1503 #pragma warning(disable:4035)
1504 #endif //_MSC_VER
1505
1506 static
1507 unsigned        GetCycleCount32()        // enough for about 40 seconds
1508 {
1509 __asm   push    EDX
1510 __asm   _emit   0x0F
1511 __asm   _emit   0x31
1512 __asm   pop     EDX
1513 };
1514
1515 #pragma warning(default:4035)
1516
1517 #endif //COUNT_CYCLES
1518
1519 #ifdef TIME_GC
1520 int mark_time, plan_time, sweep_time, reloc_time, compact_time;
1521 #endif //TIME_GC
1522
1523 #ifndef MULTIPLE_HEAPS
1524
1525 #endif // MULTIPLE_HEAPS
1526
1527 void reset_memory (uint8_t* o, size_t sizeo);
1528
1529 #ifdef WRITE_WATCH
1530
1531 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1532 static bool virtual_alloc_hardware_write_watch = false;
1533 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1534
1535 static bool hardware_write_watch_capability = false;
1536
1537 #ifndef DACCESS_COMPILE
1538
1539 //check if the write watch APIs are supported.
1540
1541 void hardware_write_watch_api_supported()
1542 {
1543     if (GCToOSInterface::SupportsWriteWatch())
1544     {
1545         hardware_write_watch_capability = true;
1546         dprintf (2, ("WriteWatch supported"));
1547     }
1548     else
1549     {
1550         dprintf (2,("WriteWatch not supported"));
1551     }
1552 }
1553
1554 #endif //!DACCESS_COMPILE
1555
1556 inline bool can_use_hardware_write_watch()
1557 {
1558     return hardware_write_watch_capability;
1559 }
1560
1561 inline bool can_use_write_watch_for_gc_heap()
1562 {
1563 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1564     return true;
1565 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1566     return can_use_hardware_write_watch();
1567 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1568 }
1569
1570 inline bool can_use_write_watch_for_card_table()
1571 {
1572 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
1573     return true;
1574 #else
1575     return can_use_hardware_write_watch();
1576 #endif
1577 }
1578
1579 #else
1580 #define mem_reserve (MEM_RESERVE)
1581 #endif //WRITE_WATCH
1582
1583 //check if the low memory notification is supported
1584
1585 #ifndef DACCESS_COMPILE
1586
1587 void WaitLongerNoInstru (int i)
1588 {
1589     // every 8th attempt:
1590     bool bToggleGC = GCToEEInterface::EnablePreemptiveGC();
1591
1592     // if we're waiting for gc to finish, we should block immediately
1593     if (g_fSuspensionPending == 0)
1594     {
1595         if  (g_num_processors > 1)
1596         {
1597             YieldProcessor();           // indicate to the processor that we are spining
1598             if  (i & 0x01f)
1599                 GCToOSInterface::YieldThread (0);
1600             else
1601                 GCToOSInterface::Sleep (5);
1602         }
1603         else
1604             GCToOSInterface::Sleep (5);
1605     }
1606
1607     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1608     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1609     // It is important that the thread is going to wait for GC.  Otherwise the thread
1610     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD.
1611     if (bToggleGC)
1612     {
1613 #ifdef _DEBUG
1614         // In debug builds, all enter_spin_lock operations go through this code.  If a GC has
1615         // started, it is important to block until the GC thread calls set_gc_done (since it is
1616         // guaranteed to have cleared g_TrapReturningThreads by this point).  This avoids livelock
1617         // conditions which can otherwise occur if threads are allowed to spin in this function
1618         // (and therefore starve the GC thread) between the point when the GC thread sets the
1619         // WaitForGC event and the point when the GC thread clears g_TrapReturningThreads.
1620         if (gc_heap::gc_started)
1621         {
1622             gc_heap::wait_for_gc_done();
1623         }
1624 #endif // _DEBUG
1625         GCToEEInterface::DisablePreemptiveGC();
1626     }
1627     else if (g_fSuspensionPending > 0)
1628     {
1629         g_theGCHeap->WaitUntilGCComplete();
1630     }
1631 }
1632
1633 inline
1634 static void safe_switch_to_thread()
1635 {
1636     bool cooperative_mode = gc_heap::enable_preemptive();
1637
1638     GCToOSInterface::YieldThread(0);
1639
1640     gc_heap::disable_preemptive(cooperative_mode);
1641 }
1642
1643 //
1644 // We need the following methods to have volatile arguments, so that they can accept
1645 // raw pointers in addition to the results of the & operator on Volatile<T>.
1646 //
1647 inline
1648 static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1649 {
1650 retry:
1651
1652     if (Interlocked::CompareExchange(lock, 0, -1) >= 0)
1653     {
1654         unsigned int i = 0;
1655         while (VolatileLoad(lock) >= 0)
1656         {
1657             if ((++i & 7) && !IsGCInProgress())
1658             {
1659                 if  (g_num_processors > 1)
1660                 {
1661 #ifndef MULTIPLE_HEAPS
1662                     int spin_count = 1024 * g_num_processors;
1663 #else //!MULTIPLE_HEAPS
1664                     int spin_count = 32 * g_num_processors;
1665 #endif //!MULTIPLE_HEAPS
1666                     for (int j = 0; j < spin_count; j++)
1667                     {
1668                         if  (VolatileLoad(lock) < 0 || IsGCInProgress())
1669                             break;
1670                         YieldProcessor();           // indicate to the processor that we are spining
1671                     }
1672                     if  (VolatileLoad(lock) >= 0 && !IsGCInProgress())
1673                     {
1674                         safe_switch_to_thread();
1675                     }
1676                 }
1677                 else
1678                 {
1679                     safe_switch_to_thread();
1680                 }
1681             }
1682             else
1683             {
1684                 WaitLongerNoInstru(i);
1685             }
1686         }
1687         goto retry;
1688     }
1689 }
1690
1691 inline
1692 static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) int32_t* lock)
1693 {
1694     return (Interlocked::CompareExchange(&*lock, 0, -1) < 0);
1695 }
1696
1697 inline
1698 static void leave_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1699 {
1700     VolatileStore<int32_t>((int32_t*)lock, -1);
1701 }
1702
1703 #ifdef _DEBUG
1704
1705 inline
1706 static void enter_spin_lock(GCSpinLock *pSpinLock)
1707 {
1708     enter_spin_lock_noinstru(&pSpinLock->lock);
1709     assert (pSpinLock->holding_thread == (Thread*)-1);
1710     pSpinLock->holding_thread = GCToEEInterface::GetThread();
1711 }
1712
1713 inline
1714 static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock)
1715 {
1716     BOOL ret = try_enter_spin_lock_noinstru(&pSpinLock->lock);
1717     if (ret)
1718         pSpinLock->holding_thread = GCToEEInterface::GetThread();
1719     return ret;
1720 }
1721
1722 inline
1723 static void leave_spin_lock(GCSpinLock *pSpinLock)
1724 {
1725     bool gc_thread_p = GCToEEInterface::WasCurrentThreadCreatedByGC();
1726 //    _ASSERTE((pSpinLock->holding_thread == GCToEEInterface::GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p);
1727     pSpinLock->released_by_gc_p = gc_thread_p;
1728     pSpinLock->holding_thread = (Thread*) -1;
1729     if (pSpinLock->lock != -1)
1730         leave_spin_lock_noinstru(&pSpinLock->lock);
1731 }
1732
1733 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) \
1734     _ASSERTE((pSpinLock)->holding_thread == GCToEEInterface::GetThread());
1735
1736 #define ASSERT_NOT_HOLDING_SPIN_LOCK(pSpinLock) \
1737     _ASSERTE((pSpinLock)->holding_thread != GCToEEInterface::GetThread());
1738
1739 #else //_DEBUG
1740
1741 //In the concurrent version, the Enable/DisablePreemptiveGC is optional because
1742 //the gc thread call WaitLonger.
1743 void WaitLonger (int i
1744 #ifdef SYNCHRONIZATION_STATS
1745     , GCSpinLock* spin_lock
1746 #endif //SYNCHRONIZATION_STATS
1747     )
1748 {
1749 #ifdef SYNCHRONIZATION_STATS
1750     (spin_lock->num_wait_longer)++;
1751 #endif //SYNCHRONIZATION_STATS
1752
1753     // every 8th attempt:
1754     bool bToggleGC = GCToEEInterface::EnablePreemptiveGC();
1755     assert (bToggleGC);
1756
1757     // if we're waiting for gc to finish, we should block immediately
1758     if (!gc_heap::gc_started)
1759     {
1760 #ifdef SYNCHRONIZATION_STATS
1761         (spin_lock->num_switch_thread_w)++;
1762 #endif //SYNCHRONIZATION_STATS
1763         if  (g_num_processors > 1)
1764         {
1765             YieldProcessor();           // indicate to the processor that we are spining
1766             if  (i & 0x01f)
1767                 GCToOSInterface::YieldThread (0);
1768             else
1769                 GCToOSInterface::Sleep (5);
1770         }
1771         else
1772             GCToOSInterface::Sleep (5);
1773     }
1774
1775     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1776     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1777     // It is important that the thread is going to wait for GC.  Otherwise the thread
1778     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD. 
1779     if (gc_heap::gc_started)
1780     {
1781         gc_heap::wait_for_gc_done();
1782     }
1783
1784     if (bToggleGC)
1785     {
1786 #ifdef SYNCHRONIZATION_STATS
1787         (spin_lock->num_disable_preemptive_w)++;
1788 #endif //SYNCHRONIZATION_STATS
1789         GCToEEInterface::DisablePreemptiveGC();
1790     }
1791 }
1792
1793 inline
1794 static void enter_spin_lock (GCSpinLock* spin_lock)
1795 {
1796 retry:
1797
1798     if (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) >= 0)
1799     {
1800         unsigned int i = 0;
1801         while (spin_lock->lock >= 0)
1802         {
1803             if ((++i & 7) && !gc_heap::gc_started)
1804             {
1805                 if  (g_num_processors > 1)
1806                 {
1807 #ifndef MULTIPLE_HEAPS
1808                     int spin_count = 1024 * g_num_processors;
1809 #else //!MULTIPLE_HEAPS
1810                     int spin_count = 32 * g_num_processors;
1811 #endif //!MULTIPLE_HEAPS
1812                     for (int j = 0; j < spin_count; j++)
1813                     {
1814                         if  (spin_lock->lock < 0 || gc_heap::gc_started)
1815                             break;
1816                         YieldProcessor();           // indicate to the processor that we are spining
1817                     }
1818                     if  (spin_lock->lock >= 0 && !gc_heap::gc_started)
1819                     {
1820 #ifdef SYNCHRONIZATION_STATS
1821                         (spin_lock->num_switch_thread)++;
1822 #endif //SYNCHRONIZATION_STATS
1823                         bool cooperative_mode = gc_heap::enable_preemptive ();
1824
1825                         GCToOSInterface::YieldThread(0);
1826
1827                         gc_heap::disable_preemptive (cooperative_mode);
1828                     }
1829                 }
1830                 else
1831                     GCToOSInterface::YieldThread(0);
1832             }
1833             else
1834             {
1835                 WaitLonger(i
1836 #ifdef SYNCHRONIZATION_STATS
1837                         , spin_lock
1838 #endif //SYNCHRONIZATION_STATS
1839                     );
1840             }
1841         }
1842         goto retry;
1843     }
1844 }
1845
1846 inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock)
1847 {
1848     return (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) < 0);
1849 }
1850
1851 inline
1852 static void leave_spin_lock (GCSpinLock * spin_lock)
1853 {
1854     spin_lock->lock = -1;
1855 }
1856
1857 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock)
1858
1859 #endif //_DEBUG
1860
1861 bool gc_heap::enable_preemptive ()
1862 {
1863     return GCToEEInterface::EnablePreemptiveGC();
1864 }
1865
1866 void gc_heap::disable_preemptive (bool restore_cooperative)
1867 {
1868     if (restore_cooperative)
1869     {
1870         GCToEEInterface::DisablePreemptiveGC();
1871     }
1872 }
1873
1874 #endif // !DACCESS_COMPILE
1875
1876 typedef void **  PTR_PTR;
1877 //This function clears a piece of memory
1878 // size has to be Dword aligned
1879
1880 inline
1881 void memclr ( uint8_t* mem, size_t size)
1882 {
1883     dprintf (3, ("MEMCLR: %Ix, %d", mem, size));
1884     assert ((size & (sizeof(PTR_PTR)-1)) == 0);
1885     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1886
1887 #if 0
1888     // The compiler will recognize this pattern and replace it with memset call. We can as well just call 
1889     // memset directly to make it obvious what's going on.
1890     PTR_PTR m = (PTR_PTR) mem;
1891     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
1892         *(m++) = 0;
1893 #endif
1894
1895     memset (mem, 0, size);
1896 }
1897
1898 void memcopy (uint8_t* dmem, uint8_t* smem, size_t size)
1899 {
1900     const size_t sz4ptr = sizeof(PTR_PTR)*4;
1901     const size_t sz2ptr = sizeof(PTR_PTR)*2;
1902     const size_t sz1ptr = sizeof(PTR_PTR)*1;
1903
1904     // size must be a multiple of the pointer size
1905     assert ((size & (sizeof (PTR_PTR)-1)) == 0);
1906     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1907
1908     // copy in groups of four pointer sized things at a time
1909     if (size >= sz4ptr)
1910     {
1911         do
1912         {
1913             ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1914             ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1915             ((PTR_PTR)dmem)[2] = ((PTR_PTR)smem)[2];
1916             ((PTR_PTR)dmem)[3] = ((PTR_PTR)smem)[3];
1917             dmem += sz4ptr;
1918             smem += sz4ptr;
1919         }
1920         while ((size -= sz4ptr) >= sz4ptr);
1921     }
1922
1923     // still two pointer sized things or more left to copy?
1924     if (size & sz2ptr)
1925     {
1926         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1927         ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1928         dmem += sz2ptr;
1929         smem += sz2ptr;
1930     }
1931
1932     // still one pointer sized thing left to copy?
1933     if (size & sz1ptr)
1934     {
1935         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1936         // dmem += sz1ptr;
1937         // smem += sz1ptr;
1938     }
1939
1940 }
1941
1942 inline
1943 ptrdiff_t round_down (ptrdiff_t add, int pitch)
1944 {
1945     return ((add / pitch) * pitch);
1946 }
1947
1948 #if defined(FEATURE_STRUCTALIGN) && defined(RESPECT_LARGE_ALIGNMENT)
1949 // FEATURE_STRUCTALIGN allows the compiler to dictate the alignment,
1950 // i.e, if a larger alignment matters or is beneficial, the compiler
1951 // generated info tells us so.  RESPECT_LARGE_ALIGNMENT is just the
1952 // converse - it's a heuristic for the GC to use a larger alignment.
1953 #error FEATURE_STRUCTALIGN should imply !RESPECT_LARGE_ALIGNMENT
1954 #endif
1955
1956 #if defined(FEATURE_STRUCTALIGN) && defined(FEATURE_LOH_COMPACTION)
1957 #error FEATURE_STRUCTALIGN and FEATURE_LOH_COMPACTION are mutually exclusive
1958 #endif
1959
1960 #if defined(GROWABLE_SEG_MAPPING_TABLE) && !defined(SEG_MAPPING_TABLE)
1961 #error if GROWABLE_SEG_MAPPING_TABLE is defined, SEG_MAPPING_TABLE must be defined
1962 #endif
1963
1964 inline
1965 BOOL same_large_alignment_p (uint8_t* p1, uint8_t* p2)
1966 {
1967 #ifdef RESPECT_LARGE_ALIGNMENT
1968     return ((((size_t)p1 ^ (size_t)p2) & 7) == 0);
1969 #else
1970     UNREFERENCED_PARAMETER(p1);
1971     UNREFERENCED_PARAMETER(p2);
1972     return TRUE;
1973 #endif //RESPECT_LARGE_ALIGNMENT
1974 }
1975
1976 inline 
1977 size_t switch_alignment_size (BOOL already_padded_p)
1978 {
1979     if (already_padded_p)
1980         return DATA_ALIGNMENT;
1981     else
1982         return (Align (min_obj_size) +((Align (min_obj_size)&DATA_ALIGNMENT)^DATA_ALIGNMENT));
1983 }
1984
1985
1986 #ifdef FEATURE_STRUCTALIGN
1987 void set_node_aligninfo (uint8_t *node, int requiredAlignment, ptrdiff_t pad);
1988 void clear_node_aligninfo (uint8_t *node);
1989 #else // FEATURE_STRUCTALIGN
1990 #define node_realigned(node)    (((plug_and_reloc*)(node))[-1].reloc & 1)
1991 void set_node_realigned (uint8_t* node);
1992 void clear_node_realigned(uint8_t* node);
1993 #endif // FEATURE_STRUCTALIGN
1994
1995 inline
1996 size_t AlignQword (size_t nbytes)
1997 {
1998 #ifdef FEATURE_STRUCTALIGN
1999     // This function is used to align everything on the large object
2000     // heap to an 8-byte boundary, to reduce the number of unaligned
2001     // accesses to (say) arrays of doubles.  With FEATURE_STRUCTALIGN,
2002     // the compiler dictates the optimal alignment instead of having
2003     // a heuristic in the GC.
2004     return Align (nbytes);
2005 #else // FEATURE_STRUCTALIGN
2006     return (nbytes + 7) & ~7;
2007 #endif // FEATURE_STRUCTALIGN
2008 }
2009
2010 inline
2011 BOOL Aligned (size_t n)
2012 {
2013     return (n & ALIGNCONST) == 0;
2014 }
2015
2016 #define OBJECT_ALIGNMENT_OFFSET (sizeof(MethodTable *))
2017
2018 #ifdef FEATURE_STRUCTALIGN
2019 #define MAX_STRUCTALIGN OS_PAGE_SIZE
2020 #else // FEATURE_STRUCTALIGN
2021 #define MAX_STRUCTALIGN 0
2022 #endif // FEATURE_STRUCTALIGN
2023
2024 #ifdef FEATURE_STRUCTALIGN
2025 inline
2026 ptrdiff_t AdjustmentForMinPadSize(ptrdiff_t pad, int requiredAlignment)
2027 {
2028     // The resulting alignpad must be either 0 or at least min_obj_size.
2029     // Note that by computing the following difference on unsigned types,
2030     // we can do the range check 0 < alignpad < min_obj_size with a
2031     // single conditional branch.
2032     if ((size_t)(pad - DATA_ALIGNMENT) < Align (min_obj_size) - DATA_ALIGNMENT)
2033     {
2034         return requiredAlignment;
2035     }
2036     return 0;
2037 }
2038
2039 inline
2040 uint8_t* StructAlign (uint8_t* origPtr, int requiredAlignment, ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
2041 {
2042     // required alignment must be a power of two
2043     _ASSERTE(((size_t)origPtr & ALIGNCONST) == 0);
2044     _ASSERTE(((requiredAlignment - 1) & requiredAlignment) == 0);
2045     _ASSERTE(requiredAlignment >= sizeof(void *));
2046     _ASSERTE(requiredAlignment <= MAX_STRUCTALIGN);
2047
2048     // When this method is invoked for individual objects (i.e., alignmentOffset
2049     // is just the size of the PostHeader), what needs to be aligned when
2050     // we're done is the pointer to the payload of the object (which means
2051     // the actual resulting object pointer is typically not aligned).
2052
2053     uint8_t* result = (uint8_t*)Align ((size_t)origPtr + alignmentOffset, requiredAlignment-1) - alignmentOffset;
2054     ptrdiff_t alignpad = result - origPtr;
2055
2056     return result + AdjustmentForMinPadSize (alignpad, requiredAlignment);
2057 }
2058
2059 inline
2060 ptrdiff_t ComputeStructAlignPad (uint8_t* plug, int requiredAlignment, size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
2061 {
2062     return StructAlign (plug, requiredAlignment, alignmentOffset) - plug;
2063 }
2064
2065 BOOL IsStructAligned (uint8_t *ptr, int requiredAlignment)
2066 {
2067     return StructAlign (ptr, requiredAlignment) == ptr;
2068 }
2069
2070 inline
2071 ptrdiff_t ComputeMaxStructAlignPad (int requiredAlignment)
2072 {
2073     if (requiredAlignment == DATA_ALIGNMENT)
2074         return 0;
2075     // Since a non-zero alignment padding cannot be less than min_obj_size (so we can fit the
2076     // alignment padding object), the worst-case alignment padding is correspondingly larger
2077     // than the required alignment.
2078     return requiredAlignment + Align (min_obj_size) - DATA_ALIGNMENT;
2079 }
2080
2081 inline
2082 ptrdiff_t ComputeMaxStructAlignPadLarge (int requiredAlignment)
2083 {
2084     if (requiredAlignment <= get_alignment_constant (TRUE)+1)
2085         return 0;
2086     // This is the same as ComputeMaxStructAlignPad, except that in addition to leaving space
2087     // for padding before the actual object, it also leaves space for filling a gap after the
2088     // actual object.  This is needed on the large object heap, as the outer allocation functions
2089     // don't operate on an allocation context (which would have left space for the final gap).
2090     return requiredAlignment + Align (min_obj_size) * 2 - DATA_ALIGNMENT;
2091 }
2092
2093 uint8_t* gc_heap::pad_for_alignment (uint8_t* newAlloc, int requiredAlignment, size_t size, alloc_context* acontext)
2094 {
2095     uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2096     if (alignedPtr != newAlloc) {
2097         make_unused_array (newAlloc, alignedPtr - newAlloc);
2098     }
2099     acontext->alloc_ptr = alignedPtr + Align (size);
2100     return alignedPtr;
2101 }
2102
2103 uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignment, size_t size)
2104 {
2105     uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2106     if (alignedPtr != newAlloc) {
2107         make_unused_array (newAlloc, alignedPtr - newAlloc);
2108     }
2109     if (alignedPtr < newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment)) {
2110         make_unused_array (alignedPtr + AlignQword (size), newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment) - alignedPtr);
2111     }
2112     return alignedPtr;
2113 }
2114 #else // FEATURE_STRUCTALIGN
2115 #define ComputeMaxStructAlignPad(requiredAlignment) 0
2116 #define ComputeMaxStructAlignPadLarge(requiredAlignment) 0
2117 #endif // FEATURE_STRUCTALIGN
2118
2119 //CLR_SIZE  is the max amount of bytes from gen0 that is set to 0 in one chunk
2120 #ifdef SERVER_GC
2121 #define CLR_SIZE ((size_t)(8*1024))
2122 #else //SERVER_GC
2123 #define CLR_SIZE ((size_t)(8*1024))
2124 #endif //SERVER_GC
2125
2126 #define END_SPACE_AFTER_GC (LARGE_OBJECT_SIZE + MAX_STRUCTALIGN)
2127
2128 #ifdef BACKGROUND_GC
2129 #define SEGMENT_INITIAL_COMMIT (2*OS_PAGE_SIZE)
2130 #else
2131 #define SEGMENT_INITIAL_COMMIT (OS_PAGE_SIZE)
2132 #endif //BACKGROUND_GC
2133
2134 #ifdef SERVER_GC
2135
2136 #ifdef BIT64
2137
2138 #define INITIAL_ALLOC ((size_t)((size_t)4*1024*1024*1024))
2139 #define LHEAP_ALLOC   ((size_t)(1024*1024*256))
2140
2141 #else
2142
2143 #define INITIAL_ALLOC ((size_t)(1024*1024*64))
2144 #define LHEAP_ALLOC   ((size_t)(1024*1024*32))
2145
2146 #endif  // BIT64
2147
2148 #else //SERVER_GC
2149
2150 #ifdef BIT64
2151
2152 #define INITIAL_ALLOC ((size_t)(1024*1024*256))
2153 #define LHEAP_ALLOC   ((size_t)(1024*1024*128))
2154
2155 #else
2156
2157 #define INITIAL_ALLOC ((size_t)(1024*1024*16))
2158 #define LHEAP_ALLOC   ((size_t)(1024*1024*16))
2159
2160 #endif  // BIT64
2161
2162 #endif //SERVER_GC
2163
2164 //amount in bytes of the etw allocation tick
2165 const size_t etw_allocation_tick = 100*1024;
2166
2167 const size_t low_latency_alloc = 256*1024;
2168
2169 const size_t fgn_check_quantum = 2*1024*1024;
2170
2171 #ifdef MH_SC_MARK
2172 const int max_snoop_level = 128;
2173 #endif //MH_SC_MARK
2174
2175
2176 #ifdef CARD_BUNDLE
2177 //threshold of heap size to turn on card bundles.
2178 #define SH_TH_CARD_BUNDLE  (40*1024*1024)
2179 #define MH_TH_CARD_BUNDLE  (180*1024*1024)
2180 #endif //CARD_BUNDLE
2181
2182 #define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000
2183
2184 inline
2185 size_t align_on_page (size_t add)
2186 {
2187     return ((add + OS_PAGE_SIZE - 1) & ~((size_t)OS_PAGE_SIZE - 1));
2188 }
2189
2190 inline
2191 uint8_t* align_on_page (uint8_t* add)
2192 {
2193     return (uint8_t*)align_on_page ((size_t) add);
2194 }
2195
2196 inline
2197 size_t align_lower_page (size_t add)
2198 {
2199     return (add & ~((size_t)OS_PAGE_SIZE - 1));
2200 }
2201
2202 inline
2203 uint8_t* align_lower_page (uint8_t* add)
2204 {
2205     return (uint8_t*)align_lower_page ((size_t)add);
2206 }
2207
2208 inline
2209 size_t align_write_watch_lower_page (size_t add)
2210 {
2211     return (add & ~(WRITE_WATCH_UNIT_SIZE - 1));
2212 }
2213
2214 inline
2215 uint8_t* align_write_watch_lower_page (uint8_t* add)
2216 {
2217     return (uint8_t*)align_lower_page ((size_t)add);
2218 }
2219
2220
2221 inline
2222 BOOL power_of_two_p (size_t integer)
2223 {
2224     return !(integer & (integer-1));
2225 }
2226
2227 inline
2228 BOOL oddp (size_t integer)
2229 {
2230     return (integer & 1) != 0;
2231 }
2232
2233 // we only ever use this for WORDs.
2234 size_t logcount (size_t word)
2235 {
2236     //counts the number of high bits in a 16 bit word.
2237     assert (word < 0x10000);
2238     size_t count;
2239     count = (word & 0x5555) + ( (word >> 1 ) & 0x5555);
2240     count = (count & 0x3333) + ( (count >> 2) & 0x3333);
2241     count = (count & 0x0F0F) + ( (count >> 4) & 0x0F0F);
2242     count = (count & 0x00FF) + ( (count >> 8) & 0x00FF);
2243     return count;
2244 }
2245
2246 #ifndef DACCESS_COMPILE
2247
2248 void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_bounds_check)
2249 {
2250     WriteBarrierParameters args = {};
2251     args.operation = WriteBarrierOp::StompResize;
2252     args.is_runtime_suspended = is_runtime_suspended;
2253     args.requires_upper_bounds_check = requires_upper_bounds_check;
2254
2255     args.card_table = g_gc_card_table;
2256 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
2257     args.card_bundle_table = g_gc_card_bundle_table;
2258 #endif
2259
2260     args.lowest_address = g_gc_lowest_address;
2261     args.highest_address = g_gc_highest_address;
2262
2263 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2264     if (SoftwareWriteWatch::IsEnabledForGCHeap())
2265     {
2266         args.write_watch_table = g_gc_sw_ww_table;
2267     }
2268 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2269
2270     GCToEEInterface::StompWriteBarrier(&args);
2271 }
2272
2273 void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2274 {
2275     WriteBarrierParameters args = {};
2276     args.operation = WriteBarrierOp::StompEphemeral;
2277     args.is_runtime_suspended = true;
2278     args.ephemeral_low = ephemeral_low;
2279     args.ephemeral_high = ephemeral_high;
2280     GCToEEInterface::StompWriteBarrier(&args);
2281 }
2282
2283 void stomp_write_barrier_initialize(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2284 {
2285     WriteBarrierParameters args = {};
2286     args.operation = WriteBarrierOp::Initialize;
2287     args.is_runtime_suspended = true;
2288     args.requires_upper_bounds_check = false;
2289     args.card_table = g_gc_card_table;
2290
2291 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
2292     args.card_bundle_table = g_gc_card_bundle_table;
2293 #endif
2294     
2295     args.lowest_address = g_gc_lowest_address;
2296     args.highest_address = g_gc_highest_address;
2297     args.ephemeral_low = ephemeral_low;
2298     args.ephemeral_high = ephemeral_high;
2299     GCToEEInterface::StompWriteBarrier(&args);
2300 }
2301
2302 #endif // DACCESS_COMPILE
2303
2304 //extract the low bits [0,low[ of a uint32_t
2305 #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1))
2306 //extract the high bits [high, 32] of a uint32_t
2307 #define highbits(wrd, bits) ((wrd) & ~((1 << (bits))-1))
2308
2309 // Things we need to manually initialize:
2310 // gen0 min_size - based on cache
2311 // gen0/1 max_size - based on segment size
2312 static static_data static_data_table[latency_level_last - latency_level_first + 1][NUMBERGENERATIONS] = 
2313 {
2314     // latency_level_memory_footprint
2315     {
2316         // gen0
2317         {0, 0, 40000, 0.5f, 9.0f, 20.0f, 1000, 1},
2318         // gen1
2319         {163840, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10},
2320         // gen2
2321         {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100},
2322         // gen3
2323         {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0}
2324     },
2325
2326     // latency_level_balanced
2327     {
2328         // gen0
2329         {0, 0, 40000, 0.5f,
2330 #ifdef MULTIPLE_HEAPS
2331             20.0f, 40.0f,
2332 #else
2333             9.0f, 20.0f,
2334 #endif //MULTIPLE_HEAPS
2335             1000, 1},
2336         // gen1
2337         {9*32*1024, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10},
2338         // gen2
2339         {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100},
2340         // gen3
2341         {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0}
2342     },
2343 };
2344
2345 class mark;
2346 class generation;
2347 class heap_segment;
2348 class CObjectHeader;
2349 class dynamic_data;
2350 class l_heap;
2351 class sorted_table;
2352 class c_synchronize;
2353
2354 #ifdef FEATURE_PREMORTEM_FINALIZATION
2355 #ifndef DACCESS_COMPILE
2356 static
2357 HRESULT AllocateCFinalize(CFinalize **pCFinalize);
2358 #endif //!DACCESS_COMPILE
2359 #endif // FEATURE_PREMORTEM_FINALIZATION
2360
2361 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address);
2362
2363
2364 #ifdef USE_INTROSORT
2365 #define _sort introsort::sort
2366 #else //USE_INTROSORT
2367 #define _sort qsort1
2368 void qsort1(uint8_t** low, uint8_t** high, unsigned int depth);
2369 #endif //USE_INTROSORT
2370
2371 void* virtual_alloc (size_t size);
2372 void virtual_free (void* add, size_t size);
2373
2374 /* per heap static initialization */
2375 #ifdef MARK_ARRAY
2376 #ifndef MULTIPLE_HEAPS
2377 uint32_t*   gc_heap::mark_array;
2378 #endif //MULTIPLE_HEAPS
2379 #endif //MARK_ARRAY
2380
2381 #ifdef MARK_LIST
2382 uint8_t**   gc_heap::g_mark_list;
2383
2384 #ifdef PARALLEL_MARK_LIST_SORT
2385 uint8_t**   gc_heap::g_mark_list_copy;
2386 #endif //PARALLEL_MARK_LIST_SORT
2387
2388 size_t      gc_heap::mark_list_size;
2389 #endif //MARK_LIST
2390
2391 #ifdef SEG_MAPPING_TABLE
2392 seg_mapping* seg_mapping_table;
2393 #endif //SEG_MAPPING_TABLE
2394
2395 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2396 sorted_table* gc_heap::seg_table;
2397 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
2398
2399 #ifdef MULTIPLE_HEAPS
2400 GCEvent     gc_heap::ee_suspend_event;
2401 size_t      gc_heap::min_balance_threshold = 0;
2402 #endif //MULTIPLE_HEAPS
2403
2404 VOLATILE(BOOL) gc_heap::gc_started;
2405
2406 #ifdef MULTIPLE_HEAPS
2407
2408 GCEvent     gc_heap::gc_start_event;
2409
2410 bool        gc_heap::gc_thread_no_affinitize_p = false;
2411
2412 int         gc_heap::n_heaps;
2413
2414 gc_heap**   gc_heap::g_heaps;
2415
2416 size_t*     gc_heap::g_promoted;
2417
2418 #ifdef MH_SC_MARK
2419 int*        gc_heap::g_mark_stack_busy;
2420 #endif //MH_SC_MARK
2421
2422
2423 #ifdef BACKGROUND_GC
2424 size_t*     gc_heap::g_bpromoted;
2425 #endif //BACKGROUND_GC
2426
2427 #else  //MULTIPLE_HEAPS
2428
2429 size_t      gc_heap::g_promoted;
2430
2431 #ifdef BACKGROUND_GC
2432 size_t      gc_heap::g_bpromoted;
2433 #endif //BACKGROUND_GC
2434
2435 #endif //MULTIPLE_HEAPS
2436
2437 size_t      gc_heap::reserved_memory = 0;
2438 size_t      gc_heap::reserved_memory_limit = 0;
2439 BOOL        gc_heap::g_low_memory_status;
2440
2441 #ifndef DACCESS_COMPILE
2442 static gc_reason gc_trigger_reason = reason_empty;
2443 #endif //DACCESS_COMPILE
2444
2445 gc_latency_level gc_heap::latency_level = latency_level_default;
2446
2447 gc_mechanisms  gc_heap::settings;
2448
2449 gc_history_global gc_heap::gc_data_global;
2450
2451 size_t      gc_heap::gc_last_ephemeral_decommit_time = 0;
2452
2453 size_t      gc_heap::gc_gen0_desired_high;
2454
2455 #ifdef SHORT_PLUGS
2456 double       gc_heap::short_plugs_pad_ratio = 0;
2457 #endif //SHORT_PLUGS
2458
2459 #if defined(BIT64)
2460 #define MAX_ALLOWED_MEM_LOAD 85
2461
2462 // consider putting this in dynamic data -
2463 // we may want different values for workstation
2464 // and server GC.
2465 #define MIN_YOUNGEST_GEN_DESIRED (16*1024*1024)
2466
2467 size_t      gc_heap::youngest_gen_desired_th;
2468 #endif //BIT64
2469
2470 uint32_t    gc_heap::last_gc_memory_load = 0;
2471
2472 size_t      gc_heap::last_gc_heap_size = 0;
2473
2474 size_t      gc_heap::last_gc_fragmentation = 0;
2475
2476 uint64_t    gc_heap::mem_one_percent = 0;
2477
2478 uint32_t    gc_heap::high_memory_load_th = 0;
2479
2480 uint64_t    gc_heap::total_physical_mem = 0;
2481
2482 uint64_t    gc_heap::entry_available_physical_mem = 0;
2483
2484 #ifdef BACKGROUND_GC
2485 GCEvent     gc_heap::bgc_start_event;
2486
2487 gc_mechanisms gc_heap::saved_bgc_settings;
2488
2489 GCEvent     gc_heap::background_gc_done_event;
2490
2491 GCEvent     gc_heap::ee_proceed_event;
2492
2493 bool        gc_heap::gc_can_use_concurrent = false;
2494
2495 bool        gc_heap::temp_disable_concurrent_p = false;
2496
2497 uint32_t    gc_heap::cm_in_progress = FALSE;
2498
2499 BOOL        gc_heap::dont_restart_ee_p = FALSE;
2500
2501 BOOL        gc_heap::keep_bgc_threads_p = FALSE;
2502
2503 GCEvent     gc_heap::bgc_threads_sync_event;
2504
2505 BOOL        gc_heap::do_ephemeral_gc_p = FALSE;
2506
2507 BOOL        gc_heap::do_concurrent_p = FALSE;
2508
2509 size_t      gc_heap::ephemeral_fgc_counts[max_generation];
2510
2511 BOOL        gc_heap::alloc_wait_event_p = FALSE;
2512
2513 VOLATILE(c_gc_state) gc_heap::current_c_gc_state = c_gc_state_free;
2514
2515 #endif //BACKGROUND_GC
2516
2517 #ifndef MULTIPLE_HEAPS
2518 #ifdef SPINLOCK_HISTORY
2519 int         gc_heap::spinlock_info_index = 0;
2520 spinlock_info gc_heap::last_spinlock_info[max_saved_spinlock_info + 8];
2521 #endif //SPINLOCK_HISTORY
2522
2523 size_t      gc_heap::fgn_last_alloc = 0;
2524
2525 int         gc_heap::generation_skip_ratio = 100;
2526
2527 uint64_t    gc_heap::loh_alloc_since_cg = 0;
2528
2529 BOOL        gc_heap::elevation_requested = FALSE;
2530
2531 BOOL        gc_heap::last_gc_before_oom = FALSE;
2532
2533 #ifdef BACKGROUND_GC
2534 uint8_t*    gc_heap::background_saved_lowest_address = 0;
2535 uint8_t*    gc_heap::background_saved_highest_address = 0;
2536 uint8_t*    gc_heap::next_sweep_obj = 0;
2537 uint8_t*    gc_heap::current_sweep_pos = 0;
2538 exclusive_sync* gc_heap::bgc_alloc_lock;
2539 #endif //BACKGROUND_GC
2540
2541 oom_history gc_heap::oom_info;
2542
2543 fgm_history gc_heap::fgm_result;
2544
2545 BOOL        gc_heap::ro_segments_in_range;
2546
2547 size_t      gc_heap::gen0_big_free_spaces = 0;
2548
2549 uint8_t*    gc_heap::ephemeral_low;
2550
2551 uint8_t*    gc_heap::ephemeral_high;
2552
2553 uint8_t*    gc_heap::lowest_address;
2554
2555 uint8_t*    gc_heap::highest_address;
2556
2557 BOOL        gc_heap::ephemeral_promotion;
2558
2559 uint8_t*    gc_heap::saved_ephemeral_plan_start[NUMBERGENERATIONS-1];
2560 size_t      gc_heap::saved_ephemeral_plan_start_size[NUMBERGENERATIONS-1];
2561
2562 short*      gc_heap::brick_table;
2563
2564 uint32_t*   gc_heap::card_table;
2565
2566 #ifdef CARD_BUNDLE
2567 uint32_t*   gc_heap::card_bundle_table;
2568 #endif //CARD_BUNDLE
2569
2570 uint8_t*    gc_heap::gc_low;
2571
2572 uint8_t*    gc_heap::gc_high;
2573
2574 uint8_t*    gc_heap::demotion_low;
2575
2576 uint8_t*    gc_heap::demotion_high;
2577
2578 BOOL        gc_heap::demote_gen1_p = TRUE;
2579
2580 uint8_t*    gc_heap::last_gen1_pin_end;
2581
2582 gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons;
2583
2584 size_t      gc_heap::etw_allocation_running_amount[2];
2585
2586 int         gc_heap::gc_policy = 0;
2587
2588 size_t      gc_heap::allocation_running_time;
2589
2590 size_t      gc_heap::allocation_running_amount;
2591
2592 heap_segment* gc_heap::ephemeral_heap_segment = 0;
2593
2594 BOOL        gc_heap::blocking_collection = FALSE;
2595
2596 heap_segment* gc_heap::freeable_large_heap_segment = 0;
2597
2598 size_t      gc_heap::time_bgc_last = 0;
2599
2600 size_t      gc_heap::mark_stack_tos = 0;
2601
2602 size_t      gc_heap::mark_stack_bos = 0;
2603
2604 size_t      gc_heap::mark_stack_array_length = 0;
2605
2606 mark*       gc_heap::mark_stack_array = 0;
2607
2608 BOOL        gc_heap::verify_pinned_queue_p = FALSE;
2609
2610 uint8_t*    gc_heap::oldest_pinned_plug = 0;
2611
2612 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
2613 size_t      gc_heap::num_pinned_objects = 0;
2614 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
2615
2616 #ifdef FEATURE_LOH_COMPACTION
2617 size_t      gc_heap::loh_pinned_queue_tos = 0;
2618
2619 size_t      gc_heap::loh_pinned_queue_bos = 0;
2620
2621 size_t      gc_heap::loh_pinned_queue_length = 0;
2622
2623 mark*       gc_heap::loh_pinned_queue = 0;
2624
2625 BOOL        gc_heap::loh_compacted_p = FALSE;
2626 #endif //FEATURE_LOH_COMPACTION
2627
2628 #ifdef BACKGROUND_GC
2629
2630 EEThreadId  gc_heap::bgc_thread_id;
2631
2632 uint8_t*    gc_heap::background_written_addresses [array_size+2];
2633
2634 heap_segment* gc_heap::freeable_small_heap_segment = 0;
2635
2636 size_t      gc_heap::bgc_overflow_count = 0;
2637
2638 size_t      gc_heap::bgc_begin_loh_size = 0;
2639 size_t      gc_heap::end_loh_size = 0;
2640
2641 uint32_t    gc_heap::bgc_alloc_spin_loh = 0;
2642
2643 size_t      gc_heap::bgc_loh_size_increased = 0;
2644
2645 size_t      gc_heap::bgc_loh_allocated_in_free = 0;
2646
2647 size_t      gc_heap::background_soh_alloc_count = 0;
2648
2649 size_t      gc_heap::background_loh_alloc_count = 0;
2650
2651 uint8_t**   gc_heap::background_mark_stack_tos = 0;
2652
2653 uint8_t**   gc_heap::background_mark_stack_array = 0;
2654
2655 size_t      gc_heap::background_mark_stack_array_length = 0;
2656
2657 uint8_t*    gc_heap::background_min_overflow_address =0;
2658
2659 uint8_t*    gc_heap::background_max_overflow_address =0;
2660
2661 BOOL        gc_heap::processed_soh_overflow_p = FALSE;
2662
2663 uint8_t*    gc_heap::background_min_soh_overflow_address =0;
2664
2665 uint8_t*    gc_heap::background_max_soh_overflow_address =0;
2666
2667 heap_segment* gc_heap::saved_sweep_ephemeral_seg = 0;
2668
2669 uint8_t*    gc_heap::saved_sweep_ephemeral_start = 0;
2670
2671 heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0;
2672
2673 Thread*     gc_heap::bgc_thread = 0;
2674
2675 BOOL        gc_heap::expanded_in_fgc = FALSE;
2676
2677 uint8_t**   gc_heap::c_mark_list = 0;
2678
2679 size_t      gc_heap::c_mark_list_length = 0;
2680
2681 size_t      gc_heap::c_mark_list_index = 0;
2682
2683 gc_history_per_heap gc_heap::bgc_data_per_heap;
2684
2685 BOOL    gc_heap::bgc_thread_running;
2686
2687 CLRCriticalSection gc_heap::bgc_threads_timeout_cs;
2688
2689 GCEvent gc_heap::gc_lh_block_event;
2690
2691 #endif //BACKGROUND_GC
2692
2693 #ifdef MARK_LIST
2694 uint8_t**   gc_heap::mark_list;
2695 uint8_t**   gc_heap::mark_list_index;
2696 uint8_t**   gc_heap::mark_list_end;
2697 #endif //MARK_LIST
2698
2699 #ifdef SNOOP_STATS
2700 snoop_stats_data gc_heap::snoop_stat;
2701 #endif //SNOOP_STATS
2702
2703 uint8_t*    gc_heap::min_overflow_address = MAX_PTR;
2704
2705 uint8_t*    gc_heap::max_overflow_address = 0;
2706
2707 uint8_t*    gc_heap::shigh = 0;
2708
2709 uint8_t*    gc_heap::slow = MAX_PTR;
2710
2711 size_t      gc_heap::ordered_free_space_indices[MAX_NUM_BUCKETS];
2712
2713 size_t      gc_heap::saved_ordered_free_space_indices[MAX_NUM_BUCKETS];
2714
2715 size_t      gc_heap::ordered_plug_indices[MAX_NUM_BUCKETS];
2716
2717 size_t      gc_heap::saved_ordered_plug_indices[MAX_NUM_BUCKETS];
2718
2719 BOOL        gc_heap::ordered_plug_indices_init = FALSE;
2720
2721 BOOL        gc_heap::use_bestfit = FALSE;
2722
2723 uint8_t*    gc_heap::bestfit_first_pin = 0;
2724
2725 BOOL        gc_heap::commit_end_of_seg = FALSE;
2726
2727 size_t      gc_heap::max_free_space_items = 0;
2728
2729 size_t      gc_heap::free_space_buckets = 0;
2730
2731 size_t      gc_heap::free_space_items = 0;
2732
2733 int         gc_heap::trimmed_free_space_index = 0;
2734
2735 size_t      gc_heap::total_ephemeral_plugs = 0;
2736
2737 seg_free_spaces* gc_heap::bestfit_seg = 0;
2738
2739 size_t      gc_heap::total_ephemeral_size = 0;
2740
2741 #ifdef HEAP_ANALYZE
2742
2743 size_t      gc_heap::internal_root_array_length = initial_internal_roots;
2744
2745 uint8_t**   gc_heap::internal_root_array = 0;
2746
2747 size_t      gc_heap::internal_root_array_index = 0;
2748
2749 BOOL        gc_heap::heap_analyze_success = TRUE;
2750
2751 uint8_t*    gc_heap::current_obj = 0;
2752 size_t      gc_heap::current_obj_size = 0;
2753
2754 #endif //HEAP_ANALYZE
2755
2756 #ifdef GC_CONFIG_DRIVEN
2757 size_t gc_heap::interesting_data_per_gc[max_idp_count];
2758 //size_t gc_heap::interesting_data_per_heap[max_idp_count];
2759 //size_t gc_heap::interesting_mechanisms_per_heap[max_im_count];
2760 #endif //GC_CONFIG_DRIVEN
2761 #endif //MULTIPLE_HEAPS
2762
2763 no_gc_region_info gc_heap::current_no_gc_region_info;
2764 BOOL gc_heap::proceed_with_gc_p = FALSE;
2765 GCSpinLock gc_heap::gc_lock;
2766
2767 size_t gc_heap::eph_gen_starts_size = 0;
2768 heap_segment* gc_heap::segment_standby_list;
2769 size_t        gc_heap::last_gc_index = 0;
2770 #ifdef SEG_MAPPING_TABLE
2771 size_t        gc_heap::min_segment_size = 0;
2772 size_t        gc_heap::min_segment_size_shr = 0;
2773 #endif //SEG_MAPPING_TABLE
2774 size_t        gc_heap::soh_segment_size = 0;
2775 size_t        gc_heap::min_loh_segment_size = 0;
2776 size_t        gc_heap::segment_info_size = 0;
2777
2778 #ifdef GC_CONFIG_DRIVEN
2779 size_t gc_heap::time_init = 0;
2780 size_t gc_heap::time_since_init = 0;
2781 size_t gc_heap::compact_or_sweep_gcs[2];
2782 #endif //GC_CONFIG_DRIVEN
2783
2784 #ifdef FEATURE_LOH_COMPACTION
2785 BOOL                   gc_heap::loh_compaction_always_p = FALSE;
2786 gc_loh_compaction_mode gc_heap::loh_compaction_mode = loh_compaction_default;
2787 int                    gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY;
2788
2789 #endif //FEATURE_LOH_COMPACTION
2790
2791 GCEvent gc_heap::full_gc_approach_event;
2792
2793 GCEvent gc_heap::full_gc_end_event;
2794
2795 uint32_t gc_heap::fgn_maxgen_percent = 0;
2796
2797 uint32_t gc_heap::fgn_loh_percent = 0;
2798
2799 #ifdef BACKGROUND_GC
2800 BOOL gc_heap::fgn_last_gc_was_concurrent = FALSE;
2801 #endif //BACKGROUND_GC
2802
2803 VOLATILE(bool) gc_heap::full_gc_approach_event_set;
2804
2805 size_t gc_heap::full_gc_counts[gc_type_max];
2806
2807 BOOL gc_heap::should_expand_in_full_gc = FALSE;
2808
2809 #ifdef HEAP_ANALYZE
2810 BOOL        gc_heap::heap_analyze_enabled = FALSE;
2811 #endif //HEAP_ANALYZE
2812
2813 #ifndef MULTIPLE_HEAPS
2814
2815 alloc_list gc_heap::loh_alloc_list [NUM_LOH_ALIST-1];
2816 alloc_list gc_heap::gen2_alloc_list[NUM_GEN2_ALIST-1];
2817
2818 dynamic_data gc_heap::dynamic_data_table [NUMBERGENERATIONS+1];
2819 gc_history_per_heap gc_heap::gc_data_per_heap;
2820 size_t gc_heap::maxgen_pinned_compact_before_advance = 0;
2821
2822 uint8_t* gc_heap::alloc_allocated = 0;
2823
2824 size_t gc_heap::allocation_quantum = CLR_SIZE;
2825
2826 GCSpinLock gc_heap::more_space_lock;
2827
2828 #ifdef SYNCHRONIZATION_STATS
2829 unsigned int gc_heap::good_suspension = 0;
2830 unsigned int gc_heap::bad_suspension = 0;
2831 uint64_t     gc_heap::total_msl_acquire = 0;
2832 unsigned int gc_heap::num_msl_acquired = 0;
2833 unsigned int gc_heap::num_high_msl_acquire = 0;
2834 unsigned int gc_heap::num_low_msl_acquire = 0;
2835 #endif //SYNCHRONIZATION_STATS
2836
2837 size_t   gc_heap::alloc_contexts_used = 0;
2838 size_t   gc_heap::soh_allocation_no_gc = 0;
2839 size_t   gc_heap::loh_allocation_no_gc = 0;
2840 bool     gc_heap::no_gc_oom_p = false;
2841 heap_segment* gc_heap::saved_loh_segment_no_gc = 0;
2842
2843 #endif //MULTIPLE_HEAPS
2844
2845 #ifndef MULTIPLE_HEAPS
2846
2847 BOOL        gc_heap::gen0_bricks_cleared = FALSE;
2848
2849 #ifdef FFIND_OBJECT
2850 int         gc_heap::gen0_must_clear_bricks = 0;
2851 #endif //FFIND_OBJECT
2852
2853 #ifdef FEATURE_PREMORTEM_FINALIZATION
2854 CFinalize*  gc_heap::finalize_queue = 0;
2855 #endif // FEATURE_PREMORTEM_FINALIZATION
2856
2857 generation gc_heap::generation_table [NUMBERGENERATIONS + 1];
2858
2859 size_t     gc_heap::interesting_data_per_heap[max_idp_count];
2860
2861 size_t     gc_heap::compact_reasons_per_heap[max_compact_reasons_count];
2862
2863 size_t     gc_heap::expand_mechanisms_per_heap[max_expand_mechanisms_count];
2864
2865 size_t     gc_heap::interesting_mechanism_bits_per_heap[max_gc_mechanism_bits_count];
2866
2867 #endif // MULTIPLE_HEAPS
2868
2869 /* end of per heap static initialization */
2870
2871 /* end of static initialization */
2872
2873 #ifndef DACCESS_COMPILE
2874
2875 void gen_to_condemn_tuning::print (int heap_num)
2876 {
2877 #ifdef DT_LOG
2878     dprintf (DT_LOG_0, ("condemned reasons (%d %d)", condemn_reasons_gen, condemn_reasons_condition));
2879     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_gen_header));
2880     gc_condemn_reason_gen r_gen;
2881     for (int i = 0; i < gcrg_max; i++)
2882     {
2883         r_gen = (gc_condemn_reason_gen)(i);
2884         str_reasons_gen[i * 2] = get_gen_char (get_gen (r_gen));
2885     }
2886     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_gen));
2887
2888     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_condition_header));
2889     gc_condemn_reason_condition r_condition;
2890     for (int i = 0; i < gcrc_max; i++)
2891     {
2892         r_condition = (gc_condemn_reason_condition)(i);
2893         str_reasons_condition[i * 2] = get_condition_char (get_condition (r_condition));
2894     }
2895
2896     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_condition));
2897 #else
2898     UNREFERENCED_PARAMETER(heap_num);
2899 #endif //DT_LOG
2900 }
2901
2902 void gc_generation_data::print (int heap_num, int gen_num)
2903 {
2904 #if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2905     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",
2906                 heap_num, gen_num, 
2907                 size_before, 
2908                 free_list_space_before, free_obj_space_before,
2909                 size_after, 
2910                 free_list_space_after, free_obj_space_after, 
2911                 in, pinned_surv, npinned_surv,
2912                 new_allocation));
2913 #else
2914     UNREFERENCED_PARAMETER(heap_num);
2915     UNREFERENCED_PARAMETER(gen_num);
2916 #endif //SIMPLE_DPRINTF && DT_LOG
2917 }
2918
2919 void gc_history_per_heap::set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value)
2920 {
2921     uint32_t* mechanism = &mechanisms[mechanism_per_heap];
2922     *mechanism = 0;
2923     *mechanism |= mechanism_mask;
2924     *mechanism |= (1 << value);
2925
2926 #ifdef DT_LOG
2927     gc_mechanism_descr* descr = &gc_mechanisms_descr[mechanism_per_heap];
2928     dprintf (DT_LOG_0, ("setting %s: %s", 
2929             descr->name,
2930             (descr->descr)[value]));
2931 #endif //DT_LOG
2932 }
2933
2934 void gc_history_per_heap::print()
2935 {
2936 #if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2937     for (int i = 0; i < (sizeof (gen_data)/sizeof (gc_generation_data)); i++)
2938     {
2939         gen_data[i].print (heap_index, i);
2940     }
2941
2942     dprintf (DT_LOG_0, ("fla %Id flr %Id esa %Id ca %Id pa %Id paa %Id, rfle %d, ec %Id", 
2943                     maxgen_size_info.free_list_allocated,
2944                     maxgen_size_info.free_list_rejected,
2945                     maxgen_size_info.end_seg_allocated,
2946                     maxgen_size_info.condemned_allocated,
2947                     maxgen_size_info.pinned_allocated,
2948                     maxgen_size_info.pinned_allocated_advance,
2949                     maxgen_size_info.running_free_list_efficiency,
2950                     extra_gen0_committed));
2951
2952     int mechanism = 0;
2953     gc_mechanism_descr* descr = 0;
2954
2955     for (int i = 0; i < max_mechanism_per_heap; i++)
2956     {
2957         mechanism = get_mechanism ((gc_mechanism_per_heap)i);
2958
2959         if (mechanism >= 0)
2960         {
2961             descr = &gc_mechanisms_descr[(gc_mechanism_per_heap)i];
2962             dprintf (DT_LOG_0, ("[%2d]%s%s", 
2963                         heap_index,
2964                         descr->name, 
2965                         (descr->descr)[mechanism]));
2966         }
2967     }
2968 #endif //SIMPLE_DPRINTF && DT_LOG
2969 }
2970
2971 void gc_history_global::print()
2972 {
2973 #ifdef DT_LOG
2974     char str_settings[64];
2975     memset (str_settings, '|', sizeof (char) * 64);
2976     str_settings[max_global_mechanisms_count*2] = 0;
2977
2978     for (int i = 0; i < max_global_mechanisms_count; i++)
2979     {
2980         str_settings[i * 2] = (get_mechanism_p ((gc_global_mechanism_p)i) ? 'Y' : 'N');
2981     }
2982
2983     dprintf (DT_LOG_0, ("[hp]|c|p|o|d|b|e|"));
2984
2985     dprintf (DT_LOG_0, ("%4d|%s", num_heaps, str_settings));
2986     dprintf (DT_LOG_0, ("Condemned gen%d(reason: %s; mode: %s), youngest budget %Id(%d), memload %d",
2987                         condemned_generation,
2988                         str_gc_reasons[reason],
2989                         str_gc_pause_modes[pause_mode],                        
2990                         final_youngest_desired,
2991                         gen0_reduction_count,
2992                         mem_pressure));
2993 #endif //DT_LOG
2994 }
2995
2996 void gc_heap::fire_per_heap_hist_event (gc_history_per_heap* current_gc_data_per_heap, int heap_num)
2997 {
2998     maxgen_size_increase* maxgen_size_info = &(current_gc_data_per_heap->maxgen_size_info);
2999     FIRE_EVENT(GCPerHeapHistory_V3, 
3000                (void *)(maxgen_size_info->free_list_allocated),
3001                (void *)(maxgen_size_info->free_list_rejected),                              
3002                (void *)(maxgen_size_info->end_seg_allocated),
3003                (void *)(maxgen_size_info->condemned_allocated),
3004                (void *)(maxgen_size_info->pinned_allocated),
3005                (void *)(maxgen_size_info->pinned_allocated_advance),
3006                maxgen_size_info->running_free_list_efficiency,
3007                current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons0(),
3008                current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons1(),
3009                current_gc_data_per_heap->mechanisms[gc_heap_compact],
3010                current_gc_data_per_heap->mechanisms[gc_heap_expand],
3011                current_gc_data_per_heap->heap_index,
3012                (void *)(current_gc_data_per_heap->extra_gen0_committed),
3013                (max_generation + 2),
3014                (uint32_t)(sizeof (gc_generation_data)),
3015                (void *)&(current_gc_data_per_heap->gen_data[0]));
3016
3017     current_gc_data_per_heap->print();
3018     current_gc_data_per_heap->gen_to_condemn_reasons.print (heap_num);
3019 }
3020
3021 void gc_heap::fire_pevents()
3022 {
3023 #ifndef CORECLR
3024     settings.record (&gc_data_global);
3025     gc_data_global.print();
3026
3027     FIRE_EVENT(GCGlobalHeapHistory_V2, gc_data_global.final_youngest_desired, 
3028                                   gc_data_global.num_heaps, 
3029                                   gc_data_global.condemned_generation, 
3030                                   gc_data_global.gen0_reduction_count, 
3031                                   gc_data_global.reason, 
3032                                   gc_data_global.global_mechanims_p, 
3033                                   gc_data_global.pause_mode, 
3034                                   gc_data_global.mem_pressure);
3035
3036 #ifdef MULTIPLE_HEAPS
3037     for (int i = 0; i < gc_heap::n_heaps; i++)
3038     {
3039         gc_heap* hp = gc_heap::g_heaps[i];
3040         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
3041         fire_per_heap_hist_event (current_gc_data_per_heap, hp->heap_number);
3042     }
3043 #else
3044     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
3045     fire_per_heap_hist_event (current_gc_data_per_heap, heap_number);
3046 #endif    
3047 #endif //!CORECLR
3048 }
3049
3050 inline BOOL
3051 gc_heap::dt_low_ephemeral_space_p (gc_tuning_point tp)
3052 {
3053     BOOL ret = FALSE;
3054
3055     switch (tp)
3056     {
3057         case tuning_deciding_condemned_gen:
3058         case tuning_deciding_compaction:
3059         case tuning_deciding_expansion:
3060         case tuning_deciding_full_gc:
3061         {
3062             ret = (!ephemeral_gen_fit_p (tp));
3063             break;
3064         }
3065         case tuning_deciding_promote_ephemeral:
3066         {
3067             size_t new_gen0size = approximate_new_allocation();
3068             ptrdiff_t plan_ephemeral_size = total_ephemeral_size;
3069             
3070             dprintf (GTC_LOG, ("h%d: plan eph size is %Id, new gen0 is %Id", 
3071                 heap_number, plan_ephemeral_size, new_gen0size));
3072
3073             // If we were in no_gc_region we could have allocated a larger than normal segment,
3074             // and the next seg we allocate will be a normal sized seg so if we can't fit the new
3075             // ephemeral generations there, do an ephemeral promotion.
3076             ret = ((soh_segment_size - segment_info_size) < (plan_ephemeral_size + new_gen0size));
3077
3078             break;
3079         }
3080         default:
3081             break;
3082     }
3083
3084     return ret;
3085 }
3086
3087 BOOL 
3088 gc_heap::dt_high_frag_p (gc_tuning_point tp, 
3089                          int gen_number, 
3090                          BOOL elevate_p)
3091 {
3092     BOOL ret = FALSE;
3093
3094     switch (tp)
3095     {
3096         case tuning_deciding_condemned_gen:
3097         {
3098             dynamic_data* dd = dynamic_data_of (gen_number);
3099             float fragmentation_burden = 0;
3100
3101             if (elevate_p)
3102             {
3103                 ret = (dd_fragmentation (dynamic_data_of (max_generation)) >= dd_max_size(dd));
3104                 dprintf (GTC_LOG, ("h%d: frag is %Id, max size is %Id",
3105                     heap_number, dd_fragmentation (dd), dd_max_size(dd)));
3106             }
3107             else
3108             {
3109 #ifndef MULTIPLE_HEAPS
3110                 if (gen_number == max_generation)
3111                 {
3112                     float frag_ratio = (float)(dd_fragmentation (dynamic_data_of (max_generation))) / (float)generation_size (max_generation);
3113                     if (frag_ratio > 0.65)
3114                     {
3115                         dprintf (GTC_LOG, ("g2 FR: %d%%", (int)(frag_ratio*100)));
3116                         return TRUE;
3117                     }
3118                 }
3119 #endif //!MULTIPLE_HEAPS
3120                 size_t fr = generation_unusable_fragmentation (generation_of (gen_number));
3121                 ret = (fr > dd_fragmentation_limit(dd));
3122                 if (ret)
3123                 {
3124                     fragmentation_burden = (float)fr / generation_size (gen_number);
3125                     ret = (fragmentation_burden > dd_v_fragmentation_burden_limit (dd));
3126                 }
3127                 dprintf (GTC_LOG, ("h%d: gen%d, frag is %Id, alloc effi: %d%%, unusable frag is %Id, ratio is %d",
3128                     heap_number, gen_number, dd_fragmentation (dd), 
3129                     (int)(100*generation_allocator_efficiency (generation_of (gen_number))),
3130                     fr, (int)(fragmentation_burden*100)));
3131             }
3132             break;
3133         }
3134         default:
3135             break;
3136     }
3137
3138     return ret;
3139 }
3140
3141 inline BOOL 
3142 gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number)
3143 {
3144     BOOL ret = FALSE;
3145
3146     switch (tp)
3147     {
3148         case tuning_deciding_condemned_gen:
3149         {
3150             if (gen_number == max_generation)
3151             {
3152                 dynamic_data* dd = dynamic_data_of (gen_number);
3153                 size_t maxgen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd));
3154                 size_t maxgen_total_size = maxgen_allocated + dd_current_size (dd);
3155                 size_t est_maxgen_surv = (size_t)((float) (maxgen_total_size) * dd_surv (dd));
3156                 size_t est_maxgen_free = maxgen_total_size - est_maxgen_surv + dd_fragmentation (dd);
3157
3158                 dprintf (GTC_LOG, ("h%d: Total gen2 size: %Id, est gen2 dead space: %Id (s: %d, allocated: %Id), frag: %Id",
3159                             heap_number,
3160                             maxgen_total_size,
3161                             est_maxgen_free, 
3162                             (int)(dd_surv (dd) * 100),
3163                             maxgen_allocated,
3164                             dd_fragmentation (dd)));
3165
3166                 uint32_t num_heaps = 1;
3167
3168 #ifdef MULTIPLE_HEAPS
3169                 num_heaps = gc_heap::n_heaps;
3170 #endif //MULTIPLE_HEAPS
3171
3172                 size_t min_frag_th = min_reclaim_fragmentation_threshold (num_heaps);
3173                 dprintf (GTC_LOG, ("h%d, min frag is %Id", heap_number, min_frag_th));
3174                 ret = (est_maxgen_free >= min_frag_th);
3175             }
3176             else
3177             {
3178                 assert (0);
3179             }
3180             break;
3181         }
3182
3183         default:
3184             break;
3185     }
3186
3187     return ret;
3188 }
3189
3190 // DTREVIEW: Right now we only estimate gen2 fragmentation. 
3191 // on 64-bit though we should consider gen1 or even gen0 fragmentatioin as
3192 // well 
3193 inline BOOL 
3194 gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, uint64_t available_mem)
3195 {
3196     BOOL ret = FALSE;
3197
3198     switch (tp)
3199     {
3200         case tuning_deciding_condemned_gen:
3201         {
3202             if (gen_number == max_generation)
3203             {
3204                 dynamic_data* dd = dynamic_data_of (gen_number);
3205                 float est_frag_ratio = 0;
3206                 if (dd_current_size (dd) == 0)
3207                 {
3208                     est_frag_ratio = 1;
3209                 }
3210                 else if ((dd_fragmentation (dd) == 0) || (dd_fragmentation (dd) + dd_current_size (dd) == 0))
3211                 {
3212                     est_frag_ratio = 0;
3213                 }
3214                 else
3215                 {
3216                     est_frag_ratio = (float)dd_fragmentation (dd) / (float)(dd_fragmentation (dd) + dd_current_size (dd));
3217                 }
3218                 
3219                 size_t est_frag = (dd_fragmentation (dd) + (size_t)((dd_desired_allocation (dd) - dd_new_allocation (dd)) * est_frag_ratio));
3220                 dprintf (GTC_LOG, ("h%d: gen%d: current_size is %Id, frag is %Id, est_frag_ratio is %d%%, estimated frag is %Id", 
3221                     heap_number,
3222                     gen_number,
3223                     dd_current_size (dd),
3224                     dd_fragmentation (dd),
3225                     (int)(est_frag_ratio*100),
3226                     est_frag));
3227
3228                 uint32_t num_heaps = 1;
3229
3230 #ifdef MULTIPLE_HEAPS
3231                 num_heaps = gc_heap::n_heaps;
3232 #endif //MULTIPLE_HEAPS
3233                 uint64_t min_frag_th = min_high_fragmentation_threshold(available_mem, num_heaps);
3234                 //dprintf (GTC_LOG, ("h%d, min frag is %I64d", heap_number, min_frag_th));
3235                 ret = (est_frag >= min_frag_th);
3236             }
3237             else
3238             {
3239                 assert (0);
3240             }
3241             break;
3242         }
3243
3244         default:
3245             break;
3246     }
3247
3248     return ret;
3249 }
3250
3251 inline BOOL 
3252 gc_heap::dt_low_card_table_efficiency_p (gc_tuning_point tp)
3253 {
3254     BOOL ret = FALSE;
3255
3256     switch (tp)
3257     {
3258     case tuning_deciding_condemned_gen:
3259     {
3260         /* promote into max-generation if the card table has too many
3261         * generation faults besides the n -> 0
3262         */
3263         ret = (generation_skip_ratio < 30);
3264         break;
3265     }
3266
3267     default:
3268         break;
3269     }
3270
3271     return ret;
3272 }
3273
3274 inline BOOL
3275 in_range_for_segment(uint8_t* add, heap_segment* seg)
3276 {
3277     return ((add >= heap_segment_mem (seg)) && (add < heap_segment_reserved (seg)));
3278 }
3279
3280 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
3281 // The array we allocate is organized as follows:
3282 // 0th element is the address of the last array we allocated.
3283 // starting from the 1st element are the segment addresses, that's
3284 // what buckets() returns.
3285 struct bk
3286 {
3287     uint8_t* add;
3288     size_t val;
3289 };
3290
3291 class sorted_table
3292 {
3293 private:
3294     ptrdiff_t size;
3295     ptrdiff_t count;
3296     bk* slots;
3297     bk* buckets() { return (slots + 1); }
3298     uint8_t*& last_slot (bk* arr) { return arr[0].add; }
3299     bk* old_slots;
3300 public:
3301     static  sorted_table* make_sorted_table ();
3302     BOOL    insert (uint8_t* add, size_t val);;
3303     size_t  lookup (uint8_t*& add);
3304     void    remove (uint8_t* add);
3305     void    clear ();
3306     void    delete_sorted_table();
3307     void    delete_old_slots();
3308     void    enqueue_old_slot(bk* sl);
3309     BOOL    ensure_space_for_insert();
3310 };
3311
3312 sorted_table*
3313 sorted_table::make_sorted_table ()
3314 {
3315     size_t size = 400;
3316
3317     // allocate one more bk to store the older slot address.
3318     sorted_table* res = (sorted_table*)new char [sizeof (sorted_table) + (size + 1) * sizeof (bk)];
3319     if (!res)
3320         return 0;
3321     res->size = size;
3322     res->slots = (bk*)(res + 1);
3323     res->old_slots = 0;
3324     res->clear();
3325     return res;
3326 }
3327
3328 void
3329 sorted_table::delete_sorted_table()
3330 {
3331     if (slots != (bk*)(this+1))
3332     {
3333         delete slots;
3334     }
3335     delete_old_slots();
3336     delete this;
3337 }
3338 void
3339 sorted_table::delete_old_slots()
3340 {
3341     uint8_t* sl = (uint8_t*)old_slots;
3342     while (sl)
3343     {
3344         uint8_t* dsl = sl;
3345         sl = last_slot ((bk*)sl);
3346         delete dsl;
3347     }
3348     old_slots = 0;
3349 }
3350 void
3351 sorted_table::enqueue_old_slot(bk* sl)
3352 {
3353     last_slot (sl) = (uint8_t*)old_slots;
3354     old_slots = sl;
3355 }
3356
3357 inline
3358 size_t
3359 sorted_table::lookup (uint8_t*& add)
3360 {
3361     ptrdiff_t high = (count-1);
3362     ptrdiff_t low = 0;
3363     ptrdiff_t ti;
3364     ptrdiff_t mid;
3365     bk* buck = buckets();
3366     while (low <= high)
3367     {
3368         mid = ((low + high)/2);
3369         ti = mid;
3370         if (buck[ti].add > add)
3371         {
3372             if ((ti > 0) && (buck[ti-1].add <= add))
3373             {
3374                 add = buck[ti-1].add;
3375                 return buck[ti - 1].val;
3376             }
3377             high = mid - 1;
3378         }
3379         else
3380         {
3381             if (buck[ti+1].add > add)
3382             {
3383                 add = buck[ti].add;
3384                 return buck[ti].val;
3385             }
3386             low = mid + 1;
3387         }
3388     }
3389     add = 0;
3390     return 0;
3391 }
3392
3393 BOOL
3394 sorted_table::ensure_space_for_insert()
3395 {
3396     if (count == size)
3397     {
3398         size = (size * 3)/2;
3399         assert((size * sizeof (bk)) > 0);
3400         bk* res = (bk*)new (nothrow) char [(size + 1) * sizeof (bk)];
3401         assert (res);
3402         if (!res)
3403             return FALSE;
3404
3405         last_slot (res) = 0;
3406         memcpy (((bk*)res + 1), buckets(), count * sizeof (bk));
3407         bk* last_old_slots = slots;
3408         slots = res;
3409         if (last_old_slots != (bk*)(this + 1))
3410             enqueue_old_slot (last_old_slots);
3411     }
3412     return TRUE;
3413 }
3414
3415 BOOL
3416 sorted_table::insert (uint8_t* add, size_t val)
3417 {
3418     //grow if no more room
3419     assert (count < size);
3420
3421     //insert sorted
3422     ptrdiff_t high = (count-1);
3423     ptrdiff_t low = 0;
3424     ptrdiff_t ti;
3425     ptrdiff_t mid;
3426     bk* buck = buckets();
3427     while (low <= high)
3428     {
3429         mid = ((low + high)/2);
3430         ti = mid;
3431         if (buck[ti].add > add)
3432         {
3433             if ((ti == 0) || (buck[ti-1].add <= add))
3434             {
3435                 // found insertion point
3436                 for (ptrdiff_t k = count; k > ti;k--)
3437                 {
3438                     buck [k] = buck [k-1];
3439                 }
3440                 buck[ti].add = add;
3441                 buck[ti].val = val;
3442                 count++;
3443                 return TRUE;
3444             }
3445             high = mid - 1;
3446         }
3447         else
3448         {
3449             if (buck[ti+1].add > add)
3450             {
3451                 //found the insertion point
3452                 for (ptrdiff_t k = count; k > ti+1;k--)
3453                 {
3454                     buck [k] = buck [k-1];
3455                 }
3456                 buck[ti+1].add = add;
3457                 buck[ti+1].val = val;
3458                 count++;
3459                 return TRUE;
3460             }
3461             low = mid + 1;
3462         }
3463     }
3464     assert (0);
3465     return TRUE;
3466 }
3467
3468 void
3469 sorted_table::remove (uint8_t* add)
3470 {
3471     ptrdiff_t high = (count-1);
3472     ptrdiff_t low = 0;
3473     ptrdiff_t ti;
3474     ptrdiff_t mid;
3475     bk* buck = buckets();
3476     while (low <= high)
3477     {
3478         mid = ((low + high)/2);
3479         ti = mid;
3480         if (buck[ti].add > add)
3481         {
3482             if (buck[ti-1].add <= add)
3483             {
3484                 // found the guy to remove
3485                 for (ptrdiff_t k = ti; k < count; k++)
3486                     buck[k-1] = buck[k];
3487                 count--;
3488                 return;
3489             }
3490             high = mid - 1;
3491         }
3492         else
3493         {
3494             if (buck[ti+1].add > add)
3495             {
3496                 // found the guy to remove
3497                 for (ptrdiff_t k = ti+1; k < count; k++)
3498                     buck[k-1] = buck[k];
3499                 count--;
3500                 return;
3501             }
3502             low = mid + 1;
3503         }
3504     }
3505     assert (0);
3506 }
3507
3508 void
3509 sorted_table::clear()
3510 {
3511     count = 1;
3512     buckets()[0].add = MAX_PTR;
3513 }
3514 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
3515
3516 #ifdef SEG_MAPPING_TABLE
3517 #ifdef GROWABLE_SEG_MAPPING_TABLE
3518 inline
3519 uint8_t* align_on_segment (uint8_t* add)
3520 {
3521     return (uint8_t*)((size_t)(add + (gc_heap::min_segment_size - 1)) & ~(gc_heap::min_segment_size - 1));
3522 }
3523
3524 inline
3525 uint8_t* align_lower_segment (uint8_t* add)
3526 {
3527     return (uint8_t*)((size_t)(add) & ~(gc_heap::min_segment_size - 1));
3528 }
3529
3530 size_t size_seg_mapping_table_of (uint8_t* from, uint8_t* end)
3531 {
3532     from = align_lower_segment (from);
3533     end = align_on_segment (end);
3534     dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((size_t)(end - from) >> gc_heap::min_segment_size_shr))));
3535     return sizeof (seg_mapping)*((size_t)(end - from) >> gc_heap::min_segment_size_shr);
3536 }
3537
3538 // for seg_mapping_table we want it to start from a pointer sized address.
3539 inline
3540 size_t align_for_seg_mapping_table (size_t size)
3541 {
3542     return ((size + (sizeof (uint8_t*) - 1)) &~ (sizeof (uint8_t*) - 1));
3543 }
3544
3545 inline
3546 size_t seg_mapping_word_of (uint8_t* add)
3547 {
3548     return (size_t)add >> gc_heap::min_segment_size_shr;
3549 }
3550 #else //GROWABLE_SEG_MAPPING_TABLE
3551 BOOL seg_mapping_table_init()
3552 {
3553 #ifdef BIT64
3554     uint64_t total_address_space = (uint64_t)8*1024*1024*1024*1024;
3555 #else
3556     uint64_t total_address_space = (uint64_t)4*1024*1024*1024;
3557 #endif // BIT64
3558
3559     size_t num_entries = (size_t)(total_address_space >> gc_heap::min_segment_size_shr);
3560     seg_mapping_table = new seg_mapping[num_entries];
3561
3562     if (seg_mapping_table)
3563     {
3564         memset (seg_mapping_table, 0, num_entries * sizeof (seg_mapping));
3565         dprintf (1, ("created %d entries for heap mapping (%Id bytes)", 
3566                      num_entries, (num_entries * sizeof (seg_mapping))));
3567         return TRUE;
3568     }
3569     else
3570     {
3571         dprintf (1, ("failed to create %d entries for heap mapping (%Id bytes)", 
3572                      num_entries, (num_entries * sizeof (seg_mapping))));
3573         return FALSE;
3574     }
3575 }
3576 #endif //GROWABLE_SEG_MAPPING_TABLE
3577
3578 #ifdef FEATURE_BASICFREEZE
3579 inline
3580 size_t ro_seg_begin_index (heap_segment* seg)
3581 {
3582     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3583     begin_index = max (begin_index, (size_t)g_gc_lowest_address >> gc_heap::min_segment_size_shr);
3584     return begin_index;
3585 }
3586
3587 inline
3588 size_t ro_seg_end_index (heap_segment* seg)
3589 {
3590     size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) >> gc_heap::min_segment_size_shr;
3591     end_index = min (end_index, (size_t)g_gc_highest_address >> gc_heap::min_segment_size_shr);
3592     return end_index;
3593 }
3594
3595 void seg_mapping_table_add_ro_segment (heap_segment* seg)
3596 {
3597 #ifdef GROWABLE_SEG_MAPPING_TABLE
3598     if ((heap_segment_reserved (seg) <= g_gc_lowest_address) || (heap_segment_mem (seg) >= g_gc_highest_address))
3599         return;
3600 #endif //GROWABLE_SEG_MAPPING_TABLE
3601
3602     for (size_t entry_index = ro_seg_begin_index (seg); entry_index <= ro_seg_end_index (seg); entry_index++)
3603         seg_mapping_table[entry_index].seg1 = (heap_segment*)((size_t)seg_mapping_table[entry_index].seg1 | ro_in_entry);
3604 }
3605
3606 void seg_mapping_table_remove_ro_segment (heap_segment* seg)
3607 {
3608     UNREFERENCED_PARAMETER(seg);
3609 #if 0
3610 // POSSIBLE PERF TODO: right now we are not doing anything because we can't simply remove the flag. If it proves
3611 // to be a perf problem, we can search in the current ro segs and see if any lands in this range and only
3612 // remove the flag if none lands in this range.
3613 #endif //0
3614 }
3615
3616 heap_segment* ro_segment_lookup (uint8_t* o)
3617 {
3618     uint8_t* ro_seg_start = o;
3619     heap_segment* seg = (heap_segment*)gc_heap::seg_table->lookup (ro_seg_start);
3620
3621     if (ro_seg_start && in_range_for_segment (o, seg))
3622         return seg;
3623     else
3624         return 0;
3625 }
3626
3627 #endif //FEATURE_BASICFREEZE
3628
3629 void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
3630 {
3631     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3632     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3633     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3634     size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
3635     seg_mapping* end_entry = &seg_mapping_table[end_index];
3636
3637     dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)", 
3638         seg, begin_index, heap_segment_reserved (seg), end_index));
3639
3640     dprintf (1, ("before add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3641         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3642         end_index, (seg_mapping_table[end_index].boundary + 1)));
3643
3644 #ifdef MULTIPLE_HEAPS
3645 #ifdef SIMPLE_DPRINTF
3646     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end %d: h0: %Ix(%d), h1: %Ix(%d)",
3647         begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3648         (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3649         end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3650         (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3651 #endif //SIMPLE_DPRINTF
3652     assert (end_entry->boundary == 0);
3653     assert (end_entry->h0 == 0);
3654     end_entry->h0 = hp;
3655     assert (begin_entry->h1 == 0);
3656     begin_entry->h1 = hp;
3657 #else
3658     UNREFERENCED_PARAMETER(hp);
3659 #endif //MULTIPLE_HEAPS
3660
3661     end_entry->boundary = (uint8_t*)seg_end;
3662
3663     dprintf (1, ("set entry %d seg1 and %d seg0 to %Ix", begin_index, end_index, seg));
3664     assert ((begin_entry->seg1 == 0) || ((size_t)(begin_entry->seg1) == ro_in_entry));
3665     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) | (size_t)seg);
3666     end_entry->seg0 = seg;
3667
3668     // for every entry inbetween we need to set its heap too.
3669     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3670     {
3671         assert (seg_mapping_table[entry_index].boundary == 0);
3672 #ifdef MULTIPLE_HEAPS
3673         assert (seg_mapping_table[entry_index].h0 == 0);
3674         seg_mapping_table[entry_index].h1 = hp;
3675 #endif //MULTIPLE_HEAPS
3676         seg_mapping_table[entry_index].seg1 = seg;
3677     }
3678
3679     dprintf (1, ("after add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3680         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3681         end_index, (seg_mapping_table[end_index].boundary + 1)));
3682 #if defined(MULTIPLE_HEAPS) && defined(SIMPLE_DPRINTF)
3683     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end: %d h0: %Ix(%d), h1: %Ix(%d)",
3684         begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3685         (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3686         end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3687         (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3688 #endif //MULTIPLE_HEAPS && SIMPLE_DPRINTF
3689 }
3690
3691 void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
3692 {
3693     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3694     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3695     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3696     size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
3697     seg_mapping* end_entry = &seg_mapping_table[end_index];
3698     dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)", 
3699         seg, begin_index, heap_segment_reserved (seg), end_index));
3700
3701     assert (end_entry->boundary == (uint8_t*)seg_end);
3702     end_entry->boundary = 0;
3703
3704 #ifdef MULTIPLE_HEAPS
3705     gc_heap* hp = heap_segment_heap (seg);
3706     assert (end_entry->h0 == hp);
3707     end_entry->h0 = 0;
3708     assert (begin_entry->h1 == hp);
3709     begin_entry->h1 = 0;
3710 #endif //MULTIPLE_HEAPS
3711
3712     assert (begin_entry->seg1 != 0);
3713     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) & ro_in_entry);
3714     end_entry->seg0 = 0;
3715
3716     // for every entry inbetween we need to reset its heap too.
3717     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3718     {
3719         assert (seg_mapping_table[entry_index].boundary == 0);
3720 #ifdef MULTIPLE_HEAPS
3721         assert (seg_mapping_table[entry_index].h0 == 0);
3722         assert (seg_mapping_table[entry_index].h1 == hp);
3723         seg_mapping_table[entry_index].h1 = 0;
3724 #endif //MULTIPLE_HEAPS
3725         seg_mapping_table[entry_index].seg1 = 0;
3726     }
3727
3728     dprintf (1, ("after remove: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3729         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3730         end_index, (seg_mapping_table[end_index].boundary + 1)));
3731 #ifdef MULTIPLE_HEAPS
3732     dprintf (1, ("begin %d: h0: %Ix, h1: %Ix; end: %d h0: %Ix, h1: %Ix",
3733         begin_index, (uint8_t*)(begin_entry->h0), (uint8_t*)(begin_entry->h1),
3734         end_index, (uint8_t*)(end_entry->h0), (uint8_t*)(end_entry->h1)));
3735 #endif //MULTIPLE_HEAPS
3736 }
3737
3738 #ifdef MULTIPLE_HEAPS
3739 inline
3740 gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o)
3741 {
3742     size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
3743     seg_mapping* entry = &seg_mapping_table[index];
3744
3745     gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
3746
3747     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
3748         o, index, (entry->boundary + 1), 
3749         (uint8_t*)(entry->h0), (uint8_t*)(entry->seg0),
3750         (uint8_t*)(entry->h1), (uint8_t*)(entry->seg1)));
3751
3752 #ifdef _DEBUG
3753     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3754 #ifdef FEATURE_BASICFREEZE
3755     if ((size_t)seg & ro_in_entry)
3756         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3757 #endif //FEATURE_BASICFREEZE
3758
3759     if (seg)
3760     {
3761         if (in_range_for_segment (o, seg))
3762         {
3763             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, seg, (uint8_t*)heap_segment_allocated (seg)));
3764         }
3765         else
3766         {
3767             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg", 
3768                 seg, (uint8_t*)heap_segment_allocated (seg), o));
3769         }
3770     }
3771     else
3772     {
3773         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3774     }
3775 #endif //_DEBUG
3776
3777     return hp;
3778 }
3779
3780 gc_heap* seg_mapping_table_heap_of (uint8_t* o)
3781 {
3782 #ifdef GROWABLE_SEG_MAPPING_TABLE
3783     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3784         return 0;
3785 #endif //GROWABLE_SEG_MAPPING_TABLE
3786
3787     return seg_mapping_table_heap_of_worker (o);
3788 }
3789
3790 gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o)
3791 {
3792 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3793     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3794         return 0;
3795 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3796
3797     return seg_mapping_table_heap_of_worker (o);
3798 }
3799 #endif //MULTIPLE_HEAPS
3800
3801 // Only returns a valid seg if we can actually find o on the seg.
3802 heap_segment* seg_mapping_table_segment_of (uint8_t* o)
3803 {
3804 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3805     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3806 #ifdef FEATURE_BASICFREEZE
3807         return ro_segment_lookup (o);
3808 #else
3809         return 0;
3810 #endif //FEATURE_BASICFREEZE
3811 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3812
3813     size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
3814     seg_mapping* entry = &seg_mapping_table[index];
3815
3816     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
3817         o, index, (entry->boundary + 1), 
3818         (uint8_t*)(entry->seg0), (uint8_t*)(entry->seg1)));
3819
3820     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3821 #ifdef FEATURE_BASICFREEZE
3822     if ((size_t)seg & ro_in_entry)
3823         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3824 #endif //FEATURE_BASICFREEZE
3825
3826     if (seg)
3827     {
3828         if (in_range_for_segment (o, seg))
3829         {
3830             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg)));
3831         }
3832         else
3833         {
3834             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg, setting it to 0", 
3835                 (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg), o));
3836             seg = 0;
3837         }
3838     }
3839     else
3840     {
3841         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3842     }
3843
3844 #ifdef FEATURE_BASICFREEZE
3845     // TODO: This was originally written assuming that the seg_mapping_table would always contain entries for ro 
3846     // segments whenever the ro segment falls into the [g_gc_lowest_address,g_gc_highest_address) range.  I.e., it had an 
3847     // extra "&& (size_t)(entry->seg1) & ro_in_entry" expression.  However, at the moment, grow_brick_card_table does 
3848     // not correctly go through the ro segments and add them back to the seg_mapping_table when the [lowest,highest) 
3849     // range changes.  We should probably go ahead and modify grow_brick_card_table and put back the 
3850     // "&& (size_t)(entry->seg1) & ro_in_entry" here.
3851     if (!seg)
3852     {
3853         seg = ro_segment_lookup (o);
3854         if (seg && !in_range_for_segment (o, seg))
3855             seg = 0;
3856     }
3857 #endif //FEATURE_BASICFREEZE
3858
3859     return seg;
3860 }
3861 #endif //SEG_MAPPING_TABLE
3862
3863 size_t gcard_of ( uint8_t*);
3864
3865 #define memref(i) *(uint8_t**)(i)
3866
3867 //GC Flags
3868 #define GC_MARKED       (size_t)0x1
3869 #define slot(i, j) ((uint8_t**)(i))[j+1]
3870
3871 #define free_object_base_size (plug_skew + sizeof(ArrayBase))
3872
3873 class CObjectHeader : public Object
3874 {
3875 public:
3876
3877 #if defined(FEATURE_REDHAWK) || defined(BUILD_AS_STANDALONE)
3878     // The GC expects the following methods that are provided by the Object class in the CLR but not provided
3879     // by Redhawk's version of Object.
3880     uint32_t GetNumComponents()
3881     {
3882         return ((ArrayBase *)this)->GetNumComponents();
3883     }
3884
3885     void Validate(BOOL bDeep=TRUE, BOOL bVerifyNextHeader = TRUE)
3886     {
3887         UNREFERENCED_PARAMETER(bVerifyNextHeader);
3888
3889         if (this == NULL)
3890             return;
3891
3892         MethodTable * pMT = GetMethodTable();
3893
3894         _ASSERTE(pMT->SanityCheck());
3895
3896         bool noRangeChecks =
3897             (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_RANGE_CHECKS) == GCConfig::HEAPVERIFY_NO_RANGE_CHECKS;
3898
3899         BOOL fSmallObjectHeapPtr = FALSE, fLargeObjectHeapPtr = FALSE;
3900         if (!noRangeChecks)
3901         {
3902             fSmallObjectHeapPtr = g_theGCHeap->IsHeapPointer(this, TRUE);
3903             if (!fSmallObjectHeapPtr)
3904                 fLargeObjectHeapPtr = g_theGCHeap->IsHeapPointer(this);
3905
3906             _ASSERTE(fSmallObjectHeapPtr || fLargeObjectHeapPtr);
3907         }
3908
3909 #ifdef FEATURE_STRUCTALIGN
3910         _ASSERTE(IsStructAligned((uint8_t *)this, GetMethodTable()->GetBaseAlignment()));
3911 #endif // FEATURE_STRUCTALIGN
3912
3913 #ifdef FEATURE_64BIT_ALIGNMENT
3914         if (pMT->RequiresAlign8())
3915         {
3916             _ASSERTE((((size_t)this) & 0x7) == (pMT->IsValueType() ? 4U : 0U));
3917         }
3918 #endif // FEATURE_64BIT_ALIGNMENT
3919
3920 #ifdef VERIFY_HEAP
3921         if (bDeep && (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC))
3922             g_theGCHeap->ValidateObjectMember(this);
3923 #endif
3924         if (fSmallObjectHeapPtr)
3925         {
3926 #ifdef FEATURE_BASICFREEZE
3927             _ASSERTE(!g_theGCHeap->IsLargeObject(pMT) || g_theGCHeap->IsInFrozenSegment(this));
3928 #else
3929             _ASSERTE(!g_theGCHeap->IsLargeObject(pMT));
3930 #endif
3931         }
3932     }
3933
3934     void ValidatePromote(ScanContext *sc, uint32_t flags)
3935     {
3936         UNREFERENCED_PARAMETER(sc);
3937         UNREFERENCED_PARAMETER(flags);
3938
3939         Validate();
3940     }
3941
3942     void ValidateHeap(Object *from, BOOL bDeep)
3943     {
3944         UNREFERENCED_PARAMETER(from);
3945
3946         Validate(bDeep, FALSE);
3947     }
3948
3949     ADIndex GetAppDomainIndex()
3950     {
3951         return (ADIndex)RH_DEFAULT_DOMAIN_ID;
3952     }
3953 #endif //FEATURE_REDHAWK
3954
3955     /////
3956     //
3957     // Header Status Information
3958     //
3959
3960     MethodTable    *GetMethodTable() const
3961     {
3962         return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED))));
3963     }
3964
3965     void SetMarked()
3966     {
3967         RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED));
3968     }
3969
3970     BOOL IsMarked() const
3971     {
3972         return !!(((size_t)RawGetMethodTable()) & GC_MARKED);
3973     }
3974
3975     void SetPinned()
3976     {
3977         assert (!(gc_heap::settings.concurrent));
3978         GetHeader()->SetGCBit();
3979     }
3980
3981     BOOL IsPinned() const
3982     {
3983         return !!((((CObjectHeader*)this)->GetHeader()->GetBits()) & BIT_SBLK_GC_RESERVE);
3984     }
3985
3986     void ClearMarked()
3987     {
3988         RawSetMethodTable( GetMethodTable() );
3989     }
3990
3991     CGCDesc *GetSlotMap ()
3992     {
3993         assert (GetMethodTable()->ContainsPointers());
3994         return CGCDesc::GetCGCDescFromMT(GetMethodTable());
3995     }
3996
3997     void SetFree(size_t size)
3998     {
3999         assert (size >= free_object_base_size);
4000
4001         assert (g_gc_pFreeObjectMethodTable->GetBaseSize() == free_object_base_size);
4002         assert (g_gc_pFreeObjectMethodTable->RawGetComponentSize() == 1);
4003
4004         RawSetMethodTable( g_gc_pFreeObjectMethodTable );
4005
4006         size_t* numComponentsPtr = (size_t*) &((uint8_t*) this)[ArrayBase::GetOffsetOfNumComponents()];
4007         *numComponentsPtr = size - free_object_base_size;
4008 #ifdef VERIFY_HEAP
4009         //This introduces a bug in the free list management. 
4010         //((void**) this)[-1] = 0;    // clear the sync block,
4011         assert (*numComponentsPtr >= 0);
4012         if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
4013             memset (((uint8_t*)this)+sizeof(ArrayBase), 0xcc, *numComponentsPtr);
4014 #endif //VERIFY_HEAP
4015     }
4016
4017     void UnsetFree()
4018     {
4019         size_t size = free_object_base_size - plug_skew;
4020
4021         // since we only need to clear 2 ptr size, we do it manually
4022         PTR_PTR m = (PTR_PTR) this;
4023         for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
4024             *(m++) = 0;
4025     }
4026
4027     BOOL IsFree () const
4028     {
4029         return (GetMethodTable() == g_gc_pFreeObjectMethodTable);
4030     }
4031
4032 #ifdef FEATURE_STRUCTALIGN
4033     int GetRequiredAlignment () const
4034     {
4035         return GetMethodTable()->GetRequiredAlignment();
4036     }
4037 #endif // FEATURE_STRUCTALIGN
4038
4039     BOOL ContainsPointers() const
4040     {
4041         return GetMethodTable()->ContainsPointers();
4042     }
4043
4044 #ifdef COLLECTIBLE_CLASS
4045     BOOL Collectible() const
4046     {
4047         return GetMethodTable()->Collectible();
4048     }
4049
4050     FORCEINLINE BOOL ContainsPointersOrCollectible() const
4051     {
4052         MethodTable *pMethodTable = GetMethodTable();
4053         return (pMethodTable->ContainsPointers() || pMethodTable->Collectible());
4054     }
4055 #endif //COLLECTIBLE_CLASS
4056
4057     Object* GetObjectBase() const
4058     {
4059         return (Object*) this;
4060     }
4061 };
4062
4063 #define header(i) ((CObjectHeader*)(i))
4064
4065 #define free_list_slot(x) ((uint8_t**)(x))[2]
4066 #define free_list_undo(x) ((uint8_t**)(x))[-1]
4067 #define UNDO_EMPTY ((uint8_t*)1)
4068
4069 #ifdef SHORT_PLUGS
4070 inline 
4071 void set_plug_padded (uint8_t* node)
4072 {
4073     header(node)->SetMarked();
4074 }
4075 inline
4076 void clear_plug_padded (uint8_t* node)
4077 {
4078     header(node)->ClearMarked();
4079 }
4080 inline
4081 BOOL is_plug_padded (uint8_t* node)
4082 {
4083     return header(node)->IsMarked();
4084 }
4085 #else //SHORT_PLUGS
4086 inline void set_plug_padded (uint8_t* node){}
4087 inline void clear_plug_padded (uint8_t* node){}
4088 inline
4089 BOOL is_plug_padded (uint8_t* node){return FALSE;}
4090 #endif //SHORT_PLUGS
4091
4092
4093 inline size_t unused_array_size(uint8_t * p)
4094 {
4095     assert(((CObjectHeader*)p)->IsFree());
4096
4097     size_t* numComponentsPtr = (size_t*)(p + ArrayBase::GetOffsetOfNumComponents());
4098     return free_object_base_size + *numComponentsPtr;
4099 }
4100
4101 heap_segment* heap_segment_rw (heap_segment* ns)
4102 {
4103     if ((ns == 0) || !heap_segment_read_only_p (ns))
4104     {
4105         return ns;
4106     }
4107     else
4108     {
4109         do
4110         {
4111             ns = heap_segment_next (ns);
4112         } while ((ns != 0) && heap_segment_read_only_p (ns));
4113         return ns;
4114     }
4115 }
4116
4117 //returns the next non ro segment.
4118 heap_segment* heap_segment_next_rw (heap_segment* seg)
4119 {
4120     heap_segment* ns = heap_segment_next (seg);
4121     return heap_segment_rw (ns);
4122 }
4123
4124 // returns the segment before seg.
4125 heap_segment* heap_segment_prev_rw (heap_segment* begin, heap_segment* seg)
4126 {
4127     assert (begin != 0);
4128     heap_segment* prev = begin;
4129     heap_segment* current = heap_segment_next_rw (begin);
4130
4131     while (current && current != seg)
4132     {
4133         prev = current;
4134         current = heap_segment_next_rw (current);
4135     }
4136
4137     if (current == seg)
4138     {
4139         return prev;
4140     }
4141     else
4142     {
4143         return 0;
4144     }
4145 }
4146
4147 // returns the segment before seg.
4148 heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg)
4149 {
4150     assert (begin != 0);
4151     heap_segment* prev = begin;
4152     heap_segment* current = heap_segment_next (begin);
4153
4154     while (current && current != seg)
4155     {
4156         prev = current;
4157         current = heap_segment_next (current);
4158     }
4159
4160     if (current == seg)
4161     {
4162         return prev;
4163     }
4164     else
4165     {
4166         return 0;
4167     }
4168 }
4169
4170 heap_segment* heap_segment_in_range (heap_segment* ns)
4171 {
4172     if ((ns == 0) || heap_segment_in_range_p (ns))
4173     {
4174         return ns;
4175     }
4176     else
4177     {
4178         do
4179         {
4180             ns = heap_segment_next (ns);
4181         } while ((ns != 0) && !heap_segment_in_range_p (ns));
4182         return ns;
4183     }
4184 }
4185
4186 heap_segment* heap_segment_next_in_range (heap_segment* seg)
4187 {
4188     heap_segment* ns = heap_segment_next (seg);
4189     return heap_segment_in_range (ns);
4190 }
4191
4192 typedef struct
4193 {
4194     uint8_t* memory_base;
4195 } imemory_data;
4196
4197 typedef struct
4198 {
4199     imemory_data *initial_memory;
4200     imemory_data *initial_normal_heap; // points into initial_memory_array
4201     imemory_data *initial_large_heap;  // points into initial_memory_array
4202
4203     size_t block_size_normal;
4204     size_t block_size_large;
4205
4206     size_t block_count;                // # of blocks in each
4207     size_t current_block_normal;
4208     size_t current_block_large;
4209
4210     enum 
4211     { 
4212         ALLATONCE = 1, 
4213         TWO_STAGE, 
4214         EACH_BLOCK 
4215     };
4216
4217     size_t allocation_pattern;
4218 } initial_memory_details;
4219
4220 initial_memory_details memory_details;
4221
4222 BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
4223 {
4224     BOOL reserve_success = FALSE;
4225
4226     // should only be called once
4227     assert (memory_details.initial_memory == 0);
4228
4229     memory_details.initial_memory = new (nothrow) imemory_data[num_heaps*2];
4230     if (memory_details.initial_memory == 0)
4231     {
4232         dprintf (2, ("failed to reserve %Id bytes for imemory_data", num_heaps*2*sizeof(imemory_data)));
4233         return FALSE;
4234     }
4235
4236     memory_details.initial_normal_heap = memory_details.initial_memory;
4237     memory_details.initial_large_heap = memory_details.initial_memory + num_heaps;
4238     memory_details.block_size_normal = normal_size;
4239     memory_details.block_size_large = large_size;
4240     memory_details.block_count = num_heaps;
4241
4242     memory_details.current_block_normal = 0;
4243     memory_details.current_block_large = 0;
4244
4245     g_gc_lowest_address = MAX_PTR;
4246     g_gc_highest_address = 0;
4247
4248     if (((size_t)MAX_PTR - large_size) < normal_size)
4249     {
4250         // we are already overflowing with just one heap.
4251         dprintf (2, ("0x%Ix + 0x%Ix already overflow", normal_size, large_size));
4252         return FALSE;
4253     }
4254
4255     if (((size_t)MAX_PTR / memory_details.block_count) < (normal_size + large_size))
4256     {
4257         dprintf (2, ("(0x%Ix + 0x%Ix)*0x%Ix overflow", normal_size, large_size, memory_details.block_count));
4258         return FALSE;
4259     }
4260
4261     size_t requestedMemory = memory_details.block_count * (normal_size + large_size);
4262
4263     uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory);
4264     if (allatonce_block)
4265     {
4266         g_gc_lowest_address =  allatonce_block;
4267         g_gc_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size));
4268         memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
4269
4270         for(size_t i = 0; i < memory_details.block_count; i++)
4271         {
4272             memory_details.initial_normal_heap[i].memory_base = allatonce_block + (i*normal_size);
4273             memory_details.initial_large_heap[i].memory_base = allatonce_block +
4274                             (memory_details.block_count*normal_size) + (i*large_size);
4275             reserve_success = TRUE;
4276         }
4277     }
4278     else
4279     {
4280         // try to allocate 2 blocks
4281         uint8_t* b1 = 0;
4282         uint8_t* b2 = 0;
4283         b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size);
4284         if (b1)
4285         {
4286             b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size);
4287             if (b2)
4288             {
4289                 memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
4290                 g_gc_lowest_address = min(b1,b2);
4291                 g_gc_highest_address = max(b1 + memory_details.block_count*normal_size,
4292                                         b2 + memory_details.block_count*large_size);
4293                 for(size_t i = 0; i < memory_details.block_count; i++)
4294                 {
4295                     memory_details.initial_normal_heap[i].memory_base = b1 + (i*normal_size);
4296                     memory_details.initial_large_heap[i].memory_base = b2 + (i*large_size);
4297                     reserve_success = TRUE;
4298                 }
4299             }
4300             else
4301             {
4302                 // b2 allocation failed, we'll go on to try allocating each block.
4303                 // We could preserve the b1 alloc, but code complexity increases
4304                 virtual_free (b1, memory_details.block_count * normal_size);
4305             }
4306         }
4307
4308         if ((b2==NULL) && ( memory_details.block_count > 1))
4309         {
4310             memory_details.allocation_pattern = initial_memory_details::EACH_BLOCK;
4311
4312             imemory_data *current_block = memory_details.initial_memory;
4313             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4314             {
4315                 size_t block_size = ((i < memory_details.block_count) ?
4316                                      memory_details.block_size_normal :
4317                                      memory_details.block_size_large);
4318                 current_block->memory_base =
4319                     (uint8_t*)virtual_alloc (block_size);
4320                 if (current_block->memory_base == 0)
4321                 {
4322                     // Free the blocks that we've allocated so far
4323                     current_block = memory_details.initial_memory;
4324                     for(size_t j = 0; j < i; j++, current_block++){
4325                         if (current_block->memory_base != 0){
4326                             block_size = ((j < memory_details.block_count) ?
4327                                      memory_details.block_size_normal :
4328                                      memory_details.block_size_large);
4329                              virtual_free (current_block->memory_base , block_size);
4330                         }
4331                     }
4332                     reserve_success = FALSE;
4333                     break;
4334                 }
4335                 else
4336                 {
4337                     if (current_block->memory_base < g_gc_lowest_address)
4338                         g_gc_lowest_address =  current_block->memory_base;
4339                     if (((uint8_t *) current_block->memory_base + block_size) > g_gc_highest_address)
4340                         g_gc_highest_address = (current_block->memory_base + block_size);
4341                 }
4342                 reserve_success = TRUE;
4343             }
4344         }
4345     }
4346
4347     return reserve_success;
4348 }
4349
4350 void destroy_initial_memory()
4351 {
4352     if (memory_details.initial_memory != NULL)
4353     {
4354         if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE)
4355         {
4356             virtual_free(memory_details.initial_memory[0].memory_base,
4357                 memory_details.block_count*(memory_details.block_size_normal +
4358                 memory_details.block_size_large));
4359         }
4360         else if (memory_details.allocation_pattern == initial_memory_details::TWO_STAGE)
4361         {
4362             virtual_free (memory_details.initial_normal_heap[0].memory_base,
4363                 memory_details.block_count*memory_details.block_size_normal);
4364
4365             virtual_free (memory_details.initial_large_heap[0].memory_base,
4366                 memory_details.block_count*memory_details.block_size_large);
4367         }
4368         else
4369         {
4370             assert (memory_details.allocation_pattern == initial_memory_details::EACH_BLOCK);
4371             imemory_data *current_block = memory_details.initial_memory;
4372             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4373             {
4374                 size_t block_size = (i < memory_details.block_count) ? memory_details.block_size_normal :
4375                                                                        memory_details.block_size_large;
4376                 if (current_block->memory_base != NULL)
4377                 {
4378                     virtual_free (current_block->memory_base, block_size);
4379                 }
4380             }
4381         }
4382
4383         delete [] memory_details.initial_memory;
4384         memory_details.initial_memory = NULL;
4385         memory_details.initial_normal_heap = NULL;
4386         memory_details.initial_large_heap = NULL;
4387     }
4388 }
4389
4390 void* next_initial_memory (size_t size)
4391 {
4392     assert ((size == memory_details.block_size_normal) || (size == memory_details.block_size_large));
4393     void *res = NULL;
4394
4395     if ((size != memory_details.block_size_normal) ||
4396         ((memory_details.current_block_normal == memory_details.block_count) &&
4397          (memory_details.block_size_normal == memory_details.block_size_large)))
4398     {
4399         // If the block sizes are the same, flow block requests from normal to large
4400         assert (memory_details.current_block_large < memory_details.block_count);
4401         assert (memory_details.initial_large_heap != 0);
4402
4403         res = memory_details.initial_large_heap[memory_details.current_block_large].memory_base;
4404         memory_details.current_block_large++;
4405     }
4406     else
4407     {
4408         assert (memory_details.current_block_normal < memory_details.block_count);
4409         assert (memory_details.initial_normal_heap != NULL);
4410
4411         res = memory_details.initial_normal_heap[memory_details.current_block_normal].memory_base;
4412         memory_details.current_block_normal++;
4413     }
4414
4415     return res;
4416 }
4417
4418 heap_segment* get_initial_segment (size_t size, int h_number)
4419 {
4420     void* mem = next_initial_memory (size);
4421     heap_segment* res = gc_heap::make_heap_segment ((uint8_t*)mem, size , h_number);
4422
4423     return res;
4424 }
4425
4426 void* virtual_alloc (size_t size)
4427 {
4428     size_t requested_size = size;
4429
4430     if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4431     {
4432         gc_heap::reserved_memory_limit =
4433             GCScan::AskForMoreReservedMemory (gc_heap::reserved_memory_limit, requested_size);
4434         if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4435         {
4436             return 0;
4437         }
4438     }
4439
4440     uint32_t flags = VirtualReserveFlags::None;
4441 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4442     if (virtual_alloc_hardware_write_watch)
4443     {
4444         flags = VirtualReserveFlags::WriteWatch;
4445     }
4446 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4447     void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags);
4448     void *aligned_mem = prgmem;
4449
4450     // We don't want (prgmem + size) to be right at the end of the address space 
4451     // because we'd have to worry about that everytime we do (address + size).
4452     // We also want to make sure that we leave LARGE_OBJECT_SIZE at the end 
4453     // so we allocate a small object we don't need to worry about overflow there
4454     // when we do alloc_ptr+size.
4455     if (prgmem)
4456     {
4457         uint8_t* end_mem = (uint8_t*)prgmem + requested_size;
4458
4459         if ((end_mem == 0) || ((size_t)(MAX_PTR - end_mem) <= END_SPACE_AFTER_GC))
4460         {
4461             GCToOSInterface::VirtualRelease (prgmem, requested_size);
4462             dprintf (2, ("Virtual Alloc size %Id returned memory right against 4GB [%Ix, %Ix[ - discarding",
4463                         requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4464             prgmem = 0;
4465             aligned_mem = 0;
4466         }
4467     }
4468
4469     if (prgmem)
4470     {
4471         gc_heap::reserved_memory += requested_size;
4472     }
4473
4474     dprintf (2, ("Virtual Alloc size %Id: [%Ix, %Ix[",
4475                  requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4476
4477     return aligned_mem;
4478 }
4479
4480 void virtual_free (void* add, size_t size)
4481 {
4482     GCToOSInterface::VirtualRelease (add, size);
4483     gc_heap::reserved_memory -= size;
4484     dprintf (2, ("Virtual Free size %Id: [%Ix, %Ix[",
4485                  size, (size_t)add, (size_t)((uint8_t*)add+size)));
4486 }
4487
4488 static size_t get_valid_segment_size (BOOL large_seg=FALSE)
4489 {
4490     size_t seg_size, initial_seg_size;
4491
4492     if (!large_seg)
4493     {
4494         initial_seg_size = INITIAL_ALLOC;
4495         seg_size = static_cast<size_t>(GCConfig::GetSegmentSize());
4496     }
4497     else
4498     {
4499         initial_seg_size = LHEAP_ALLOC;
4500         seg_size = static_cast<size_t>(GCConfig::GetSegmentSize()) / 2;
4501     }
4502
4503 #ifdef MULTIPLE_HEAPS
4504 #ifdef BIT64
4505     if (!large_seg)
4506 #endif // BIT64
4507     {
4508         if (g_num_processors > 4)
4509             initial_seg_size /= 2;
4510         if (g_num_processors > 8)
4511             initial_seg_size /= 2;
4512     }
4513 #endif //MULTIPLE_HEAPS
4514
4515     // if seg_size is small but not 0 (0 is default if config not set)
4516     // then set the segment to the minimum size
4517     if (!g_theGCHeap->IsValidSegmentSize(seg_size))
4518     {
4519         // if requested size is between 1 byte and 4MB, use min
4520         if ((seg_size >> 1) && !(seg_size >> 22))
4521             seg_size = 1024*1024*4;
4522         else
4523             seg_size = initial_seg_size;
4524     }
4525
4526 #ifdef SEG_MAPPING_TABLE
4527 #ifdef BIT64
4528     seg_size = round_up_power2 (seg_size);
4529 #else
4530     seg_size = round_down_power2 (seg_size);
4531 #endif // BIT64
4532 #endif //SEG_MAPPING_TABLE
4533
4534     return (seg_size);
4535 }
4536
4537 void
4538 gc_heap::compute_new_ephemeral_size()
4539 {
4540     int eph_gen_max = max_generation - 1 - (settings.promotion ? 1 : 0);
4541     size_t padding_size = 0;
4542
4543     for (int i = 0; i <= eph_gen_max; i++)
4544     {
4545         dynamic_data* dd = dynamic_data_of (i);
4546         total_ephemeral_size += (dd_survived_size (dd) - dd_pinned_survived_size (dd));
4547 #ifdef RESPECT_LARGE_ALIGNMENT
4548         total_ephemeral_size += dd_num_npinned_plugs (dd) * switch_alignment_size (FALSE);
4549 #endif //RESPECT_LARGE_ALIGNMENT
4550 #ifdef FEATURE_STRUCTALIGN
4551         total_ephemeral_size += dd_num_npinned_plugs (dd) * MAX_STRUCTALIGN;
4552 #endif //FEATURE_STRUCTALIGN
4553
4554 #ifdef SHORT_PLUGS
4555         padding_size += dd_padding_size (dd);
4556 #endif //SHORT_PLUGS
4557     }
4558
4559     total_ephemeral_size += eph_gen_starts_size;
4560
4561 #ifdef RESPECT_LARGE_ALIGNMENT
4562     size_t planned_ephemeral_size = heap_segment_plan_allocated (ephemeral_heap_segment) -
4563                                        generation_plan_allocation_start (generation_of (max_generation-1));
4564     total_ephemeral_size = min (total_ephemeral_size, planned_ephemeral_size);
4565 #endif //RESPECT_LARGE_ALIGNMENT
4566
4567 #ifdef SHORT_PLUGS
4568     total_ephemeral_size = Align ((size_t)((double)total_ephemeral_size * short_plugs_pad_ratio) + 1);
4569     total_ephemeral_size += Align (DESIRED_PLUG_LENGTH);
4570 #endif //SHORT_PLUGS
4571
4572     dprintf (3, ("total ephemeral size is %Ix, padding %Ix(%Ix)", 
4573         total_ephemeral_size,
4574         padding_size, (total_ephemeral_size - padding_size)));
4575 }
4576
4577 #ifdef _MSC_VER
4578 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
4579 #endif // _MSC_VER
4580
4581 heap_segment*
4582 gc_heap::soh_get_segment_to_expand()
4583 {
4584     size_t size = soh_segment_size;
4585
4586     ordered_plug_indices_init = FALSE;
4587     use_bestfit = FALSE;
4588
4589     //compute the size of the new ephemeral heap segment.
4590     compute_new_ephemeral_size();
4591
4592     if ((settings.pause_mode != pause_low_latency) &&
4593         (settings.pause_mode != pause_no_gc)
4594 #ifdef BACKGROUND_GC
4595         && (!recursive_gc_sync::background_running_p())
4596 #endif //BACKGROUND_GC
4597         )
4598     {
4599         allocator*  gen_alloc = ((settings.condemned_generation == max_generation) ? 0 :
4600                               generation_allocator (generation_of (max_generation)));
4601         dprintf (2, ("(gen%d)soh_get_segment_to_expand", settings.condemned_generation));
4602
4603         // try to find one in the gen 2 segment list, search backwards because the first segments
4604         // tend to be more compact than the later ones.
4605         heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
4606
4607         PREFIX_ASSUME(fseg != NULL);
4608
4609 #ifdef SEG_REUSE_STATS
4610         int try_reuse = 0;
4611 #endif //SEG_REUSE_STATS
4612
4613         heap_segment* seg = ephemeral_heap_segment;
4614         while ((seg = heap_segment_prev_rw (fseg, seg)) && (seg != fseg))
4615         {
4616 #ifdef SEG_REUSE_STATS
4617         try_reuse++;
4618 #endif //SEG_REUSE_STATS
4619
4620             if (can_expand_into_p (seg, size/3, total_ephemeral_size, gen_alloc))
4621             {
4622                 get_gc_data_per_heap()->set_mechanism (gc_heap_expand, 
4623                     (use_bestfit ? expand_reuse_bestfit : expand_reuse_normal));
4624                 if (settings.condemned_generation == max_generation)
4625                 {
4626                     if (use_bestfit)
4627                     {
4628                         build_ordered_free_spaces (seg);
4629                         dprintf (GTC_LOG, ("can use best fit"));
4630                     }
4631
4632 #ifdef SEG_REUSE_STATS
4633                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse", 
4634                         settings.condemned_generation, try_reuse));
4635 #endif //SEG_REUSE_STATS
4636                     dprintf (GTC_LOG, ("max_gen: Found existing segment to expand into %Ix", (size_t)seg));
4637                     return seg;
4638                 }
4639                 else
4640                 {
4641 #ifdef SEG_REUSE_STATS
4642                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse - returning", 
4643                         settings.condemned_generation, try_reuse));
4644 #endif //SEG_REUSE_STATS
4645                     dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix", (size_t)seg));
4646
4647                     // If we return 0 here, the allocator will think since we are short on end
4648                     // of seg we neeed to trigger a full compacting GC. So if sustained low latency 
4649                     // is set we should acquire a new seg instead, that way we wouldn't be short.
4650                     // The real solution, of course, is to actually implement seg reuse in gen1.
4651                     if (settings.pause_mode != pause_sustained_low_latency)
4652                     {
4653                         dprintf (GTC_LOG, ("max_gen-1: SustainedLowLatency is set, acquire a new seg"));
4654                         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_next_full_gc);
4655                         return 0;
4656                     }
4657                 }
4658             }
4659         }
4660     }
4661
4662     heap_segment* result = get_segment (size, FALSE);
4663
4664     if(result)
4665     {
4666 #ifdef BACKGROUND_GC
4667         if (current_c_gc_state == c_gc_state_planning)
4668         {
4669             // When we expand heap during bgc sweep, we set the seg to be swept so 
4670             // we'll always look at cards for objects on the new segment.
4671             result->flags |= heap_segment_flags_swept;
4672         }
4673 #endif //BACKGROUND_GC
4674
4675         FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(result),
4676                                   (size_t)(heap_segment_reserved (result) - heap_segment_mem(result)),
4677                                   gc_etw_segment_small_object_heap);
4678     }
4679
4680     get_gc_data_per_heap()->set_mechanism (gc_heap_expand, (result ? expand_new_seg : expand_no_memory));
4681
4682     if (result == 0)
4683     {
4684         dprintf (2, ("h%d: failed to allocate a new segment!", heap_number));
4685     }
4686     else
4687     {
4688 #ifdef MULTIPLE_HEAPS
4689         heap_segment_heap (result) = this;
4690 #endif //MULTIPLE_HEAPS
4691     }
4692
4693     dprintf (GTC_LOG, ("(gen%d)creating new segment %Ix", settings.condemned_generation, result));
4694     return result;
4695 }
4696
4697 #ifdef _MSC_VER
4698 #pragma warning(default:4706)
4699 #endif // _MSC_VER
4700
4701 //returns 0 in case of allocation failure
4702 heap_segment*
4703 gc_heap::get_segment (size_t size, BOOL loh_p)
4704 {
4705     heap_segment* result = 0;
4706
4707     if (segment_standby_list != 0)
4708     {
4709         result = segment_standby_list;
4710         heap_segment* last = 0;
4711         while (result)
4712         {
4713             size_t hs = (size_t)(heap_segment_reserved (result) - (uint8_t*)result);
4714             if ((hs >= size) && ((hs / 2) < size))
4715             {
4716                 dprintf (2, ("Hoarded segment %Ix found", (size_t) result));
4717                 if (last)
4718                 {
4719                     heap_segment_next (last) = heap_segment_next (result);
4720                 }
4721                 else
4722                 {
4723                     segment_standby_list = heap_segment_next (result);
4724                 }
4725                 break;
4726             }
4727             else
4728             {
4729                 last = result;
4730                 result = heap_segment_next (result);
4731             }
4732         }
4733     }
4734
4735     if (result)
4736     {
4737         init_heap_segment (result);
4738 #ifdef BACKGROUND_GC
4739         if (should_commit_mark_array())
4740         {
4741             dprintf (GC_TABLE_LOG, ("hoarded seg %Ix, mark_array is %Ix", result, mark_array));
4742             if (!commit_mark_array_new_seg (__this, result))
4743             {
4744                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for hoarded seg"));
4745                 // If we can't use it we need to thread it back.
4746                 if (segment_standby_list != 0)
4747                 {
4748                     heap_segment_next (result) = segment_standby_list;
4749                     segment_standby_list = result;
4750                 }
4751                 else
4752                 {
4753                     segment_standby_list = result;
4754                 }
4755
4756                 result = 0;
4757             }
4758         }
4759 #endif //BACKGROUND_GC
4760
4761 #ifdef SEG_MAPPING_TABLE
4762         if (result)
4763             seg_mapping_table_add_segment (result, __this);
4764 #endif //SEG_MAPPING_TABLE
4765     }
4766
4767     if (!result)
4768     {
4769 #ifndef SEG_MAPPING_TABLE
4770         if (!seg_table->ensure_space_for_insert ())
4771             return 0;
4772 #endif //SEG_MAPPING_TABLE
4773         void* mem = virtual_alloc (size);
4774         if (!mem)
4775         {
4776             fgm_result.set_fgm (fgm_reserve_segment, size, loh_p);
4777             return 0;
4778         }
4779
4780         result = gc_heap::make_heap_segment ((uint8_t*)mem, size, heap_number);
4781
4782         if (result)
4783         {
4784             uint8_t* start;
4785             uint8_t* end;
4786             if (mem < g_gc_lowest_address)
4787             {
4788                 start =  (uint8_t*)mem;
4789             }
4790             else
4791             {
4792                 start = (uint8_t*)g_gc_lowest_address;
4793             }
4794
4795             if (((uint8_t*)mem + size) > g_gc_highest_address)
4796             {
4797                 end = (uint8_t*)mem + size;
4798             }
4799             else
4800             {
4801                 end = (uint8_t*)g_gc_highest_address;
4802             }
4803
4804             if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0)
4805             {
4806                 virtual_free (mem, size);
4807                 return 0;
4808             }
4809         }
4810         else
4811         {
4812             fgm_result.set_fgm (fgm_commit_segment_beg, SEGMENT_INITIAL_COMMIT, loh_p);
4813             virtual_free (mem, size);
4814         }
4815
4816         if (result)
4817         {
4818 #ifdef SEG_MAPPING_TABLE
4819             seg_mapping_table_add_segment (result, __this);
4820 #else //SEG_MAPPING_TABLE
4821             gc_heap::seg_table->insert ((uint8_t*)result, delta);
4822 #endif //SEG_MAPPING_TABLE
4823         }
4824     }
4825
4826 #ifdef BACKGROUND_GC
4827     if (result)
4828     {
4829         ::record_changed_seg ((uint8_t*)result, heap_segment_reserved (result), 
4830                             settings.gc_index, current_bgc_state,
4831                             seg_added);
4832         bgc_verify_mark_array_cleared (result);
4833     }
4834 #endif //BACKGROUND_GC
4835
4836     dprintf (GC_TABLE_LOG, ("h%d: new seg: %Ix-%Ix (%Id)", heap_number, result, ((uint8_t*)result + size), size));
4837     return result;
4838 }
4839
4840 void release_segment (heap_segment* sg)
4841 {
4842     ptrdiff_t delta = 0;
4843     FIRE_EVENT(GCFreeSegment_V1, heap_segment_mem(sg));
4844     virtual_free (sg, (uint8_t*)heap_segment_reserved (sg)-(uint8_t*)sg);
4845 }
4846
4847 heap_segment* gc_heap::get_segment_for_loh (size_t size
4848 #ifdef MULTIPLE_HEAPS
4849                                            , gc_heap* hp
4850 #endif //MULTIPLE_HEAPS
4851                                            )
4852 {
4853 #ifndef MULTIPLE_HEAPS
4854     gc_heap* hp = 0;
4855 #endif //MULTIPLE_HEAPS
4856     heap_segment* res = hp->get_segment (size, TRUE);
4857     if (res != 0)
4858     {
4859 #ifdef MULTIPLE_HEAPS
4860         heap_segment_heap (res) = hp;
4861 #endif //MULTIPLE_HEAPS
4862         res->flags |= heap_segment_flags_loh;
4863
4864         FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(res), (size_t)(heap_segment_reserved (res) - heap_segment_mem(res)), gc_etw_segment_large_object_heap);
4865
4866         GCToEEInterface::DiagUpdateGenerationBounds();
4867
4868 #ifdef MULTIPLE_HEAPS
4869         hp->thread_loh_segment (res);
4870 #else
4871         thread_loh_segment (res);
4872 #endif //MULTIPLE_HEAPS
4873     }
4874
4875     return res;
4876 }
4877
4878 void gc_heap::thread_loh_segment (heap_segment* new_seg)
4879 {
4880     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
4881
4882     while (heap_segment_next_rw (seg))
4883         seg = heap_segment_next_rw (seg);
4884     heap_segment_next (seg) = new_seg;
4885 }
4886
4887 heap_segment*
4888 gc_heap::get_large_segment (size_t size, BOOL* did_full_compact_gc)
4889 {
4890     *did_full_compact_gc = FALSE;
4891     size_t last_full_compact_gc_count = get_full_compact_gc_count();
4892
4893     //access to get_segment needs to be serialized
4894     add_saved_spinlock_info (me_release, mt_get_large_seg);
4895
4896     dprintf (SPINLOCK_LOG, ("[%d]Seg: Lmsl", heap_number));
4897     leave_spin_lock (&more_space_lock);
4898     enter_spin_lock (&gc_heap::gc_lock);
4899     dprintf (SPINLOCK_LOG, ("[%d]Seg: Egc", heap_number));
4900     // if a GC happened between here and before we ask for a segment in 
4901     // get_large_segment, we need to count that GC.
4902     size_t current_full_compact_gc_count = get_full_compact_gc_count();
4903
4904     if (current_full_compact_gc_count > last_full_compact_gc_count)
4905     {
4906         *did_full_compact_gc = TRUE;
4907     }
4908
4909 #ifdef BACKGROUND_GC
4910     while (current_c_gc_state == c_gc_state_planning)
4911     {
4912         dprintf (3, ("lh state planning, waiting to get a large seg"));
4913
4914         dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Lgc", heap_number));
4915         leave_spin_lock (&gc_lock);
4916         background_gc_wait_lh (awr_get_loh_seg);
4917         enter_spin_lock (&gc_lock);
4918         dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Egc", heap_number));
4919     }
4920     assert ((current_c_gc_state == c_gc_state_free) ||
4921             (current_c_gc_state == c_gc_state_marking));
4922 #endif //BACKGROUND_GC
4923
4924     heap_segment* res = get_segment_for_loh (size
4925 #ifdef MULTIPLE_HEAPS
4926                                             , this
4927 #endif //MULTIPLE_HEAPS
4928                                             );
4929
4930     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Lgc", heap_number));
4931     leave_spin_lock (&gc_heap::gc_lock);
4932     enter_spin_lock (&more_space_lock);
4933     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Emsl", heap_number));
4934     add_saved_spinlock_info (me_acquire, mt_get_large_seg);
4935     
4936 #ifdef BACKGROUND_GC
4937     wait_for_background_planning (awr_get_loh_seg);
4938 #endif //BACKGROUND_GC
4939
4940     return res;
4941 }
4942
4943 #if 0
4944 BOOL gc_heap::unprotect_segment (heap_segment* seg)
4945 {
4946     uint8_t* start = align_lower_page (heap_segment_mem (seg));
4947     ptrdiff_t region_size = heap_segment_allocated (seg) - start;
4948
4949     if (region_size != 0 )
4950     {
4951         dprintf (3, ("unprotecting segment %Ix:", (size_t)seg));
4952
4953         BOOL status = GCToOSInterface::VirtualUnprotect (start, region_size);
4954         assert (status);
4955         return status;
4956     }
4957     return FALSE;
4958 }
4959 #endif
4960
4961 #ifdef MULTIPLE_HEAPS
4962 #ifdef _X86_
4963 #ifdef _MSC_VER
4964 #pragma warning(disable:4035)
4965     static ptrdiff_t  get_cycle_count()
4966     {
4967         __asm   rdtsc
4968     }
4969 #pragma warning(default:4035)
4970 #elif defined(__GNUC__)
4971     static ptrdiff_t  get_cycle_count()
4972     {
4973         ptrdiff_t cycles;
4974         ptrdiff_t cyclesHi;
4975         __asm__ __volatile__
4976         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4977         return cycles;
4978     }
4979 #else //_MSC_VER
4980 #error Unknown compiler
4981 #endif //_MSC_VER
4982 #elif defined(_TARGET_AMD64_) 
4983 #ifdef _MSC_VER
4984 extern "C" uint64_t __rdtsc();
4985 #pragma intrinsic(__rdtsc)
4986     static ptrdiff_t get_cycle_count()
4987     {
4988         return (ptrdiff_t)__rdtsc();
4989     }
4990 #elif defined(__clang__)    
4991     static ptrdiff_t get_cycle_count()
4992     {
4993         ptrdiff_t cycles;
4994         ptrdiff_t cyclesHi;
4995         __asm__ __volatile__
4996         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4997         return (cyclesHi << 32) | cycles;
4998     }
4999 #else // _MSC_VER
5000     extern "C" ptrdiff_t get_cycle_count(void);
5001 #endif // _MSC_VER
5002 #elif defined(_TARGET_ARM_)
5003     static ptrdiff_t get_cycle_count()
5004     {
5005         // @ARMTODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
5006         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
5007         // all buffer access times being reported as equal in access_time().
5008         return 0;
5009     }
5010 #elif defined(_TARGET_ARM64_)
5011     static ptrdiff_t get_cycle_count()
5012     {
5013         // @ARM64TODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
5014         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
5015         // all buffer access times being reported as equal in access_time().
5016         return 0;
5017     }
5018 #else
5019 #error NYI platform: get_cycle_count
5020 #endif //_TARGET_X86_
5021
5022 class heap_select
5023 {
5024     heap_select() {}
5025     static uint8_t* sniff_buffer;
5026     static unsigned n_sniff_buffers;
5027     static unsigned cur_sniff_index;
5028
5029     static uint16_t proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5030     static uint16_t heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5031     static uint16_t heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5032     static uint16_t heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5033     static uint16_t heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5034     static uint16_t numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5035
5036     static int access_time(uint8_t *sniff_buffer, int heap_number, unsigned sniff_index, unsigned n_sniff_buffers)
5037     {
5038         ptrdiff_t start_cycles = get_cycle_count();
5039         uint8_t sniff = sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE];
5040         assert (sniff == 0);
5041         ptrdiff_t elapsed_cycles = get_cycle_count() - start_cycles;
5042         // add sniff here just to defeat the optimizer
5043         elapsed_cycles += sniff;
5044         return (int) elapsed_cycles;
5045     }
5046
5047 public:
5048     static BOOL init(int n_heaps)
5049     {
5050         assert (sniff_buffer == NULL && n_sniff_buffers == 0);
5051         if (!GCToOSInterface::CanGetCurrentProcessorNumber())
5052         {
5053             n_sniff_buffers = n_heaps*2+1;
5054             size_t n_cache_lines = 1 + n_heaps * n_sniff_buffers + 1;
5055             size_t sniff_buf_size = n_cache_lines * HS_CACHE_LINE_SIZE;
5056             if (sniff_buf_size / HS_CACHE_LINE_SIZE != n_cache_lines) // check for overlow
5057             {
5058                 return FALSE;
5059             }
5060
5061             sniff_buffer = new (nothrow) uint8_t[sniff_buf_size];
5062             if (sniff_buffer == 0)
5063                 return FALSE;
5064             memset(sniff_buffer, 0, sniff_buf_size*sizeof(uint8_t));
5065         }
5066
5067         //can not enable gc numa aware, force all heaps to be in
5068         //one numa node by filling the array with all 0s
5069         if (!GCToOSInterface::CanEnableGCNumaAware())
5070             memset(heap_no_to_numa_node, 0, sizeof (heap_no_to_numa_node)); 
5071
5072         return TRUE;
5073     }
5074
5075     static void init_cpu_mapping(gc_heap * /*heap*/, int heap_number)
5076     {
5077         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5078         {
5079             uint32_t proc_no = GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps;
5080             // We can safely cast heap_number to a uint16_t 'cause GetCurrentProcessCpuCount
5081             // only returns up to MAX_SUPPORTED_CPUS procs right now. We only ever create at most
5082             // MAX_SUPPORTED_CPUS GC threads.
5083             proc_no_to_heap_no[proc_no] = (uint16_t)heap_number;
5084         }
5085     }
5086
5087     static void mark_heap(int heap_number)
5088     {
5089         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5090             return;
5091
5092         for (unsigned sniff_index = 0; sniff_index < n_sniff_buffers; sniff_index++)
5093             sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5094     }
5095
5096     static int select_heap(alloc_context* acontext, int /*hint*/)
5097     {
5098         UNREFERENCED_PARAMETER(acontext); // only referenced by dprintf
5099
5100         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5101             return proc_no_to_heap_no[GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps];
5102
5103         unsigned sniff_index = Interlocked::Increment(&cur_sniff_index);
5104         sniff_index %= n_sniff_buffers;
5105
5106         int best_heap = 0;
5107         int best_access_time = 1000*1000*1000;
5108         int second_best_access_time = best_access_time;
5109
5110         uint8_t *l_sniff_buffer = sniff_buffer;
5111         unsigned l_n_sniff_buffers = n_sniff_buffers;
5112         for (int heap_number = 0; heap_number < gc_heap::n_heaps; heap_number++)
5113         {
5114             int this_access_time = access_time(l_sniff_buffer, heap_number, sniff_index, l_n_sniff_buffers);
5115             if (this_access_time < best_access_time)
5116             {
5117                 second_best_access_time = best_access_time;
5118                 best_access_time = this_access_time;
5119                 best_heap = heap_number;
5120             }
5121             else if (this_access_time < second_best_access_time)
5122             {
5123                 second_best_access_time = this_access_time;
5124             }
5125         }
5126
5127         if (best_access_time*2 < second_best_access_time)
5128         {
5129             sniff_buffer[(1 + best_heap*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5130
5131             dprintf (3, ("select_heap yields crisp %d for context %p\n", best_heap, (void *)acontext));
5132         }
5133         else
5134         {
5135             dprintf (3, ("select_heap yields vague %d for context %p\n", best_heap, (void *)acontext ));
5136         }
5137
5138         return best_heap;
5139     }
5140
5141     static bool can_find_heap_fast()
5142     {
5143         return GCToOSInterface::CanGetCurrentProcessorNumber();
5144     }
5145
5146     static uint16_t find_proc_no_from_heap_no(int heap_number)
5147     {
5148         return heap_no_to_proc_no[heap_number];
5149     }
5150
5151     static void set_proc_no_for_heap(int heap_number, uint16_t proc_no)
5152     {
5153         heap_no_to_proc_no[heap_number] = proc_no;
5154     }
5155
5156     static uint16_t find_numa_node_from_heap_no(int heap_number)
5157     {
5158         return heap_no_to_numa_node[heap_number];
5159     }
5160
5161     static void set_numa_node_for_heap(int heap_number, uint16_t numa_node)
5162     {
5163         heap_no_to_numa_node[heap_number] = numa_node;
5164     }
5165
5166     static uint16_t find_cpu_group_from_heap_no(int heap_number)
5167     {
5168         return heap_no_to_cpu_group[heap_number];
5169     }
5170
5171     static void set_cpu_group_for_heap(int heap_number, uint16_t group_number)
5172     {
5173         heap_no_to_cpu_group[heap_number] = group_number;
5174     }
5175
5176     static uint16_t find_group_proc_from_heap_no(int heap_number)
5177     {
5178         return heap_no_to_group_proc[heap_number];
5179     }
5180
5181     static void set_group_proc_for_heap(int heap_number, uint16_t group_proc)
5182     {
5183         heap_no_to_group_proc[heap_number] = group_proc;
5184     }
5185
5186     static void init_numa_node_to_heap_map(int nheaps)
5187     {   // called right after GCHeap::Init() for each heap is finished
5188         // when numa is not enabled, heap_no_to_numa_node[] are all filled
5189         // with 0s during initialization, and will be treated as one node
5190         numa_node_to_heap_map[0] = 0;
5191         int node_index = 1;
5192
5193         for (int i=1; i < nheaps; i++)
5194         {
5195             if (heap_no_to_numa_node[i] != heap_no_to_numa_node[i-1])
5196                 numa_node_to_heap_map[node_index++] = (uint16_t)i;
5197         }
5198         numa_node_to_heap_map[node_index] = (uint16_t)nheaps; //mark the end with nheaps
5199     }
5200
5201     static void get_heap_range_for_heap(int hn, int* start, int* end)
5202     {   // 1-tier/no numa case: heap_no_to_numa_node[] all zeros, 
5203         // and treated as in one node. thus: start=0, end=n_heaps
5204         uint16_t numa_node = heap_no_to_numa_node[hn];
5205         *start = (int)numa_node_to_heap_map[numa_node];
5206         *end   = (int)(numa_node_to_heap_map[numa_node+1]);
5207     }
5208 };
5209 uint8_t* heap_select::sniff_buffer;
5210 unsigned heap_select::n_sniff_buffers;
5211 unsigned heap_select::cur_sniff_index;
5212 uint16_t heap_select::proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5213 uint16_t heap_select::heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5214 uint16_t heap_select::heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5215 uint16_t heap_select::heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5216 uint16_t heap_select::heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5217 uint16_t heap_select::numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5218
5219 BOOL gc_heap::create_thread_support (unsigned number_of_heaps)
5220 {
5221     BOOL ret = FALSE;
5222     if (!gc_start_event.CreateOSManualEventNoThrow (FALSE))
5223     {
5224         goto cleanup;
5225     }
5226     if (!ee_suspend_event.CreateOSAutoEventNoThrow (FALSE))
5227     {
5228         goto cleanup;
5229     }
5230     if (!gc_t_join.init (number_of_heaps, join_flavor_server_gc))
5231     {
5232         goto cleanup;
5233     }
5234
5235     ret = TRUE;
5236
5237 cleanup:
5238
5239     if (!ret)
5240     {
5241         destroy_thread_support();
5242     }
5243
5244     return ret;
5245 }
5246
5247 void gc_heap::destroy_thread_support ()
5248 {
5249     if (ee_suspend_event.IsValid())
5250     {
5251         ee_suspend_event.CloseEvent();
5252     }
5253     if (gc_start_event.IsValid())
5254     {
5255         gc_start_event.CloseEvent();
5256     }
5257 }
5258
5259 void set_thread_group_affinity_for_heap(int heap_number, GCThreadAffinity* affinity)
5260 {
5261     affinity->Group = GCThreadAffinity::None;
5262     affinity->Processor = GCThreadAffinity::None;
5263
5264     uint16_t gn, gpn;
5265     GCToOSInterface::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
5266
5267     int bit_number = 0;
5268     for (uintptr_t mask = 1; mask !=0; mask <<=1) 
5269     {
5270         if (bit_number == gpn)
5271         {
5272             dprintf(3, ("using processor group %d, mask %Ix for heap %d\n", gn, mask, heap_number));
5273             affinity->Processor = gpn;
5274             affinity->Group = gn;
5275             heap_select::set_cpu_group_for_heap(heap_number, gn);
5276             heap_select::set_group_proc_for_heap(heap_number, gpn);
5277             if (GCToOSInterface::CanEnableGCNumaAware())
5278             {  
5279                 PROCESSOR_NUMBER proc_no;
5280                 proc_no.Group    = gn;
5281                 proc_no.Number   = (uint8_t)gpn;
5282                 proc_no.Reserved = 0;
5283
5284                 uint16_t node_no = 0;
5285                 if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no))
5286                     heap_select::set_numa_node_for_heap(heap_number, node_no);
5287             }
5288             else
5289             {   // no numa setting, each cpu group is treated as a node
5290                 heap_select::set_numa_node_for_heap(heap_number, gn);
5291             }
5292             return;
5293         }
5294         bit_number++;
5295     }
5296 }
5297
5298 void set_thread_affinity_mask_for_heap(int heap_number, GCThreadAffinity* affinity)
5299 {
5300     affinity->Group = GCThreadAffinity::None;
5301     affinity->Processor = GCThreadAffinity::None;
5302
5303     uintptr_t pmask, smask;
5304     if (GCToOSInterface::GetCurrentProcessAffinityMask(&pmask, &smask))
5305     {
5306         pmask &= smask;
5307         int bit_number = 0; 
5308         uint8_t proc_number = 0;
5309         for (uintptr_t mask = 1; mask != 0; mask <<= 1)
5310         {
5311             if ((mask & pmask) != 0)
5312             {
5313                 if (bit_number == heap_number)
5314                 {
5315                     dprintf (3, ("Using processor %d for heap %d", proc_number, heap_number));
5316                     affinity->Processor = proc_number;
5317                     heap_select::set_proc_no_for_heap(heap_number, proc_number);
5318                     if (GCToOSInterface::CanEnableGCNumaAware())
5319                     {
5320                         uint16_t node_no = 0;
5321                         PROCESSOR_NUMBER proc_no;
5322                         proc_no.Group = 0;
5323                         proc_no.Number = (uint8_t)proc_number;
5324                         proc_no.Reserved = 0;
5325                         if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no))
5326                         {
5327                             heap_select::set_numa_node_for_heap(heap_number, node_no);
5328                         }
5329                     }
5330                     return;
5331                 }
5332                 bit_number++;
5333             }
5334             proc_number++;
5335         }
5336     }
5337 }
5338
5339 bool gc_heap::create_gc_thread ()
5340 {
5341     dprintf (3, ("Creating gc thread\n"));
5342     return GCToEEInterface::CreateThread(gc_thread_stub, this, false, ".NET Server GC");
5343 }
5344
5345 #ifdef _MSC_VER
5346 #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
5347 #endif //_MSC_VER
5348 void gc_heap::gc_thread_function ()
5349 {
5350     assert (gc_done_event.IsValid());
5351     assert (gc_start_event.IsValid());
5352     dprintf (3, ("gc thread started"));
5353
5354     heap_select::init_cpu_mapping(this, heap_number);
5355
5356     while (1)
5357     {
5358         assert (!gc_t_join.joined());
5359
5360         if (heap_number == 0)
5361         {
5362             gc_heap::ee_suspend_event.Wait(INFINITE, FALSE);
5363
5364             BEGIN_TIMING(suspend_ee_during_log);
5365             GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
5366             END_TIMING(suspend_ee_during_log);
5367
5368             proceed_with_gc_p = TRUE;
5369
5370             if (!should_proceed_with_gc())
5371             {
5372                 update_collection_counts_for_no_gc();
5373                 proceed_with_gc_p = FALSE;
5374             }
5375             else
5376             {
5377                 settings.init_mechanisms();
5378                 gc_start_event.Set();
5379             }
5380             dprintf (3, ("%d gc thread waiting...", heap_number));
5381         }
5382         else
5383         {
5384             gc_start_event.Wait(INFINITE, FALSE);
5385             dprintf (3, ("%d gc thread waiting... Done", heap_number));
5386         }
5387
5388         assert ((heap_number == 0) || proceed_with_gc_p);
5389
5390         if (proceed_with_gc_p)
5391             garbage_collect (GCHeap::GcCondemnedGeneration);
5392
5393         if (heap_number == 0)
5394         {
5395             if (proceed_with_gc_p && (!settings.concurrent))
5396             {
5397                 do_post_gc();
5398             }
5399
5400 #ifdef BACKGROUND_GC
5401             recover_bgc_settings();
5402 #endif //BACKGROUND_GC
5403
5404 #ifdef MULTIPLE_HEAPS
5405             for (int i = 0; i < gc_heap::n_heaps; i++)
5406             {
5407                 gc_heap* hp = gc_heap::g_heaps[i];
5408                 hp->add_saved_spinlock_info (me_release, mt_block_gc);
5409                 dprintf (SPINLOCK_LOG, ("[%d]GC Lmsl", i));
5410                 leave_spin_lock(&hp->more_space_lock);
5411             }
5412 #endif //MULTIPLE_HEAPS
5413
5414             gc_heap::gc_started = FALSE;
5415
5416             BEGIN_TIMING(restart_ee_during_log);
5417             GCToEEInterface::RestartEE(TRUE);
5418             END_TIMING(restart_ee_during_log);
5419             process_sync_log_stats();
5420
5421             dprintf (SPINLOCK_LOG, ("GC Lgc"));
5422             leave_spin_lock (&gc_heap::gc_lock);
5423
5424             gc_heap::internal_gc_done = true;
5425
5426             if (proceed_with_gc_p)
5427                 set_gc_done();
5428             else
5429             {
5430                 // If we didn't actually do a GC, it means we didn't wait up the other threads,
5431                 // we still need to set the gc_done_event for those threads.
5432                 for (int i = 0; i < gc_heap::n_heaps; i++)
5433                 {
5434                     gc_heap* hp = gc_heap::g_heaps[i];
5435                     hp->set_gc_done();
5436                 }
5437             }
5438         }
5439         else
5440         {
5441             int spin_count = 32 * (gc_heap::n_heaps - 1);
5442
5443             // wait until RestartEE has progressed to a stage where we can restart user threads
5444             while (!gc_heap::internal_gc_done && !GCHeap::SafeToRestartManagedThreads())
5445             {
5446                 spin_and_switch (spin_count, (gc_heap::internal_gc_done || GCHeap::SafeToRestartManagedThreads()));
5447             }
5448             set_gc_done();
5449         }
5450     }
5451 }
5452 #ifdef _MSC_VER
5453 #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
5454 #endif //_MSC_VER
5455
5456 #endif //MULTIPLE_HEAPS
5457
5458 bool virtual_alloc_commit_for_heap(void* addr, size_t size, int h_number)
5459 {
5460 #if defined(MULTIPLE_HEAPS) && !defined(FEATURE_REDHAWK)
5461     // Currently there is no way for us to specific the numa node to allocate on via hosting interfaces to
5462     // a host. This will need to be added later.
5463 #if !defined(FEATURE_CORECLR)
5464     if (!CLRMemoryHosted())
5465 #endif
5466     {
5467         if (GCToOSInterface::CanEnableGCNumaAware())
5468         {
5469             uint32_t numa_node = heap_select::find_numa_node_from_heap_no(h_number);
5470             if (GCToOSInterface::VirtualCommit(addr, size, numa_node))
5471                 return true;
5472         }
5473     }
5474 #else
5475     UNREFERENCED_PARAMETER(h_number);
5476 #endif
5477
5478     //numa aware not enabled, or call failed --> fallback to VirtualCommit()
5479     return GCToOSInterface::VirtualCommit(addr, size);
5480 }
5481
5482 #ifndef SEG_MAPPING_TABLE
5483 inline
5484 heap_segment* gc_heap::segment_of (uint8_t* add, ptrdiff_t& delta, BOOL verify_p)
5485 {
5486     uint8_t* sadd = add;
5487     heap_segment* hs = 0;
5488     heap_segment* hs1 = 0;
5489     if (!((add >= g_gc_lowest_address) && (add < g_gc_highest_address)))
5490     {
5491         delta = 0;
5492         return 0;
5493     }
5494     //repeat in case there is a concurrent insertion in the table.
5495     do
5496     {
5497         hs = hs1;
5498         sadd = add;
5499         seg_table->lookup (sadd);
5500         hs1 = (heap_segment*)sadd;
5501     } while (hs1 && !in_range_for_segment (add, hs1) && (hs != hs1));
5502
5503     hs = hs1;
5504
5505     if ((hs == 0) ||
5506         (verify_p && (add > heap_segment_reserved ((heap_segment*)(sadd + delta)))))
5507         delta = 0;
5508     return hs;
5509 }
5510 #endif //SEG_MAPPING_TABLE
5511
5512 class mark
5513 {
5514 public:
5515     uint8_t* first;
5516     size_t len;
5517
5518     // If we want to save space we can have a pool of plug_and_gap's instead of 
5519     // always having 2 allocated for each pinned plug.
5520     gap_reloc_pair saved_pre_plug;
5521     // If we decide to not compact, we need to restore the original values.
5522     gap_reloc_pair saved_pre_plug_reloc;
5523
5524     gap_reloc_pair saved_post_plug;
5525
5526     // Supposedly Pinned objects cannot have references but we are seeing some from pinvoke 
5527     // frames. Also if it's an artificially pinned plug created by us, it can certainly 
5528     // have references. 
5529     // We know these cases will be rare so we can optimize this to be only allocated on decommand. 
5530     gap_reloc_pair saved_post_plug_reloc;
5531
5532     // We need to calculate this after we are done with plan phase and before compact
5533     // phase because compact phase will change the bricks so relocate_address will no 
5534     // longer work.
5535     uint8_t* saved_pre_plug_info_reloc_start;
5536
5537     // We need to save this because we will have no way to calculate it, unlike the 
5538     // pre plug info start which is right before this plug.
5539     uint8_t* saved_post_plug_info_start;
5540
5541 #ifdef SHORT_PLUGS
5542     uint8_t* allocation_context_start_region;
5543 #endif //SHORT_PLUGS
5544
5545     // How the bits in these bytes are organized:
5546     // MSB --> LSB
5547     // 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
5548     // 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.
5549     BOOL saved_pre_p;
5550     BOOL saved_post_p;
5551
5552 #ifdef _DEBUG
5553     // We are seeing this is getting corrupted for a PP with a NP after.
5554     // Save it when we first set it and make sure it doesn't change.
5555     gap_reloc_pair saved_post_plug_debug;
5556 #endif //_DEBUG
5557
5558     size_t get_max_short_bits()
5559     {
5560         return (sizeof (gap_reloc_pair) / sizeof (uint8_t*));
5561     }
5562
5563     // pre bits
5564     size_t get_pre_short_start_bit ()
5565     {
5566         return (sizeof (saved_pre_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5567     }
5568
5569     BOOL pre_short_p()
5570     {
5571         return (saved_pre_p & (1 << (sizeof (saved_pre_p) * 8 - 1)));
5572     }
5573
5574     void set_pre_short()
5575     {
5576         saved_pre_p |= (1 << (sizeof (saved_pre_p) * 8 - 1));
5577     }
5578
5579     void set_pre_short_bit (size_t bit)
5580     {
5581         saved_pre_p |= 1 << (get_pre_short_start_bit() + bit);
5582     }
5583
5584     BOOL pre_short_bit_p (size_t bit)
5585     {
5586         return (saved_pre_p & (1 << (get_pre_short_start_bit() + bit)));
5587     }
5588
5589 #ifdef COLLECTIBLE_CLASS
5590     void set_pre_short_collectible()
5591     {
5592         saved_pre_p |= 2;
5593     }
5594
5595     BOOL pre_short_collectible_p()
5596     {
5597         return (saved_pre_p & 2);
5598     }
5599 #endif //COLLECTIBLE_CLASS
5600
5601     // post bits
5602     size_t get_post_short_start_bit ()
5603     {
5604         return (sizeof (saved_post_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5605     }
5606
5607     BOOL post_short_p()
5608     {
5609         return (saved_post_p & (1 << (sizeof (saved_post_p) * 8 - 1)));
5610     }
5611
5612     void set_post_short()
5613     {
5614         saved_post_p |= (1 << (sizeof (saved_post_p) * 8 - 1));
5615     }
5616
5617     void set_post_short_bit (size_t bit)
5618     {
5619         saved_post_p |= 1 << (get_post_short_start_bit() + bit);
5620     }
5621
5622     BOOL post_short_bit_p (size_t bit)
5623     {
5624         return (saved_post_p & (1 << (get_post_short_start_bit() + bit)));
5625     }
5626
5627 #ifdef COLLECTIBLE_CLASS
5628     void set_post_short_collectible()
5629     {
5630         saved_post_p |= 2;
5631     }
5632
5633     BOOL post_short_collectible_p()
5634     {
5635         return (saved_post_p & 2);
5636     }
5637 #endif //COLLECTIBLE_CLASS
5638
5639     uint8_t* get_plug_address() { return first; }
5640
5641     BOOL has_pre_plug_info() { return saved_pre_p; }
5642     BOOL has_post_plug_info() { return saved_post_p; }
5643
5644     gap_reloc_pair* get_pre_plug_reloc_info() { return &saved_pre_plug_reloc; }
5645     gap_reloc_pair* get_post_plug_reloc_info() { return &saved_post_plug_reloc; }
5646     void set_pre_plug_info_reloc_start (uint8_t* reloc) { saved_pre_plug_info_reloc_start = reloc; }
5647     uint8_t* get_post_plug_info_start() { return saved_post_plug_info_start; }
5648
5649     // We need to temporarily recover the shortened plugs for compact phase so we can
5650     // copy over the whole plug and their related info (mark bits/cards). But we will
5651     // need to set the artificial gap back so compact phase can keep reading the plug info.
5652     // We also need to recover the saved info because we'll need to recover it later.
5653     // 
5654     // So we would call swap_p*_plug_and_saved once to recover the object info; then call 
5655     // it again to recover the artificial gap.
5656     void swap_pre_plug_and_saved()
5657     {
5658         gap_reloc_pair temp;
5659         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5660         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5661         saved_pre_plug_reloc = temp;
5662     }
5663
5664     void swap_post_plug_and_saved()
5665     {
5666         gap_reloc_pair temp;
5667         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5668         memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5669         saved_post_plug_reloc = temp;
5670     }
5671
5672     void swap_pre_plug_and_saved_for_profiler()
5673     {
5674         gap_reloc_pair temp;
5675         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5676         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5677         saved_pre_plug = temp;
5678     }
5679
5680     void swap_post_plug_and_saved_for_profiler()
5681     {
5682         gap_reloc_pair temp;
5683         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5684         memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5685         saved_post_plug = temp;
5686     }
5687
5688     // We should think about whether it's really necessary to have to copy back the pre plug
5689     // info since it was already copied during compacting plugs. But if a plug doesn't move
5690     // by >= 3 ptr size (the size of gap_reloc_pair), it means we'd have to recover pre plug info.
5691     void recover_plug_info() 
5692     {
5693         if (saved_pre_p)
5694         {
5695             if (gc_heap::settings.compaction)
5696             {
5697                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
5698                     first,
5699                     &saved_pre_plug_reloc, 
5700                     saved_pre_plug_info_reloc_start));
5701                 memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5702             }
5703             else
5704             {
5705                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
5706                     first,
5707                     &saved_pre_plug, 
5708                     (first - sizeof (plug_and_gap))));
5709                 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5710             }
5711         }
5712
5713         if (saved_post_p)
5714         {
5715             if (gc_heap::settings.compaction)
5716             {
5717                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
5718                     first,
5719                     &saved_post_plug_reloc, 
5720                     saved_post_plug_info_start));
5721                 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5722             }
5723             else
5724             {
5725                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
5726                     first,
5727                     &saved_post_plug, 
5728                     saved_post_plug_info_start));
5729                 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5730             }
5731         }
5732     }
5733 };
5734
5735
5736 void gc_mechanisms::init_mechanisms()
5737 {
5738     condemned_generation = 0;
5739     promotion = FALSE;//TRUE;
5740     compaction = TRUE;
5741 #ifdef FEATURE_LOH_COMPACTION
5742     loh_compaction = gc_heap::should_compact_loh();
5743 #else
5744     loh_compaction = FALSE;
5745 #endif //FEATURE_LOH_COMPACTION
5746     heap_expansion = FALSE;
5747     concurrent = FALSE;
5748     demotion = FALSE;
5749     elevation_reduced = FALSE;
5750     found_finalizers = FALSE;
5751 #ifdef BACKGROUND_GC
5752     background_p = recursive_gc_sync::background_running_p() != FALSE;
5753     allocations_allowed = TRUE;
5754 #endif //BACKGROUND_GC
5755
5756     entry_memory_load = 0;
5757     exit_memory_load = 0;
5758
5759 #ifdef STRESS_HEAP
5760     stress_induced = FALSE;
5761 #endif // STRESS_HEAP
5762 }
5763
5764 void gc_mechanisms::first_init()
5765 {
5766     gc_index = 0;
5767     gen0_reduction_count = 0;
5768     should_lock_elevation = FALSE;
5769     elevation_locked_count = 0;
5770     reason = reason_empty;
5771 #ifdef BACKGROUND_GC
5772     pause_mode = gc_heap::gc_can_use_concurrent ? pause_interactive : pause_batch;
5773 #ifdef _DEBUG
5774     int debug_pause_mode = static_cast<int>(GCConfig::GetLatencyMode());
5775     if (debug_pause_mode >= 0)
5776     {
5777         assert (debug_pause_mode <= pause_sustained_low_latency);
5778         pause_mode = (gc_pause_mode)debug_pause_mode;
5779     }
5780 #endif //_DEBUG
5781 #else //BACKGROUND_GC
5782     pause_mode = pause_batch;
5783 #endif //BACKGROUND_GC
5784
5785     init_mechanisms();
5786 }
5787
5788 void gc_mechanisms::record (gc_history_global* history)
5789 {
5790 #ifdef MULTIPLE_HEAPS
5791     history->num_heaps = gc_heap::n_heaps;
5792 #else
5793     history->num_heaps = 1;
5794 #endif //MULTIPLE_HEAPS
5795
5796     history->condemned_generation = condemned_generation;
5797     history->gen0_reduction_count = gen0_reduction_count;
5798     history->reason = reason;
5799     history->pause_mode = (int)pause_mode;
5800     history->mem_pressure = entry_memory_load;
5801     history->global_mechanims_p = 0;
5802
5803     // start setting the boolean values.
5804     if (concurrent)
5805         history->set_mechanism_p (global_concurrent);
5806     
5807     if (compaction)
5808         history->set_mechanism_p (global_compaction);
5809
5810     if (promotion)
5811         history->set_mechanism_p (global_promotion);
5812     
5813     if (demotion)
5814         history->set_mechanism_p (global_demotion);
5815
5816     if (card_bundles)
5817         history->set_mechanism_p (global_card_bundles);
5818
5819     if (elevation_reduced)
5820         history->set_mechanism_p (global_elevation);
5821 }
5822
5823 /**********************************
5824    called at the beginning of GC to fix the allocated size to
5825    what is really allocated, or to turn the free area into an unused object
5826    It needs to be called after all of the other allocation contexts have been
5827    fixed since it relies on alloc_allocated.
5828  ********************************/
5829
5830 //for_gc_p indicates that the work is being done for GC,
5831 //as opposed to concurrent heap verification
5832 void gc_heap::fix_youngest_allocation_area (BOOL for_gc_p)
5833 {
5834     UNREFERENCED_PARAMETER(for_gc_p);
5835
5836     // The gen 0 alloc context is never used for allocation in the allocator path. It's
5837     // still used in the allocation path during GCs.
5838     assert (generation_allocation_pointer (youngest_generation) == nullptr);
5839     assert (generation_allocation_limit (youngest_generation) == nullptr);
5840     heap_segment_allocated (ephemeral_heap_segment) = alloc_allocated;
5841 }
5842
5843 void gc_heap::fix_large_allocation_area (BOOL for_gc_p)
5844 {
5845     UNREFERENCED_PARAMETER(for_gc_p);
5846
5847 #ifdef _DEBUG
5848     alloc_context* acontext = 
5849 #endif // _DEBUG
5850         generation_alloc_context (large_object_generation);
5851     assert (acontext->alloc_ptr == 0);
5852     assert (acontext->alloc_limit == 0); 
5853 #if 0
5854     dprintf (3, ("Large object alloc context: ptr: %Ix, limit %Ix",
5855                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5856     fix_allocation_context (acontext, FALSE, get_alignment_constant (FALSE));
5857     if (for_gc_p)
5858     {
5859         acontext->alloc_ptr = 0;
5860         acontext->alloc_limit = acontext->alloc_ptr;
5861     }
5862 #endif //0
5863 }
5864
5865 //for_gc_p indicates that the work is being done for GC,
5866 //as opposed to concurrent heap verification
5867 void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
5868                                       int align_const)
5869 {
5870     dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
5871                  (size_t)acontext,
5872                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5873
5874     if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) ||
5875         !for_gc_p)
5876     {
5877         uint8_t*  point = acontext->alloc_ptr;
5878         if (point != 0)
5879         {
5880             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
5881             // the allocation area was from the free list
5882             // it was shortened by Align (min_obj_size) to make room for
5883             // at least the shortest unused object
5884             size += Align (min_obj_size, align_const);
5885             assert ((size >= Align (min_obj_size)));
5886
5887             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point,
5888                        (size_t)point + size ));
5889             make_unused_array (point, size);
5890
5891             if (for_gc_p)
5892             {
5893                 generation_free_obj_space (generation_of (0)) += size;
5894                 alloc_contexts_used ++;
5895             }
5896         }
5897     }
5898     else if (for_gc_p)
5899     {
5900         alloc_allocated = acontext->alloc_ptr;
5901         assert (heap_segment_allocated (ephemeral_heap_segment) <=
5902                 heap_segment_committed (ephemeral_heap_segment));
5903         alloc_contexts_used ++;
5904     }
5905
5906     if (for_gc_p)
5907     {
5908         // We need to update the alloc_bytes to reflect the portion that we have not used  
5909         acontext->alloc_bytes -= (acontext->alloc_limit - acontext->alloc_ptr);  
5910         acontext->alloc_ptr = 0;
5911         acontext->alloc_limit = acontext->alloc_ptr;
5912     }
5913 }
5914
5915 //used by the heap verification for concurrent gc.
5916 //it nulls out the words set by fix_allocation_context for heap_verification
5917 void repair_allocation (gc_alloc_context* acontext, void*)
5918 {
5919     uint8_t*  point = acontext->alloc_ptr;
5920
5921     if (point != 0)
5922     {
5923         dprintf (3, ("Clearing [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5924                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
5925         memclr (acontext->alloc_ptr - plug_skew,
5926                 (acontext->alloc_limit - acontext->alloc_ptr)+Align (min_obj_size));
5927     }
5928 }
5929
5930 void void_allocation (gc_alloc_context* acontext, void*)
5931 {
5932     uint8_t*  point = acontext->alloc_ptr;
5933
5934     if (point != 0)
5935     {
5936         dprintf (3, ("Void [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5937                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
5938         acontext->alloc_ptr = 0;
5939         acontext->alloc_limit = acontext->alloc_ptr;
5940     }
5941 }
5942
5943 void gc_heap::repair_allocation_contexts (BOOL repair_p)
5944 {
5945     GCToEEInterface::GcEnumAllocContexts (repair_p ? repair_allocation : void_allocation, NULL);
5946 }
5947
5948 struct fix_alloc_context_args
5949 {
5950     BOOL for_gc_p;
5951     void* heap;
5952 };
5953
5954 void fix_alloc_context(gc_alloc_context* acontext, void* param)
5955 {
5956     fix_alloc_context_args* args = (fix_alloc_context_args*)param;
5957     g_theGCHeap->FixAllocContext(acontext, false, (void*)(size_t)(args->for_gc_p), args->heap);
5958 }
5959
5960 void gc_heap::fix_allocation_contexts(BOOL for_gc_p)
5961 {
5962     fix_alloc_context_args args;
5963     args.for_gc_p = for_gc_p;
5964     args.heap = __this;
5965
5966     GCToEEInterface::GcEnumAllocContexts(fix_alloc_context, &args);
5967     fix_youngest_allocation_area(for_gc_p);
5968     fix_large_allocation_area(for_gc_p);
5969 }
5970
5971 void gc_heap::fix_older_allocation_area (generation* older_gen)
5972 {
5973     heap_segment* older_gen_seg = generation_allocation_segment (older_gen);
5974     if (generation_allocation_limit (older_gen) !=
5975         heap_segment_plan_allocated (older_gen_seg))
5976     {
5977         uint8_t*  point = generation_allocation_pointer (older_gen);
5978
5979         size_t  size = (generation_allocation_limit (older_gen) -
5980                                generation_allocation_pointer (older_gen));
5981         if (size != 0)
5982         {
5983             assert ((size >= Align (min_obj_size)));
5984             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, (size_t)point+size));
5985             make_unused_array (point, size);
5986         }
5987     }
5988     else
5989     {
5990         assert (older_gen_seg != ephemeral_heap_segment);
5991         heap_segment_plan_allocated (older_gen_seg) =
5992             generation_allocation_pointer (older_gen);
5993         generation_allocation_limit (older_gen) =
5994             generation_allocation_pointer (older_gen);
5995     }
5996 }
5997
5998 void gc_heap::set_allocation_heap_segment (generation* gen)
5999 {
6000     uint8_t* p = generation_allocation_start (gen);
6001     assert (p);
6002     heap_segment* seg = generation_allocation_segment (gen);
6003     if (in_range_for_segment (p, seg))
6004         return;
6005
6006     // try ephemeral heap segment in case of heap expansion
6007     seg = ephemeral_heap_segment;
6008     if (!in_range_for_segment (p, seg))
6009     {
6010         seg = heap_segment_rw (generation_start_segment (gen));
6011
6012         PREFIX_ASSUME(seg != NULL);
6013
6014         while (!in_range_for_segment (p, seg))
6015         {
6016             seg = heap_segment_next_rw (seg);
6017             PREFIX_ASSUME(seg != NULL);
6018         }
6019     }
6020
6021     generation_allocation_segment (gen) = seg;
6022 }
6023
6024 void gc_heap::reset_allocation_pointers (generation* gen, uint8_t* start)
6025 {
6026     assert (start);
6027     assert (Align ((size_t)start) == (size_t)start);
6028     generation_allocation_start (gen) = start;
6029     generation_allocation_pointer (gen) =  0;//start + Align (min_obj_size);
6030     generation_allocation_limit (gen) = 0;//generation_allocation_pointer (gen);
6031     set_allocation_heap_segment (gen);
6032 }
6033
6034 #ifdef BACKGROUND_GC
6035 //TODO BACKGROUND_GC this is for test only
6036 void
6037 gc_heap::disallow_new_allocation (int gen_number)
6038 {
6039     UNREFERENCED_PARAMETER(gen_number);
6040     settings.allocations_allowed = FALSE;
6041 }
6042 void
6043 gc_heap::allow_new_allocation (int gen_number)
6044 {
6045     UNREFERENCED_PARAMETER(gen_number);
6046     settings.allocations_allowed = TRUE;
6047 }
6048
6049 #endif //BACKGROUND_GC
6050
6051 bool gc_heap::new_allocation_allowed (int gen_number)
6052 {
6053 #ifdef BACKGROUND_GC
6054     //TODO BACKGROUND_GC this is for test only
6055     if (!settings.allocations_allowed)
6056     {
6057         dprintf (2, ("new allocation not allowed"));
6058         return FALSE;
6059     }
6060 #endif //BACKGROUND_GC
6061
6062     if (dd_new_allocation (dynamic_data_of (gen_number)) < 0)
6063     {
6064         if (gen_number != 0)
6065         {
6066             // For LOH we will give it more budget before we try a GC.
6067             if (settings.concurrent)
6068             {
6069                 dynamic_data* dd2 = dynamic_data_of (max_generation + 1 );
6070
6071                 if (dd_new_allocation (dd2) <= (ptrdiff_t)(-2 * dd_desired_allocation (dd2)))
6072                 {
6073                     return TRUE;
6074                 }
6075             }
6076         }
6077         return FALSE;
6078     }
6079 #ifndef MULTIPLE_HEAPS
6080     else if ((settings.pause_mode != pause_no_gc) && (gen_number == 0))
6081     {
6082         dprintf (3, ("evaluating allocation rate"));
6083         dynamic_data* dd0 = dynamic_data_of (0);
6084         if ((allocation_running_amount - dd_new_allocation (dd0)) >
6085             dd_min_size (dd0))
6086         {
6087             uint32_t ctime = GCToOSInterface::GetLowPrecisionTimeStamp();
6088             if ((ctime - allocation_running_time) > 1000)
6089             {
6090                 dprintf (2, (">1s since last gen0 gc"));
6091                 return FALSE;
6092             }
6093             else
6094             {
6095                 allocation_running_amount = dd_new_allocation (dd0);
6096             }
6097         }
6098     }
6099 #endif //MULTIPLE_HEAPS
6100     return TRUE;
6101 }
6102
6103 inline
6104 ptrdiff_t gc_heap::get_desired_allocation (int gen_number)
6105 {
6106     return dd_desired_allocation (dynamic_data_of (gen_number));
6107 }
6108
6109 inline
6110 ptrdiff_t  gc_heap::get_new_allocation (int gen_number)
6111 {
6112     return dd_new_allocation (dynamic_data_of (gen_number));
6113 }
6114
6115 //return the amount allocated so far in gen_number
6116 inline
6117 ptrdiff_t  gc_heap::get_allocation (int gen_number)
6118 {
6119     dynamic_data* dd = dynamic_data_of (gen_number);
6120
6121     return dd_desired_allocation (dd) - dd_new_allocation (dd);
6122 }
6123
6124 inline
6125 BOOL grow_mark_stack (mark*& m, size_t& len, size_t init_len)
6126 {
6127     size_t new_size = max (init_len, 2*len);
6128     mark* tmp = new (nothrow) mark [new_size];
6129     if (tmp)
6130     {
6131         memcpy (tmp, m, len * sizeof (mark));
6132         delete m;
6133         m = tmp;
6134         len = new_size;
6135         return TRUE;
6136     }
6137     else
6138     {
6139         dprintf (1, ("Failed to allocate %Id bytes for mark stack", (len * sizeof (mark))));
6140         return FALSE;
6141     }
6142 }
6143
6144 inline
6145 uint8_t* pinned_plug (mark* m)
6146 {
6147    return m->first;
6148 }
6149
6150 inline
6151 size_t& pinned_len (mark* m)
6152 {
6153     return m->len;
6154 }
6155
6156 inline
6157 void set_new_pin_info (mark* m, uint8_t* pin_free_space_start)
6158 {
6159     m->len = pinned_plug (m) - pin_free_space_start;
6160 #ifdef SHORT_PLUGS
6161     m->allocation_context_start_region = pin_free_space_start;
6162 #endif //SHORT_PLUGS
6163 }
6164
6165 #ifdef SHORT_PLUGS
6166 inline
6167 uint8_t*& pin_allocation_context_start_region (mark* m)
6168 {
6169     return m->allocation_context_start_region;
6170 }
6171
6172 uint8_t* get_plug_start_in_saved (uint8_t* old_loc, mark* pinned_plug_entry)
6173 {
6174     uint8_t* saved_pre_plug_info = (uint8_t*)(pinned_plug_entry->get_pre_plug_reloc_info());
6175     uint8_t* plug_start_in_saved = saved_pre_plug_info + (old_loc - (pinned_plug (pinned_plug_entry) - sizeof (plug_and_gap)));
6176     //dprintf (1, ("detected a very short plug: %Ix before PP %Ix, pad %Ix", 
6177     //    old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6178     dprintf (1, ("EP: %Ix(%Ix), %Ix", old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6179     return plug_start_in_saved;
6180 }
6181
6182 inline
6183 void set_padding_in_expand (uint8_t* old_loc,
6184                             BOOL set_padding_on_saved_p,
6185                             mark* pinned_plug_entry)
6186 {
6187     if (set_padding_on_saved_p)
6188     {
6189         set_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6190     }
6191     else
6192     {
6193         set_plug_padded (old_loc);
6194     }
6195 }
6196
6197 inline
6198 void clear_padding_in_expand (uint8_t* old_loc,
6199                               BOOL set_padding_on_saved_p,
6200                               mark* pinned_plug_entry)
6201 {
6202     if (set_padding_on_saved_p)
6203     {
6204         clear_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6205     }
6206     else
6207     {
6208         clear_plug_padded (old_loc);
6209     }
6210 }
6211 #endif //SHORT_PLUGS
6212
6213 void gc_heap::reset_pinned_queue()
6214 {
6215     mark_stack_tos = 0;
6216     mark_stack_bos = 0;
6217 }
6218
6219 void gc_heap::reset_pinned_queue_bos()
6220 {
6221     mark_stack_bos = 0;
6222 }
6223
6224 // last_pinned_plug is only for asserting purpose.
6225 void gc_heap::merge_with_last_pinned_plug (uint8_t* last_pinned_plug, size_t plug_size)
6226 {
6227     if (last_pinned_plug)
6228     {
6229         mark& last_m = mark_stack_array[mark_stack_tos - 1];
6230         assert (last_pinned_plug == last_m.first);
6231         if (last_m.saved_post_p)
6232         {
6233             last_m.saved_post_p = FALSE;
6234             dprintf (3, ("setting last plug %Ix post to false", last_m.first));
6235             // We need to recover what the gap has overwritten.
6236             memcpy ((last_m.first + last_m.len - sizeof (plug_and_gap)), &(last_m.saved_post_plug), sizeof (gap_reloc_pair));
6237         }
6238         last_m.len += plug_size;
6239         dprintf (3, ("recovered the last part of plug %Ix, setting its plug size to %Ix", last_m.first, last_m.len));
6240     }
6241 }
6242
6243 void gc_heap::set_allocator_next_pin (uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6244 {
6245     dprintf (3, ("sanp: ptr: %Ix, limit: %Ix", alloc_pointer, alloc_limit));
6246     dprintf (3, ("oldest %Id: %Ix", mark_stack_bos, pinned_plug (oldest_pin())));
6247     if (!(pinned_plug_que_empty_p()))
6248     {
6249         mark*  oldest_entry = oldest_pin();
6250         uint8_t* plug = pinned_plug (oldest_entry);
6251         if ((plug >= alloc_pointer) && (plug < alloc_limit))
6252         {
6253             alloc_limit = pinned_plug (oldest_entry);
6254             dprintf (3, ("now setting alloc context: %Ix->%Ix(%Id)",
6255                 alloc_pointer, alloc_limit, (alloc_limit - alloc_pointer)));
6256         }
6257     }
6258 }
6259
6260 void gc_heap::set_allocator_next_pin (generation* gen)
6261 {
6262     dprintf (3, ("SANP: gen%d, ptr; %Ix, limit: %Ix", gen->gen_num, generation_allocation_pointer (gen), generation_allocation_limit (gen)));
6263     if (!(pinned_plug_que_empty_p()))
6264     {
6265         mark*  oldest_entry = oldest_pin();
6266         uint8_t* plug = pinned_plug (oldest_entry);
6267         if ((plug >= generation_allocation_pointer (gen)) &&
6268             (plug <  generation_allocation_limit (gen)))
6269         {
6270             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
6271             dprintf (3, ("SANP: get next pin free space in gen%d for alloc: %Ix->%Ix(%Id)", 
6272                 gen->gen_num,
6273                 generation_allocation_pointer (gen), generation_allocation_limit (gen),
6274                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
6275         }
6276         else
6277             assert (!((plug < generation_allocation_pointer (gen)) &&
6278                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
6279     }
6280 }
6281
6282 // After we set the info, we increase tos.
6283 void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6284 {
6285     UNREFERENCED_PARAMETER(last_pinned_plug);
6286
6287     mark& m = mark_stack_array[mark_stack_tos];
6288     assert (m.first == last_pinned_plug);
6289
6290     m.len = plug_len;
6291     mark_stack_tos++;
6292     set_allocator_next_pin (alloc_pointer, alloc_limit);
6293 }
6294
6295 // After we set the info, we increase tos.
6296 void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, generation* gen)
6297 {
6298     UNREFERENCED_PARAMETER(last_pinned_plug);
6299
6300     mark& m = mark_stack_array[mark_stack_tos];
6301     assert (m.first == last_pinned_plug);
6302
6303     m.len = plug_len;
6304     mark_stack_tos++;
6305     assert (gen != 0);
6306     // Why are we checking here? gen is never 0.
6307     if (gen != 0)
6308     {
6309         set_allocator_next_pin (gen);
6310     }
6311 }
6312
6313 size_t gc_heap::deque_pinned_plug ()
6314 {
6315     dprintf (3, ("dequed: %Id", mark_stack_bos));
6316     size_t m = mark_stack_bos;
6317     mark_stack_bos++;
6318     return m;
6319 }
6320
6321 inline
6322 mark* gc_heap::pinned_plug_of (size_t bos)
6323 {
6324     return &mark_stack_array [ bos ];
6325 }
6326
6327 inline
6328 mark* gc_heap::oldest_pin ()
6329 {
6330     return pinned_plug_of (mark_stack_bos);
6331 }
6332
6333 inline
6334 BOOL gc_heap::pinned_plug_que_empty_p ()
6335 {
6336     return (mark_stack_bos == mark_stack_tos);
6337 }
6338
6339 inline
6340 mark* gc_heap::before_oldest_pin()
6341 {
6342     if (mark_stack_bos >= 1)
6343         return pinned_plug_of (mark_stack_bos-1);
6344     else
6345         return 0;
6346 }
6347
6348 inline
6349 BOOL gc_heap::ephemeral_pointer_p (uint8_t* o)
6350 {
6351     return ((o >= ephemeral_low) && (o < ephemeral_high));
6352 }
6353
6354 #ifdef MH_SC_MARK
6355 inline
6356 int& gc_heap::mark_stack_busy()
6357 {
6358     return  g_mark_stack_busy [(heap_number+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
6359 }
6360 #endif //MH_SC_MARK
6361
6362 void gc_heap::make_mark_stack (mark* arr)
6363 {
6364     reset_pinned_queue();
6365     mark_stack_array = arr;
6366     mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6367 #ifdef MH_SC_MARK
6368     mark_stack_busy() = 0;
6369 #endif //MH_SC_MARK
6370 }
6371
6372 #ifdef BACKGROUND_GC
6373 inline
6374 size_t& gc_heap::bpromoted_bytes(int thread)
6375 {
6376 #ifdef MULTIPLE_HEAPS
6377     return g_bpromoted [thread*16];
6378 #else //MULTIPLE_HEAPS
6379     UNREFERENCED_PARAMETER(thread);
6380     return g_bpromoted;
6381 #endif //MULTIPLE_HEAPS
6382 }
6383
6384 void gc_heap::make_background_mark_stack (uint8_t** arr)
6385 {
6386     background_mark_stack_array = arr;
6387     background_mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6388     background_mark_stack_tos = arr;
6389 }
6390
6391 void gc_heap::make_c_mark_list (uint8_t** arr)
6392 {
6393     c_mark_list = arr;
6394     c_mark_list_index = 0;
6395     c_mark_list_length = 1 + (OS_PAGE_SIZE / MIN_OBJECT_SIZE);
6396 }
6397 #endif //BACKGROUND_GC
6398
6399
6400 #ifdef CARD_BUNDLE
6401
6402 // The card bundle keeps track of groups of card words.
6403 static const size_t card_bundle_word_width = 32;
6404
6405 // How do we express the fact that 32 bits (card_word_width) is one uint32_t?
6406 static const size_t card_bundle_size = (size_t)(GC_PAGE_SIZE / (sizeof(uint32_t)*card_bundle_word_width));
6407
6408 inline
6409 size_t card_bundle_word (size_t cardb)
6410 {
6411     return cardb / card_bundle_word_width;
6412 }
6413
6414 inline
6415 uint32_t card_bundle_bit (size_t cardb)
6416 {
6417     return (uint32_t)(cardb % card_bundle_word_width);
6418 }
6419
6420 size_t align_cardw_on_bundle (size_t cardw)
6421 {
6422     return ((size_t)(cardw + card_bundle_size - 1) & ~(card_bundle_size - 1 ));
6423 }
6424
6425 // Get the card bundle representing a card word
6426 size_t cardw_card_bundle (size_t cardw)
6427 {
6428     return cardw / card_bundle_size;
6429 }
6430
6431 // Get the first card word in a card bundle
6432 size_t card_bundle_cardw (size_t cardb)
6433 {
6434     return cardb * card_bundle_size;
6435 }
6436
6437 // Clear the specified card bundle
6438 void gc_heap::card_bundle_clear (size_t cardb)
6439 {
6440     card_bundle_table [card_bundle_word (cardb)] &= ~(1 << card_bundle_bit (cardb));
6441     dprintf (1,("Cleared card bundle %Ix [%Ix, %Ix[", cardb, (size_t)card_bundle_cardw (cardb),
6442               (size_t)card_bundle_cardw (cardb+1)));
6443 }
6444
6445 void gc_heap::card_bundle_set (size_t cardb)
6446 {
6447     if (!card_bundle_set_p (cardb))
6448     {
6449         card_bundle_table [card_bundle_word (cardb)] |= (1 << card_bundle_bit (cardb));
6450     }
6451 }
6452
6453 // Set the card bundle bits between start_cardb and end_cardb
6454 void gc_heap::card_bundles_set (size_t start_cardb, size_t end_cardb)
6455 {
6456     if (start_cardb == end_cardb)
6457     {
6458         card_bundle_set(start_cardb);
6459         return;
6460     }
6461
6462     size_t start_word = card_bundle_word (start_cardb);
6463     size_t end_word = card_bundle_word (end_cardb);
6464
6465     if (start_word < end_word)
6466     {
6467         // Set the partial words
6468         card_bundle_table [start_word] |= highbits (~0u, card_bundle_bit (start_cardb));
6469
6470         if (card_bundle_bit (end_cardb))
6471             card_bundle_table [end_word] |= lowbits (~0u, card_bundle_bit (end_cardb));
6472
6473         // Set the full words
6474         for (size_t i = start_word + 1; i < end_word; i++)
6475             card_bundle_table [i] = ~0u;
6476     }
6477     else
6478     {
6479         card_bundle_table [start_word] |= (highbits (~0u, card_bundle_bit (start_cardb)) &
6480                                             lowbits (~0u, card_bundle_bit (end_cardb)));
6481     }
6482 }
6483
6484 // Indicates whether the specified bundle is set.
6485 BOOL gc_heap::card_bundle_set_p (size_t cardb)
6486 {
6487     return (card_bundle_table[card_bundle_word(cardb)] & (1 << card_bundle_bit (cardb)));
6488 }
6489
6490 // Returns the size (in bytes) of a card bundle representing the region from 'from' to 'end'
6491 size_t size_card_bundle_of (uint8_t* from, uint8_t* end)
6492 {
6493     // Number of heap bytes represented by a card bundle word
6494     size_t cbw_span = card_size * card_word_width * card_bundle_size * card_bundle_word_width;
6495
6496     // Align the start of the region down
6497     from = (uint8_t*)((size_t)from & ~(cbw_span - 1));
6498
6499     // Align the end of the region up
6500     end = (uint8_t*)((size_t)(end + (cbw_span - 1)) & ~(cbw_span - 1));
6501
6502     // Make sure they're really aligned
6503     assert (((size_t)from & (cbw_span - 1)) == 0);
6504     assert (((size_t)end  & (cbw_span - 1)) == 0);
6505
6506     return ((end - from) / cbw_span) * sizeof (uint32_t);
6507 }
6508
6509 // Takes a pointer to a card bundle table and an address, and returns a pointer that represents
6510 // where a theoretical card bundle table that represents every address (starting from 0) would
6511 // start if the bundle word representing the address were to be located at the pointer passed in.
6512 // The returned 'translated' pointer makes it convenient/fast to calculate where the card bundle
6513 // for a given address is using a simple shift operation on the address.
6514 uint32_t* translate_card_bundle_table (uint32_t* cb, uint8_t* lowest_address)
6515 {
6516     // The number of bytes of heap memory represented by a card bundle word
6517     const size_t heap_bytes_for_bundle_word = card_size * card_word_width * card_bundle_size * card_bundle_word_width;
6518
6519     // Each card bundle word is 32 bits
6520     return (uint32_t*)((uint8_t*)cb - (((size_t)lowest_address / heap_bytes_for_bundle_word) * sizeof (uint32_t)));
6521 }
6522
6523 void gc_heap::enable_card_bundles ()
6524 {
6525     if (can_use_write_watch_for_card_table() && (!card_bundles_enabled()))
6526     {
6527         dprintf (1, ("Enabling card bundles"));
6528
6529         // We initially set all of the card bundles
6530         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
6531                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
6532         settings.card_bundles = TRUE;
6533     }
6534 }
6535
6536 BOOL gc_heap::card_bundles_enabled ()
6537 {
6538     return settings.card_bundles;
6539 }
6540
6541 #endif // CARD_BUNDLE
6542
6543 #if defined (_TARGET_AMD64_)
6544 #define brick_size ((size_t)4096)
6545 #else
6546 #define brick_size ((size_t)2048)
6547 #endif //_TARGET_AMD64_
6548
6549 inline
6550 size_t gc_heap::brick_of (uint8_t* add)
6551 {
6552     return (size_t)(add - lowest_address) / brick_size;
6553 }
6554
6555 inline
6556 uint8_t* gc_heap::brick_address (size_t brick)
6557 {
6558     return lowest_address + (brick_size * brick);
6559 }
6560
6561
6562 void gc_heap::clear_brick_table (uint8_t* from, uint8_t* end)
6563 {
6564     for (size_t i = brick_of (from);i < brick_of (end); i++)
6565         brick_table[i] = 0;
6566 }
6567
6568 //codes for the brick entries:
6569 //entry == 0 -> not assigned
6570 //entry >0 offset is entry-1
6571 //entry <0 jump back entry bricks
6572
6573
6574 inline
6575 void gc_heap::set_brick (size_t index, ptrdiff_t val)
6576 {
6577     if (val < -32767)
6578     {
6579         val = -32767;
6580     }
6581     assert (val < 32767);
6582     if (val >= 0)
6583         brick_table [index] = (short)val+1;
6584     else
6585         brick_table [index] = (short)val;
6586 }
6587
6588 inline
6589 int gc_heap::get_brick_entry (size_t index)
6590 {
6591 #ifdef MULTIPLE_HEAPS
6592     return VolatileLoadWithoutBarrier(&brick_table [index]);
6593 #else
6594     return brick_table[index];
6595 #endif
6596 }
6597
6598
6599 inline
6600 uint8_t* align_on_brick (uint8_t* add)
6601 {
6602     return (uint8_t*)((size_t)(add + brick_size - 1) & ~(brick_size - 1));
6603 }
6604
6605 inline
6606 uint8_t* align_lower_brick (uint8_t* add)
6607 {
6608     return (uint8_t*)(((size_t)add) & ~(brick_size - 1));
6609 }
6610
6611 size_t size_brick_of (uint8_t* from, uint8_t* end)
6612 {
6613     assert (((size_t)from & (brick_size-1)) == 0);
6614     assert (((size_t)end  & (brick_size-1)) == 0);
6615
6616     return ((end - from) / brick_size) * sizeof (short);
6617 }
6618
6619 inline
6620 uint8_t* gc_heap::card_address (size_t card)
6621 {
6622     return  (uint8_t*) (card_size * card);
6623 }
6624
6625 inline
6626 size_t gc_heap::card_of ( uint8_t* object)
6627 {
6628     return (size_t)(object) / card_size;
6629 }
6630
6631 inline
6632 size_t gc_heap::card_to_brick (size_t card)
6633 {
6634     return brick_of (card_address (card));
6635 }
6636
6637 inline
6638 uint8_t* align_on_card (uint8_t* add)
6639 {
6640     return (uint8_t*)((size_t)(add + card_size - 1) & ~(card_size - 1 ));
6641 }
6642 inline
6643 uint8_t* align_on_card_word (uint8_t* add)
6644 {
6645     return (uint8_t*) ((size_t)(add + (card_size*card_word_width)-1) & ~(card_size*card_word_width - 1));
6646 }
6647
6648 inline
6649 uint8_t* align_lower_card (uint8_t* add)
6650 {
6651     return (uint8_t*)((size_t)add & ~(card_size-1));
6652 }
6653
6654 inline
6655 void gc_heap::clear_card (size_t card)
6656 {
6657     card_table [card_word (card)] =
6658         (card_table [card_word (card)] & ~(1 << card_bit (card)));
6659     dprintf (3,("Cleared card %Ix [%Ix, %Ix[", card, (size_t)card_address (card),
6660               (size_t)card_address (card+1)));
6661 }
6662
6663 inline
6664 void gc_heap::set_card (size_t card)
6665 {
6666     size_t word = card_word (card);
6667     card_table[word] = (card_table [word] | (1 << card_bit (card)));
6668
6669 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
6670     // Also set the card bundle that corresponds to the card
6671     size_t bundle_to_set = cardw_card_bundle(word);
6672
6673     card_bundle_set(bundle_to_set);
6674
6675     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));
6676     assert(card_bundle_set_p(bundle_to_set) != 0);
6677 #endif
6678 }
6679
6680 inline
6681 BOOL  gc_heap::card_set_p (size_t card)
6682 {
6683     return ( card_table [ card_word (card) ] & (1 << card_bit (card)));
6684 }
6685
6686 // Returns the number of DWORDs in the card table that cover the
6687 // range of addresses [from, end[.
6688 size_t count_card_of (uint8_t* from, uint8_t* end)
6689 {
6690     return card_word (gcard_of (end - 1)) - card_word (gcard_of (from)) + 1;
6691 }
6692
6693 // Returns the number of bytes to allocate for a card table
6694 // that covers the range of addresses [from, end[.
6695 size_t size_card_of (uint8_t* from, uint8_t* end)
6696 {
6697     return count_card_of (from, end) * sizeof(uint32_t);
6698 }
6699
6700 // We don't store seg_mapping_table in card_table_info because there's only always one view.
6701 class card_table_info
6702 {
6703 public:
6704     unsigned    recount;
6705     uint8_t*    lowest_address;
6706     uint8_t*    highest_address;
6707     short*      brick_table;
6708
6709 #ifdef CARD_BUNDLE
6710     uint32_t*   card_bundle_table;
6711 #endif //CARD_BUNDLE
6712
6713     // mark_array is always at the end of the data structure because we
6714     // want to be able to make one commit call for everything before it.
6715 #ifdef MARK_ARRAY
6716     uint32_t*   mark_array;
6717 #endif //MARK_ARRAY
6718
6719     size_t      size;
6720     uint32_t*   next_card_table;
6721 };
6722
6723 //These are accessors on untranslated cardtable
6724 inline
6725 unsigned& card_table_refcount (uint32_t* c_table)
6726 {
6727     return *(unsigned*)((char*)c_table - sizeof (card_table_info));
6728 }
6729
6730 inline
6731 uint8_t*& card_table_lowest_address (uint32_t* c_table)
6732 {
6733     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->lowest_address;
6734 }
6735
6736 uint32_t* translate_card_table (uint32_t* ct)
6737 {
6738     return (uint32_t*)((uint8_t*)ct - card_word (gcard_of (card_table_lowest_address (ct))) * sizeof(uint32_t));
6739 }
6740
6741 inline
6742 uint8_t*& card_table_highest_address (uint32_t* c_table)
6743 {
6744     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->highest_address;
6745 }
6746
6747 inline
6748 short*& card_table_brick_table (uint32_t* c_table)
6749 {
6750     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->brick_table;
6751 }
6752
6753 #ifdef CARD_BUNDLE
6754 // Get the card bundle table for the specified card table.
6755 inline
6756 uint32_t*& card_table_card_bundle_table (uint32_t* c_table)
6757 {
6758     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->card_bundle_table;
6759 }
6760 #endif //CARD_BUNDLE
6761
6762 #ifdef MARK_ARRAY
6763 /* Support for mark_array */
6764
6765 inline
6766 uint32_t*& card_table_mark_array (uint32_t* c_table)
6767 {
6768     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->mark_array;
6769 }
6770
6771 #ifdef BIT64
6772 #define mark_bit_pitch ((size_t)16)
6773 #else
6774 #define mark_bit_pitch ((size_t)8)
6775 #endif // BIT64
6776 #define mark_word_width ((size_t)32)
6777 #define mark_word_size (mark_word_width * mark_bit_pitch)
6778
6779 inline
6780 uint8_t* align_on_mark_bit (uint8_t* add)
6781 {
6782     return (uint8_t*)((size_t)(add + (mark_bit_pitch - 1)) & ~(mark_bit_pitch - 1));
6783 }
6784
6785 inline
6786 uint8_t* align_lower_mark_bit (uint8_t* add)
6787 {
6788     return (uint8_t*)((size_t)(add) & ~(mark_bit_pitch - 1));
6789 }
6790
6791 inline
6792 BOOL is_aligned_on_mark_word (uint8_t* add)
6793 {
6794     return ((size_t)add == ((size_t)(add) & ~(mark_word_size - 1)));
6795 }
6796
6797 inline
6798 uint8_t* align_on_mark_word (uint8_t* add)
6799 {
6800     return (uint8_t*)((size_t)(add + mark_word_size - 1) & ~(mark_word_size - 1));
6801 }
6802
6803 inline
6804 uint8_t* align_lower_mark_word (uint8_t* add)
6805 {
6806     return (uint8_t*)((size_t)(add) & ~(mark_word_size - 1));
6807 }
6808
6809 inline
6810 size_t mark_bit_of (uint8_t* add)
6811 {
6812     return ((size_t)add / mark_bit_pitch);
6813 }
6814
6815 inline
6816 unsigned int mark_bit_bit (size_t mark_bit)
6817 {
6818     return (unsigned int)(mark_bit % mark_word_width);
6819 }
6820
6821 inline
6822 size_t mark_bit_word (size_t mark_bit)
6823 {
6824     return (mark_bit / mark_word_width);
6825 }
6826
6827 inline
6828 size_t mark_word_of (uint8_t* add)
6829 {
6830     return ((size_t)add) / mark_word_size;
6831 }
6832
6833 uint8_t* mark_word_address (size_t wd)
6834 {
6835     return (uint8_t*)(wd*mark_word_size);
6836 }
6837
6838 uint8_t* mark_bit_address (size_t mark_bit)
6839 {
6840     return (uint8_t*)(mark_bit*mark_bit_pitch);
6841 }
6842
6843 inline
6844 size_t mark_bit_bit_of (uint8_t* add)
6845 {
6846     return  (((size_t)add / mark_bit_pitch) % mark_word_width);
6847 }
6848
6849 inline
6850 unsigned int gc_heap::mark_array_marked(uint8_t* add)
6851 {
6852     return mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add));
6853 }
6854
6855 inline
6856 BOOL gc_heap::is_mark_bit_set (uint8_t* add)
6857 {
6858     return (mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add)));
6859 }
6860
6861 inline
6862 void gc_heap::mark_array_set_marked (uint8_t* add)
6863 {
6864     size_t index = mark_word_of (add);
6865     uint32_t val = (1 << mark_bit_bit_of (add));
6866 #ifdef MULTIPLE_HEAPS
6867     Interlocked::Or (&(mark_array [index]), val);
6868 #else
6869     mark_array [index] |= val;
6870 #endif 
6871 }
6872
6873 inline
6874 void gc_heap::mark_array_clear_marked (uint8_t* add)
6875 {
6876     mark_array [mark_word_of (add)] &= ~(1 << mark_bit_bit_of (add));
6877 }
6878
6879 size_t size_mark_array_of (uint8_t* from, uint8_t* end)
6880 {
6881     assert (((size_t)from & ((mark_word_size)-1)) == 0);
6882     assert (((size_t)end  & ((mark_word_size)-1)) == 0);
6883     return sizeof (uint32_t)*(((end - from) / mark_word_size));
6884 }
6885
6886 //In order to eliminate the lowest_address in the mark array
6887 //computations (mark_word_of, etc) mark_array is offset
6888 // according to the lowest_address.
6889 uint32_t* translate_mark_array (uint32_t* ma)
6890 {
6891     return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_gc_lowest_address));
6892 }
6893
6894 // from and end must be page aligned addresses. 
6895 void gc_heap::clear_mark_array (uint8_t* from, uint8_t* end, BOOL check_only/*=TRUE*/
6896 #ifdef FEATURE_BASICFREEZE
6897                                 , BOOL read_only/*=FALSE*/
6898 #endif // FEATURE_BASICFREEZE
6899                                 )
6900 {
6901     if(!gc_can_use_concurrent)
6902         return;
6903
6904 #ifdef FEATURE_BASICFREEZE
6905     if (!read_only)
6906 #endif // FEATURE_BASICFREEZE
6907     {
6908         assert (from == align_on_mark_word (from));
6909     }
6910     assert (end == align_on_mark_word (end));
6911
6912 #ifdef BACKGROUND_GC
6913     uint8_t* current_lowest_address = background_saved_lowest_address;
6914     uint8_t* current_highest_address = background_saved_highest_address;
6915 #else
6916     uint8_t* current_lowest_address = lowest_address;
6917     uint8_t* current_highest_address = highest_address;
6918 #endif //BACKGROUND_GC
6919
6920     //there is a possibility of the addresses to be
6921     //outside of the covered range because of a newly allocated
6922     //large object segment
6923     if ((end <= current_highest_address) && (from >= current_lowest_address))
6924     {
6925         size_t beg_word = mark_word_of (align_on_mark_word (from));
6926         MAYBE_UNUSED_VAR(beg_word);
6927         //align end word to make sure to cover the address
6928         size_t end_word = mark_word_of (align_on_mark_word (end));
6929         MAYBE_UNUSED_VAR(end_word);
6930         dprintf (3, ("Calling clearing mark array [%Ix, %Ix[ for addresses [%Ix, %Ix[(%s)",
6931                      (size_t)mark_word_address (beg_word),
6932                      (size_t)mark_word_address (end_word),
6933                      (size_t)from, (size_t)end,
6934                      (check_only ? "check_only" : "clear")));
6935         if (!check_only)
6936         {
6937             uint8_t* op = from;
6938             while (op < mark_word_address (beg_word))
6939             {
6940                 mark_array_clear_marked (op);
6941                 op += mark_bit_pitch;
6942             }
6943
6944             memset (&mark_array[beg_word], 0, (end_word - beg_word)*sizeof (uint32_t));
6945         }
6946 #ifdef _DEBUG
6947         else
6948         {
6949             //Beware, it is assumed that the mark array word straddling
6950             //start has been cleared before
6951             //verify that the array is empty.
6952             size_t  markw = mark_word_of (align_on_mark_word (from));
6953             size_t  markw_end = mark_word_of (align_on_mark_word (end));
6954             while (markw < markw_end)
6955             {
6956                 assert (!(mark_array [markw]));
6957                 markw++;
6958             }
6959             uint8_t* p = mark_word_address (markw_end);
6960             while (p < end)
6961             {
6962                 assert (!(mark_array_marked (p)));
6963                 p++;
6964             }
6965         }
6966 #endif //_DEBUG
6967     }
6968 }
6969 #endif //MARK_ARRAY
6970
6971 //These work on untranslated card tables
6972 inline
6973 uint32_t*& card_table_next (uint32_t* c_table)
6974 {
6975     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->next_card_table;
6976 }
6977
6978 inline
6979 size_t& card_table_size (uint32_t* c_table)
6980 {
6981     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->size;
6982 }
6983
6984 void own_card_table (uint32_t* c_table)
6985 {
6986     card_table_refcount (c_table) += 1;
6987 }
6988
6989 void destroy_card_table (uint32_t* c_table);
6990
6991 void delete_next_card_table (uint32_t* c_table)
6992 {
6993     uint32_t* n_table = card_table_next (c_table);
6994     if (n_table)
6995     {
6996         if (card_table_next (n_table))
6997         {
6998             delete_next_card_table (n_table);
6999         }
7000         if (card_table_refcount (n_table) == 0)
7001         {
7002             destroy_card_table (n_table);
7003             card_table_next (c_table) = 0;
7004         }
7005     }
7006 }
7007
7008 void release_card_table (uint32_t* c_table)
7009 {
7010     assert (card_table_refcount (c_table) >0);
7011     card_table_refcount (c_table) -= 1;
7012     if (card_table_refcount (c_table) == 0)
7013     {
7014         delete_next_card_table (c_table);
7015         if (card_table_next (c_table) == 0)
7016         {
7017             destroy_card_table (c_table);
7018             // sever the link from the parent
7019             if (&g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))] == c_table)
7020             {
7021                 g_gc_card_table = 0;
7022
7023 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7024                 g_gc_card_bundle_table = 0;
7025 #endif
7026 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7027                 SoftwareWriteWatch::StaticClose();
7028 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7029             }
7030             else
7031             {
7032                 uint32_t* p_table = &g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))];
7033                 if (p_table)
7034                 {
7035                     while (p_table && (card_table_next (p_table) != c_table))
7036                         p_table = card_table_next (p_table);
7037                     card_table_next (p_table) = 0;
7038                 }
7039             }
7040         }
7041     }
7042 }
7043
7044 void destroy_card_table (uint32_t* c_table)
7045 {
7046 //  delete (uint32_t*)&card_table_refcount(c_table);
7047
7048     GCToOSInterface::VirtualRelease (&card_table_refcount(c_table), card_table_size(c_table));
7049     dprintf (2, ("Table Virtual Free : %Ix", (size_t)&card_table_refcount(c_table)));
7050 }
7051
7052 uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end)
7053 {
7054     assert (g_gc_lowest_address == start);
7055     assert (g_gc_highest_address == end);
7056
7057     uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7058
7059     size_t bs = size_brick_of (start, end);
7060     size_t cs = size_card_of (start, end);
7061 #ifdef MARK_ARRAY
7062     size_t ms = (gc_can_use_concurrent ? 
7063                  size_mark_array_of (start, end) :
7064                  0);
7065 #else
7066     size_t ms = 0;
7067 #endif //MARK_ARRAY
7068
7069     size_t cb = 0;
7070
7071 #ifdef CARD_BUNDLE
7072     if (can_use_write_watch_for_card_table())
7073     {
7074         cb = size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address);
7075 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7076         // If we're not manually managing the card bundles, we will need to use OS write
7077         // watch APIs over this region to track changes.
7078         virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
7079 #endif
7080     }
7081 #endif //CARD_BUNDLE
7082
7083     size_t wws = 0;
7084 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7085     size_t sw_ww_table_offset = 0;
7086     if (gc_can_use_concurrent)
7087     {
7088         size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7089         sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7090         wws = sw_ww_table_offset - sw_ww_size_before_table + SoftwareWriteWatch::GetTableByteSize(start, end);
7091     }
7092 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7093
7094 #ifdef GROWABLE_SEG_MAPPING_TABLE
7095     size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7096     size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7097     size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7098
7099     st += (st_table_offset_aligned - st_table_offset);
7100 #else //GROWABLE_SEG_MAPPING_TABLE
7101     size_t st = 0;
7102 #endif //GROWABLE_SEG_MAPPING_TABLE
7103
7104     // it is impossible for alloc_size to overflow due bounds on each of 
7105     // its components.
7106     size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7107     uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags);
7108
7109     if (!mem)
7110         return 0;
7111
7112     dprintf (2, ("Init - Card table alloc for %Id bytes: [%Ix, %Ix[",
7113                  alloc_size, (size_t)mem, (size_t)(mem+alloc_size)));
7114
7115     // mark array will be committed separately (per segment).
7116     size_t commit_size = alloc_size - ms;
7117
7118     if (!GCToOSInterface::VirtualCommit (mem, commit_size))
7119     {
7120         dprintf (2, ("Card table commit failed"));
7121         GCToOSInterface::VirtualRelease (mem, alloc_size);
7122         return 0;
7123     }
7124
7125     // initialize the ref count
7126     uint32_t* ct = (uint32_t*)(mem+sizeof (card_table_info));
7127     card_table_refcount (ct) = 0;
7128     card_table_lowest_address (ct) = start;
7129     card_table_highest_address (ct) = end;
7130     card_table_brick_table (ct) = (short*)((uint8_t*)ct + cs);
7131     card_table_size (ct) = alloc_size;
7132     card_table_next (ct) = 0;
7133
7134 #ifdef CARD_BUNDLE
7135     card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7136
7137 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7138     g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), g_gc_lowest_address);
7139 #endif
7140
7141 #endif //CARD_BUNDLE
7142
7143 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7144     if (gc_can_use_concurrent)
7145     {
7146         SoftwareWriteWatch::InitializeUntranslatedTable(mem + sw_ww_table_offset, start);
7147     }
7148 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7149
7150 #ifdef GROWABLE_SEG_MAPPING_TABLE
7151     seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7152     seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table - 
7153                                         size_seg_mapping_table_of (0, (align_lower_segment (g_gc_lowest_address))));
7154 #endif //GROWABLE_SEG_MAPPING_TABLE
7155
7156 #ifdef MARK_ARRAY
7157     if (gc_can_use_concurrent)
7158         card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7159     else
7160         card_table_mark_array (ct) = NULL;
7161 #endif //MARK_ARRAY
7162
7163     return translate_card_table(ct);
7164 }
7165
7166 void gc_heap::set_fgm_result (failure_get_memory f, size_t s, BOOL loh_p)
7167 {
7168 #ifdef MULTIPLE_HEAPS
7169     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
7170     {
7171         gc_heap* hp = gc_heap::g_heaps [hn];
7172         hp->fgm_result.set_fgm (f, s, loh_p);
7173     }
7174 #else //MULTIPLE_HEAPS
7175     fgm_result.set_fgm (f, s, loh_p);
7176 #endif //MULTIPLE_HEAPS
7177 }
7178
7179 //returns 0 for success, -1 otherwise
7180 // We are doing all the decommitting here because we want to make sure we have
7181 // enough memory to do so - if we do this during copy_brick_card_table and 
7182 // and fail to decommit it would make the failure case very complicated to 
7183 // handle. This way we can waste some decommit if we call this multiple 
7184 // times before the next FGC but it's easier to handle the failure case.
7185 int gc_heap::grow_brick_card_tables (uint8_t* start,
7186                                      uint8_t* end,
7187                                      size_t size,
7188                                      heap_segment* new_seg, 
7189                                      gc_heap* hp, 
7190                                      BOOL loh_p)
7191 {
7192     uint8_t* la = g_gc_lowest_address;
7193     uint8_t* ha = g_gc_highest_address;
7194     uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address);
7195     uint8_t* saved_g_highest_address = max (end, g_gc_highest_address);
7196     seg_mapping* new_seg_mapping_table = nullptr;
7197 #ifdef BACKGROUND_GC
7198     // This value is only for logging purpose - it's not necessarily exactly what we 
7199     // would commit for mark array but close enough for diagnostics purpose.
7200     size_t logging_ma_commit_size = size_mark_array_of (0, (uint8_t*)size);
7201 #endif //BACKGROUND_GC
7202
7203     // See if the address is already covered
7204     if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address))
7205     {
7206         {
7207             //modify the higest address so the span covered
7208             //is twice the previous one.
7209             uint8_t* top = (uint8_t*)0 + Align (GCToOSInterface::GetVirtualMemoryLimit());
7210             // On non-Windows systems, we get only an approximate value that can possibly be
7211             // slightly lower than the saved_g_highest_address.
7212             // In such case, we set the top to the saved_g_highest_address so that the
7213             // card and brick tables always cover the whole new range.
7214             if (top < saved_g_highest_address)
7215             {
7216                 top = saved_g_highest_address;
7217             }
7218             size_t ps = ha-la;
7219 #ifdef BIT64
7220             if (ps > (uint64_t)200*1024*1024*1024)
7221                 ps += (uint64_t)100*1024*1024*1024;
7222             else
7223 #endif // BIT64
7224                 ps *= 2;
7225
7226             if (saved_g_lowest_address < g_gc_lowest_address)
7227             {
7228                 if (ps > (size_t)g_gc_lowest_address)
7229                     saved_g_lowest_address = (uint8_t*)(size_t)OS_PAGE_SIZE;
7230                 else
7231                 {
7232                     assert (((size_t)g_gc_lowest_address - ps) >= OS_PAGE_SIZE);
7233                     saved_g_lowest_address = min (saved_g_lowest_address, (g_gc_lowest_address - ps));
7234                 }
7235             }
7236
7237             if (saved_g_highest_address > g_gc_highest_address)
7238             {
7239                 saved_g_highest_address = max ((saved_g_lowest_address + ps), saved_g_highest_address);
7240                 if (saved_g_highest_address > top)
7241                     saved_g_highest_address = top;
7242             }
7243         }
7244         dprintf (GC_TABLE_LOG, ("Growing card table [%Ix, %Ix[",
7245                                 (size_t)saved_g_lowest_address,
7246                                 (size_t)saved_g_highest_address));
7247
7248         bool write_barrier_updated = false;
7249         uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7250         uint32_t* saved_g_card_table = g_gc_card_table;
7251
7252 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7253         uint32_t* saved_g_card_bundle_table = g_gc_card_bundle_table;
7254 #endif
7255
7256         uint32_t* ct = 0;
7257         uint32_t* translated_ct = 0;
7258         short* bt = 0;
7259
7260         size_t cs = size_card_of (saved_g_lowest_address, saved_g_highest_address);
7261         size_t bs = size_brick_of (saved_g_lowest_address, saved_g_highest_address);
7262
7263 #ifdef MARK_ARRAY
7264         size_t ms = (gc_heap::gc_can_use_concurrent ? 
7265                     size_mark_array_of (saved_g_lowest_address, saved_g_highest_address) :
7266                     0);
7267 #else
7268         size_t ms = 0;
7269 #endif //MARK_ARRAY
7270
7271         size_t cb = 0;
7272
7273 #ifdef CARD_BUNDLE
7274         if (can_use_write_watch_for_card_table())
7275         {
7276             cb = size_card_bundle_of (saved_g_lowest_address, saved_g_highest_address);
7277
7278 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7279             // If we're not manually managing the card bundles, we will need to use OS write
7280             // watch APIs over this region to track changes.
7281             virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
7282 #endif
7283         }
7284 #endif //CARD_BUNDLE
7285
7286         size_t wws = 0;
7287 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7288         size_t sw_ww_table_offset = 0;
7289         if (gc_can_use_concurrent)
7290         {
7291             size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7292             sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7293             wws =
7294                 sw_ww_table_offset -
7295                 sw_ww_size_before_table +
7296                 SoftwareWriteWatch::GetTableByteSize(saved_g_lowest_address, saved_g_highest_address);
7297         }
7298 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7299
7300 #ifdef GROWABLE_SEG_MAPPING_TABLE
7301         size_t st = size_seg_mapping_table_of (saved_g_lowest_address, saved_g_highest_address);
7302         size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7303         size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7304         st += (st_table_offset_aligned - st_table_offset);
7305 #else //GROWABLE_SEG_MAPPING_TABLE
7306         size_t st = 0;
7307 #endif //GROWABLE_SEG_MAPPING_TABLE
7308
7309         // it is impossible for alloc_size to overflow due bounds on each of 
7310         // its components.
7311         size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7312         dprintf (GC_TABLE_LOG, ("card table: %Id; brick table: %Id; card bundle: %Id; sw ww table: %Id; seg table: %Id; mark array: %Id",
7313                                   cs, bs, cb, wws, st, ms));
7314
7315         uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags);
7316
7317         if (!mem)
7318         {
7319             set_fgm_result (fgm_grow_table, alloc_size, loh_p);
7320             goto fail;
7321         }
7322
7323         dprintf (GC_TABLE_LOG, ("Table alloc for %Id bytes: [%Ix, %Ix[",
7324                                  alloc_size, (size_t)mem, (size_t)((uint8_t*)mem+alloc_size)));
7325
7326         {   
7327             // mark array will be committed separately (per segment).
7328             size_t commit_size = alloc_size - ms;
7329
7330             if (!GCToOSInterface::VirtualCommit (mem, commit_size))
7331             {
7332                 dprintf (GC_TABLE_LOG, ("Table commit failed"));
7333                 set_fgm_result (fgm_commit_table, commit_size, loh_p);
7334                 goto fail;
7335             }
7336         }
7337
7338         ct = (uint32_t*)(mem + sizeof (card_table_info));
7339         card_table_refcount (ct) = 0;
7340         card_table_lowest_address (ct) = saved_g_lowest_address;
7341         card_table_highest_address (ct) = saved_g_highest_address;
7342         card_table_next (ct) = &g_gc_card_table[card_word (gcard_of (la))];
7343
7344         //clear the card table
7345 /*
7346         memclr ((uint8_t*)ct,
7347                 (((saved_g_highest_address - saved_g_lowest_address)*sizeof (uint32_t) /
7348                   (card_size * card_word_width))
7349                  + sizeof (uint32_t)));
7350 */
7351
7352         bt = (short*)((uint8_t*)ct + cs);
7353
7354         // No initialization needed, will be done in copy_brick_card
7355
7356         card_table_brick_table (ct) = bt;
7357
7358 #ifdef CARD_BUNDLE
7359         card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7360         //set all bundle to look at all of the cards
7361         memset(card_table_card_bundle_table (ct), 0xFF, cb);
7362 #endif //CARD_BUNDLE
7363
7364 #ifdef GROWABLE_SEG_MAPPING_TABLE
7365         {
7366             new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7367             new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table -
7368                                               size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address))));
7369             memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7370                 &seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7371                 size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address));
7372
7373             // new_seg_mapping_table gets assigned to seg_mapping_table at the bottom of this function,
7374             // not here. The reason for this is that, if we fail at mark array committing (OOM) and we've
7375             // already switched seg_mapping_table to point to the new mapping table, we'll decommit it and
7376             // run into trouble. By not assigning here, we're making sure that we will not change seg_mapping_table
7377             // if an OOM occurs.
7378         }
7379 #endif //GROWABLE_SEG_MAPPING_TABLE
7380
7381 #ifdef MARK_ARRAY
7382         if(gc_can_use_concurrent)
7383             card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7384         else
7385             card_table_mark_array (ct) = NULL;
7386 #endif //MARK_ARRAY
7387
7388         translated_ct = translate_card_table (ct);
7389
7390         dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix", 
7391             (size_t)ct, (size_t)translated_ct, (size_t)new_seg_mapping_table, (size_t)card_table_mark_array (ct)));
7392
7393 #ifdef BACKGROUND_GC
7394         if (hp->should_commit_mark_array())
7395         {
7396             dprintf (GC_TABLE_LOG, ("new low: %Ix, new high: %Ix, latest mark array is %Ix(translate: %Ix)", 
7397                                     saved_g_lowest_address, saved_g_highest_address,
7398                                     card_table_mark_array (ct),
7399                                     translate_mark_array (card_table_mark_array (ct))));
7400             uint32_t* new_mark_array = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, saved_g_lowest_address));
7401             if (!commit_new_mark_array_global (new_mark_array))
7402             {
7403                 dprintf (GC_TABLE_LOG, ("failed to commit portions in the mark array for existing segments"));
7404                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7405                 goto fail;
7406             }
7407
7408             if (!commit_mark_array_new_seg (hp, new_seg, translated_ct, saved_g_lowest_address))
7409             {
7410                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg"));
7411                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7412                 goto fail;
7413             }
7414         }
7415         else
7416         {
7417             clear_commit_flag_global();
7418         }
7419 #endif //BACKGROUND_GC
7420
7421 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7422         if (gc_can_use_concurrent)
7423         {
7424             // The current design of software write watch requires that the runtime is suspended during resize. Suspending
7425             // on resize is preferred because it is a far less frequent operation than GetWriteWatch() / ResetWriteWatch().
7426             // Suspending here allows copying dirty state from the old table into the new table, and not have to merge old
7427             // table info lazily as done for card tables.
7428
7429             // Either this thread was the thread that did the suspension which means we are suspended; or this is called
7430             // from a GC thread which means we are in a blocking GC and also suspended.
7431             bool is_runtime_suspended = GCToEEInterface::IsGCThread();
7432             if (!is_runtime_suspended)
7433             {
7434                 // Note on points where the runtime is suspended anywhere in this function. Upon an attempt to suspend the
7435                 // runtime, a different thread may suspend first, causing this thread to block at the point of the suspend call.
7436                 // So, at any suspend point, externally visible state needs to be consistent, as code that depends on that state
7437                 // may run while this thread is blocked. This includes updates to g_gc_card_table, g_gc_lowest_address, and
7438                 // g_gc_highest_address.
7439                 suspend_EE();
7440             }
7441
7442             g_gc_card_table = translated_ct;
7443
7444 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7445             g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address);
7446 #endif
7447
7448             SoftwareWriteWatch::SetResizedUntranslatedTable(
7449                 mem + sw_ww_table_offset,
7450                 saved_g_lowest_address,
7451                 saved_g_highest_address);
7452
7453             // Since the runtime is already suspended, update the write barrier here as well.
7454             // This passes a bool telling whether we need to switch to the post
7455             // grow version of the write barrier.  This test tells us if the new
7456             // segment was allocated at a lower address than the old, requiring
7457             // that we start doing an upper bounds check in the write barrier.
7458             g_gc_lowest_address = saved_g_lowest_address;
7459             g_gc_highest_address = saved_g_highest_address;
7460             stomp_write_barrier_resize(true, la != saved_g_lowest_address);
7461             write_barrier_updated = true;
7462
7463             if (!is_runtime_suspended)
7464             {
7465                 restart_EE();
7466             }
7467         }
7468         else
7469 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7470         {
7471             g_gc_card_table = translated_ct;
7472
7473 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7474             g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address);
7475 #endif
7476         }
7477
7478         seg_mapping_table = new_seg_mapping_table;
7479
7480         GCToOSInterface::FlushProcessWriteBuffers();
7481         g_gc_lowest_address = saved_g_lowest_address;
7482         g_gc_highest_address = saved_g_highest_address;
7483
7484         if (!write_barrier_updated)
7485         {
7486             // This passes a bool telling whether we need to switch to the post
7487             // grow version of the write barrier.  This test tells us if the new
7488             // segment was allocated at a lower address than the old, requiring
7489             // that we start doing an upper bounds check in the write barrier.
7490             // This will also suspend the runtime if the write barrier type needs
7491             // to be changed, so we are doing this after all global state has
7492             // been updated. See the comment above suspend_EE() above for more
7493             // info.
7494             stomp_write_barrier_resize(GCToEEInterface::IsGCThread(), la != saved_g_lowest_address);
7495         }
7496
7497
7498         return 0;
7499         
7500 fail:
7501         //cleanup mess and return -1;
7502
7503         if (mem)
7504         {
7505             assert(g_gc_card_table == saved_g_card_table);
7506
7507 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7508             assert(g_gc_card_bundle_table  == saved_g_card_bundle_table);
7509 #endif
7510
7511             //delete (uint32_t*)((uint8_t*)ct - sizeof(card_table_info));
7512             if (!GCToOSInterface::VirtualRelease (mem, alloc_size))
7513             {
7514                 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualRelease failed"));
7515                 assert (!"release failed");
7516             }
7517         }
7518
7519         return -1;
7520     }
7521     else
7522     {
7523 #ifdef BACKGROUND_GC
7524         if (hp->should_commit_mark_array())
7525         {
7526             dprintf (GC_TABLE_LOG, ("in range new seg %Ix, mark_array is %Ix", new_seg, hp->mark_array));
7527             if (!commit_mark_array_new_seg (hp, new_seg))
7528             {
7529                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg in range"));
7530                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7531                 return -1;
7532             }
7533         }
7534 #endif //BACKGROUND_GC
7535     }
7536
7537     return 0;
7538 }
7539
7540 //copy all of the arrays managed by the card table for a page aligned range
7541 void gc_heap::copy_brick_card_range (uint8_t* la, uint32_t* old_card_table,
7542                                      short* old_brick_table,
7543                                      heap_segment* seg,
7544                                      uint8_t* start, uint8_t* end)
7545 {
7546     ptrdiff_t brick_offset = brick_of (start) - brick_of (la);
7547
7548
7549     dprintf (2, ("copying tables for range [%Ix %Ix[", (size_t)start, (size_t)end));
7550
7551     // copy brick table
7552     short* brick_start = &brick_table [brick_of (start)];
7553     if (old_brick_table)
7554     {
7555         // segments are always on page boundaries
7556         memcpy (brick_start, &old_brick_table[brick_offset],
7557                 size_brick_of (start, end));
7558
7559     }
7560     else
7561     {
7562         // This is a large heap, just clear the brick table
7563     }
7564
7565     uint32_t* old_ct = &old_card_table[card_word (card_of (la))];
7566 #ifdef MARK_ARRAY
7567 #ifdef BACKGROUND_GC
7568     UNREFERENCED_PARAMETER(seg);
7569     if (recursive_gc_sync::background_running_p())
7570     {
7571         uint32_t* old_mark_array = card_table_mark_array (old_ct);
7572
7573         // We don't need to go through all the card tables here because 
7574         // we only need to copy from the GC version of the mark array - when we
7575         // mark (even in allocate_large_object) we always use that mark array.
7576         if ((card_table_highest_address (old_ct) >= start) &&
7577             (card_table_lowest_address (old_ct) <= end))
7578         {
7579             if ((background_saved_highest_address >= start) &&
7580                 (background_saved_lowest_address <= end))
7581             {
7582                 //copy the mark bits
7583                 // segments are always on page boundaries
7584                 uint8_t* m_start = max (background_saved_lowest_address, start);
7585                 uint8_t* m_end = min (background_saved_highest_address, end);
7586                 memcpy (&mark_array[mark_word_of (m_start)],
7587                         &old_mark_array[mark_word_of (m_start) - mark_word_of (la)],
7588                         size_mark_array_of (m_start, m_end));
7589             }
7590         }
7591         else
7592         {
7593             //only large segments can be out of range
7594             assert (old_brick_table == 0);
7595         }
7596     }
7597 #else //BACKGROUND_GC
7598     assert (seg != 0);
7599     clear_mark_array (start, heap_segment_committed(seg));
7600 #endif //BACKGROUND_GC
7601 #endif //MARK_ARRAY
7602
7603     // n way merge with all of the card table ever used in between
7604     uint32_t* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]);
7605
7606     assert (ct);
7607     while (card_table_next (old_ct) != ct)
7608     {
7609         //copy if old card table contained [start, end[
7610         if ((card_table_highest_address (ct) >= end) &&
7611             (card_table_lowest_address (ct) <= start))
7612         {
7613             // or the card_tables
7614
7615             size_t start_word = card_word (card_of (start));
7616
7617             uint32_t* dest = &card_table[start_word];
7618             uint32_t* src = &((translate_card_table (ct))[start_word]);
7619             ptrdiff_t count = count_card_of (start, end);
7620             for (int x = 0; x < count; x++)
7621             {
7622                 *dest |= *src;
7623
7624 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7625                 if (*src != 0)
7626                 {
7627                     card_bundle_set(cardw_card_bundle(start_word+x));
7628                 }
7629 #endif
7630
7631                 dest++;
7632                 src++;
7633             }
7634         }
7635         ct = card_table_next (ct);
7636     }
7637 }
7638
7639 //initialize all of the arrays managed by the card table for a page aligned range when an existing ro segment becomes in range
7640 void gc_heap::init_brick_card_range (heap_segment* seg)
7641 {
7642     dprintf (2, ("initialising tables for range [%Ix %Ix[",
7643                  (size_t)heap_segment_mem (seg),
7644                  (size_t)heap_segment_allocated (seg)));
7645
7646     // initialize the brick table
7647     for (size_t b = brick_of (heap_segment_mem (seg));
7648          b < brick_of (align_on_brick (heap_segment_allocated (seg)));
7649          b++)
7650     {
7651         set_brick (b, -1);
7652     }
7653
7654 #ifdef MARK_ARRAY
7655     if (recursive_gc_sync::background_running_p() && (seg->flags & heap_segment_flags_ma_committed))
7656     {
7657         assert (seg != 0);
7658         clear_mark_array (heap_segment_mem (seg), heap_segment_committed(seg));
7659     }
7660 #endif //MARK_ARRAY
7661
7662     clear_card_for_addresses (heap_segment_mem (seg),
7663                               heap_segment_allocated (seg));
7664 }
7665
7666 void gc_heap::copy_brick_card_table()
7667 {
7668     uint8_t* la = lowest_address;
7669     uint8_t* ha = highest_address;
7670     MAYBE_UNUSED_VAR(ha);
7671     uint32_t* old_card_table = card_table;
7672     short* old_brick_table = brick_table;
7673
7674     assert (la == card_table_lowest_address (&old_card_table[card_word (card_of (la))]));
7675     assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))]));
7676
7677     /* todo: Need a global lock for this */
7678     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
7679     own_card_table (ct);
7680     card_table = translate_card_table (ct);
7681     /* End of global lock */
7682     highest_address = card_table_highest_address (ct);
7683     lowest_address = card_table_lowest_address (ct);
7684
7685     brick_table = card_table_brick_table (ct);
7686
7687 #ifdef MARK_ARRAY
7688     if (gc_can_use_concurrent)
7689     {
7690         mark_array = translate_mark_array (card_table_mark_array (ct));
7691         assert (mark_word_of (g_gc_highest_address) ==
7692             mark_word_of (align_on_mark_word (g_gc_highest_address)));
7693     }
7694     else
7695         mark_array = NULL;
7696 #endif //MARK_ARRAY
7697
7698 #ifdef CARD_BUNDLE
7699 #if defined(MARK_ARRAY) && defined(_DEBUG)
7700 #ifdef GROWABLE_SEG_MAPPING_TABLE
7701     size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7702 #else  //GROWABLE_SEG_MAPPING_TABLE
7703     size_t st = 0;
7704 #endif //GROWABLE_SEG_MAPPING_TABLE
7705 #endif //MARK_ARRAY && _DEBUG
7706     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
7707
7708     // Ensure that the word that represents g_gc_lowest_address in the translated table is located at the
7709     // start of the untranslated table.
7710     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
7711             card_table_card_bundle_table (ct));
7712
7713     //set the card table if we are in a heap growth scenario
7714     if (card_bundles_enabled())
7715     {
7716         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
7717                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
7718     }
7719     //check if we need to turn on card_bundles.
7720 #ifdef MULTIPLE_HEAPS
7721     // use INT64 arithmetic here because of possible overflow on 32p
7722     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*gc_heap::n_heaps;
7723 #else
7724     // use INT64 arithmetic here because of possible overflow on 32p
7725     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
7726 #endif //MULTIPLE_HEAPS
7727     if (reserved_memory >= th)
7728     {
7729         enable_card_bundles();
7730     }
7731
7732 #endif //CARD_BUNDLE
7733
7734     // for each of the segments and heaps, copy the brick table and
7735     // or the card table
7736     heap_segment* seg = generation_start_segment (generation_of (max_generation));
7737     while (seg)
7738     {
7739         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7740         {
7741             //check if it became in range
7742             if ((heap_segment_reserved (seg) > lowest_address) &&
7743                 (heap_segment_mem (seg) < highest_address))
7744             {
7745                 set_ro_segment_in_range (seg);
7746             }
7747         }
7748         else
7749         {
7750
7751             uint8_t* end = align_on_page (heap_segment_allocated (seg));
7752             copy_brick_card_range (la, old_card_table,
7753                                    old_brick_table,
7754                                    seg,
7755                                    align_lower_page (heap_segment_mem (seg)),
7756                                    end);
7757         }
7758         seg = heap_segment_next (seg);
7759     }
7760
7761     seg = generation_start_segment (large_object_generation);
7762     while (seg)
7763     {
7764         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7765         {
7766             //check if it became in range
7767             if ((heap_segment_reserved (seg) > lowest_address) &&
7768                 (heap_segment_mem (seg) < highest_address))
7769             {
7770                 set_ro_segment_in_range (seg);
7771             }
7772         }
7773         else
7774         {
7775             uint8_t* end = align_on_page (heap_segment_allocated (seg));
7776             copy_brick_card_range (la, old_card_table,
7777                                    0,
7778                                    seg,
7779                                    align_lower_page (heap_segment_mem (seg)),
7780                                    end);
7781         }
7782         seg = heap_segment_next (seg);
7783     }
7784
7785     release_card_table (&old_card_table[card_word (card_of(la))]);
7786 }
7787
7788 #ifdef FEATURE_BASICFREEZE
7789 BOOL gc_heap::insert_ro_segment (heap_segment* seg)
7790 {
7791     enter_spin_lock (&gc_heap::gc_lock);
7792
7793     if (!gc_heap::seg_table->ensure_space_for_insert ()
7794         || (should_commit_mark_array() && !commit_mark_array_new_seg(__this, seg)))
7795     {
7796         leave_spin_lock(&gc_heap::gc_lock);
7797         return FALSE;
7798     }
7799
7800     //insert at the head of the segment list
7801     generation* gen2 = generation_of (max_generation);
7802     heap_segment* oldhead = generation_start_segment (gen2);
7803     heap_segment_next (seg) = oldhead;
7804     generation_start_segment (gen2) = seg;
7805
7806     seg_table->insert (heap_segment_mem(seg), (size_t)seg);
7807
7808 #ifdef SEG_MAPPING_TABLE
7809     seg_mapping_table_add_ro_segment (seg);
7810 #endif //SEG_MAPPING_TABLE
7811
7812     //test if in range
7813     if ((heap_segment_reserved (seg) > lowest_address) &&
7814         (heap_segment_mem (seg) < highest_address))
7815     {
7816         set_ro_segment_in_range (seg);
7817     }
7818
7819     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg), (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), gc_etw_segment_read_only_heap);
7820
7821     leave_spin_lock (&gc_heap::gc_lock);
7822     return TRUE;
7823 }
7824
7825 // No one is calling this function right now. If this is getting called we need
7826 // to take care of decommitting the mark array for it - we will need to remember
7827 // which portion of the mark array was committed and only decommit that.
7828 void gc_heap::remove_ro_segment (heap_segment* seg)
7829 {
7830 //clear the mark bits so a new segment allocated in its place will have a clear mark bits
7831 #ifdef MARK_ARRAY
7832     if (gc_can_use_concurrent)
7833     {
7834         clear_mark_array (align_lower_mark_word (max (heap_segment_mem (seg), lowest_address)),
7835                       align_on_card_word (min (heap_segment_allocated (seg), highest_address)),
7836                       false); // read_only segments need the mark clear
7837     }
7838 #endif //MARK_ARRAY
7839
7840     enter_spin_lock (&gc_heap::gc_lock);
7841
7842     seg_table->remove ((uint8_t*)seg);
7843
7844 #ifdef SEG_MAPPING_TABLE
7845     seg_mapping_table_remove_ro_segment (seg);
7846 #endif //SEG_MAPPING_TABLE
7847
7848     // Locate segment (and previous segment) in the list.
7849     generation* gen2 = generation_of (max_generation);
7850     heap_segment* curr_seg = generation_start_segment (gen2);
7851     heap_segment* prev_seg = NULL;
7852
7853     while (curr_seg && curr_seg != seg)
7854     {
7855         prev_seg = curr_seg;
7856         curr_seg = heap_segment_next (curr_seg);
7857     }
7858     assert (curr_seg == seg);
7859
7860     // Patch previous segment (or list head if there is none) to skip the removed segment.
7861     if (prev_seg)
7862         heap_segment_next (prev_seg) = heap_segment_next (curr_seg);
7863     else
7864         generation_start_segment (gen2) = heap_segment_next (curr_seg);
7865
7866     leave_spin_lock (&gc_heap::gc_lock);
7867 }
7868 #endif //FEATURE_BASICFREEZE
7869
7870 BOOL gc_heap::set_ro_segment_in_range (heap_segment* seg)
7871 {
7872     //set it in range
7873     seg->flags |= heap_segment_flags_inrange;
7874 //    init_brick_card_range (seg);
7875     ro_segments_in_range = TRUE;
7876     //right now, segments aren't protected
7877     //unprotect_segment (seg);
7878     return TRUE;
7879 }
7880
7881 #ifdef MARK_LIST
7882
7883 uint8_t** make_mark_list (size_t size)
7884 {
7885     uint8_t** mark_list = new (nothrow) uint8_t* [size];
7886     return mark_list;
7887 }
7888
7889 #define swap(a,b){uint8_t* t; t = a; a = b; b = t;}
7890
7891 void verify_qsort_array (uint8_t* *low, uint8_t* *high)
7892 {
7893     uint8_t **i = 0;
7894
7895     for (i = low+1; i <= high; i++)
7896     {
7897         if (*i < *(i-1))
7898         {
7899             FATAL_GC_ERROR();
7900         }
7901     }
7902 }
7903
7904 #ifndef USE_INTROSORT
7905 void qsort1( uint8_t* *low, uint8_t* *high, unsigned int depth)
7906 {
7907     if (((low + 16) >= high) || (depth > 100))
7908     {
7909         //insertion sort
7910         uint8_t **i, **j;
7911         for (i = low+1; i <= high; i++)
7912         {
7913             uint8_t* val = *i;
7914             for (j=i;j >low && val<*(j-1);j--)
7915             {
7916                 *j=*(j-1);
7917             }
7918             *j=val;
7919         }
7920     }
7921     else
7922     {
7923         uint8_t *pivot, **left, **right;
7924
7925         //sort low middle and high
7926         if (*(low+((high-low)/2)) < *low)
7927             swap (*(low+((high-low)/2)), *low);
7928         if (*high < *low)
7929             swap (*low, *high);
7930         if (*high < *(low+((high-low)/2)))
7931             swap (*(low+((high-low)/2)), *high);
7932
7933         swap (*(low+((high-low)/2)), *(high-1));
7934         pivot =  *(high-1);
7935         left = low; right = high-1;
7936         while (1) {
7937             while (*(--right) > pivot);
7938             while (*(++left)  < pivot);
7939             if (left < right)
7940             {
7941                 swap(*left, *right);
7942             }
7943             else
7944                 break;
7945         }
7946         swap (*left, *(high-1));
7947         qsort1(low, left-1, depth+1);
7948         qsort1(left+1, high, depth+1);
7949     }
7950 }
7951 #endif //USE_INTROSORT
7952 void rqsort1( uint8_t* *low, uint8_t* *high)
7953 {
7954     if ((low + 16) >= high)
7955     {
7956         //insertion sort
7957         uint8_t **i, **j;
7958         for (i = low+1; i <= high; i++)
7959         {
7960             uint8_t* val = *i;
7961             for (j=i;j >low && val>*(j-1);j--)
7962             {
7963                 *j=*(j-1);
7964             }
7965             *j=val;
7966         }
7967     }
7968     else
7969     {
7970         uint8_t *pivot, **left, **right;
7971
7972         //sort low middle and high
7973         if (*(low+((high-low)/2)) > *low)
7974             swap (*(low+((high-low)/2)), *low);
7975         if (*high > *low)
7976             swap (*low, *high);
7977         if (*high > *(low+((high-low)/2)))
7978             swap (*(low+((high-low)/2)), *high);
7979
7980         swap (*(low+((high-low)/2)), *(high-1));
7981         pivot =  *(high-1);
7982         left = low; right = high-1;
7983         while (1) {
7984             while (*(--right) < pivot);
7985             while (*(++left)  > pivot);
7986             if (left < right)
7987             {
7988                 swap(*left, *right);
7989             }
7990             else
7991                 break;
7992         }
7993         swap (*left, *(high-1));
7994         rqsort1(low, left-1);
7995         rqsort1(left+1, high);
7996     }
7997 }
7998
7999 #ifdef USE_INTROSORT
8000 class introsort 
8001 {
8002
8003 private: 
8004     static const int size_threshold = 64;
8005     static const int max_depth = 100;
8006
8007
8008 inline static void swap_elements(uint8_t** i,uint8_t** j)
8009     {
8010         uint8_t* t=*i;
8011         *i=*j; 
8012         *j=t;
8013     }
8014
8015 public:
8016     static void sort (uint8_t** begin, uint8_t** end, int ignored)
8017     {
8018         ignored = 0;
8019         introsort_loop (begin, end, max_depth);
8020         insertionsort (begin, end);
8021     }
8022
8023 private: 
8024
8025     static void introsort_loop (uint8_t** lo, uint8_t** hi, int depth_limit)
8026     {
8027         while (hi-lo >= size_threshold)
8028         {
8029             if (depth_limit == 0)
8030             {
8031                 heapsort (lo, hi);
8032                 return;
8033             }
8034             uint8_t** p=median_partition (lo, hi);
8035             depth_limit=depth_limit-1;
8036             introsort_loop (p, hi, depth_limit);
8037             hi=p-1;
8038         }        
8039     }
8040
8041     static uint8_t** median_partition (uint8_t** low, uint8_t** high)
8042     {
8043         uint8_t *pivot, **left, **right;
8044
8045         //sort low middle and high
8046         if (*(low+((high-low)/2)) < *low)
8047             swap_elements ((low+((high-low)/2)), low);
8048         if (*high < *low)
8049             swap_elements (low, high);
8050         if (*high < *(low+((high-low)/2)))
8051             swap_elements ((low+((high-low)/2)), high);
8052
8053         swap_elements ((low+((high-low)/2)), (high-1));
8054         pivot =  *(high-1);
8055         left = low; right = high-1;
8056         while (1) {
8057             while (*(--right) > pivot);
8058             while (*(++left)  < pivot);
8059             if (left < right)
8060             {
8061                 swap_elements(left, right);
8062             }
8063             else
8064                 break;
8065         }
8066         swap_elements (left, (high-1));
8067         return left;
8068     }
8069
8070
8071     static void insertionsort (uint8_t** lo, uint8_t** hi)
8072     {
8073         for (uint8_t** i=lo+1; i <= hi; i++)
8074         {
8075             uint8_t** j = i;
8076             uint8_t* t = *i;
8077             while((j > lo) && (t <*(j-1)))
8078             {
8079                 *j = *(j-1);
8080                 j--;
8081             }
8082             *j = t;
8083         }
8084     }
8085
8086     static void heapsort (uint8_t** lo, uint8_t** hi)
8087     { 
8088         size_t n = hi - lo + 1;
8089         for (size_t i=n / 2; i >= 1; i--)
8090         {
8091             downheap (i,n,lo);
8092         }
8093         for (size_t i = n; i > 1; i--)
8094         {
8095             swap_elements (lo, lo + i - 1);
8096             downheap(1, i - 1,  lo);
8097         }
8098     }
8099
8100     static void downheap (size_t i, size_t n, uint8_t** lo)
8101     {
8102         uint8_t* d = *(lo + i - 1);
8103         size_t child;
8104         while (i <= n / 2)
8105         {
8106             child = 2*i;
8107             if (child < n && *(lo + child - 1)<(*(lo + child)))
8108             {
8109                 child++;
8110             }
8111             if (!(d<*(lo + child - 1))) 
8112             {
8113                 break;
8114             }
8115             *(lo + i - 1) = *(lo + child - 1);
8116             i = child;
8117         }
8118         *(lo + i - 1) = d;
8119     }
8120
8121 };
8122
8123 #endif //USE_INTROSORT    
8124
8125 #ifdef MULTIPLE_HEAPS
8126 #ifdef PARALLEL_MARK_LIST_SORT
8127 void gc_heap::sort_mark_list()
8128 {
8129     // if this heap had a mark list overflow, we don't do anything
8130     if (mark_list_index > mark_list_end)
8131     {
8132 //        printf("sort_mark_list: overflow on heap %d\n", heap_number);
8133         return;
8134     }
8135
8136     // if any other heap had a mark list overflow, we fake one too,
8137     // so we don't use an incomplete mark list by mistake
8138     for (int i = 0; i < n_heaps; i++)
8139     {
8140         if (g_heaps[i]->mark_list_index > g_heaps[i]->mark_list_end)
8141         {
8142             mark_list_index = mark_list_end + 1;
8143 //            printf("sort_mark_list: overflow on heap %d\n", i);
8144             return;
8145         }
8146     }
8147
8148 //    unsigned long start = GetCycleCount32();
8149
8150     dprintf (3, ("Sorting mark lists"));
8151     if (mark_list_index > mark_list)
8152         _sort (mark_list, mark_list_index - 1, 0);
8153
8154 //    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);
8155 //    start = GetCycleCount32();
8156
8157     // first set the pieces for all heaps to empty
8158     int heap_num;
8159     for (heap_num = 0; heap_num < n_heaps; heap_num++)
8160     {
8161         mark_list_piece_start[heap_num] = NULL;
8162         mark_list_piece_end[heap_num] = NULL;
8163     }
8164
8165     uint8_t** x = mark_list;
8166
8167 // predicate means: x is still within the mark list, and within the bounds of this heap
8168 #define predicate(x) (((x) < mark_list_index) && (*(x) < heap->ephemeral_high))
8169
8170     heap_num = -1;
8171     while (x < mark_list_index)
8172     {
8173         gc_heap* heap;
8174         // find the heap x points into - searching cyclically from the last heap,
8175         // because in many cases the right heap is the next one or comes soon after
8176         int last_heap_num = heap_num;
8177         MAYBE_UNUSED_VAR(last_heap_num);
8178         do
8179         {
8180             heap_num++;
8181             if (heap_num >= n_heaps)
8182                 heap_num = 0;
8183             assert(heap_num != last_heap_num); // we should always find the heap - infinite loop if not!
8184             heap = g_heaps[heap_num];
8185         }
8186         while (!(*x >= heap->ephemeral_low && *x < heap->ephemeral_high));
8187
8188         // x is the start of the mark list piece for this heap
8189         mark_list_piece_start[heap_num] = x;
8190
8191         // to find the end of the mark list piece for this heap, find the first x
8192         // that has !predicate(x), i.e. that is either not in this heap, or beyond the end of the list
8193         if (predicate(x))
8194         {
8195             // let's see if we get lucky and the whole rest belongs to this piece
8196             if (predicate(mark_list_index-1))
8197             {
8198                 x = mark_list_index;
8199                 mark_list_piece_end[heap_num] = x;
8200                 break;
8201             }
8202
8203             // we play a variant of binary search to find the point sooner.
8204             // the first loop advances by increasing steps until the predicate turns false.
8205             // then we retreat the last step, and the second loop advances by decreasing steps, keeping the predicate true.
8206             unsigned inc = 1;
8207             do
8208             {
8209                 inc *= 2;
8210                 uint8_t** temp_x = x;
8211                 x += inc;
8212                 if (temp_x > x)
8213                 {
8214                     break;
8215                 }
8216             }
8217             while (predicate(x));
8218             // we know that only the last step was wrong, so we undo it
8219             x -= inc;
8220             do
8221             {
8222                 // loop invariant - predicate holds at x, but not x + inc
8223                 assert (predicate(x) && !(((x + inc) > x) && predicate(x + inc)));
8224                 inc /= 2;
8225                 if (((x + inc) > x) && predicate(x + inc))
8226                 {
8227                     x += inc;
8228                 }
8229             }
8230             while (inc > 1);
8231             // the termination condition and the loop invariant together imply this:
8232             assert(predicate(x) && !predicate(x + inc) && (inc == 1));
8233             // so the spot we're looking for is one further
8234             x += 1;
8235         }
8236         mark_list_piece_end[heap_num] = x;
8237     }
8238
8239 #undef predicate
8240
8241 //    printf("second phase of sort_mark_list for heap %d took %u cycles\n", this->heap_number, GetCycleCount32() - start);
8242 }
8243
8244 void gc_heap::append_to_mark_list(uint8_t **start, uint8_t **end)
8245 {
8246     size_t slots_needed = end - start;
8247     size_t slots_available = mark_list_end + 1 - mark_list_index;
8248     size_t slots_to_copy = min(slots_needed, slots_available);
8249     memcpy(mark_list_index, start, slots_to_copy*sizeof(*start));
8250     mark_list_index += slots_to_copy;
8251 //    printf("heap %d: appended %Id slots to mark_list\n", heap_number, slots_to_copy);
8252 }
8253
8254 void gc_heap::merge_mark_lists()
8255 {
8256     uint8_t** source[MAX_SUPPORTED_CPUS];
8257     uint8_t** source_end[MAX_SUPPORTED_CPUS];
8258     int source_heap[MAX_SUPPORTED_CPUS];
8259     int source_count = 0;
8260
8261     // in case of mark list overflow, don't bother
8262     if (mark_list_index >  mark_list_end)
8263     {
8264 //        printf("merge_mark_lists: overflow\n");
8265         return;
8266     }
8267
8268     dprintf(3, ("merge_mark_lists: heap_number = %d  starts out with %Id entries", heap_number, mark_list_index - mark_list));
8269 //    unsigned long start = GetCycleCount32();
8270     for (int i = 0; i < n_heaps; i++)
8271     {
8272         gc_heap* heap = g_heaps[i];
8273         if (heap->mark_list_piece_start[heap_number] < heap->mark_list_piece_end[heap_number])
8274         {
8275             source[source_count] = heap->mark_list_piece_start[heap_number];
8276             source_end[source_count] = heap->mark_list_piece_end[heap_number];
8277             source_heap[source_count] = i;
8278             if (source_count < MAX_SUPPORTED_CPUS)
8279                 source_count++;
8280         }
8281     }
8282 //    printf("first phase of merge_mark_lists for heap %d took %u cycles\n", heap_number, GetCycleCount32() - start);
8283
8284     dprintf(3, ("heap_number = %d  has %d sources\n", heap_number, source_count));
8285 #if defined(_DEBUG) || defined(TRACE_GC)
8286     for (int j = 0; j < source_count; j++)
8287     {
8288         dprintf(3, ("heap_number = %d  ", heap_number));
8289         dprintf(3, (" source from heap %d = %Ix .. %Ix (%Id entries)",
8290             (size_t)(source_heap[j]), (size_t)(source[j][0]), (size_t)(source_end[j][-1]), (size_t)(source_end[j] - source[j])));
8291        // the sources should all be sorted
8292         for (uint8_t **x = source[j]; x < source_end[j] - 1; x++)
8293         {
8294             if (x[0] > x[1])
8295             {
8296                 dprintf(3, ("oops, mark_list from source %d for heap %d isn't sorted\n", j, heap_number));
8297                 assert (0);
8298             }
8299         }
8300     }
8301 #endif //_DEBUG || TRACE_GC
8302
8303 //    start = GetCycleCount32();
8304
8305     mark_list = &g_mark_list_copy [heap_number*mark_list_size];
8306     mark_list_index = mark_list;
8307     mark_list_end = &mark_list [mark_list_size-1];
8308     int piece_count = 0;
8309     if (source_count == 0)
8310     {
8311         ; // nothing to do
8312     }
8313     else if (source_count == 1)
8314     {
8315         mark_list = source[0];
8316         mark_list_index = source_end[0];
8317         mark_list_end = mark_list_index;
8318         piece_count++;
8319     }
8320     else
8321     {
8322         while (source_count > 1)
8323         {
8324             // find the lowest and second lowest value in the sources we're merging from
8325             int lowest_source = 0;
8326             uint8_t *lowest = *source[0];
8327             uint8_t *second_lowest = *source[1];
8328             for (int i = 1; i < source_count; i++)
8329             {
8330                 if (lowest > *source[i])
8331                 {
8332                     second_lowest = lowest;
8333                     lowest = *source[i];
8334                     lowest_source = i;
8335                 }
8336                 else if (second_lowest > *source[i])
8337                 {
8338                     second_lowest = *source[i];
8339                 }
8340             }
8341
8342             // find the point in the lowest source where it either runs out or is not <= second_lowest anymore
8343
8344             // let's first try to get lucky and see if the whole source is <= second_lowest -- this is actually quite common
8345             uint8_t **x;
8346             if (source_end[lowest_source][-1] <= second_lowest)
8347                 x = source_end[lowest_source];
8348             else
8349             {
8350                 // use linear search to find the end -- could also use binary search as in sort_mark_list,
8351                 // but saw no improvement doing that
8352                 for (x = source[lowest_source]; x < source_end[lowest_source] && *x <= second_lowest; x++)
8353                     ;
8354             }
8355
8356             // blast this piece to the mark list
8357             append_to_mark_list(source[lowest_source], x);
8358             piece_count++;
8359
8360             source[lowest_source] = x;
8361
8362             // check whether this source is now exhausted
8363             if (x >= source_end[lowest_source])
8364             {
8365                 // if it's not the source with the highest index, copy the source with the highest index
8366                 // over it so the non-empty sources are always at the beginning
8367                 if (lowest_source < source_count-1)
8368                 {
8369                     source[lowest_source] = source[source_count-1];
8370                     source_end[lowest_source] = source_end[source_count-1];
8371                 }
8372                 source_count--;
8373             }
8374         }
8375         // we're left with just one source that we copy
8376         append_to_mark_list(source[0], source_end[0]);
8377         piece_count++;
8378     }
8379
8380 //    printf("second phase of merge_mark_lists for heap %d took %u cycles to merge %d pieces\n", heap_number, GetCycleCount32() - start, piece_count);
8381
8382 #if defined(_DEBUG) || defined(TRACE_GC)
8383     // the final mark list must be sorted
8384     for (uint8_t **x = mark_list; x < mark_list_index - 1; x++)
8385     {
8386         if (x[0] > x[1])
8387         {
8388             dprintf(3, ("oops, mark_list for heap %d isn't sorted at the end of merge_mark_lists", heap_number));
8389             assert (0);
8390         }
8391     }
8392 #endif //defined(_DEBUG) || defined(TRACE_GC)
8393 }
8394 #else //PARALLEL_MARK_LIST_SORT
8395 void gc_heap::combine_mark_lists()
8396 {
8397     dprintf (3, ("Combining mark lists"));
8398     //verify if a heap has overflowed its mark list
8399     BOOL use_mark_list = TRUE;
8400     for (int i = 0; i < n_heaps; i++)
8401     {
8402         if (g_heaps [i]->mark_list_index >  g_heaps [i]->mark_list_end)
8403         {
8404             use_mark_list = FALSE;
8405             break;
8406         }
8407     }
8408
8409     if (use_mark_list)
8410     {
8411         dprintf (3, ("Using mark list"));
8412         //compact the gaps out of the mark list
8413         int gn = 0;
8414         uint8_t** current_gap = g_heaps [gn]->mark_list_index;
8415         uint8_t** current_gap_end = g_heaps[gn]->mark_list_end + 1;
8416         uint8_t** dst_last = current_gap-1;
8417
8418         int srcn = n_heaps-1;
8419         gc_heap* srch = g_heaps [srcn];
8420         uint8_t** src = srch->mark_list_index - 1;
8421         uint8_t** src_beg = srch->mark_list;
8422
8423         while (current_gap <= src)
8424         {
8425             while ((gn < n_heaps-1) && (current_gap >= current_gap_end))
8426             {
8427                 //go to the next gap
8428                 gn++;
8429                 dprintf (3, ("Going to the next gap %d", gn));
8430                 assert (gn < n_heaps);
8431                 current_gap = g_heaps [gn]->mark_list_index;
8432                 current_gap_end = g_heaps[gn]->mark_list_end + 1;
8433                 assert ((gn == (n_heaps-1)) || (current_gap_end == g_heaps[gn+1]->mark_list));
8434             }
8435             while ((srcn > 0) && (src < src_beg))
8436             {
8437                 //go to the previous source
8438                 srcn--;
8439                 dprintf (3, ("going to the previous source %d", srcn));
8440                 assert (srcn>=0);
8441                 gc_heap* srch = g_heaps [srcn];
8442                 src = srch->mark_list_index - 1;
8443                 src_beg = srch->mark_list;
8444             }
8445             if (current_gap < src)
8446             {
8447                 dst_last = current_gap;
8448                 *current_gap++ = *src--;
8449             }
8450         }
8451         dprintf (3, ("src: %Ix dst_last: %Ix", (size_t)src, (size_t)dst_last));
8452
8453         uint8_t** end_of_list = max (src, dst_last);
8454
8455         //sort the resulting compacted list
8456         assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]);
8457         if (end_of_list > &g_mark_list[0])
8458             _sort (&g_mark_list[0], end_of_list, 0);
8459         //adjust the mark_list to the begining of the resulting mark list.
8460         for (int i = 0; i < n_heaps; i++)
8461         {
8462             g_heaps [i]->mark_list = g_mark_list;
8463             g_heaps [i]->mark_list_index = end_of_list + 1;
8464             g_heaps [i]->mark_list_end = end_of_list + 1;
8465         }
8466     }
8467     else
8468     {
8469         uint8_t** end_of_list = g_mark_list;
8470         //adjust the mark_list to the begining of the resulting mark list.
8471         //put the index beyond the end to turn off mark list processing
8472         for (int i = 0; i < n_heaps; i++)
8473         {
8474             g_heaps [i]->mark_list = g_mark_list;
8475             g_heaps [i]->mark_list_index = end_of_list + 1;
8476             g_heaps [i]->mark_list_end = end_of_list;
8477         }
8478     }
8479 }
8480 #endif // PARALLEL_MARK_LIST_SORT
8481 #endif //MULTIPLE_HEAPS
8482 #endif //MARK_LIST
8483
8484 class seg_free_spaces
8485 {
8486     struct seg_free_space
8487     {
8488         BOOL is_plug;
8489         void* start;
8490     };
8491
8492     struct free_space_bucket
8493     {
8494         seg_free_space* free_space;
8495         ptrdiff_t count_add; // Assigned when we first contruct the array.
8496         ptrdiff_t count_fit; // How many items left when we are fitting plugs.
8497     };
8498
8499     void move_bucket (int old_power2, int new_power2)
8500     {
8501         // PREFAST warning 22015: old_power2 could be negative
8502         assert (old_power2 >= 0);
8503         assert (old_power2 >= new_power2);
8504
8505         if (old_power2 == new_power2)
8506         {
8507             return;
8508         }
8509
8510         seg_free_space* src_index = free_space_buckets[old_power2].free_space;
8511         for (int i = old_power2; i > new_power2; i--)
8512         {
8513             seg_free_space** dest = &(free_space_buckets[i].free_space);
8514             (*dest)++;
8515
8516             seg_free_space* dest_index = free_space_buckets[i - 1].free_space;
8517             if (i > (new_power2 + 1))
8518             {
8519                 seg_free_space temp = *src_index;
8520                 *src_index = *dest_index;
8521                 *dest_index = temp;
8522             }
8523             src_index = dest_index;
8524         }
8525
8526         free_space_buckets[old_power2].count_fit--;
8527         free_space_buckets[new_power2].count_fit++;
8528     }
8529
8530 #ifdef _DEBUG
8531
8532     void dump_free_space (seg_free_space* item)
8533     {
8534         uint8_t* addr = 0;
8535         size_t len = 0;
8536
8537         if (item->is_plug)
8538         {
8539             mark* m = (mark*)(item->start);
8540             len = pinned_len (m);
8541             addr = pinned_plug (m) - len;
8542         }
8543         else
8544         {
8545             heap_segment* seg = (heap_segment*)(item->start);
8546             addr = heap_segment_plan_allocated (seg);
8547             len = heap_segment_committed (seg) - addr;
8548         }
8549
8550         dprintf (SEG_REUSE_LOG_1, ("[%d]0x%Ix %Id", heap_num, addr, len));
8551     }
8552
8553     void dump()
8554     {
8555         seg_free_space* item = NULL;
8556         int i = 0;
8557
8558         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------\nnow the free spaces look like:", heap_num));
8559         for (i = 0; i < (free_space_bucket_count - 1); i++)
8560         {
8561             dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8562             dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8563             item = free_space_buckets[i].free_space;
8564             while (item < free_space_buckets[i + 1].free_space)
8565             {
8566                 dump_free_space (item);
8567                 item++;
8568             }
8569             dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8570         }
8571
8572         dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8573         dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8574         item = free_space_buckets[i].free_space;
8575
8576         while (item <= &seg_free_space_array[free_space_item_count - 1])
8577         {
8578             dump_free_space (item);
8579             item++;
8580         }
8581         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8582     }
8583
8584 #endif //_DEBUG
8585
8586     free_space_bucket* free_space_buckets;
8587     seg_free_space* seg_free_space_array;
8588     ptrdiff_t free_space_bucket_count;
8589     ptrdiff_t free_space_item_count;
8590     int base_power2;
8591     int heap_num;
8592 #ifdef _DEBUG
8593     BOOL has_end_of_seg;
8594 #endif //_DEBUG
8595
8596 public:
8597
8598     seg_free_spaces (int h_number)
8599     {
8600         heap_num = h_number;
8601     }
8602
8603     BOOL alloc ()
8604     {
8605         size_t total_prealloc_size = 
8606             MAX_NUM_BUCKETS * sizeof (free_space_bucket) +
8607             MAX_NUM_FREE_SPACES * sizeof (seg_free_space);
8608
8609         free_space_buckets = (free_space_bucket*) new (nothrow) uint8_t[total_prealloc_size];
8610
8611         return (!!free_space_buckets);
8612     }
8613
8614     // We take the ordered free space array we got from the 1st pass,
8615     // and feed the portion that we decided to use to this method, ie,
8616     // the largest item_count free spaces.
8617     void add_buckets (int base, size_t* ordered_free_spaces, int bucket_count, size_t item_count)
8618     {
8619         assert (free_space_buckets);
8620         assert (item_count <= (size_t)MAX_PTR);
8621
8622         free_space_bucket_count = bucket_count;
8623         free_space_item_count = item_count;
8624         base_power2 = base;
8625 #ifdef _DEBUG
8626         has_end_of_seg = FALSE;
8627 #endif //_DEBUG
8628
8629         ptrdiff_t total_item_count = 0;
8630         ptrdiff_t i = 0;
8631
8632         seg_free_space_array = (seg_free_space*)(free_space_buckets + free_space_bucket_count);
8633
8634         for (i = 0; i < (ptrdiff_t)item_count; i++)
8635         {
8636             seg_free_space_array[i].start = 0;
8637             seg_free_space_array[i].is_plug = FALSE;
8638         }
8639
8640         for (i = 0; i < bucket_count; i++)
8641         {
8642             free_space_buckets[i].count_add = ordered_free_spaces[i];
8643             free_space_buckets[i].count_fit = ordered_free_spaces[i];
8644             free_space_buckets[i].free_space = &seg_free_space_array[total_item_count];
8645             total_item_count += free_space_buckets[i].count_add;
8646         }
8647
8648         assert (total_item_count == (ptrdiff_t)item_count);
8649     }
8650
8651     // If we are adding a free space before a plug we pass the
8652     // mark stack position so we can update the length; we could
8653     // also be adding the free space after the last plug in which
8654     // case start is the segment which we'll need to update the 
8655     // heap_segment_plan_allocated.
8656     void add (void* start, BOOL plug_p, BOOL first_p)
8657     {
8658         size_t size = (plug_p ? 
8659                        pinned_len ((mark*)start) : 
8660                        (heap_segment_committed ((heap_segment*)start) - 
8661                            heap_segment_plan_allocated ((heap_segment*)start)));
8662         
8663         if (plug_p)
8664         {
8665             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space before plug: %Id", heap_num, size));
8666         }
8667         else
8668         {
8669             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space at end of seg: %Id", heap_num, size));
8670 #ifdef _DEBUG
8671             has_end_of_seg = TRUE;
8672 #endif //_DEBUG
8673         }
8674                   
8675         if (first_p)
8676         {
8677             size_t eph_gen_starts = gc_heap::eph_gen_starts_size;
8678             size -= eph_gen_starts;
8679             if (plug_p)
8680             {
8681                 mark* m = (mark*)(start);
8682                 pinned_len (m) -= eph_gen_starts;
8683             }
8684             else
8685             {
8686                 heap_segment* seg = (heap_segment*)start;
8687                 heap_segment_plan_allocated (seg) += eph_gen_starts;
8688             }
8689         }
8690
8691         int bucket_power2 = index_of_set_bit (round_down_power2 (size));
8692         if (bucket_power2 < base_power2)
8693         {
8694             return;
8695         }
8696
8697         free_space_bucket* bucket = &free_space_buckets[bucket_power2 - base_power2];
8698
8699         seg_free_space* bucket_free_space = bucket->free_space;
8700         assert (plug_p || (!plug_p && bucket->count_add));
8701
8702         if (bucket->count_add == 0)
8703         {
8704             dprintf (SEG_REUSE_LOG_1, ("[%d]Already have enough of 2^%d", heap_num, bucket_power2));
8705             return;
8706         }
8707
8708         ptrdiff_t index = bucket->count_add - 1;
8709
8710         dprintf (SEG_REUSE_LOG_1, ("[%d]Building free spaces: adding %Ix; len: %Id (2^%d)", 
8711                     heap_num, 
8712                     (plug_p ? 
8713                         (pinned_plug ((mark*)start) - pinned_len ((mark*)start)) : 
8714                         heap_segment_plan_allocated ((heap_segment*)start)),
8715                     size,
8716                     bucket_power2));
8717
8718         if (plug_p)
8719         {
8720             bucket_free_space[index].is_plug = TRUE;
8721         }
8722
8723         bucket_free_space[index].start = start;
8724         bucket->count_add--;
8725     }
8726
8727 #ifdef _DEBUG
8728
8729     // Do a consistency check after all free spaces are added.
8730     void check()
8731     {
8732         ptrdiff_t i = 0;
8733         int end_of_seg_count = 0;
8734
8735         for (i = 0; i < free_space_item_count; i++)
8736         {
8737             assert (seg_free_space_array[i].start);
8738             if (!(seg_free_space_array[i].is_plug))
8739             {
8740                 end_of_seg_count++;
8741             }
8742         }
8743         
8744         if (has_end_of_seg)
8745         {
8746             assert (end_of_seg_count == 1);
8747         }
8748         else
8749         {
8750             assert (end_of_seg_count == 0);
8751         }
8752
8753         for (i = 0; i < free_space_bucket_count; i++)
8754         {
8755             assert (free_space_buckets[i].count_add == 0);
8756         }
8757     }
8758
8759 #endif //_DEBUG
8760
8761     uint8_t* fit (uint8_t* old_loc,
8762 #ifdef SHORT_PLUGS
8763                BOOL set_padding_on_saved_p,
8764                mark* pinned_plug_entry,
8765 #endif //SHORT_PLUGS
8766                size_t plug_size
8767                REQD_ALIGN_AND_OFFSET_DCL)
8768     {
8769         if (old_loc)
8770         {
8771 #ifdef SHORT_PLUGS
8772             assert (!is_plug_padded (old_loc));
8773 #endif //SHORT_PLUGS
8774             assert (!node_realigned (old_loc));
8775         }
8776
8777         size_t saved_plug_size = plug_size;
8778
8779 #ifdef FEATURE_STRUCTALIGN
8780         // BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account
8781         _ASSERTE(requiredAlignment == DATA_ALIGNMENT && false);
8782 #endif // FEATURE_STRUCTALIGN
8783         // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the 
8784         // the bucket.
8785
8786         size_t plug_size_to_fit = plug_size;
8787
8788         int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
8789
8790 #ifdef SHORT_PLUGS
8791         plug_size_to_fit += (pad_in_front ? Align(min_obj_size) : 0);
8792 #endif //SHORT_PLUGS
8793
8794         int plug_power2 = index_of_set_bit (round_up_power2 (plug_size_to_fit + Align(min_obj_size)));
8795         ptrdiff_t i;
8796         uint8_t* new_address = 0;
8797
8798         if (plug_power2 < base_power2)
8799         {
8800             plug_power2 = base_power2;
8801         }
8802
8803         int chosen_power2 = plug_power2 - base_power2;
8804 retry:
8805         for (i = chosen_power2; i < free_space_bucket_count; i++)
8806         {
8807             if (free_space_buckets[i].count_fit != 0)
8808             {
8809                 break;
8810             }
8811             chosen_power2++;
8812         }
8813
8814         dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting plug len %Id (2^%d) using 2^%d free space", 
8815             heap_num, 
8816             plug_size, 
8817             plug_power2, 
8818             (chosen_power2 + base_power2)));
8819
8820         assert (i < free_space_bucket_count);
8821         
8822         seg_free_space* bucket_free_space = free_space_buckets[chosen_power2].free_space;
8823         ptrdiff_t free_space_count = free_space_buckets[chosen_power2].count_fit;
8824         size_t new_free_space_size = 0;
8825         BOOL can_fit = FALSE;
8826         size_t pad = 0;
8827
8828         for (i = 0; i < free_space_count; i++)
8829         {
8830             size_t free_space_size = 0;
8831             pad = 0;
8832 #ifdef SHORT_PLUGS
8833             BOOL short_plugs_padding_p = FALSE;
8834 #endif //SHORT_PLUGS
8835             BOOL realign_padding_p = FALSE;
8836
8837             if (bucket_free_space[i].is_plug)
8838             {
8839                 mark* m = (mark*)(bucket_free_space[i].start);
8840                 uint8_t* plug_free_space_start = pinned_plug (m) - pinned_len (m);
8841                 
8842 #ifdef SHORT_PLUGS
8843                 if ((pad_in_front & USE_PADDING_FRONT) &&
8844                     (((plug_free_space_start - pin_allocation_context_start_region (m))==0) ||
8845                     ((plug_free_space_start - pin_allocation_context_start_region (m))>=DESIRED_PLUG_LENGTH)))
8846                 {
8847                     pad = Align (min_obj_size);
8848                     short_plugs_padding_p = TRUE;
8849                 }
8850 #endif //SHORT_PLUGS
8851
8852                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, plug_free_space_start+pad)))
8853                 {
8854                     pad += switch_alignment_size (pad != 0);
8855                     realign_padding_p = TRUE;
8856                 }
8857
8858                 plug_size = saved_plug_size + pad;
8859
8860                 free_space_size = pinned_len (m);
8861                 new_address = pinned_plug (m) - pinned_len (m);
8862
8863                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8864                     free_space_size == plug_size)
8865                 {
8866                     new_free_space_size = free_space_size - plug_size;
8867                     pinned_len (m) = new_free_space_size;
8868 #ifdef SIMPLE_DPRINTF
8869                     dprintf (SEG_REUSE_LOG_0, ("[%d]FP: 0x%Ix->0x%Ix(%Ix)(%Ix), [0x%Ix (2^%d) -> [0x%Ix (2^%d)",
8870                                 heap_num, 
8871                                 old_loc,
8872                                 new_address, 
8873                                 (plug_size - pad),
8874                                 pad,
8875                                 pinned_plug (m), 
8876                                 index_of_set_bit (round_down_power2 (free_space_size)),
8877                                 (pinned_plug (m) - pinned_len (m)), 
8878                                 index_of_set_bit (round_down_power2 (new_free_space_size))));
8879 #endif //SIMPLE_DPRINTF
8880
8881 #ifdef SHORT_PLUGS
8882                     if (short_plugs_padding_p)
8883                     {
8884                         pin_allocation_context_start_region (m) = plug_free_space_start;
8885                         set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
8886                     }
8887 #endif //SHORT_PLUGS
8888
8889                     if (realign_padding_p)
8890                     {
8891                         set_node_realigned (old_loc);
8892                     }
8893
8894                     can_fit = TRUE;
8895                 }
8896             }
8897             else
8898             {
8899                 heap_segment* seg = (heap_segment*)(bucket_free_space[i].start);
8900                 free_space_size = heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
8901
8902                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, heap_segment_plan_allocated (seg))))
8903                 {
8904                     pad = switch_alignment_size (FALSE);
8905                     realign_padding_p = TRUE;
8906                 }
8907
8908                 plug_size = saved_plug_size + pad;
8909
8910                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8911                     free_space_size == plug_size)
8912                 {
8913                     new_address = heap_segment_plan_allocated (seg);
8914                     new_free_space_size = free_space_size - plug_size;
8915                     heap_segment_plan_allocated (seg) = new_address + plug_size;
8916 #ifdef SIMPLE_DPRINTF
8917                     dprintf (SEG_REUSE_LOG_0, ("[%d]FS: 0x%Ix-> 0x%Ix(%Ix) (2^%d) -> 0x%Ix (2^%d)",
8918                                 heap_num, 
8919                                 old_loc,
8920                                 new_address, 
8921                                 (plug_size - pad),
8922                                 index_of_set_bit (round_down_power2 (free_space_size)),
8923                                 heap_segment_plan_allocated (seg), 
8924                                 index_of_set_bit (round_down_power2 (new_free_space_size))));
8925 #endif //SIMPLE_DPRINTF
8926
8927                     if (realign_padding_p)
8928                         set_node_realigned (old_loc);
8929
8930                     can_fit = TRUE;
8931                 }
8932             }
8933
8934             if (can_fit)
8935             {
8936                 break;
8937             }
8938         }
8939
8940         if (!can_fit)
8941         {
8942             assert (chosen_power2 == 0);
8943             chosen_power2 = 1;
8944             goto retry;
8945         }
8946         else
8947         {
8948             if (pad)
8949             {
8950                 new_address += pad;
8951             }
8952             assert ((chosen_power2 && (i == 0)) ||
8953                     (!chosen_power2) && (i < free_space_count));
8954         }
8955
8956         int new_bucket_power2 = index_of_set_bit (round_down_power2 (new_free_space_size));
8957
8958         if (new_bucket_power2 < base_power2)
8959         {
8960             new_bucket_power2 = base_power2;
8961         }
8962
8963         move_bucket (chosen_power2, new_bucket_power2 - base_power2);
8964
8965         //dump();
8966
8967         return new_address;
8968     }
8969
8970     void cleanup ()
8971     {
8972         if (free_space_buckets)
8973         {
8974             delete [] free_space_buckets;
8975         }
8976         if (seg_free_space_array)
8977         {
8978             delete [] seg_free_space_array;
8979         }
8980     }
8981 };
8982
8983
8984 #define marked(i) header(i)->IsMarked()
8985 #define set_marked(i) header(i)->SetMarked()
8986 #define clear_marked(i) header(i)->ClearMarked()
8987 #define pinned(i) header(i)->IsPinned()
8988 #define set_pinned(i) header(i)->SetPinned()
8989 #define clear_pinned(i) header(i)->GetHeader()->ClrGCBit();
8990
8991 inline size_t my_get_size (Object* ob)
8992 {
8993     MethodTable* mT = header(ob)->GetMethodTable();
8994     return (mT->GetBaseSize() +
8995             (mT->HasComponentSize() ?
8996              ((size_t)((CObjectHeader*)ob)->GetNumComponents() * mT->RawGetComponentSize()) : 0));
8997 }
8998
8999 //#define size(i) header(i)->GetSize()
9000 #define size(i) my_get_size (header(i))
9001
9002 #define contain_pointers(i) header(i)->ContainsPointers()
9003 #ifdef COLLECTIBLE_CLASS
9004 #define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible()
9005
9006 #define get_class_object(i) GCToEEInterface::GetLoaderAllocatorObjectForGC((Object *)i)
9007 #define is_collectible(i) method_table(i)->Collectible()
9008 #else //COLLECTIBLE_CLASS
9009 #define contain_pointers_or_collectible(i) header(i)->ContainsPointers()
9010 #endif //COLLECTIBLE_CLASS
9011
9012 #if defined (MARK_ARRAY) && defined (BACKGROUND_GC)
9013 inline
9014 void gc_heap::seg_clear_mark_array_bits_soh (heap_segment* seg)
9015 {
9016     uint8_t* range_beg = 0;
9017     uint8_t* range_end = 0;
9018     if (bgc_mark_array_range (seg, FALSE, &range_beg, &range_end))
9019     {
9020         clear_mark_array (range_beg, align_on_mark_word (range_end), FALSE
9021 #ifdef FEATURE_BASICFREEZE
9022             , TRUE
9023 #endif // FEATURE_BASICFREEZE
9024             );
9025     }
9026 }
9027
9028 void gc_heap::clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
9029 {
9030     if ((start < background_saved_highest_address) &&
9031         (end > background_saved_lowest_address))
9032     {
9033         start = max (start, background_saved_lowest_address);
9034         end = min (end, background_saved_highest_address);
9035
9036         size_t start_mark_bit = mark_bit_of (start);
9037         size_t end_mark_bit = mark_bit_of (end);
9038         unsigned int startbit = mark_bit_bit (start_mark_bit);
9039         unsigned int endbit = mark_bit_bit (end_mark_bit);
9040         size_t startwrd = mark_bit_word (start_mark_bit);
9041         size_t endwrd = mark_bit_word (end_mark_bit);
9042
9043         dprintf (3, ("Clearing all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
9044             (size_t)start, (size_t)start_mark_bit, 
9045             (size_t)end, (size_t)end_mark_bit));
9046
9047         unsigned int firstwrd = lowbits (~0, startbit);
9048         unsigned int lastwrd = highbits (~0, endbit);
9049
9050         if (startwrd == endwrd)
9051         {
9052             unsigned int wrd = firstwrd | lastwrd;
9053             mark_array[startwrd] &= wrd;
9054             return;
9055         }
9056
9057         // clear the first mark word.
9058         if (startbit)
9059         {
9060             mark_array[startwrd] &= firstwrd;
9061             startwrd++;
9062         }
9063
9064         for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
9065         {
9066             mark_array[wrdtmp] = 0;
9067         }
9068
9069         // clear the last mark word.
9070         if (endbit)
9071         {
9072             mark_array[endwrd] &= lastwrd;
9073         }
9074     }
9075 }
9076
9077 void gc_heap::bgc_clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
9078 {
9079     if ((start < background_saved_highest_address) &&
9080         (end > background_saved_lowest_address))
9081     {
9082         start = max (start, background_saved_lowest_address);
9083         end = min (end, background_saved_highest_address);
9084
9085         clear_batch_mark_array_bits (start, end);
9086     }
9087 }
9088
9089 void gc_heap::clear_mark_array_by_objects (uint8_t* from, uint8_t* end, BOOL loh_p)
9090 {
9091     dprintf (3, ("clearing mark array bits by objects for addr [%Ix,[%Ix", 
9092                   from, end));
9093     int align_const = get_alignment_constant (!loh_p);
9094
9095     uint8_t* o = from;
9096
9097     while (o < end)
9098     {
9099         uint8_t*  next_o = o + Align (size (o), align_const);
9100
9101         if (background_object_marked (o, TRUE))
9102         {
9103             dprintf (3, ("%Ix was marked by bgc, is now cleared", o));
9104         }
9105
9106         o = next_o;
9107     }
9108 }
9109 #endif //MARK_ARRAY && BACKGROUND_GC
9110
9111 inline
9112 BOOL gc_heap::is_mark_set (uint8_t* o)
9113 {
9114     return marked (o);
9115 }
9116
9117 #if defined (_MSC_VER) && defined (_TARGET_X86_)
9118 #pragma optimize("y", on)        // Small critical routines, don't put in EBP frame 
9119 #endif //_MSC_VER && _TARGET_X86_
9120
9121 // return the generation number of an object.
9122 // It is assumed that the object is valid.
9123 //Note that this will return max_generation for a LOH object
9124 int gc_heap::object_gennum (uint8_t* o)
9125 {
9126     if (in_range_for_segment (o, ephemeral_heap_segment) &&
9127         (o >= generation_allocation_start (generation_of (max_generation-1))))
9128     {
9129         // in an ephemeral generation.
9130         for ( int i = 0; i < max_generation-1; i++)
9131         {
9132             if ((o >= generation_allocation_start (generation_of (i))))
9133                 return i;
9134         }
9135         return max_generation-1;
9136     }
9137     else
9138     {
9139         return max_generation;
9140     }
9141 }
9142
9143 int gc_heap::object_gennum_plan (uint8_t* o)
9144 {
9145     if (in_range_for_segment (o, ephemeral_heap_segment))
9146     {
9147         for (int i = 0; i <= max_generation-1; i++)
9148         {
9149             uint8_t* plan_start = generation_plan_allocation_start (generation_of (i));
9150             if (plan_start && (o >= plan_start))
9151             {
9152                 return i;
9153             }
9154         }
9155     }
9156     return max_generation;
9157 }
9158
9159 #if defined(_MSC_VER) && defined(_TARGET_X86_)
9160 #pragma optimize("", on)        // Go back to command line default optimizations
9161 #endif //_MSC_VER && _TARGET_X86_
9162
9163 heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h_number)
9164 {
9165     size_t initial_commit = SEGMENT_INITIAL_COMMIT;
9166
9167     //Commit the first page
9168     if (!virtual_alloc_commit_for_heap (new_pages, initial_commit, h_number))
9169     {
9170         return 0;
9171     }
9172
9173     //overlay the heap_segment
9174     heap_segment* new_segment = (heap_segment*)new_pages;
9175
9176     uint8_t* start = new_pages + segment_info_size;
9177     heap_segment_mem (new_segment) = start;
9178     heap_segment_used (new_segment) = start;
9179     heap_segment_reserved (new_segment) = new_pages + size;
9180     heap_segment_committed (new_segment) = new_pages + initial_commit;
9181     init_heap_segment (new_segment);
9182     dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
9183     return new_segment;
9184 }
9185
9186 void gc_heap::init_heap_segment (heap_segment* seg)
9187 {
9188     seg->flags = 0;
9189     heap_segment_next (seg) = 0;
9190     heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
9191     heap_segment_allocated (seg) = heap_segment_mem (seg);
9192 #ifdef BACKGROUND_GC
9193     heap_segment_background_allocated (seg) = 0;
9194     heap_segment_saved_bg_allocated (seg) = 0;
9195 #endif //BACKGROUND_GC
9196 }
9197
9198 //Releases the segment to the OS.
9199 // this is always called on one thread only so calling seg_table->remove is fine.
9200 void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding)
9201 {
9202     if (!heap_segment_loh_p (seg))
9203     {
9204         //cleanup the brick table back to the empty value
9205         clear_brick_table (heap_segment_mem (seg), heap_segment_reserved (seg));
9206     }
9207
9208     if (consider_hoarding)
9209     {
9210         assert ((heap_segment_mem (seg) - (uint8_t*)seg) <= ptrdiff_t(2*OS_PAGE_SIZE));
9211         size_t ss = (size_t) (heap_segment_reserved (seg) - (uint8_t*)seg);
9212         //Don't keep the big ones.
9213         if (ss <= INITIAL_ALLOC)
9214         {
9215             dprintf (2, ("Hoarding segment %Ix", (size_t)seg));
9216 #ifdef BACKGROUND_GC
9217             // We don't need to clear the decommitted flag because when this segment is used
9218             // for a new segment the flags will be cleared.
9219             if (!heap_segment_decommitted_p (seg))
9220 #endif //BACKGROUND_GC
9221             {
9222                 decommit_heap_segment (seg);
9223             }
9224
9225 #ifdef SEG_MAPPING_TABLE
9226             seg_mapping_table_remove_segment (seg);
9227 #endif //SEG_MAPPING_TABLE
9228
9229             heap_segment_next (seg) = segment_standby_list;
9230             segment_standby_list = seg;
9231             seg = 0;
9232         }
9233     }
9234
9235     if (seg != 0)
9236     {
9237         dprintf (2, ("h%d: del seg: [%Ix, %Ix[", 
9238                      heap_number, (size_t)seg,
9239                      (size_t)(heap_segment_reserved (seg))));
9240
9241 #ifdef BACKGROUND_GC
9242         ::record_changed_seg ((uint8_t*)seg, heap_segment_reserved (seg), 
9243                             settings.gc_index, current_bgc_state,
9244                             seg_deleted);
9245         decommit_mark_array_by_seg (seg);
9246 #endif //BACKGROUND_GC
9247
9248 #ifdef SEG_MAPPING_TABLE
9249         seg_mapping_table_remove_segment (seg);
9250 #else //SEG_MAPPING_TABLE
9251         seg_table->remove ((uint8_t*)seg);
9252 #endif //SEG_MAPPING_TABLE
9253
9254         release_segment (seg);
9255     }
9256 }
9257
9258 //resets the pages beyond allocates size so they won't be swapped out and back in
9259
9260 void gc_heap::reset_heap_segment_pages (heap_segment* seg)
9261 {
9262     size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg));
9263     size_t size = (size_t)heap_segment_committed (seg) - page_start;
9264     if (size != 0)
9265         GCToOSInterface::VirtualReset((void*)page_start, size, false /* unlock */);
9266 }
9267
9268 void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
9269                                            size_t extra_space)
9270 {
9271     uint8_t*  page_start = align_on_page (heap_segment_allocated(seg));
9272     size_t size = heap_segment_committed (seg) - page_start;
9273     extra_space = align_on_page (extra_space);
9274     if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE))
9275     {
9276         page_start += max(extra_space, 32*OS_PAGE_SIZE);
9277         size -= max (extra_space, 32*OS_PAGE_SIZE);
9278
9279         GCToOSInterface::VirtualDecommit (page_start, size);
9280         dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)", 
9281             (size_t)page_start, 
9282             (size_t)(page_start + size),
9283             size));
9284         heap_segment_committed (seg) = page_start;
9285         if (heap_segment_used (seg) > heap_segment_committed (seg))
9286         {
9287             heap_segment_used (seg) = heap_segment_committed (seg);
9288         }
9289     }
9290 }
9291
9292 //decommit all pages except one or 2
9293 void gc_heap::decommit_heap_segment (heap_segment* seg)
9294 {
9295     uint8_t*  page_start = align_on_page (heap_segment_mem (seg));
9296
9297     dprintf (3, ("Decommitting heap segment %Ix", (size_t)seg));
9298
9299 #ifdef BACKGROUND_GC
9300     page_start += OS_PAGE_SIZE;
9301 #endif //BACKGROUND_GC
9302
9303     size_t size = heap_segment_committed (seg) - page_start;
9304     GCToOSInterface::VirtualDecommit (page_start, size);
9305
9306     //re-init the segment object
9307     heap_segment_committed (seg) = page_start;
9308     if (heap_segment_used (seg) > heap_segment_committed (seg))
9309     {
9310         heap_segment_used (seg) = heap_segment_committed (seg);
9311     }
9312 }
9313
9314 void gc_heap::clear_gen0_bricks()
9315 {
9316     if (!gen0_bricks_cleared)
9317     {
9318         gen0_bricks_cleared = TRUE;
9319         //initialize brick table for gen 0
9320         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
9321                 b < brick_of (align_on_brick
9322                             (heap_segment_allocated (ephemeral_heap_segment)));
9323                 b++)
9324         {
9325             set_brick (b, -1);
9326         }
9327     }
9328 }
9329
9330 #ifdef BACKGROUND_GC
9331 void gc_heap::rearrange_small_heap_segments()
9332 {
9333     heap_segment* seg = freeable_small_heap_segment;
9334     while (seg)
9335     {
9336         heap_segment* next_seg = heap_segment_next (seg);
9337         // TODO: we need to consider hoarding here.
9338         delete_heap_segment (seg, FALSE);
9339         seg = next_seg;
9340     }
9341     freeable_small_heap_segment = 0;
9342 }
9343 #endif //BACKGROUND_GC
9344
9345 void gc_heap::rearrange_large_heap_segments()
9346 {
9347     dprintf (2, ("deleting empty large segments"));
9348     heap_segment* seg = freeable_large_heap_segment;
9349     while (seg)
9350     {
9351         heap_segment* next_seg = heap_segment_next (seg);
9352         delete_heap_segment (seg, GCConfig::GetRetainVM());
9353         seg = next_seg;
9354     }
9355     freeable_large_heap_segment = 0;
9356 }
9357
9358 void gc_heap::rearrange_heap_segments(BOOL compacting)
9359 {
9360     heap_segment* seg =
9361         generation_start_segment (generation_of (max_generation));
9362
9363     heap_segment* prev_seg = 0;
9364     heap_segment* next_seg = 0;
9365     while (seg)
9366     {
9367         next_seg = heap_segment_next (seg);
9368
9369         //link ephemeral segment when expanding
9370         if ((next_seg == 0) && (seg != ephemeral_heap_segment))
9371         {
9372             seg->next = ephemeral_heap_segment;
9373             next_seg = heap_segment_next (seg);
9374         }
9375
9376         //re-used expanded heap segment
9377         if ((seg == ephemeral_heap_segment) && next_seg)
9378         {
9379             heap_segment_next (prev_seg) = next_seg;
9380             heap_segment_next (seg) = 0;
9381         }
9382         else
9383         {
9384             uint8_t* end_segment = (compacting ?
9385                                  heap_segment_plan_allocated (seg) : 
9386                                  heap_segment_allocated (seg));
9387             // check if the segment was reached by allocation
9388             if ((end_segment == heap_segment_mem (seg))&&
9389                 !heap_segment_read_only_p (seg))
9390             {
9391                 //if not, unthread and delete
9392                 assert (prev_seg);
9393                 assert (seg != ephemeral_heap_segment);
9394                 heap_segment_next (prev_seg) = next_seg;
9395                 delete_heap_segment (seg, GCConfig::GetRetainVM());
9396
9397                 dprintf (2, ("Deleting heap segment %Ix", (size_t)seg));
9398             }
9399             else
9400             {
9401                 if (!heap_segment_read_only_p (seg))
9402                 {
9403                     if (compacting)
9404                     {
9405                         heap_segment_allocated (seg) =
9406                             heap_segment_plan_allocated (seg);
9407                     }
9408
9409                     // reset the pages between allocated and committed.
9410                     if (seg != ephemeral_heap_segment)
9411                     {
9412                         decommit_heap_segment_pages (seg, 0);
9413                     }
9414                 }
9415                 prev_seg = seg;
9416             }
9417         }
9418
9419         seg = next_seg;
9420     }
9421 }
9422
9423
9424 #ifdef WRITE_WATCH
9425
9426 uint8_t* g_addresses [array_size+2]; // to get around the bug in GetWriteWatch
9427
9428 #ifdef TIME_WRITE_WATCH
9429 static unsigned int tot_cycles = 0;
9430 #endif //TIME_WRITE_WATCH
9431
9432 #ifdef CARD_BUNDLE
9433
9434 inline void gc_heap::verify_card_bundle_bits_set(size_t first_card_word, size_t last_card_word)
9435 {
9436 #ifdef _DEBUG
9437     for (size_t x = cardw_card_bundle (first_card_word); x < cardw_card_bundle (last_card_word); x++)
9438     {
9439         if (!card_bundle_set_p (x))
9440         {
9441             assert (!"Card bundle not set");
9442             dprintf (3, ("Card bundle %Ix not set", x));
9443         }
9444     }
9445 #endif
9446 }
9447
9448 // Verifies that any bundles that are not set represent only cards that are not set.
9449 inline void gc_heap::verify_card_bundles()
9450 {
9451 #ifdef _DEBUG
9452     size_t lowest_card = card_word (card_of (lowest_address));
9453     size_t highest_card = card_word (card_of (highest_address));
9454     size_t cardb = cardw_card_bundle (lowest_card);
9455     size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (highest_card));
9456
9457     while (cardb < end_cardb)
9458     {
9459         uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb), lowest_card)];
9460         uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1), highest_card)];
9461
9462         if (card_bundle_set_p (cardb) == 0)
9463         {
9464             // Verify that no card is set
9465             while (card_word < card_word_end)
9466             {
9467                 if (*card_word != 0)
9468                 {
9469                     dprintf  (3, ("gc: %d, Card word %Ix for address %Ix set, card_bundle %Ix clear",
9470                             dd_collection_count (dynamic_data_of (0)), 
9471                             (size_t)(card_word-&card_table[0]),
9472                             (size_t)(card_address ((size_t)(card_word-&card_table[0]) * card_word_width)), cardb));
9473                 }
9474
9475                 assert((*card_word)==0);
9476                 card_word++;
9477             }
9478         }
9479
9480         cardb++;
9481     }
9482 #endif
9483 }
9484
9485 // If card bundles are enabled, use write watch to find pages in the card table that have 
9486 // been dirtied, and set the corresponding card bundle bits.
9487 void gc_heap::update_card_table_bundle()
9488 {
9489     if (card_bundles_enabled())
9490     {
9491         // The address of the card word containing the card representing the lowest heap address
9492         uint8_t* base_address = (uint8_t*)(&card_table[card_word (card_of (lowest_address))]);
9493
9494         // The address of the card word containing the card representing the highest heap address
9495         uint8_t* high_address = (uint8_t*)(&card_table[card_word (card_of (highest_address))]);
9496         
9497         uint8_t* saved_base_address = base_address;
9498         uintptr_t bcount = array_size;
9499         size_t saved_region_size = align_on_page (high_address) - saved_base_address;
9500
9501         do
9502         {
9503             size_t region_size = align_on_page (high_address) - base_address;
9504
9505             dprintf (3,("Probing card table pages [%Ix, %Ix[", (size_t)base_address, (size_t)base_address+region_size));
9506             bool success = GCToOSInterface::GetWriteWatch(false /* resetState */,
9507                                                           base_address,
9508                                                           region_size,
9509                                                           (void**)g_addresses,
9510                                                           &bcount);
9511             assert (success && "GetWriteWatch failed!");
9512
9513             dprintf (3,("Found %d pages written", bcount));
9514             for (unsigned i = 0; i < bcount; i++)
9515             {
9516                 // Offset of the dirty page from the start of the card table (clamped to base_address)
9517                 size_t bcardw = (uint32_t*)(max(g_addresses[i],base_address)) - &card_table[0];
9518
9519                 // Offset of the end of the page from the start of the card table (clamped to high addr)
9520                 size_t ecardw = (uint32_t*)(min(g_addresses[i]+OS_PAGE_SIZE, high_address)) - &card_table[0];
9521                 assert (bcardw >= card_word (card_of (g_gc_lowest_address)));
9522
9523                 // Set the card bundle bits representing the dirty card table page
9524                 card_bundles_set (cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw)));
9525                 dprintf (3,("Set Card bundle [%Ix, %Ix[", cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))));
9526
9527                 verify_card_bundle_bits_set(bcardw, ecardw);
9528             }
9529
9530             if (bcount >= array_size)
9531             {
9532                 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
9533                 bcount = array_size;
9534             }
9535
9536         } while ((bcount >= array_size) && (base_address < high_address));
9537
9538         // Now that we've updated the card bundle bits, reset the write-tracking state. 
9539         GCToOSInterface::ResetWriteWatch (saved_base_address, saved_region_size);
9540     }
9541 }
9542 #endif //CARD_BUNDLE
9543
9544 // static
9545 void gc_heap::reset_write_watch_for_gc_heap(void* base_address, size_t region_size)
9546 {
9547 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9548     SoftwareWriteWatch::ClearDirty(base_address, region_size);
9549 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9550     GCToOSInterface::ResetWriteWatch(base_address, region_size);
9551 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9552 }
9553
9554 // static
9555 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)
9556 {
9557 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9558     SoftwareWriteWatch::GetDirty(base_address, region_size, dirty_pages, dirty_page_count_ref, reset, is_runtime_suspended);
9559 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9560     UNREFERENCED_PARAMETER(is_runtime_suspended);
9561     bool success = GCToOSInterface::GetWriteWatch(reset, base_address, region_size, dirty_pages, dirty_page_count_ref);
9562     assert(success);
9563 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9564 }
9565
9566 const size_t ww_reset_quantum = 128*1024*1024;
9567
9568 inline
9569 void gc_heap::switch_one_quantum()
9570 {
9571     enable_preemptive ();
9572     GCToOSInterface::Sleep (1);
9573     disable_preemptive (true);
9574 }
9575
9576 void gc_heap::reset_ww_by_chunk (uint8_t* start_address, size_t total_reset_size)
9577 {
9578     size_t reset_size = 0;
9579     size_t remaining_reset_size = 0;
9580     size_t next_reset_size = 0;
9581
9582     while (reset_size != total_reset_size)
9583     {
9584         remaining_reset_size = total_reset_size - reset_size;
9585         next_reset_size = ((remaining_reset_size >= ww_reset_quantum) ? ww_reset_quantum : remaining_reset_size);
9586         if (next_reset_size)
9587         {
9588             reset_write_watch_for_gc_heap(start_address, next_reset_size);
9589             reset_size += next_reset_size;
9590
9591             switch_one_quantum();
9592         }
9593     }
9594
9595     assert (reset_size == total_reset_size);
9596 }
9597
9598 // This does a Sleep(1) for every reset ww_reset_quantum bytes of reset 
9599 // we do concurrently.
9600 void gc_heap::switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size)
9601 {
9602     if (concurrent_p)
9603     {
9604         *current_total_reset_size += last_reset_size;
9605
9606         dprintf (2, ("reset %Id bytes so far", *current_total_reset_size));
9607
9608         if (*current_total_reset_size > ww_reset_quantum)
9609         {
9610             switch_one_quantum();
9611
9612             *current_total_reset_size = 0;
9613         }
9614     }
9615 }
9616
9617 void gc_heap::reset_write_watch (BOOL concurrent_p)
9618 {
9619 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9620     // Software write watch currently requires the runtime to be suspended during reset. See SoftwareWriteWatch::ClearDirty().
9621     assert(!concurrent_p);
9622 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9623
9624     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
9625
9626     PREFIX_ASSUME(seg != NULL);
9627
9628     size_t reset_size = 0;
9629     size_t region_size = 0;
9630
9631     dprintf (2, ("bgc lowest: %Ix, bgc highest: %Ix", background_saved_lowest_address, background_saved_highest_address));
9632
9633     while (seg)
9634     {
9635         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9636         base_address = max (base_address, background_saved_lowest_address);
9637
9638         uint8_t* high_address = 0;
9639         high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
9640         high_address = min (high_address, background_saved_highest_address);
9641         
9642         if (base_address < high_address)
9643         {
9644             region_size = high_address - base_address;
9645
9646 #ifdef TIME_WRITE_WATCH
9647             unsigned int time_start = GetCycleCount32();
9648 #endif //TIME_WRITE_WATCH
9649             dprintf (3, ("h%d: soh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9650             //reset_ww_by_chunk (base_address, region_size);
9651             reset_write_watch_for_gc_heap(base_address, region_size);
9652
9653 #ifdef TIME_WRITE_WATCH
9654             unsigned int time_stop = GetCycleCount32();
9655             tot_cycles += time_stop - time_start;
9656             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9657                     time_stop - time_start, tot_cycles);
9658 #endif //TIME_WRITE_WATCH
9659
9660             switch_on_reset (concurrent_p, &reset_size, region_size);
9661         }
9662
9663         seg = heap_segment_next_rw (seg);
9664
9665         concurrent_print_time_delta ("CRWW soh");
9666     }
9667
9668     //concurrent_print_time_delta ("CRW soh");
9669
9670     seg = heap_segment_rw (generation_start_segment (large_object_generation));
9671
9672     PREFIX_ASSUME(seg != NULL);
9673
9674     while (seg)
9675     {
9676         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9677         uint8_t* high_address =  heap_segment_allocated (seg);
9678
9679         base_address = max (base_address, background_saved_lowest_address);
9680         high_address = min (high_address, background_saved_highest_address);
9681
9682         if (base_address < high_address)
9683         {
9684             region_size = high_address - base_address;
9685             
9686 #ifdef TIME_WRITE_WATCH
9687             unsigned int time_start = GetCycleCount32();
9688 #endif //TIME_WRITE_WATCH
9689             dprintf (3, ("h%d: loh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9690             //reset_ww_by_chunk (base_address, region_size);
9691             reset_write_watch_for_gc_heap(base_address, region_size);
9692
9693 #ifdef TIME_WRITE_WATCH
9694             unsigned int time_stop = GetCycleCount32();
9695             tot_cycles += time_stop - time_start;
9696             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9697                     time_stop - time_start, tot_cycles);
9698 #endif //TIME_WRITE_WATCH
9699     
9700             switch_on_reset (concurrent_p, &reset_size, region_size);
9701         }
9702
9703         seg = heap_segment_next_rw (seg);
9704
9705         concurrent_print_time_delta ("CRWW loh");
9706     }
9707
9708 #ifdef DEBUG_WRITE_WATCH
9709     debug_write_watch = (uint8_t**)~0;
9710 #endif //DEBUG_WRITE_WATCH
9711 }
9712
9713 #endif //WRITE_WATCH
9714
9715 #ifdef BACKGROUND_GC
9716 void gc_heap::restart_vm()
9717 {
9718     //assert (generation_allocation_pointer (youngest_generation) == 0);
9719     dprintf (3, ("Restarting EE"));
9720     STRESS_LOG0(LF_GC, LL_INFO10000, "Concurrent GC: Retarting EE\n");
9721     ee_proceed_event.Set();
9722 }
9723
9724 inline
9725 void fire_alloc_wait_event (alloc_wait_reason awr, BOOL begin_p)
9726 {
9727     if (awr != awr_ignored)
9728     {
9729         if (begin_p)
9730         {
9731             FIRE_EVENT(BGCAllocWaitBegin, awr);
9732         }
9733         else
9734         {
9735             FIRE_EVENT(BGCAllocWaitEnd, awr);
9736         }
9737     }
9738 }
9739
9740
9741 void gc_heap::fire_alloc_wait_event_begin (alloc_wait_reason awr)
9742 {
9743     fire_alloc_wait_event (awr, TRUE);
9744 }
9745
9746
9747 void gc_heap::fire_alloc_wait_event_end (alloc_wait_reason awr)
9748 {
9749     fire_alloc_wait_event (awr, FALSE);
9750 }
9751 #endif //BACKGROUND_GC
9752 void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* start, uint8_t* pointer)
9753 {
9754     gen.allocation_start = start;
9755     gen.allocation_context.alloc_ptr = pointer;
9756     gen.allocation_context.alloc_limit = pointer;
9757     gen.allocation_context.alloc_bytes = 0;
9758     gen.allocation_context.alloc_bytes_loh = 0;
9759     gen.allocation_context_start_region = pointer;
9760     gen.start_segment = seg;
9761     gen.allocation_segment = seg;
9762     gen.plan_allocation_start = 0;
9763     gen.free_list_space = 0;
9764     gen.pinned_allocated = 0; 
9765     gen.free_list_allocated = 0; 
9766     gen.end_seg_allocated = 0;
9767     gen.condemned_allocated = 0; 
9768     gen.free_obj_space = 0;
9769     gen.allocation_size = 0;
9770     gen.pinned_allocation_sweep_size = 0;
9771     gen.pinned_allocation_compact_size = 0;
9772     gen.allocate_end_seg_p = FALSE;
9773     gen.free_list_allocator.clear();
9774
9775 #ifdef FREE_USAGE_STATS
9776     memset (gen.gen_free_spaces, 0, sizeof (gen.gen_free_spaces));
9777     memset (gen.gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces));
9778     memset (gen.gen_plugs, 0, sizeof (gen.gen_plugs));
9779 #endif //FREE_USAGE_STATS
9780 }
9781
9782 void gc_heap::adjust_ephemeral_limits ()
9783 {
9784     ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
9785     ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);
9786
9787     dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
9788                  (size_t)ephemeral_low, (size_t)ephemeral_high))
9789
9790 #ifndef MULTIPLE_HEAPS
9791     // This updates the write barrier helpers with the new info.
9792     stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high);
9793 #endif // MULTIPLE_HEAPS
9794 }
9795
9796 #if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN)
9797 FILE* CreateLogFile(const GCConfigStringHolder& temp_logfile_name, bool is_config)
9798 {
9799     FILE* logFile;
9800
9801     if (!temp_logfile_name.Get())
9802     {
9803         return nullptr;
9804     }
9805
9806     char logfile_name[MAX_LONGPATH+1];
9807     uint32_t pid = GCToOSInterface::GetCurrentProcessId();
9808     const char* suffix = is_config ? ".config.log" : ".log";
9809     _snprintf_s(logfile_name, MAX_LONGPATH+1, _TRUNCATE, "%s.%d%s", temp_logfile_name.Get(), pid, suffix);
9810     logFile = fopen(logfile_name, "wb");
9811     return logFile;
9812 }
9813 #endif //TRACE_GC || GC_CONFIG_DRIVEN
9814
9815 HRESULT gc_heap::initialize_gc (size_t segment_size,
9816                                 size_t heap_size
9817 #ifdef MULTIPLE_HEAPS
9818                                 ,unsigned number_of_heaps
9819 #endif //MULTIPLE_HEAPS
9820 )
9821 {
9822 #ifdef TRACE_GC
9823     if (GCConfig::GetLogEnabled())
9824     {
9825         gc_log = CreateLogFile(GCConfig::GetLogFile(), false);
9826
9827         if (gc_log == NULL)
9828             return E_FAIL;
9829
9830         // GCLogFileSize in MBs.
9831         gc_log_file_size = static_cast<size_t>(GCConfig::GetLogFileSize());
9832
9833         if (gc_log_file_size <= 0 || gc_log_file_size > 500)
9834         {
9835             fclose (gc_log);
9836             return E_FAIL;
9837         }
9838
9839         gc_log_lock.Initialize();
9840         gc_log_buffer = new (nothrow) uint8_t [gc_log_buffer_size];
9841         if (!gc_log_buffer)
9842         {
9843             fclose(gc_log);
9844             return E_FAIL;
9845         }
9846
9847         memset (gc_log_buffer, '*', gc_log_buffer_size);
9848
9849         max_gc_buffers = gc_log_file_size * 1024 * 1024 / gc_log_buffer_size;
9850     }
9851 #endif // TRACE_GC
9852
9853 #ifdef GC_CONFIG_DRIVEN
9854     if (GCConfig::GetConfigLogEnabled())
9855     {
9856         gc_config_log = CreateLogFile(GCConfig::GetConfigLogFile(), true);
9857
9858         if (gc_config_log == NULL)
9859             return E_FAIL;
9860
9861         gc_config_log_buffer = new (nothrow) uint8_t [gc_config_log_buffer_size];
9862         if (!gc_config_log_buffer)
9863         {
9864             fclose(gc_config_log);
9865             return E_FAIL;
9866         }
9867
9868         compact_ratio = static_cast<int>(GCConfig::GetCompactRatio());
9869
9870         //         h#  | GC  | gen | C   | EX   | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP | 
9871         cprintf (("%2s | %6s | %1s | %1s | %2s | %2s | %2s | %2s | %2s || %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s |",
9872                 "h#", // heap index
9873                 "GC", // GC index
9874                 "g", // generation
9875                 "C",  // compaction (empty means sweeping), 'M' means it was mandatory, 'W' means it was not
9876                 "EX", // heap expansion
9877                 "NF", // normal fit
9878                 "BF", // best fit (if it indicates neither NF nor BF it means it had to acquire a new seg.
9879                 "ML", // mark list
9880                 "DM", // demotion
9881                 "PreS", // short object before pinned plug
9882                 "PostS", // short object after pinned plug
9883                 "Merge", // merged pinned plugs
9884                 "Conv", // converted to pinned plug
9885                 "Pre", // plug before pinned plug but not after
9886                 "Post", // plug after pinned plug but not before
9887                 "PrPo", // plug both before and after pinned plug
9888                 "PreP", // pre short object padded
9889                 "PostP" // post short object padded
9890                 ));
9891     }
9892 #endif //GC_CONFIG_DRIVEN
9893
9894 #ifdef GC_STATS
9895     GCConfigStringHolder logFileName = GCConfig::GetMixLogFile();
9896     if (logFileName.Get() != nullptr)
9897     {
9898         GCStatistics::logFileName = _strdup(logFileName.Get());
9899         GCStatistics::logFile = fopen(GCStatistics::logFileName, "a");
9900         if (!GCStatistics::logFile)
9901         {
9902             return E_FAIL;
9903         }
9904     }
9905 #endif // GC_STATS
9906
9907     HRESULT hres = S_OK;
9908
9909 #ifdef WRITE_WATCH
9910     hardware_write_watch_api_supported();
9911 #ifdef BACKGROUND_GC
9912     if (can_use_write_watch_for_gc_heap() && GCConfig::GetConcurrentGC())
9913     {
9914         gc_can_use_concurrent = true;
9915 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9916         virtual_alloc_hardware_write_watch = true;
9917 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9918     }
9919     else
9920     {
9921         gc_can_use_concurrent = false;
9922     }
9923 #endif //BACKGROUND_GC
9924 #endif //WRITE_WATCH
9925
9926 #ifdef BACKGROUND_GC
9927     // leave the first page to contain only segment info
9928     // because otherwise we could need to revisit the first page frequently in 
9929     // background GC.
9930     segment_info_size = OS_PAGE_SIZE;
9931 #else
9932     segment_info_size = Align (sizeof (heap_segment), get_alignment_constant (FALSE));
9933 #endif //BACKGROUND_GC
9934
9935     reserved_memory = 0;
9936     unsigned block_count;
9937 #ifdef MULTIPLE_HEAPS
9938     reserved_memory_limit = (segment_size + heap_size) * number_of_heaps;
9939     block_count = number_of_heaps;
9940 #else //MULTIPLE_HEAPS
9941     reserved_memory_limit = segment_size + heap_size;
9942     block_count = 1;
9943 #endif //MULTIPLE_HEAPS
9944
9945     if (!reserve_initial_memory(segment_size,heap_size,block_count))
9946         return E_OUTOFMEMORY;
9947
9948 #ifdef CARD_BUNDLE
9949     //check if we need to turn on card_bundles.
9950 #ifdef MULTIPLE_HEAPS
9951     // use INT64 arithmetic here because of possible overflow on 32p
9952     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*number_of_heaps;
9953 #else
9954     // use INT64 arithmetic here because of possible overflow on 32p
9955     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
9956 #endif //MULTIPLE_HEAPS
9957
9958     if (can_use_write_watch_for_card_table() && reserved_memory >= th)
9959     {
9960         settings.card_bundles = TRUE;
9961     }
9962     else
9963     {
9964         settings.card_bundles = FALSE;
9965     }
9966 #endif //CARD_BUNDLE
9967
9968     settings.first_init();
9969
9970     int latency_level_from_config = static_cast<int>(GCConfig::GetLatencyLevel());
9971     if (latency_level_from_config >= latency_level_first && latency_level_from_config <= latency_level_last)
9972     {
9973         gc_heap::latency_level = static_cast<gc_latency_level>(latency_level_from_config);
9974     }
9975
9976     init_static_data();
9977
9978     g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address);
9979
9980     if (!g_gc_card_table)
9981         return E_OUTOFMEMORY;
9982
9983     gc_started = FALSE;
9984
9985 #ifdef MULTIPLE_HEAPS
9986     n_heaps = number_of_heaps;
9987
9988     g_heaps = new (nothrow) gc_heap* [number_of_heaps];
9989     if (!g_heaps)
9990         return E_OUTOFMEMORY;
9991
9992 #ifdef _PREFAST_ 
9993 #pragma warning(push)
9994 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
9995 #endif // _PREFAST_
9996     g_promoted = new (nothrow) size_t [number_of_heaps*16];
9997     g_bpromoted = new (nothrow) size_t [number_of_heaps*16];
9998 #ifdef MH_SC_MARK
9999     g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
10000 #endif //MH_SC_MARK
10001 #ifdef _PREFAST_ 
10002 #pragma warning(pop)
10003 #endif // _PREFAST_
10004     if (!g_promoted || !g_bpromoted)
10005         return E_OUTOFMEMORY;
10006
10007 #ifdef MH_SC_MARK
10008     if (!g_mark_stack_busy)
10009         return E_OUTOFMEMORY;
10010 #endif //MH_SC_MARK
10011
10012     if (!create_thread_support (number_of_heaps))
10013         return E_OUTOFMEMORY;
10014
10015     if (!heap_select::init (number_of_heaps))
10016         return E_OUTOFMEMORY;
10017
10018 #endif //MULTIPLE_HEAPS
10019
10020     if (!init_semi_shared())
10021     {
10022         hres = E_FAIL;
10023     }
10024
10025     return hres;
10026 }
10027
10028 //Initializes PER_HEAP_ISOLATED data members.
10029 int
10030 gc_heap::init_semi_shared()
10031 {
10032     int ret = 0;
10033
10034     // This is used for heap expansion - it's to fix exactly the start for gen 0
10035     // through (max_generation-1). When we expand the heap we allocate all these
10036     // gen starts at the beginning of the new ephemeral seg. 
10037     eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
10038
10039 #ifdef MARK_LIST
10040 #ifdef MULTIPLE_HEAPS
10041     mark_list_size = min (150*1024, max (8192, soh_segment_size/(2*10*32)));
10042     g_mark_list = make_mark_list (mark_list_size*n_heaps);
10043
10044     min_balance_threshold = alloc_quantum_balance_units * CLR_SIZE * 2;
10045 #ifdef PARALLEL_MARK_LIST_SORT
10046     g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
10047     if (!g_mark_list_copy)
10048     {
10049         goto cleanup;
10050     }
10051 #endif //PARALLEL_MARK_LIST_SORT
10052
10053 #else //MULTIPLE_HEAPS
10054
10055     mark_list_size = max (8192, soh_segment_size/(64*32));
10056     g_mark_list = make_mark_list (mark_list_size);
10057
10058 #endif //MULTIPLE_HEAPS
10059
10060     dprintf (3, ("mark_list_size: %d", mark_list_size));
10061
10062     if (!g_mark_list)
10063     {
10064         goto cleanup;
10065     }
10066 #endif //MARK_LIST
10067
10068 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10069     if (!seg_mapping_table_init())
10070         goto cleanup;
10071 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10072
10073 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10074     seg_table = sorted_table::make_sorted_table();
10075
10076     if (!seg_table)
10077         goto cleanup;
10078 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10079
10080     segment_standby_list = 0;
10081
10082     if (!full_gc_approach_event.CreateManualEventNoThrow(FALSE))
10083     {
10084         goto cleanup;
10085     }
10086     if (!full_gc_end_event.CreateManualEventNoThrow(FALSE))
10087     {
10088         goto cleanup;
10089     }
10090
10091     fgn_maxgen_percent = 0;
10092     fgn_loh_percent = 0;
10093     full_gc_approach_event_set = false;
10094
10095     memset (full_gc_counts, 0, sizeof (full_gc_counts));
10096
10097     last_gc_index = 0;
10098     should_expand_in_full_gc = FALSE;
10099
10100 #ifdef FEATURE_LOH_COMPACTION
10101     loh_compaction_always_p = GCConfig::GetLOHCompactionMode() != 0;
10102     loh_compaction_mode = loh_compaction_default;
10103 #endif //FEATURE_LOH_COMPACTION
10104
10105 #ifdef BACKGROUND_GC
10106     memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
10107     bgc_alloc_spin_count = static_cast<uint32_t>(GCConfig::GetBGCSpinCount());
10108     bgc_alloc_spin = static_cast<uint32_t>(GCConfig::GetBGCSpin());
10109
10110     {   
10111         int number_bgc_threads = 1;
10112 #ifdef MULTIPLE_HEAPS
10113         number_bgc_threads = n_heaps;
10114 #endif //MULTIPLE_HEAPS
10115         if (!create_bgc_threads_support (number_bgc_threads))
10116         {
10117             goto cleanup;
10118         }
10119     }
10120 #endif //BACKGROUND_GC
10121
10122     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
10123
10124 #ifdef GC_CONFIG_DRIVEN
10125     compact_or_sweep_gcs[0] = 0;
10126     compact_or_sweep_gcs[1] = 0;
10127 #endif //GC_CONFIG_DRIVEN
10128
10129 #ifdef SHORT_PLUGS
10130     short_plugs_pad_ratio = (double)DESIRED_PLUG_LENGTH / (double)(DESIRED_PLUG_LENGTH - Align (min_obj_size));
10131 #endif //SHORT_PLUGS
10132
10133     ret = 1;
10134
10135 cleanup:
10136
10137     if (!ret)
10138     {
10139         if (full_gc_approach_event.IsValid())
10140         {
10141             full_gc_approach_event.CloseEvent();
10142         }
10143         if (full_gc_end_event.IsValid())
10144         {
10145             full_gc_end_event.CloseEvent();
10146         }
10147     }
10148
10149     return ret;
10150 }
10151
10152 gc_heap* gc_heap::make_gc_heap (
10153 #ifdef MULTIPLE_HEAPS
10154                                 GCHeap* vm_hp,
10155                                 int heap_number
10156 #endif //MULTIPLE_HEAPS
10157                                 )
10158 {
10159     gc_heap* res = 0;
10160
10161 #ifdef MULTIPLE_HEAPS
10162     res = new (nothrow) gc_heap;
10163     if (!res)
10164         return 0;
10165
10166     res->vm_heap = vm_hp;
10167     res->alloc_context_count = 0;
10168
10169 #ifdef MARK_LIST
10170 #ifdef PARALLEL_MARK_LIST_SORT
10171     res->mark_list_piece_start = new (nothrow) uint8_t**[n_heaps];
10172     if (!res->mark_list_piece_start)
10173         return 0;
10174
10175 #ifdef _PREFAST_ 
10176 #pragma warning(push)
10177 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10178 #endif // _PREFAST_
10179     res->mark_list_piece_end = new (nothrow) uint8_t**[n_heaps + 32]; // +32 is padding to reduce false sharing
10180 #ifdef _PREFAST_ 
10181 #pragma warning(pop)
10182 #endif // _PREFAST_
10183
10184     if (!res->mark_list_piece_end)
10185         return 0;
10186 #endif //PARALLEL_MARK_LIST_SORT
10187 #endif //MARK_LIST
10188
10189
10190 #endif //MULTIPLE_HEAPS
10191
10192     if (res->init_gc_heap (
10193 #ifdef MULTIPLE_HEAPS
10194         heap_number
10195 #else  //MULTIPLE_HEAPS
10196         0
10197 #endif //MULTIPLE_HEAPS
10198         )==0)
10199     {
10200         return 0;
10201     }
10202
10203 #ifdef MULTIPLE_HEAPS
10204     return res;
10205 #else
10206     return (gc_heap*)1;
10207 #endif //MULTIPLE_HEAPS
10208 }
10209
10210 uint32_t
10211 gc_heap::wait_for_gc_done(int32_t timeOut)
10212 {
10213     bool cooperative_mode = enable_preemptive ();
10214
10215     uint32_t dwWaitResult = NOERROR;
10216
10217     gc_heap* wait_heap = NULL;
10218     while (gc_heap::gc_started)
10219     {       
10220 #ifdef MULTIPLE_HEAPS
10221         wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
10222         dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
10223 #endif // MULTIPLE_HEAPS
10224
10225 #ifdef _PREFAST_
10226         PREFIX_ASSUME(wait_heap != NULL);
10227 #endif // _PREFAST_
10228
10229         dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE); 
10230     }
10231     disable_preemptive (cooperative_mode);
10232
10233     return dwWaitResult;
10234 }
10235
10236 void 
10237 gc_heap::set_gc_done()
10238 {
10239     enter_gc_done_event_lock();
10240     if (!gc_done_event_set)
10241     {
10242         gc_done_event_set = true;
10243         dprintf (2, ("heap %d: setting gc_done_event", heap_number));
10244         gc_done_event.Set();
10245     }
10246     exit_gc_done_event_lock();
10247 }
10248
10249 void 
10250 gc_heap::reset_gc_done()
10251 {
10252     enter_gc_done_event_lock();
10253     if (gc_done_event_set)
10254     {
10255         gc_done_event_set = false;
10256         dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
10257         gc_done_event.Reset();
10258     }
10259     exit_gc_done_event_lock();
10260 }
10261
10262 void 
10263 gc_heap::enter_gc_done_event_lock()
10264 {
10265     uint32_t dwSwitchCount = 0;
10266 retry:
10267
10268     if (Interlocked::CompareExchange(&gc_done_event_lock, 0, -1) >= 0)
10269     {
10270         while (gc_done_event_lock >= 0)
10271         {
10272             if  (g_num_processors > 1)
10273             {
10274                 int spin_count = 32 * g_num_processors;
10275                 for (int j = 0; j < spin_count; j++)
10276                 {
10277                     if  (gc_done_event_lock < 0)
10278                         break;
10279                     YieldProcessor();           // indicate to the processor that we are spining
10280                 }
10281                 if  (gc_done_event_lock >= 0)
10282                     GCToOSInterface::YieldThread(++dwSwitchCount);
10283             }
10284             else
10285                 GCToOSInterface::YieldThread(++dwSwitchCount);
10286         }
10287         goto retry;
10288     }
10289 }
10290
10291 void 
10292 gc_heap::exit_gc_done_event_lock()
10293 {
10294     gc_done_event_lock = -1;
10295 }
10296
10297 #ifndef MULTIPLE_HEAPS
10298
10299 #ifdef RECORD_LOH_STATE
10300 int gc_heap::loh_state_index = 0;
10301 gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
10302 #endif //RECORD_LOH_STATE
10303
10304 VOLATILE(int32_t) gc_heap::gc_done_event_lock;
10305 VOLATILE(bool) gc_heap::gc_done_event_set;
10306 GCEvent gc_heap::gc_done_event;
10307 #endif //!MULTIPLE_HEAPS
10308 VOLATILE(bool) gc_heap::internal_gc_done;
10309
10310 void gc_heap::add_saved_spinlock_info (
10311             msl_enter_state enter_state, 
10312             msl_take_state take_state)
10313
10314 {
10315 #ifdef SPINLOCK_HISTORY
10316     spinlock_info* current = &last_spinlock_info[spinlock_info_index];
10317
10318     current->enter_state = enter_state;
10319     current->take_state = take_state;
10320     current->thread_id.SetToCurrentThread();
10321
10322     spinlock_info_index++;
10323
10324     assert (spinlock_info_index <= max_saved_spinlock_info);
10325
10326     if (spinlock_info_index >= max_saved_spinlock_info)
10327     {
10328         spinlock_info_index = 0;
10329     }
10330 #else
10331     MAYBE_UNUSED_VAR(enter_state);
10332     MAYBE_UNUSED_VAR(take_state);
10333 #endif //SPINLOCK_HISTORY
10334 }
10335
10336 int
10337 gc_heap::init_gc_heap (int  h_number)
10338 {
10339 #ifdef MULTIPLE_HEAPS
10340
10341     time_bgc_last = 0;
10342
10343 #ifdef SPINLOCK_HISTORY
10344     spinlock_info_index = 0;
10345     memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
10346 #endif //SPINLOCK_HISTORY
10347
10348     // initialize per heap members.
10349     ephemeral_low = (uint8_t*)1;
10350
10351     ephemeral_high = MAX_PTR;
10352
10353     ephemeral_heap_segment = 0;
10354
10355     freeable_large_heap_segment = 0;
10356
10357     condemned_generation_num = 0;
10358
10359     blocking_collection = FALSE;
10360
10361     generation_skip_ratio = 100;
10362
10363     mark_stack_tos = 0;
10364
10365     mark_stack_bos = 0;
10366
10367     mark_stack_array_length = 0;
10368
10369     mark_stack_array = 0;
10370
10371     verify_pinned_queue_p = FALSE;
10372
10373     loh_pinned_queue_tos = 0;
10374
10375     loh_pinned_queue_bos = 0;
10376
10377     loh_pinned_queue_length = 0;
10378
10379     loh_pinned_queue_decay = LOH_PIN_DECAY;
10380
10381     loh_pinned_queue = 0;
10382
10383     min_overflow_address = MAX_PTR;
10384
10385     max_overflow_address = 0;
10386
10387     gen0_bricks_cleared = FALSE;
10388
10389     gen0_must_clear_bricks = 0;
10390
10391     allocation_quantum = CLR_SIZE;
10392
10393     more_space_lock = gc_lock;
10394
10395     ro_segments_in_range = FALSE;
10396
10397     loh_alloc_since_cg = 0;
10398
10399     new_heap_segment = NULL;
10400
10401 #ifdef RECORD_LOH_STATE
10402     loh_state_index = 0;
10403 #endif //RECORD_LOH_STATE
10404 #endif //MULTIPLE_HEAPS
10405
10406 #ifdef MULTIPLE_HEAPS
10407     if (h_number > n_heaps)
10408     {
10409         assert (!"Number of heaps exceeded");
10410         return 0;
10411     }
10412
10413     heap_number = h_number;
10414 #endif //MULTIPLE_HEAPS
10415
10416     memset (&oom_info, 0, sizeof (oom_info));
10417     memset (&fgm_result, 0, sizeof (fgm_result));
10418     if (!gc_done_event.CreateManualEventNoThrow(FALSE))
10419     {
10420         return 0;
10421     }
10422     gc_done_event_lock = -1;
10423     gc_done_event_set = false;
10424
10425 #ifndef SEG_MAPPING_TABLE
10426     if (!gc_heap::seg_table->ensure_space_for_insert ())
10427     {
10428         return 0;
10429     }
10430 #endif //!SEG_MAPPING_TABLE
10431
10432     heap_segment* seg = get_initial_segment (soh_segment_size, h_number);
10433     if (!seg)
10434         return 0;
10435
10436     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg),
10437                               (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)),
10438                               gc_etw_segment_small_object_heap);
10439     
10440 #ifdef SEG_MAPPING_TABLE
10441     seg_mapping_table_add_segment (seg, __this);
10442 #else //SEG_MAPPING_TABLE
10443     seg_table->insert ((uint8_t*)seg, sdelta);
10444 #endif //SEG_MAPPING_TABLE
10445
10446 #ifdef MULTIPLE_HEAPS
10447     heap_segment_heap (seg) = this;
10448 #endif //MULTIPLE_HEAPS
10449
10450     /* todo: Need a global lock for this */
10451     uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))];
10452     own_card_table (ct);
10453     card_table = translate_card_table (ct);
10454     /* End of global lock */
10455
10456     brick_table = card_table_brick_table (ct);
10457     highest_address = card_table_highest_address (ct);
10458     lowest_address = card_table_lowest_address (ct);
10459
10460 #ifdef CARD_BUNDLE
10461     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
10462     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
10463             card_table_card_bundle_table (ct));
10464 #endif //CARD_BUNDLE
10465
10466 #ifdef MARK_ARRAY
10467     if (gc_can_use_concurrent)
10468         mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))]));
10469     else
10470         mark_array = NULL;
10471 #endif //MARK_ARRAY
10472
10473     uint8_t*  start = heap_segment_mem (seg);
10474
10475     for (int i = 0; i < 1 + max_generation; i++)
10476     {
10477         make_generation (generation_table [ (max_generation - i) ],
10478                          seg, start, 0);
10479         generation_table [(max_generation - i)].gen_num = max_generation - i;
10480         start += Align (min_obj_size);
10481     }
10482
10483     heap_segment_allocated (seg) = start;
10484     alloc_allocated = start;
10485     heap_segment_used (seg) = start - plug_skew;
10486
10487     ephemeral_heap_segment = seg;
10488
10489 #ifndef SEG_MAPPING_TABLE
10490     if (!gc_heap::seg_table->ensure_space_for_insert ())
10491     {
10492         return 0;
10493     }
10494 #endif //!SEG_MAPPING_TABLE
10495     //Create the large segment generation
10496     heap_segment* lseg = get_initial_segment(min_loh_segment_size, h_number);
10497     if (!lseg)
10498         return 0;
10499     lseg->flags |= heap_segment_flags_loh;
10500
10501     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(lseg),
10502                               (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)),
10503                               gc_etw_segment_large_object_heap);
10504
10505 #ifdef SEG_MAPPING_TABLE
10506     seg_mapping_table_add_segment (lseg, __this);
10507 #else //SEG_MAPPING_TABLE
10508     seg_table->insert ((uint8_t*)lseg, sdelta);
10509 #endif //SEG_MAPPING_TABLE
10510
10511     generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
10512     //assign the alloc_list for the large generation 
10513     generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
10514     generation_table [max_generation+1].gen_num = max_generation+1;
10515     make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
10516     heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
10517     heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
10518
10519     for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
10520     {
10521         generation*  gen = generation_of (gen_num);
10522         make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
10523     }
10524
10525 #ifdef MULTIPLE_HEAPS
10526     heap_segment_heap (lseg) = this;
10527
10528     //initialize the alloc context heap
10529     generation_alloc_context (generation_of (0))->set_alloc_heap(vm_heap);
10530
10531     //initialize the alloc context heap
10532     generation_alloc_context (generation_of (max_generation+1))->set_alloc_heap(vm_heap);
10533
10534 #endif //MULTIPLE_HEAPS
10535
10536     //Do this only once
10537 #ifdef MULTIPLE_HEAPS
10538     if (h_number == 0)
10539 #endif //MULTIPLE_HEAPS
10540     {
10541 #ifndef INTERIOR_POINTERS
10542         //set the brick_table for large objects
10543         //but default value is clearded
10544         //clear_brick_table ((uint8_t*)heap_segment_mem (lseg),
10545         //                   (uint8_t*)heap_segment_reserved (lseg));
10546
10547 #else //INTERIOR_POINTERS
10548
10549         //Because of the interior pointer business, we have to clear
10550         //the whole brick table
10551         //but the default value is cleared
10552         // clear_brick_table (lowest_address, highest_address);
10553 #endif //INTERIOR_POINTERS
10554     }
10555
10556     if (!init_dynamic_data())
10557     {
10558         return 0;
10559     }
10560
10561     etw_allocation_running_amount[0] = 0;
10562     etw_allocation_running_amount[1] = 0;
10563
10564     //needs to be done after the dynamic data has been initialized
10565 #ifndef MULTIPLE_HEAPS
10566     allocation_running_amount = dd_min_size (dynamic_data_of (0));
10567 #endif //!MULTIPLE_HEAPS
10568
10569     fgn_last_alloc = dd_min_size (dynamic_data_of (0));
10570
10571     mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
10572     if (!arr)
10573         return 0;
10574
10575     make_mark_stack(arr);
10576
10577 #ifdef BACKGROUND_GC
10578     freeable_small_heap_segment = 0;
10579     gchist_index_per_heap = 0;
10580     uint8_t** b_arr = new (nothrow) (uint8_t* [MARK_STACK_INITIAL_LENGTH]);
10581     if (!b_arr)
10582         return 0;
10583
10584     make_background_mark_stack (b_arr);
10585 #endif //BACKGROUND_GC
10586
10587     ephemeral_low = generation_allocation_start(generation_of(max_generation - 1));
10588     ephemeral_high = heap_segment_reserved(ephemeral_heap_segment);
10589     if (heap_number == 0)
10590     {
10591         stomp_write_barrier_initialize(
10592 #ifdef MULTIPLE_HEAPS
10593             reinterpret_cast<uint8_t*>(1), reinterpret_cast<uint8_t*>(~0)
10594 #else
10595             ephemeral_low, ephemeral_high
10596 #endif //!MULTIPLE_HEAPS
10597         );
10598     }
10599
10600 #ifdef MARK_ARRAY
10601     // why would we clear the mark array for this page? it should be cleared..
10602     // clear the first committed page
10603     //if(gc_can_use_concurrent)
10604     //{
10605     //    clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10606     //}
10607 #endif //MARK_ARRAY
10608
10609 #ifdef MULTIPLE_HEAPS
10610     //register the heap in the heaps array
10611
10612     if (!create_gc_thread ())
10613         return 0;
10614
10615     g_heaps [heap_number] = this;
10616
10617 #endif //MULTIPLE_HEAPS
10618
10619 #ifdef FEATURE_PREMORTEM_FINALIZATION
10620     HRESULT hr = AllocateCFinalize(&finalize_queue);
10621     if (FAILED(hr))
10622         return 0;
10623 #endif // FEATURE_PREMORTEM_FINALIZATION
10624
10625     max_free_space_items = MAX_NUM_FREE_SPACES;
10626
10627     bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10628
10629     if (!bestfit_seg)
10630     {
10631         return 0;
10632     }
10633
10634     if (!bestfit_seg->alloc())
10635     {
10636         return 0;
10637     }
10638
10639     last_gc_before_oom = FALSE;
10640
10641 #ifdef MULTIPLE_HEAPS
10642
10643 #ifdef HEAP_ANALYZE
10644
10645     heap_analyze_success = TRUE;
10646
10647     internal_root_array  = 0;
10648
10649     internal_root_array_index = 0;
10650
10651     internal_root_array_length = initial_internal_roots;
10652
10653     current_obj          = 0;
10654
10655     current_obj_size     = 0;
10656
10657 #endif //HEAP_ANALYZE
10658
10659 #endif // MULTIPLE_HEAPS
10660
10661 #ifdef BACKGROUND_GC
10662     bgc_thread_id.Clear();
10663
10664     if (!create_bgc_thread_support())
10665     {
10666         return 0;
10667     }
10668
10669     bgc_alloc_lock = new (nothrow) exclusive_sync;
10670     if (!bgc_alloc_lock)
10671     {
10672         return 0;
10673     }
10674
10675     bgc_alloc_lock->init();
10676
10677     if (h_number == 0)
10678     {
10679         if (!recursive_gc_sync::init())
10680             return 0;
10681     }
10682
10683     bgc_thread_running = 0;
10684     bgc_thread = 0;
10685     bgc_threads_timeout_cs.Initialize();
10686     expanded_in_fgc = 0;
10687     current_bgc_state = bgc_not_in_process;
10688     background_soh_alloc_count = 0;
10689     background_loh_alloc_count = 0;
10690     bgc_overflow_count = 0;
10691     end_loh_size = dd_min_size (dynamic_data_of (max_generation + 1));
10692 #endif //BACKGROUND_GC
10693
10694 #ifdef GC_CONFIG_DRIVEN
10695     memset (interesting_data_per_heap, 0, sizeof (interesting_data_per_heap));
10696     memset(compact_reasons_per_heap, 0, sizeof (compact_reasons_per_heap));
10697     memset(expand_mechanisms_per_heap, 0, sizeof (expand_mechanisms_per_heap));
10698     memset(interesting_mechanism_bits_per_heap, 0, sizeof (interesting_mechanism_bits_per_heap));
10699 #endif //GC_CONFIG_DRIVEN
10700
10701     return 1;
10702 }
10703
10704 void
10705 gc_heap::destroy_semi_shared()
10706 {
10707 //TODO: will need to move this to per heap
10708 //#ifdef BACKGROUND_GC
10709 //    if (c_mark_list)
10710 //        delete c_mark_list;
10711 //#endif //BACKGROUND_GC
10712
10713 #ifdef MARK_LIST
10714     if (g_mark_list)
10715         delete g_mark_list;
10716 #endif //MARK_LIST
10717
10718 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10719     if (seg_mapping_table)
10720         delete seg_mapping_table;
10721 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10722
10723 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10724     //destroy the segment map
10725     seg_table->delete_sorted_table();
10726 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10727 }
10728
10729 void
10730 gc_heap::self_destroy()
10731 {
10732 #ifdef BACKGROUND_GC
10733     kill_gc_thread();
10734 #endif //BACKGROUND_GC
10735
10736     if (gc_done_event.IsValid())
10737     {
10738         gc_done_event.CloseEvent();
10739     }
10740
10741     // destroy every segment.
10742     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10743
10744     PREFIX_ASSUME(seg != NULL);
10745
10746     heap_segment* next_seg;
10747     while (seg)
10748     {
10749         next_seg = heap_segment_next_rw (seg);
10750         delete_heap_segment (seg);
10751         seg = next_seg;
10752     }
10753
10754     seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10755
10756     PREFIX_ASSUME(seg != NULL);
10757
10758     while (seg)
10759     {
10760         next_seg = heap_segment_next_rw (seg);
10761         delete_heap_segment (seg);
10762         seg = next_seg;
10763     }
10764
10765     // get rid of the card table
10766     release_card_table (card_table);
10767
10768     // destroy the mark stack
10769     delete mark_stack_array;
10770
10771 #ifdef FEATURE_PREMORTEM_FINALIZATION
10772     if (finalize_queue)
10773         delete finalize_queue;
10774 #endif // FEATURE_PREMORTEM_FINALIZATION
10775 }
10776
10777 void
10778 gc_heap::destroy_gc_heap(gc_heap* heap)
10779 {
10780     heap->self_destroy();
10781     delete heap;
10782 }
10783
10784 // Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10785 // the finalizer queue has been drained.
10786 void gc_heap::shutdown_gc()
10787 {
10788     destroy_semi_shared();
10789
10790 #ifdef MULTIPLE_HEAPS
10791     //delete the heaps array
10792     delete g_heaps;
10793     destroy_thread_support();
10794     n_heaps = 0;
10795 #endif //MULTIPLE_HEAPS
10796     //destroy seg_manager
10797
10798     destroy_initial_memory();
10799
10800     GCToOSInterface::Shutdown();
10801 }
10802
10803 inline
10804 BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10805                           uint8_t* old_loc, int use_padding)
10806 {
10807     BOOL already_padded = FALSE;
10808 #ifdef SHORT_PLUGS
10809     if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
10810     {
10811         alloc_pointer = alloc_pointer + Align (min_obj_size);
10812         already_padded = TRUE;
10813     }
10814 #endif //SHORT_PLUGS
10815
10816     if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
10817         size = size + switch_alignment_size (already_padded);
10818
10819 #ifdef FEATURE_STRUCTALIGN
10820     alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
10821 #endif // FEATURE_STRUCTALIGN
10822
10823     // in allocate_in_condemned_generation we can have this when we
10824     // set the alloc_limit to plan_allocated which could be less than 
10825     // alloc_ptr
10826     if (alloc_limit < alloc_pointer)
10827     {
10828         return FALSE;
10829     }
10830
10831     if (old_loc != 0)
10832     {
10833         return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0))) 
10834 #ifdef SHORT_PLUGS
10835                 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
10836 #else //SHORT_PLUGS
10837                 ||((alloc_pointer + size) == alloc_limit)
10838 #endif //SHORT_PLUGS
10839             );
10840     }
10841     else
10842     {
10843         assert (size == Align (min_obj_size));
10844         return ((size_t)(alloc_limit - alloc_pointer) >= size);
10845     }
10846 }
10847
10848 inline
10849 BOOL gc_heap::a_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10850                             int align_const)
10851 {
10852     // We could have run into cases where this is true when alloc_allocated is the 
10853     // the same as the seg committed.
10854     if (alloc_limit < alloc_pointer)
10855     {
10856         return FALSE;
10857     }
10858
10859     return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
10860 }
10861
10862 // Grow by committing more pages
10863 BOOL gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* high_address)
10864 {
10865     assert (high_address <= heap_segment_reserved (seg));
10866
10867     //return 0 if we are at the end of the segment.
10868     if (align_on_page (high_address) > heap_segment_reserved (seg))
10869         return FALSE;
10870
10871     if (high_address <= heap_segment_committed (seg))
10872         return TRUE;
10873
10874     size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
10875     c_size = max (c_size, 16*OS_PAGE_SIZE);
10876     c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
10877
10878     if (c_size == 0)
10879         return FALSE;
10880
10881     STRESS_LOG2(LF_GC, LL_INFO10000,
10882                 "Growing heap_segment: %Ix high address: %Ix\n",
10883                 (size_t)seg, (size_t)high_address);
10884
10885     dprintf(3, ("Growing segment allocation %Ix %Ix", (size_t)heap_segment_committed(seg),c_size));
10886     
10887     if (!virtual_alloc_commit_for_heap(heap_segment_committed (seg), c_size, heap_number))
10888     {
10889         dprintf(3, ("Cannot grow heap segment"));
10890         return FALSE;
10891     }
10892 #ifdef MARK_ARRAY
10893 #ifndef BACKGROUND_GC
10894     clear_mark_array (heap_segment_committed (seg),
10895                       heap_segment_committed (seg)+c_size, TRUE);
10896 #endif //BACKGROUND_GC
10897 #endif //MARK_ARRAY
10898     heap_segment_committed (seg) += c_size;
10899     STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
10900                 (size_t)heap_segment_committed (seg));
10901
10902     assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
10903
10904     assert (high_address <= heap_segment_committed (seg));
10905
10906     return TRUE;
10907 }
10908
10909 inline
10910 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)
10911 {
10912 #ifdef SHORT_PLUGS
10913     if ((old_loc != 0) && pad_front_p)
10914     {
10915         allocated = allocated + Align (min_obj_size);
10916     }
10917 #endif //SHORT_PLUGS
10918
10919     if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
10920         size = size + switch_alignment_size (FALSE);
10921 #ifdef FEATURE_STRUCTALIGN
10922     size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
10923     return grow_heap_segment (seg, allocated + pad + size);
10924 #else // FEATURE_STRUCTALIGN
10925     return grow_heap_segment (seg, allocated + size);
10926 #endif // FEATURE_STRUCTALIGN
10927 }
10928
10929 //used only in older generation allocation (i.e during gc).
10930 void gc_heap::adjust_limit (uint8_t* start, size_t limit_size, generation* gen,
10931                             int gennum)
10932 {
10933     UNREFERENCED_PARAMETER(gennum);
10934     dprintf (3, ("gc Expanding segment allocation"));
10935     heap_segment* seg = generation_allocation_segment (gen);
10936     if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
10937     {
10938         if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
10939         {
10940             assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
10941             assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
10942             heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
10943         }
10944         else
10945         {
10946             uint8_t*  hole = generation_allocation_pointer (gen);
10947             size_t  size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
10948
10949             if (size != 0)
10950             {
10951                 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
10952                 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
10953                 if (size >= Align (min_free_list))
10954                 {
10955                     if (allocated_size < min_free_list)
10956                     {
10957                         if (size >= (Align (min_free_list) + Align (min_obj_size)))
10958                         {
10959                             //split hole into min obj + threadable free item
10960                             make_unused_array (hole, min_obj_size);
10961                             generation_free_obj_space (gen) += Align (min_obj_size);
10962                             make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
10963                             generation_free_list_space (gen) += size - Align (min_obj_size);
10964                             generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size), 
10965                                                                           size - Align (min_obj_size));
10966                             add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
10967                         }
10968                         else
10969                         {
10970                             dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
10971                             make_unused_array (hole, size);
10972                             generation_free_obj_space (gen) += size;
10973                         }
10974                     }
10975                     else 
10976                     {
10977                         dprintf (3, ("threading hole in front of free list"));
10978                         make_unused_array (hole, size);
10979                         generation_free_list_space (gen) += size;
10980                         generation_allocator(gen)->thread_item_front (hole, size);
10981                         add_gen_free (gen->gen_num, size);
10982                     }
10983                 }
10984                 else
10985                 {
10986                     make_unused_array (hole, size);
10987                     generation_free_obj_space (gen) += size;
10988                 }
10989             }
10990         }
10991         generation_allocation_pointer (gen) = start;
10992         generation_allocation_context_start_region (gen) = start;
10993     }
10994     generation_allocation_limit (gen) = (start + limit_size);
10995 }
10996
10997 void verify_mem_cleared (uint8_t* start, size_t size)
10998 {
10999     if (!Aligned (size))
11000     {
11001         FATAL_GC_ERROR();
11002     }
11003
11004     PTR_PTR curr_ptr = (PTR_PTR) start;
11005     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
11006     {
11007         if (*(curr_ptr++) != 0)
11008         {
11009             FATAL_GC_ERROR();
11010         }
11011     }
11012 }
11013
11014 #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
11015 void gc_heap::set_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11016 {
11017     size_t start_mark_bit = mark_bit_of (start);
11018     size_t end_mark_bit = mark_bit_of (end);
11019     unsigned int startbit = mark_bit_bit (start_mark_bit);
11020     unsigned int endbit = mark_bit_bit (end_mark_bit);
11021     size_t startwrd = mark_bit_word (start_mark_bit);
11022     size_t endwrd = mark_bit_word (end_mark_bit);
11023
11024     dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11025         (size_t)start, (size_t)start_mark_bit, 
11026         (size_t)end, (size_t)end_mark_bit));
11027
11028     unsigned int firstwrd = ~(lowbits (~0, startbit));
11029     unsigned int lastwrd = ~(highbits (~0, endbit));
11030
11031     if (startwrd == endwrd)
11032     {
11033         unsigned int wrd = firstwrd & lastwrd;
11034         mark_array[startwrd] |= wrd;
11035         return;
11036     }
11037
11038     // set the first mark word.
11039     if (startbit)
11040     {
11041         mark_array[startwrd] |= firstwrd;
11042         startwrd++;
11043     }
11044
11045     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11046     {
11047         mark_array[wrdtmp] = ~(unsigned int)0;
11048     }
11049
11050     // set the last mark word.
11051     if (endbit)
11052     {
11053         mark_array[endwrd] |= lastwrd;
11054     }
11055 }
11056
11057 // makes sure that the mark array bits between start and end are 0.
11058 void gc_heap::check_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11059 {
11060     size_t start_mark_bit = mark_bit_of (start);
11061     size_t end_mark_bit = mark_bit_of (end);
11062     unsigned int startbit = mark_bit_bit (start_mark_bit);
11063     unsigned int endbit = mark_bit_bit (end_mark_bit);
11064     size_t startwrd = mark_bit_word (start_mark_bit);
11065     size_t endwrd = mark_bit_word (end_mark_bit);
11066
11067     //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11068     //    (size_t)start, (size_t)start_mark_bit, 
11069     //    (size_t)end, (size_t)end_mark_bit));
11070
11071     unsigned int firstwrd = ~(lowbits (~0, startbit));
11072     unsigned int lastwrd = ~(highbits (~0, endbit));
11073
11074     if (startwrd == endwrd)
11075     {
11076         unsigned int wrd = firstwrd & lastwrd;
11077         if (mark_array[startwrd] & wrd)
11078         {
11079             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11080                             wrd, startwrd, 
11081                             mark_array [startwrd], mark_word_address (startwrd)));
11082             FATAL_GC_ERROR();
11083         }
11084         return;
11085     }
11086
11087     // set the first mark word.
11088     if (startbit)
11089     {
11090         if (mark_array[startwrd] & firstwrd)
11091         {
11092             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11093                             firstwrd, startwrd, 
11094                             mark_array [startwrd], mark_word_address (startwrd)));
11095             FATAL_GC_ERROR();
11096         }
11097
11098         startwrd++;
11099     }
11100
11101     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11102     {
11103         if (mark_array[wrdtmp])
11104         {
11105             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11106                             wrdtmp, 
11107                             mark_array [wrdtmp], mark_word_address (wrdtmp)));
11108             FATAL_GC_ERROR();
11109         }
11110     }
11111
11112     // set the last mark word.
11113     if (endbit)
11114     {
11115         if (mark_array[endwrd] & lastwrd)
11116         {
11117             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11118                             lastwrd, lastwrd, 
11119                             mark_array [lastwrd], mark_word_address (lastwrd)));
11120             FATAL_GC_ERROR();
11121         }
11122     }
11123 }
11124 #endif //VERIFY_HEAP && BACKGROUND_GC
11125
11126 allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
11127 {
11128     assert (num_b < MAX_BUCKET_COUNT);
11129     num_buckets = num_b;
11130     frst_bucket_size = fbs;
11131     buckets = b;
11132 }
11133
11134 alloc_list& allocator::alloc_list_of (unsigned int bn)
11135 {
11136     assert (bn < num_buckets);
11137     if (bn == 0)
11138         return first_bucket;
11139     else
11140         return buckets [bn-1];
11141 }
11142
11143 size_t& allocator::alloc_list_damage_count_of (unsigned int bn)
11144 {
11145     assert (bn < num_buckets);
11146     if (bn == 0)
11147         return first_bucket.alloc_list_damage_count();
11148     else
11149         return buckets [bn-1].alloc_list_damage_count();
11150 }
11151
11152 void allocator::unlink_item (unsigned int bn, uint8_t* item, uint8_t* prev_item, BOOL use_undo_p)
11153 {
11154     //unlink the free_item
11155     alloc_list* al = &alloc_list_of (bn);
11156     if (prev_item)
11157     {
11158         if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
11159         {
11160             assert (item == free_list_slot (prev_item));
11161             free_list_undo (prev_item) = item;
11162             alloc_list_damage_count_of (bn)++;
11163         }
11164         free_list_slot (prev_item) = free_list_slot(item);
11165     }
11166     else
11167     {
11168         al->alloc_list_head() = (uint8_t*)free_list_slot(item);
11169     }
11170     if (al->alloc_list_tail() == item)
11171     {
11172         al->alloc_list_tail() = prev_item;
11173     }
11174 }
11175
11176 void allocator::clear()
11177 {
11178     for (unsigned int i = 0; i < num_buckets; i++)
11179     {
11180         alloc_list_head_of (i) = 0;
11181         alloc_list_tail_of (i) = 0;
11182     }
11183 }
11184
11185 //always thread to the end.
11186 void allocator::thread_free_item (uint8_t* item, uint8_t*& head, uint8_t*& tail)
11187 {
11188     free_list_slot (item) = 0;
11189     free_list_undo (item) = UNDO_EMPTY;
11190     assert (item != head);
11191
11192     if (head == 0)
11193     {
11194        head = item;
11195     }
11196     //TODO: This shouldn't happen anymore - verify that's the case.
11197     //the following is necessary because the last free element
11198     //may have been truncated, and tail isn't updated.
11199     else if (free_list_slot (head) == 0)
11200     {
11201         free_list_slot (head) = item;
11202     }
11203     else
11204     {
11205         assert (item != tail);
11206         assert (free_list_slot(tail) == 0);
11207         free_list_slot (tail) = item;
11208     }
11209     tail = item;
11210 }
11211
11212 void allocator::thread_item (uint8_t* item, size_t size)
11213 {
11214     size_t sz = frst_bucket_size;
11215     unsigned int a_l_number = 0; 
11216
11217     for (; a_l_number < (num_buckets-1); a_l_number++)
11218     {
11219         if (size < sz)
11220         {
11221             break;
11222         }
11223         sz = sz * 2;
11224     }
11225     alloc_list* al = &alloc_list_of (a_l_number);
11226     thread_free_item (item, 
11227                       al->alloc_list_head(),
11228                       al->alloc_list_tail());
11229 }
11230
11231 void allocator::thread_item_front (uint8_t* item, size_t size)
11232 {
11233     //find right free list
11234     size_t sz = frst_bucket_size;
11235     unsigned int a_l_number = 0; 
11236     for (; a_l_number < (num_buckets-1); a_l_number++)
11237     {
11238         if (size < sz)
11239         {
11240             break;
11241         }
11242         sz = sz * 2;
11243     }
11244     alloc_list* al = &alloc_list_of (a_l_number);
11245     free_list_slot (item) = al->alloc_list_head();
11246     free_list_undo (item) = UNDO_EMPTY;
11247
11248     if (al->alloc_list_tail() == 0)
11249     {
11250         al->alloc_list_tail() = al->alloc_list_head();
11251     }
11252     al->alloc_list_head() = item;
11253     if (al->alloc_list_tail() == 0)
11254     {
11255         al->alloc_list_tail() = item;
11256     }
11257 }
11258
11259 void allocator::copy_to_alloc_list (alloc_list* toalist)
11260 {
11261     for (unsigned int i = 0; i < num_buckets; i++)
11262     {
11263         toalist [i] = alloc_list_of (i);
11264 #ifdef FL_VERIFICATION
11265         uint8_t* free_item = alloc_list_head_of (i);
11266         size_t count = 0;
11267         while (free_item)
11268         {
11269             count++;
11270             free_item = free_list_slot (free_item);
11271         }
11272
11273         toalist[i].item_count = count;
11274 #endif //FL_VERIFICATION
11275     }
11276 }
11277
11278 void allocator::copy_from_alloc_list (alloc_list* fromalist)
11279 {
11280     BOOL repair_list = !discard_if_no_fit_p ();
11281     for (unsigned int i = 0; i < num_buckets; i++)
11282     {
11283         size_t count = alloc_list_damage_count_of (i);
11284         alloc_list_of (i) = fromalist [i];
11285         assert (alloc_list_damage_count_of (i) == 0);
11286
11287         if (repair_list)
11288         {
11289             //repair the the list
11290             //new items may have been added during the plan phase 
11291             //items may have been unlinked. 
11292             uint8_t* free_item = alloc_list_head_of (i);
11293             while (free_item && count)
11294             {
11295                 assert (((CObjectHeader*)free_item)->IsFree());
11296                 if ((free_list_undo (free_item) != UNDO_EMPTY))
11297                 {
11298                     count--;
11299                     free_list_slot (free_item) = free_list_undo (free_item);
11300                     free_list_undo (free_item) = UNDO_EMPTY;
11301                 }
11302
11303                 free_item = free_list_slot (free_item);
11304             }
11305
11306 #ifdef FL_VERIFICATION
11307             free_item = alloc_list_head_of (i);
11308             size_t item_count = 0;
11309             while (free_item)
11310             {
11311                 item_count++;
11312                 free_item = free_list_slot (free_item);
11313             }
11314
11315             assert (item_count == alloc_list_of (i).item_count);
11316 #endif //FL_VERIFICATION
11317         }
11318 #ifdef DEBUG
11319         uint8_t* tail_item = alloc_list_tail_of (i);
11320         assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
11321 #endif
11322     }
11323 }
11324
11325 void allocator::commit_alloc_list_changes()
11326 {
11327     BOOL repair_list = !discard_if_no_fit_p ();
11328     if (repair_list)
11329     {
11330         for (unsigned int i = 0; i < num_buckets; i++)
11331         {
11332             //remove the undo info from list. 
11333             uint8_t* free_item = alloc_list_head_of (i);
11334             size_t count = alloc_list_damage_count_of (i);
11335             while (free_item && count)
11336             {
11337                 assert (((CObjectHeader*)free_item)->IsFree());
11338
11339                 if (free_list_undo (free_item) != UNDO_EMPTY)
11340                 {
11341                     free_list_undo (free_item) = UNDO_EMPTY;
11342                     count--;
11343                 }
11344
11345                 free_item = free_list_slot (free_item);
11346             }
11347
11348             alloc_list_damage_count_of (i) = 0; 
11349         }
11350     }
11351 }
11352
11353 void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size,
11354                                 alloc_context* acontext, heap_segment* seg,
11355                                 int align_const, int gen_number)
11356 {
11357     size_t aligned_min_obj_size = Align(min_obj_size, align_const);
11358
11359     //probably should pass seg==0 for free lists.
11360     if (seg)
11361     {
11362         assert (heap_segment_used (seg) <= heap_segment_committed (seg));
11363     }
11364
11365     dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
11366                (size_t)start + limit_size - aligned_min_obj_size));
11367
11368     if ((acontext->alloc_limit != start) &&
11369         (acontext->alloc_limit + aligned_min_obj_size)!= start)
11370     {
11371         uint8_t*  hole = acontext->alloc_ptr;
11372         if (hole != 0)
11373         {
11374             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
11375             dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
11376             // when we are finishing an allocation from a free list
11377             // we know that the free area was Align(min_obj_size) larger
11378             acontext->alloc_bytes -= size;
11379             size_t free_obj_size = size + aligned_min_obj_size;
11380             make_unused_array (hole, free_obj_size);
11381             generation_free_obj_space (generation_of (gen_number)) += free_obj_size;
11382         }
11383         acontext->alloc_ptr = start;
11384     }
11385     else  
11386     {  
11387         // If the next alloc context is right up against the current one it means we are absorbing the min  
11388         // object, so need to account for that.  
11389         acontext->alloc_bytes += (start - acontext->alloc_limit);  
11390     }  
11391
11392     acontext->alloc_limit = (start + limit_size - aligned_min_obj_size);
11393     acontext->alloc_bytes += limit_size - ((gen_number < max_generation + 1) ? aligned_min_obj_size : 0);
11394
11395 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11396     if (g_fEnableARM)
11397     {
11398         AppDomain* alloc_appdomain = GetAppDomain();
11399         alloc_appdomain->RecordAllocBytes (limit_size, heap_number);
11400     }
11401 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11402
11403     uint8_t* saved_used = 0;
11404
11405     if (seg)
11406     {
11407         saved_used = heap_segment_used (seg);
11408     }
11409
11410     if (seg == ephemeral_heap_segment)
11411     {
11412         //Sometimes the allocated size is advanced without clearing the
11413         //memory. Let's catch up here
11414         if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
11415         {
11416 #ifdef MARK_ARRAY
11417 #ifndef BACKGROUND_GC
11418             clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
11419 #endif //BACKGROUND_GC
11420 #endif //MARK_ARRAY
11421             heap_segment_used (seg) = alloc_allocated - plug_skew;
11422         }
11423     }
11424 #ifdef BACKGROUND_GC
11425     else if (seg)
11426     {
11427         uint8_t* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
11428 #ifdef FEATURE_LOH_COMPACTION
11429         old_allocated -= Align (loh_padding_obj_size, align_const);
11430 #endif //FEATURE_LOH_COMPACTION
11431
11432         assert (heap_segment_used (seg) >= old_allocated);
11433     }
11434 #endif //BACKGROUND_GC
11435     if ((seg == 0) ||
11436         (start - plug_skew + limit_size) <= heap_segment_used (seg))
11437     {
11438         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory(1)", heap_number));
11439         add_saved_spinlock_info (me_release, mt_clr_mem);
11440         leave_spin_lock (&more_space_lock);
11441         dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
11442         memclr (start - plug_skew, limit_size);
11443     }
11444     else
11445     {
11446         uint8_t* used = heap_segment_used (seg);
11447         heap_segment_used (seg) = start + limit_size - plug_skew;
11448
11449         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory", heap_number));
11450         add_saved_spinlock_info (me_release, mt_clr_mem);
11451         leave_spin_lock (&more_space_lock);
11452         if ((start - plug_skew) < used)
11453         {
11454             if (used != saved_used)
11455             {
11456                 FATAL_GC_ERROR ();
11457             }
11458
11459             dprintf (2, ("clearing memory before used at %Ix for %Id bytes", 
11460                 (start - plug_skew), (plug_skew + used - start)));
11461             memclr (start - plug_skew, used - (start - plug_skew));
11462         }
11463     }
11464
11465     //this portion can be done after we release the lock
11466     if (seg == ephemeral_heap_segment)
11467     {
11468 #ifdef FFIND_OBJECT
11469         if (gen0_must_clear_bricks > 0)
11470         {
11471             //set the brick table to speed up find_object
11472             size_t b = brick_of (acontext->alloc_ptr);
11473             set_brick (b, acontext->alloc_ptr - brick_address (b));
11474             b++;
11475             dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
11476                          b, brick_of (align_on_brick (start + limit_size))));
11477             volatile short* x = &brick_table [b];
11478             short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
11479
11480             for (;x < end_x;x++)
11481                 *x = -1;
11482         }
11483         else
11484 #endif //FFIND_OBJECT
11485         {
11486             gen0_bricks_cleared = FALSE;
11487         }
11488     }
11489
11490     // verifying the memory is completely cleared.
11491     //verify_mem_cleared (start - plug_skew, limit_size);
11492 }
11493
11494 /* in order to make the allocator faster, allocate returns a
11495  * 0 filled object. Care must be taken to set the allocation limit to the
11496  * allocation pointer after gc
11497  */
11498
11499 size_t gc_heap::limit_from_size (size_t size, size_t room, int gen_number,
11500                                  int align_const)
11501 {
11502     size_t new_limit = new_allocation_limit ((size + Align (min_obj_size, align_const)),
11503                                              min (room,max (size + Align (min_obj_size, align_const),
11504                                                             ((gen_number < max_generation+1) ?
11505                                                              allocation_quantum :
11506                                                              0))),
11507                                              gen_number);
11508     assert (new_limit >= (size + Align (min_obj_size, align_const)));
11509     dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
11510     return new_limit;
11511 }
11512
11513 void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size, 
11514                           uint8_t* allocated, uint8_t* reserved)
11515 {
11516     dprintf (1, ("total committed on the heap is %Id", get_total_committed_size()));
11517
11518     UNREFERENCED_PARAMETER(heap_num);
11519
11520     if (reason == oom_budget)
11521     {
11522         alloc_size = dd_min_size (dynamic_data_of (0)) / 2;
11523     }
11524
11525     if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
11526     {
11527         // This means during the last GC we needed to reserve and/or commit more memory
11528         // but we couldn't. We proceeded with the GC and ended up not having enough
11529         // memory at the end. This is a legitimate OOM situtation. Otherwise we 
11530         // probably made a mistake and didn't expand the heap when we should have.
11531         reason = oom_low_mem;
11532     }
11533
11534     oom_info.reason = reason;
11535     oom_info.allocated = allocated;
11536     oom_info.reserved = reserved;
11537     oom_info.alloc_size = alloc_size;
11538     oom_info.gc_index = settings.gc_index;
11539     oom_info.fgm = fgm_result.fgm;
11540     oom_info.size = fgm_result.size;
11541     oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
11542     oom_info.loh_p = fgm_result.loh_p;
11543
11544     fgm_result.fgm = fgm_no_failure;
11545
11546     // Break early - before the more_space_lock is release so no other threads
11547     // could have allocated on the same heap when OOM happened.
11548     if (GCConfig::GetBreakOnOOM())
11549     {
11550         GCToOSInterface::DebugBreak();
11551     }
11552 }
11553
11554 #ifdef BACKGROUND_GC
11555 BOOL gc_heap::background_allowed_p()
11556 {
11557     return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
11558 }
11559 #endif //BACKGROUND_GC
11560
11561 void gc_heap::check_for_full_gc (int gen_num, size_t size)
11562 {
11563     BOOL should_notify = FALSE;
11564     // if we detect full gc because of the allocation budget specified this is TRUE;
11565     // it's FALSE if it's due to other factors.
11566     BOOL alloc_factor = TRUE; 
11567     int i = 0;
11568     int n = 0;
11569     int n_initial = gen_num;
11570     BOOL local_blocking_collection = FALSE;
11571     BOOL local_elevation_requested = FALSE;
11572     int new_alloc_remain_percent = 0;
11573
11574     if (full_gc_approach_event_set)
11575     {
11576         return;
11577     }
11578     
11579     if (gen_num != (max_generation + 1))
11580     {
11581         gen_num = max_generation;
11582     }
11583
11584     dynamic_data* dd_full = dynamic_data_of (gen_num);
11585     ptrdiff_t new_alloc_remain = 0;
11586     uint32_t pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
11587
11588     for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
11589     {
11590         dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)", 
11591                      heap_number, gen_index,
11592                      dd_new_allocation (dynamic_data_of (gen_index)),
11593                      dd_desired_allocation (dynamic_data_of (gen_index))));
11594     }
11595
11596     // For small object allocations we only check every fgn_check_quantum bytes.
11597     if (n_initial == 0)
11598     {
11599         dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
11600         dynamic_data* dd_0 = dynamic_data_of (n_initial);
11601         if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
11602             (dd_new_allocation (dd_0) >= 0))
11603         {
11604             return;
11605         }
11606         else
11607         {
11608             fgn_last_alloc = dd_new_allocation (dd_0);
11609             dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
11610         }
11611
11612         // We don't consider the size that came from soh 'cause it doesn't contribute to the
11613         // gen2 budget.
11614         size = 0;
11615     }
11616
11617     for (i = n+1; i <= max_generation; i++)
11618     {
11619         if (get_new_allocation (i) <= 0)
11620         {
11621             n = min (i, max_generation);
11622         }
11623         else
11624             break;
11625     }
11626
11627     dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
11628     if (gen_num == max_generation)
11629     {
11630         // If it's small object heap we should first see if we will even be looking at gen2 budget
11631         // in the next GC or not. If not we should go directly to checking other factors.
11632         if (n < (max_generation - 1))
11633         {
11634             goto check_other_factors;
11635         }
11636     }
11637
11638     new_alloc_remain = dd_new_allocation (dd_full) - size;
11639
11640     new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
11641
11642     dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%", 
11643                  gen_num, pct, new_alloc_remain_percent));
11644
11645     if (new_alloc_remain_percent <= (int)pct)
11646     {
11647 #ifdef BACKGROUND_GC
11648         // If background GC is enabled, we still want to check whether this will
11649         // be a blocking GC or not because we only want to notify when it's a 
11650         // blocking full GC.
11651         if (background_allowed_p())
11652         {
11653             goto check_other_factors;
11654         }
11655 #endif //BACKGROUND_GC
11656
11657         should_notify = TRUE;
11658         goto done;
11659     }
11660
11661 check_other_factors:
11662
11663     dprintf (2, ("FGC: checking other factors"));
11664     n = generation_to_condemn (n, 
11665                                &local_blocking_collection, 
11666                                &local_elevation_requested, 
11667                                TRUE);
11668
11669     if (local_elevation_requested && (n == max_generation))
11670     {
11671         if (settings.should_lock_elevation)
11672         {
11673             int local_elevation_locked_count = settings.elevation_locked_count + 1;
11674             if (local_elevation_locked_count != 6)
11675             {
11676                 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1", 
11677                     local_elevation_locked_count));
11678                 n = max_generation - 1;
11679             }
11680         }
11681     }
11682
11683     dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11684
11685 #ifdef BACKGROUND_GC
11686     // When background GC is enabled it decreases the accuracy of our predictability -
11687     // by the time the GC happens, we may not be under BGC anymore. If we try to 
11688     // predict often enough it should be ok.
11689     if ((n == max_generation) &&
11690         (recursive_gc_sync::background_running_p()))
11691     {
11692         n = max_generation - 1;
11693         dprintf (2, ("FGN: bgc - 1 instead of 2"));
11694     }
11695
11696     if ((n == max_generation) && !local_blocking_collection)
11697     {
11698         if (!background_allowed_p())
11699         {
11700             local_blocking_collection = TRUE;
11701         }
11702     }
11703 #endif //BACKGROUND_GC
11704
11705     dprintf (2, ("FGN: we estimate gen%d will be collected: %s", 
11706                        n, 
11707                        (local_blocking_collection ? "blocking" : "background")));
11708
11709     if ((n == max_generation) && local_blocking_collection)
11710     {
11711         alloc_factor = FALSE;
11712         should_notify = TRUE;
11713         goto done;
11714     }
11715
11716 done:
11717
11718     if (should_notify)
11719     {
11720         dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)", 
11721                      n_initial,
11722                      (alloc_factor ? "alloc" : "other"),
11723                      dd_collection_count (dynamic_data_of (0)),
11724                      new_alloc_remain_percent, 
11725                      gen_num));
11726
11727         send_full_gc_notification (n_initial, alloc_factor);
11728     }
11729 }
11730
11731 void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11732 {
11733     if (!full_gc_approach_event_set)
11734     {
11735         assert (full_gc_approach_event.IsValid());
11736         FIRE_EVENT(GCFullNotify_V1, gen_num, due_to_alloc_p);
11737
11738         full_gc_end_event.Reset();
11739         full_gc_approach_event.Set();
11740         full_gc_approach_event_set = true;
11741     }
11742 }
11743
11744 wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms)
11745 {
11746     if (fgn_maxgen_percent == 0)
11747     {
11748         return wait_full_gc_na;
11749     }
11750
11751     uint32_t wait_result = user_thread_wait(event, FALSE, time_out_ms);
11752
11753     if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11754     {
11755         if (fgn_maxgen_percent == 0)
11756         {
11757             return wait_full_gc_cancelled;
11758         }
11759         
11760         if (wait_result == WAIT_OBJECT_0)
11761         {
11762 #ifdef BACKGROUND_GC
11763             if (fgn_last_gc_was_concurrent)
11764             {
11765                 fgn_last_gc_was_concurrent = FALSE;
11766                 return wait_full_gc_na;
11767             }
11768             else
11769 #endif //BACKGROUND_GC
11770             {
11771                 return wait_full_gc_success;
11772             }
11773         }
11774         else
11775         {
11776             return wait_full_gc_timeout;
11777         }
11778     }
11779     else
11780     {
11781         return wait_full_gc_failed;
11782     }
11783 }
11784
11785 size_t gc_heap::get_full_compact_gc_count()
11786 {
11787     return full_gc_counts[gc_type_compacting];
11788 }
11789
11790 // DTREVIEW - we should check this in dt_low_ephemeral_space_p
11791 // as well.
11792 inline
11793 BOOL gc_heap::short_on_end_of_seg (int gen_number,
11794                                    heap_segment* seg,
11795                                    int align_const)
11796 {
11797     UNREFERENCED_PARAMETER(gen_number);
11798     uint8_t* allocated = heap_segment_allocated(seg);
11799
11800     return (!a_size_fit_p (end_space_after_gc(),
11801                           allocated,
11802                           heap_segment_reserved (seg), 
11803                           align_const));
11804 }
11805
11806 #ifdef _MSC_VER
11807 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
11808 #endif // _MSC_VER
11809
11810 inline
11811 BOOL gc_heap::a_fit_free_list_p (int gen_number, 
11812                                  size_t size, 
11813                                  alloc_context* acontext,
11814                                  int align_const)
11815 {
11816     BOOL can_fit = FALSE;
11817     generation* gen = generation_of (gen_number);
11818     allocator* gen_allocator = generation_allocator (gen);
11819     size_t sz_list = gen_allocator->first_bucket_size();
11820     for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
11821     {
11822         if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
11823         {
11824             uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
11825             uint8_t* prev_free_item = 0;
11826
11827             while (free_list != 0)
11828             {
11829                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11830                 size_t free_list_size = unused_array_size (free_list);
11831                 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
11832                 {
11833                     dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
11834                                  (size_t)free_list, free_list_size));
11835
11836                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11837                     // We ask for more Align (min_obj_size)
11838                     // to make sure that we can insert a free object
11839                     // in adjust_limit will set the limit lower
11840                     size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
11841
11842                     uint8_t*  remain = (free_list + limit);
11843                     size_t remain_size = (free_list_size - limit);
11844                     if (remain_size >= Align(min_free_list, align_const))
11845                     {
11846                         make_unused_array (remain, remain_size);
11847                         gen_allocator->thread_item_front (remain, remain_size);
11848                         assert (remain_size >= Align (min_obj_size, align_const));
11849                     }
11850                     else
11851                     {
11852                         //absorb the entire free list
11853                         limit += remain_size;
11854                     }
11855                     generation_free_list_space (gen) -= limit;
11856
11857                     adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
11858
11859                     can_fit = TRUE;
11860                     goto end;
11861                 }
11862                 else if (gen_allocator->discard_if_no_fit_p())
11863                 {
11864                     assert (prev_free_item == 0);
11865                     dprintf (3, ("couldn't use this free area, discarding"));
11866                     generation_free_obj_space (gen) += free_list_size;
11867
11868                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11869                     generation_free_list_space (gen) -= free_list_size;
11870                 }
11871                 else
11872                 {
11873                     prev_free_item = free_list;
11874                 }
11875                 free_list = free_list_slot (free_list); 
11876             }
11877         }
11878         sz_list = sz_list * 2;
11879     }
11880 end:
11881     return can_fit;
11882 }
11883
11884
11885 #ifdef BACKGROUND_GC
11886 void gc_heap::bgc_loh_alloc_clr (uint8_t* alloc_start,
11887                                  size_t size, 
11888                                  alloc_context* acontext,
11889                                  int align_const, 
11890                                  int lock_index,
11891                                  BOOL check_used_p,
11892                                  heap_segment* seg)
11893 {
11894     make_unused_array (alloc_start, size);
11895
11896 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11897     if (g_fEnableARM)
11898     {
11899         AppDomain* alloc_appdomain = GetAppDomain();
11900         alloc_appdomain->RecordAllocBytes (size, heap_number);
11901     }
11902 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11903
11904     size_t size_of_array_base = sizeof(ArrayBase);
11905
11906     bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
11907
11908     // clear memory while not holding the lock. 
11909     size_t size_to_skip = size_of_array_base;
11910     size_t size_to_clear = size - size_to_skip - plug_skew;
11911     size_t saved_size_to_clear = size_to_clear;
11912     if (check_used_p)
11913     {
11914         uint8_t* end = alloc_start + size - plug_skew;
11915         uint8_t* used = heap_segment_used (seg);
11916         if (used < end)
11917         {
11918             if ((alloc_start + size_to_skip) < used)
11919             {
11920                 size_to_clear = used - (alloc_start + size_to_skip);
11921             }
11922             else
11923             {
11924                 size_to_clear = 0;
11925             }
11926             dprintf (2, ("bgc loh: setting used to %Ix", end));
11927             heap_segment_used (seg) = end;
11928         }
11929
11930         dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
11931                      used, alloc_start, end, size_to_clear));
11932     }
11933     else
11934     {
11935         dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
11936     }
11937
11938 #ifdef VERIFY_HEAP
11939     // since we filled in 0xcc for free object when we verify heap,
11940     // we need to make sure we clear those bytes.
11941     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
11942     {
11943         if (size_to_clear < saved_size_to_clear)
11944         {
11945             size_to_clear = saved_size_to_clear;
11946         }
11947     }
11948 #endif //VERIFY_HEAP
11949     
11950     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
11951     add_saved_spinlock_info (me_release, mt_clr_large_mem);
11952     leave_spin_lock (&more_space_lock);
11953     memclr (alloc_start + size_to_skip, size_to_clear);
11954
11955     bgc_alloc_lock->loh_alloc_set (alloc_start);
11956
11957     acontext->alloc_ptr = alloc_start;
11958     acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
11959
11960     // need to clear the rest of the object before we hand it out.
11961     clear_unused_array(alloc_start, size);
11962 }
11963 #endif //BACKGROUND_GC
11964
11965 BOOL gc_heap::a_fit_free_list_large_p (size_t size, 
11966                                        alloc_context* acontext,
11967                                        int align_const)
11968 {
11969 #ifdef BACKGROUND_GC
11970     wait_for_background_planning (awr_loh_alloc_during_plan);
11971 #endif //BACKGROUND_GC
11972
11973     BOOL can_fit = FALSE;
11974     int gen_number = max_generation + 1;
11975     generation* gen = generation_of (gen_number);
11976     allocator* loh_allocator = generation_allocator (gen); 
11977
11978 #ifdef FEATURE_LOH_COMPACTION
11979     size_t loh_pad = Align (loh_padding_obj_size, align_const);
11980 #endif //FEATURE_LOH_COMPACTION
11981
11982 #ifdef BACKGROUND_GC
11983     int cookie = -1;
11984 #endif //BACKGROUND_GC
11985     size_t sz_list = loh_allocator->first_bucket_size();
11986     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
11987     {
11988         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
11989         {
11990             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
11991             uint8_t* prev_free_item = 0;
11992             while (free_list != 0)
11993             {
11994                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11995
11996                 size_t free_list_size = unused_array_size(free_list);
11997
11998 #ifdef FEATURE_LOH_COMPACTION
11999                 if ((size + loh_pad) <= free_list_size)
12000 #else
12001                 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
12002                     (size == free_list_size))
12003 #endif //FEATURE_LOH_COMPACTION
12004                 {
12005 #ifdef BACKGROUND_GC
12006                     cookie = bgc_alloc_lock->loh_alloc_set (free_list);
12007 #endif //BACKGROUND_GC
12008
12009                     //unlink the free_item
12010                     loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12011
12012                     // Substract min obj size because limit_from_size adds it. Not needed for LOH
12013                     size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size, 
12014                                                     gen_number, align_const);
12015
12016 #ifdef FEATURE_LOH_COMPACTION
12017                     make_unused_array (free_list, loh_pad);
12018                     limit -= loh_pad;
12019                     free_list += loh_pad;
12020                     free_list_size -= loh_pad;
12021 #endif //FEATURE_LOH_COMPACTION
12022
12023                     uint8_t*  remain = (free_list + limit);
12024                     size_t remain_size = (free_list_size - limit);
12025                     if (remain_size != 0)
12026                     {
12027                         assert (remain_size >= Align (min_obj_size, align_const));
12028                         make_unused_array (remain, remain_size);
12029                     }
12030                     if (remain_size >= Align(min_free_list, align_const))
12031                     {
12032                         loh_thread_gap_front (remain, remain_size, gen);
12033                         assert (remain_size >= Align (min_obj_size, align_const));
12034                     }
12035                     else
12036                     {
12037                         generation_free_obj_space (gen) += remain_size;
12038                     }
12039                     generation_free_list_space (gen) -= free_list_size;
12040                     dprintf (3, ("found fit on loh at %Ix", free_list));
12041 #ifdef BACKGROUND_GC
12042                     if (cookie != -1)
12043                     {
12044                         bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
12045                     }
12046                     else
12047 #endif //BACKGROUND_GC
12048                     {
12049                         adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
12050                     }
12051
12052                     //fix the limit to compensate for adjust_limit_clr making it too short 
12053                     acontext->alloc_limit += Align (min_obj_size, align_const);
12054                     can_fit = TRUE;
12055                     goto exit;
12056                 }
12057                 prev_free_item = free_list;
12058                 free_list = free_list_slot (free_list); 
12059             }
12060         }
12061         sz_list = sz_list * 2;
12062     }
12063 exit:
12064     return can_fit;
12065 }
12066
12067 #ifdef _MSC_VER
12068 #pragma warning(default:4706)
12069 #endif // _MSC_VER
12070
12071 BOOL gc_heap::a_fit_segment_end_p (int gen_number,
12072                                    heap_segment* seg,
12073                                    size_t size, 
12074                                    alloc_context* acontext,
12075                                    int align_const,
12076                                    BOOL* commit_failed_p)
12077 {
12078     *commit_failed_p = FALSE;
12079     size_t limit = 0;
12080 #ifdef BACKGROUND_GC
12081     int cookie = -1;
12082 #endif //BACKGROUND_GC
12083
12084     uint8_t*& allocated = ((gen_number == 0) ?
12085                         alloc_allocated : 
12086                         heap_segment_allocated(seg));
12087
12088     size_t pad = Align (min_obj_size, align_const);
12089
12090 #ifdef FEATURE_LOH_COMPACTION
12091     if (gen_number == (max_generation + 1))
12092     {
12093         pad += Align (loh_padding_obj_size, align_const);
12094     }
12095 #endif //FEATURE_LOH_COMPACTION
12096
12097     uint8_t* end = heap_segment_committed (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         goto found_fit;
12105     }
12106
12107     end = heap_segment_reserved (seg) - pad;
12108
12109     if (a_size_fit_p (size, allocated, end, align_const))
12110     {
12111         limit = limit_from_size (size, 
12112                                  (end - allocated), 
12113                                  gen_number, align_const);
12114         if (grow_heap_segment (seg, allocated + limit))
12115         {
12116             goto found_fit;
12117         }
12118         else
12119         {
12120             dprintf (2, ("can't grow segment, doing a full gc"));
12121             *commit_failed_p = TRUE;
12122         }
12123     }
12124     goto found_no_fit;
12125
12126 found_fit:
12127
12128 #ifdef BACKGROUND_GC
12129     if (gen_number != 0)
12130     {
12131         cookie = bgc_alloc_lock->loh_alloc_set (allocated);
12132     }
12133 #endif //BACKGROUND_GC
12134
12135     uint8_t* old_alloc;
12136     old_alloc = allocated;
12137 #ifdef FEATURE_LOH_COMPACTION
12138     if (gen_number == (max_generation + 1))
12139     {
12140         size_t loh_pad = Align (loh_padding_obj_size, align_const);
12141         make_unused_array (old_alloc, loh_pad);
12142         old_alloc += loh_pad;
12143         allocated += loh_pad;
12144         limit -= loh_pad;
12145     }
12146 #endif //FEATURE_LOH_COMPACTION
12147
12148 #if defined (VERIFY_HEAP) && defined (_DEBUG)
12149         ((void**) allocated)[-1] = 0;     //clear the sync block
12150 #endif //VERIFY_HEAP && _DEBUG
12151     allocated += limit;
12152
12153     dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
12154
12155 #ifdef BACKGROUND_GC
12156     if (cookie != -1)
12157     {
12158         bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
12159     }
12160     else
12161 #endif //BACKGROUND_GC
12162     {
12163         adjust_limit_clr (old_alloc, limit, acontext, seg, align_const, gen_number);
12164     }
12165
12166     return TRUE;
12167
12168 found_no_fit:
12169
12170     return FALSE;
12171 }
12172
12173 BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
12174                                        size_t size, 
12175                                        alloc_context* acontext,
12176                                        int align_const,
12177                                        BOOL* commit_failed_p,
12178                                        oom_reason* oom_r)
12179 {
12180     *commit_failed_p = FALSE;
12181     heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
12182     BOOL can_allocate_p = FALSE;
12183
12184     while (seg)
12185     {
12186         if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)), 
12187                                  acontext, align_const, commit_failed_p))
12188         {
12189             acontext->alloc_limit += Align (min_obj_size, align_const);
12190             can_allocate_p = TRUE;
12191             break;
12192         }
12193         else
12194         {
12195             if (*commit_failed_p)
12196             {
12197                 *oom_r = oom_cant_commit;
12198                 break;
12199             }
12200             else
12201             {
12202                 seg = heap_segment_next_rw (seg);
12203             }
12204         }
12205     }
12206
12207     return can_allocate_p;
12208 }
12209
12210 #ifdef BACKGROUND_GC
12211 inline
12212 void gc_heap::wait_for_background (alloc_wait_reason awr)
12213 {
12214     dprintf (2, ("BGC is already in progress, waiting for it to finish"));
12215     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc done", heap_number));
12216     add_saved_spinlock_info (me_release, mt_wait_bgc);
12217     leave_spin_lock (&more_space_lock);
12218     background_gc_wait (awr);
12219     enter_spin_lock (&more_space_lock);
12220     add_saved_spinlock_info (me_acquire, mt_wait_bgc);
12221     dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc done", heap_number));
12222 }
12223
12224 void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr)
12225 {
12226     if (recursive_gc_sync::background_running_p())
12227     {
12228         uint32_t memory_load;
12229         get_memory_info (&memory_load);
12230         if (memory_load >= 95)
12231         {
12232             dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
12233             wait_for_background (awr);
12234         }
12235     }
12236 }
12237
12238 #endif //BACKGROUND_GC
12239
12240 // We request to trigger an ephemeral GC but we may get a full compacting GC.
12241 // return TRUE if that's the case.
12242 BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
12243 {
12244 #ifdef BACKGROUND_GC
12245     wait_for_bgc_high_memory (awr_loh_oos_bgc);
12246 #endif //BACKGROUND_GC
12247
12248     BOOL did_full_compact_gc = FALSE;
12249
12250     dprintf (2, ("triggering a gen1 GC"));
12251     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12252     vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
12253
12254 #ifdef MULTIPLE_HEAPS
12255     enter_spin_lock (&more_space_lock);
12256     add_saved_spinlock_info (me_acquire, mt_t_eph_gc);
12257     dprintf (SPINLOCK_LOG, ("[%d]Emsl after a GC", heap_number));
12258 #endif //MULTIPLE_HEAPS
12259
12260     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12261
12262     if (current_full_compact_gc_count > last_full_compact_gc_count)
12263     {
12264         dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
12265         did_full_compact_gc = TRUE;
12266     }
12267
12268     return did_full_compact_gc;
12269 }
12270
12271 BOOL gc_heap::soh_try_fit (int gen_number,
12272                            size_t size, 
12273                            alloc_context* acontext,
12274                            int align_const,
12275                            BOOL* commit_failed_p,
12276                            BOOL* short_seg_end_p)
12277 {
12278     BOOL can_allocate = TRUE;
12279     if (short_seg_end_p)
12280     {
12281         *short_seg_end_p = FALSE;
12282     }
12283
12284     can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
12285     if (!can_allocate)
12286     {
12287         if (short_seg_end_p)
12288         {
12289             *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
12290         }
12291         // If the caller doesn't care, we always try to fit at the end of seg;
12292         // otherwise we would only try if we are actually not short at end of seg.
12293         if (!short_seg_end_p || !(*short_seg_end_p))
12294         {
12295             can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size, 
12296                                                 acontext, align_const, commit_failed_p);
12297         }
12298     }
12299
12300     return can_allocate;
12301 }
12302
12303 BOOL gc_heap::allocate_small (int gen_number,
12304                               size_t size, 
12305                               alloc_context* acontext,
12306                               int align_const)
12307 {
12308 #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
12309     if (recursive_gc_sync::background_running_p())
12310     {
12311         background_soh_alloc_count++;
12312         if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
12313         {
12314             add_saved_spinlock_info (me_release, mt_alloc_small);
12315             dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl", heap_number));
12316             leave_spin_lock (&more_space_lock);
12317             bool cooperative_mode = enable_preemptive ();
12318             GCToOSInterface::Sleep (bgc_alloc_spin);
12319             disable_preemptive (cooperative_mode);
12320             enter_spin_lock (&more_space_lock);
12321             add_saved_spinlock_info (me_acquire, mt_alloc_small);
12322             dprintf (SPINLOCK_LOG, ("[%d]spin Emsl", heap_number));
12323         }
12324         else
12325         {
12326             //GCToOSInterface::YieldThread (0);
12327         }
12328     }
12329 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
12330
12331     gc_reason gr = reason_oos_soh;
12332     oom_reason oom_r = oom_no_failure;
12333
12334     // No variable values should be "carried over" from one state to the other. 
12335     // That's why there are local variable for each state
12336
12337     allocation_state soh_alloc_state = a_state_start;
12338
12339     // If we can get a new seg it means allocation will succeed.
12340     while (1)
12341     {
12342         dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
12343         switch (soh_alloc_state)
12344         {
12345             case a_state_can_allocate:
12346             case a_state_cant_allocate:
12347             {
12348                 goto exit;
12349             }
12350             case a_state_start:
12351             {
12352                 soh_alloc_state = a_state_try_fit;
12353                 break;
12354             }
12355             case a_state_try_fit:
12356             {
12357                 BOOL commit_failed_p = FALSE;
12358                 BOOL can_use_existing_p = FALSE;
12359
12360                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12361                                                   align_const, &commit_failed_p,
12362                                                   NULL);
12363                 soh_alloc_state = (can_use_existing_p ?
12364                                         a_state_can_allocate : 
12365                                         (commit_failed_p ? 
12366                                             a_state_trigger_full_compact_gc :
12367                                             a_state_trigger_ephemeral_gc));
12368                 break;
12369             }
12370             case a_state_try_fit_after_bgc:
12371             {
12372                 BOOL commit_failed_p = FALSE;
12373                 BOOL can_use_existing_p = FALSE;
12374                 BOOL short_seg_end_p = FALSE;
12375
12376                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12377                                                   align_const, &commit_failed_p,
12378                                                   &short_seg_end_p);
12379                 soh_alloc_state = (can_use_existing_p ? 
12380                                         a_state_can_allocate : 
12381                                         (short_seg_end_p ? 
12382                                             a_state_trigger_2nd_ephemeral_gc : 
12383                                             a_state_trigger_full_compact_gc));
12384                 break;
12385             }
12386             case a_state_try_fit_after_cg:
12387             {
12388                 BOOL commit_failed_p = FALSE;
12389                 BOOL can_use_existing_p = FALSE;
12390                 BOOL short_seg_end_p = FALSE;
12391
12392                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12393                                                   align_const, &commit_failed_p,
12394                                                   &short_seg_end_p);
12395                 if (short_seg_end_p)
12396                 {
12397                     soh_alloc_state = a_state_cant_allocate;
12398                     oom_r = oom_budget;
12399                 }
12400                 else
12401                 {
12402                     if (can_use_existing_p)
12403                     {
12404                         soh_alloc_state = a_state_can_allocate;
12405                     }
12406                     else
12407                     {
12408 #ifdef MULTIPLE_HEAPS
12409                         if (!commit_failed_p)
12410                         {
12411                             // some other threads already grabbed the more space lock and allocated
12412                             // so we should attempt an ephemeral GC again.
12413                             assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
12414                             soh_alloc_state = a_state_trigger_ephemeral_gc; 
12415                         }
12416                         else
12417 #endif //MULTIPLE_HEAPS
12418                         {
12419                             assert (commit_failed_p);
12420                             soh_alloc_state = a_state_cant_allocate;
12421                             oom_r = oom_cant_commit;
12422                         }
12423                     }
12424                 }
12425                 break;
12426             }
12427             case a_state_check_and_wait_for_bgc:
12428             {
12429                 BOOL bgc_in_progress_p = FALSE;
12430                 BOOL did_full_compacting_gc = FALSE;
12431
12432                 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc);
12433                 soh_alloc_state = (did_full_compacting_gc ? 
12434                                         a_state_try_fit_after_cg : 
12435                                         a_state_try_fit_after_bgc);
12436                 break;
12437             }
12438             case a_state_trigger_ephemeral_gc:
12439             {
12440                 BOOL commit_failed_p = FALSE;
12441                 BOOL can_use_existing_p = FALSE;
12442                 BOOL short_seg_end_p = FALSE;
12443                 BOOL bgc_in_progress_p = FALSE;
12444                 BOOL did_full_compacting_gc = FALSE;
12445
12446                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12447                 if (did_full_compacting_gc)
12448                 {
12449                     soh_alloc_state = a_state_try_fit_after_cg;
12450                 }
12451                 else
12452                 {
12453                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12454                                                       align_const, &commit_failed_p,
12455                                                       &short_seg_end_p);
12456 #ifdef BACKGROUND_GC
12457                     bgc_in_progress_p = recursive_gc_sync::background_running_p();
12458 #endif //BACKGROUND_GC
12459
12460                     if (short_seg_end_p)
12461                     {
12462                         soh_alloc_state = (bgc_in_progress_p ? 
12463                                                 a_state_check_and_wait_for_bgc : 
12464                                                 a_state_trigger_full_compact_gc);
12465
12466                         if (fgn_maxgen_percent)
12467                         {
12468                             dprintf (2, ("FGN: doing last GC before we throw OOM"));
12469                             send_full_gc_notification (max_generation, FALSE);
12470                         }
12471                     }
12472                     else
12473                     {
12474                         if (can_use_existing_p)
12475                         {
12476                             soh_alloc_state = a_state_can_allocate;
12477                         }
12478                         else
12479                         {
12480 #ifdef MULTIPLE_HEAPS
12481                             if (!commit_failed_p)
12482                             {
12483                                 // some other threads already grabbed the more space lock and allocated
12484                                 // so we should attempt an ephemeral GC again.
12485                                 assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
12486                                 soh_alloc_state = a_state_trigger_ephemeral_gc;
12487                             }
12488                             else
12489 #endif //MULTIPLE_HEAPS
12490                             {
12491                                 soh_alloc_state = a_state_trigger_full_compact_gc;
12492                                 if (fgn_maxgen_percent)
12493                                 {
12494                                     dprintf (2, ("FGN: failed to commit, doing full compacting GC"));
12495                                     send_full_gc_notification (max_generation, FALSE);
12496                                 }
12497                             }
12498                         }
12499                     }
12500                 }
12501                 break;
12502             }
12503             case a_state_trigger_2nd_ephemeral_gc:
12504             {
12505                 BOOL commit_failed_p = FALSE;
12506                 BOOL can_use_existing_p = FALSE;
12507                 BOOL short_seg_end_p = FALSE;
12508                 BOOL did_full_compacting_gc = FALSE;
12509
12510
12511                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12512                 
12513                 if (did_full_compacting_gc)
12514                 {
12515                     soh_alloc_state = a_state_try_fit_after_cg;
12516                 }
12517                 else
12518                 {
12519                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12520                                                       align_const, &commit_failed_p,
12521                                                       &short_seg_end_p);
12522                     if (short_seg_end_p || commit_failed_p)
12523                     {
12524                         soh_alloc_state = a_state_trigger_full_compact_gc;
12525                     }
12526                     else
12527                     {
12528                         assert (can_use_existing_p);
12529                         soh_alloc_state = a_state_can_allocate;
12530                     }
12531                 }
12532                 break;
12533             }
12534             case a_state_trigger_full_compact_gc:
12535             {
12536                 BOOL got_full_compacting_gc = FALSE;
12537
12538                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
12539                 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12540                 break;
12541             }
12542             default:
12543             {
12544                 assert (!"Invalid state!");
12545                 break;
12546             }
12547         }
12548     }
12549
12550 exit:
12551     if (soh_alloc_state == a_state_cant_allocate)
12552     {
12553         assert (oom_r != oom_no_failure);
12554         handle_oom (heap_number, 
12555                     oom_r, 
12556                     size,
12557                     heap_segment_allocated (ephemeral_heap_segment),
12558                     heap_segment_reserved (ephemeral_heap_segment));
12559
12560         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for oom", heap_number));
12561         add_saved_spinlock_info (me_release, mt_alloc_small_cant);
12562         leave_spin_lock (&more_space_lock);
12563     }
12564
12565     return (soh_alloc_state == a_state_can_allocate);
12566 }
12567
12568 #ifdef BACKGROUND_GC
12569 inline
12570 void gc_heap::wait_for_background_planning (alloc_wait_reason awr)
12571 {
12572     while (current_c_gc_state == c_gc_state_planning)
12573     {
12574         dprintf (3, ("lh state planning, cannot allocate"));
12575
12576         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc plan", heap_number));
12577         add_saved_spinlock_info (me_release, mt_wait_bgc_plan);
12578         leave_spin_lock (&more_space_lock);
12579         background_gc_wait_lh (awr);
12580         enter_spin_lock (&more_space_lock);
12581         add_saved_spinlock_info (me_acquire, mt_wait_bgc_plan);
12582         dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc plan", heap_number));
12583     }
12584     assert ((current_c_gc_state == c_gc_state_free) ||
12585             (current_c_gc_state == c_gc_state_marking));
12586 }
12587
12588 BOOL gc_heap::bgc_loh_should_allocate()
12589 {
12590     size_t min_gc_size = dd_min_size(dynamic_data_of (max_generation + 1));
12591
12592     if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
12593     {
12594         return TRUE;
12595     }
12596
12597     if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
12598     {
12599         if ((bgc_begin_loh_size / end_loh_size) > 2)
12600         {
12601             dprintf (3, ("alloc-ed too much before bgc started"));
12602         }
12603         else
12604         {
12605             dprintf (3, ("alloc-ed too much after bgc started"));
12606         }
12607         return FALSE;
12608     }
12609     else
12610     {
12611         bgc_alloc_spin_loh = (uint32_t)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
12612         return TRUE;
12613     }
12614 }
12615 #endif //BACKGROUND_GC
12616
12617 size_t gc_heap::get_large_seg_size (size_t size)
12618 {
12619     size_t default_seg_size = min_loh_segment_size;
12620 #ifdef SEG_MAPPING_TABLE
12621     size_t align_size =  default_seg_size;
12622 #else //SEG_MAPPING_TABLE
12623     size_t align_size =  default_seg_size / 2;
12624 #endif //SEG_MAPPING_TABLE
12625     int align_const = get_alignment_constant (FALSE);
12626     size_t large_seg_size = align_on_page (
12627         max (default_seg_size,
12628             ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE +
12629             align_size) / align_size * align_size)));
12630     return large_seg_size;
12631 }
12632
12633 BOOL gc_heap::loh_get_new_seg (generation* gen,
12634                                size_t size,
12635                                int align_const,
12636                                BOOL* did_full_compact_gc,
12637                                oom_reason* oom_r)
12638 {
12639     UNREFERENCED_PARAMETER(gen);
12640     UNREFERENCED_PARAMETER(align_const);
12641
12642     *did_full_compact_gc = FALSE;
12643
12644     size_t seg_size = get_large_seg_size (size);
12645
12646     heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
12647
12648     if (new_seg)
12649     {
12650         loh_alloc_since_cg += seg_size;
12651     }
12652     else
12653     {
12654         *oom_r = oom_loh;
12655     }
12656
12657     return (new_seg != 0);
12658 }
12659
12660 BOOL gc_heap::retry_full_compact_gc (size_t size)
12661 {
12662     size_t seg_size = get_large_seg_size (size);
12663
12664     if (loh_alloc_since_cg >= (2 * (uint64_t)seg_size))
12665     {
12666         return TRUE;
12667     }
12668
12669 #ifdef MULTIPLE_HEAPS
12670     uint64_t total_alloc_size = 0;
12671     for (int i = 0; i < n_heaps; i++)
12672     {
12673         total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12674     }
12675
12676     if (total_alloc_size >= (2 * (uint64_t)seg_size))
12677     {
12678         return TRUE;
12679     }
12680 #endif //MULTIPLE_HEAPS
12681
12682     return FALSE;
12683 }
12684
12685 BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12686                                       BOOL* did_full_compact_gc)
12687 {
12688     BOOL bgc_in_progress = FALSE;
12689     *did_full_compact_gc = FALSE;
12690 #ifdef BACKGROUND_GC
12691     if (recursive_gc_sync::background_running_p())
12692     {
12693         bgc_in_progress = TRUE;
12694         size_t last_full_compact_gc_count = get_full_compact_gc_count();
12695         wait_for_background (awr);
12696         size_t current_full_compact_gc_count = get_full_compact_gc_count();
12697         if (current_full_compact_gc_count > last_full_compact_gc_count)
12698         {
12699             *did_full_compact_gc = TRUE;
12700         }
12701     }
12702 #endif //BACKGROUND_GC
12703
12704     return bgc_in_progress;
12705 }
12706
12707 BOOL gc_heap::loh_try_fit (int gen_number,
12708                            size_t size, 
12709                            alloc_context* acontext,
12710                            int align_const,
12711                            BOOL* commit_failed_p,
12712                            oom_reason* oom_r)
12713 {
12714     BOOL can_allocate = TRUE;
12715
12716     if (!a_fit_free_list_large_p (size, acontext, align_const))
12717     {
12718         can_allocate = loh_a_fit_segment_end_p (gen_number, size, 
12719                                                 acontext, align_const, 
12720                                                 commit_failed_p, oom_r);
12721
12722 #ifdef BACKGROUND_GC
12723         if (can_allocate && recursive_gc_sync::background_running_p())
12724         {
12725             bgc_loh_size_increased += size;
12726         }
12727 #endif //BACKGROUND_GC
12728     }
12729 #ifdef BACKGROUND_GC
12730     else
12731     {
12732         if (recursive_gc_sync::background_running_p())
12733         {
12734             bgc_loh_allocated_in_free += size;
12735         }
12736     }
12737 #endif //BACKGROUND_GC
12738
12739     return can_allocate;
12740 }
12741
12742 BOOL gc_heap::trigger_full_compact_gc (gc_reason gr, 
12743                                        oom_reason* oom_r)
12744 {
12745     BOOL did_full_compact_gc = FALSE;
12746
12747     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12748
12749     // Set this so the next GC will be a full compacting GC.
12750     if (!last_gc_before_oom)
12751     {
12752         last_gc_before_oom = TRUE;
12753     }
12754
12755 #ifdef BACKGROUND_GC
12756     if (recursive_gc_sync::background_running_p())
12757     {
12758         wait_for_background ((gr == reason_oos_soh) ? awr_gen0_oos_bgc : awr_loh_oos_bgc);
12759         dprintf (2, ("waited for BGC - done"));
12760     }
12761 #endif //BACKGROUND_GC
12762
12763     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12764     if (current_full_compact_gc_count > last_full_compact_gc_count)
12765     {
12766         dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
12767         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12768         did_full_compact_gc = TRUE;
12769         goto exit;
12770     }
12771
12772     dprintf (3, ("h%d full GC", heap_number));
12773     vm_heap->GarbageCollectGeneration(max_generation, gr);
12774
12775 #ifdef MULTIPLE_HEAPS
12776     enter_spin_lock (&more_space_lock);
12777     dprintf (SPINLOCK_LOG, ("[%d]Emsl after full gc", heap_number));
12778     add_saved_spinlock_info (me_acquire, mt_t_full_gc);
12779 #endif //MULTIPLE_HEAPS
12780
12781     current_full_compact_gc_count = get_full_compact_gc_count();
12782
12783     if (current_full_compact_gc_count == last_full_compact_gc_count)
12784     {
12785         dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
12786         // We requested a full GC but didn't get because of the elevation logic
12787         // which means we should fail.
12788         *oom_r = oom_unproductive_full_gc;
12789     }
12790     else
12791     {
12792         dprintf (3, ("h%d: T full compacting GC (%d->%d)", 
12793             heap_number, 
12794             last_full_compact_gc_count, 
12795             current_full_compact_gc_count));
12796
12797         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12798         did_full_compact_gc = TRUE;
12799     }
12800
12801 exit:
12802     return did_full_compact_gc;
12803 }
12804
12805 #ifdef RECORD_LOH_STATE
12806 void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, EEThreadId thread_id)
12807 {
12808     // When the state is can_allocate we already have released the more
12809     // space lock. So we are not logging states here since this code
12810     // is not thread safe.
12811     if (loh_state_to_save != a_state_can_allocate)
12812     {
12813         last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
12814         last_loh_states[loh_state_index].thread_id = thread_id;
12815         loh_state_index++;
12816
12817         if (loh_state_index == max_saved_loh_states)
12818         {
12819             loh_state_index = 0;
12820         }
12821
12822         assert (loh_state_index < max_saved_loh_states);
12823     }
12824 }
12825 #endif //RECORD_LOH_STATE
12826
12827 BOOL gc_heap::allocate_large (int gen_number,
12828                               size_t size, 
12829                               alloc_context* acontext,
12830                               int align_const)
12831 {
12832 #ifdef BACKGROUND_GC
12833     if (recursive_gc_sync::background_running_p() && (current_c_gc_state != c_gc_state_planning))
12834     {
12835         background_loh_alloc_count++;
12836         //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
12837         {
12838             if (bgc_loh_should_allocate())
12839             {
12840                 if (!bgc_alloc_spin_loh)
12841                 {
12842                     add_saved_spinlock_info (me_release, mt_alloc_large);
12843                     dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl loh", heap_number));
12844                     leave_spin_lock (&more_space_lock);
12845                     bool cooperative_mode = enable_preemptive ();
12846                     GCToOSInterface::YieldThread (bgc_alloc_spin_loh);
12847                     disable_preemptive (cooperative_mode);
12848                     enter_spin_lock (&more_space_lock);
12849                     add_saved_spinlock_info (me_acquire, mt_alloc_large);
12850                     dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
12851                 }
12852             }
12853             else
12854             {
12855                 wait_for_background (awr_loh_alloc_during_bgc);
12856             }
12857         }
12858     }
12859 #endif //BACKGROUND_GC
12860
12861     gc_reason gr = reason_oos_loh;
12862     generation* gen = generation_of (gen_number);
12863     oom_reason oom_r = oom_no_failure;
12864     size_t current_full_compact_gc_count = 0;
12865
12866     // No variable values should be "carried over" from one state to the other. 
12867     // That's why there are local variable for each state
12868     allocation_state loh_alloc_state = a_state_start;
12869 #ifdef RECORD_LOH_STATE
12870     EEThreadId current_thread_id;
12871     current_thread_id.SetToCurrentThread();
12872 #endif //RECORD_LOH_STATE
12873
12874     // If we can get a new seg it means allocation will succeed.
12875     while (1)
12876     {
12877         dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
12878
12879 #ifdef RECORD_LOH_STATE
12880         add_saved_loh_state (loh_alloc_state, current_thread_id);
12881 #endif //RECORD_LOH_STATE
12882         switch (loh_alloc_state)
12883         {
12884             case a_state_can_allocate:
12885             case a_state_cant_allocate:
12886             {
12887                 goto exit;
12888             }
12889             case a_state_start:
12890             {
12891                 loh_alloc_state = a_state_try_fit;
12892                 break;
12893             }
12894             case a_state_try_fit:
12895             {
12896                 BOOL commit_failed_p = FALSE;
12897                 BOOL can_use_existing_p = FALSE;
12898
12899                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12900                                                   align_const, &commit_failed_p, &oom_r);
12901                 loh_alloc_state = (can_use_existing_p ?
12902                                         a_state_can_allocate : 
12903                                         (commit_failed_p ? 
12904                                             a_state_trigger_full_compact_gc :
12905                                             a_state_acquire_seg));
12906                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12907                 break;
12908             }
12909             case a_state_try_fit_new_seg:
12910             {
12911                 BOOL commit_failed_p = FALSE;
12912                 BOOL can_use_existing_p = FALSE;
12913
12914                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12915                                                   align_const, &commit_failed_p, &oom_r);
12916                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12917                 // another LOH allocating thread could have beat us to acquire the msl so 
12918                 // we need to try again.
12919                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
12920                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12921                 break;
12922             }
12923             case a_state_try_fit_new_seg_after_cg:
12924             {
12925                 BOOL commit_failed_p = FALSE;
12926                 BOOL can_use_existing_p = FALSE;
12927
12928                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12929                                                   align_const, &commit_failed_p, &oom_r);
12930                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12931                 // another LOH allocating thread could have beat us to acquire the msl so 
12932                 // we need to try again. However, if we failed to commit, which means we 
12933                 // did have space on the seg, we bail right away 'cause we already did a 
12934                 // full compacting GC.
12935                 loh_alloc_state = (can_use_existing_p ? 
12936                                         a_state_can_allocate : 
12937                                         (commit_failed_p ? 
12938                                             a_state_cant_allocate :
12939                                             a_state_acquire_seg_after_cg));
12940                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12941                 break;
12942             }
12943             case a_state_try_fit_no_seg:
12944             {
12945                 BOOL commit_failed_p = FALSE;
12946                 BOOL can_use_existing_p = FALSE;
12947
12948                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12949                                                   align_const, &commit_failed_p, &oom_r);
12950                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_cant_allocate);
12951                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12952                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12953                 break;
12954             }
12955             case a_state_try_fit_after_cg:
12956             {
12957                 BOOL commit_failed_p = FALSE;
12958                 BOOL can_use_existing_p = FALSE;
12959
12960                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12961                                                   align_const, &commit_failed_p, &oom_r);
12962                 loh_alloc_state = (can_use_existing_p ?
12963                                         a_state_can_allocate : 
12964                                         (commit_failed_p ? 
12965                                             a_state_cant_allocate :
12966                                             a_state_acquire_seg_after_cg));
12967                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12968                 break;
12969             }
12970             case a_state_try_fit_after_bgc:
12971             {
12972                 BOOL commit_failed_p = FALSE;
12973                 BOOL can_use_existing_p = FALSE;
12974
12975                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12976                                                   align_const, &commit_failed_p, &oom_r);
12977                 loh_alloc_state = (can_use_existing_p ?
12978                                         a_state_can_allocate : 
12979                                         (commit_failed_p ? 
12980                                             a_state_trigger_full_compact_gc :
12981                                             a_state_acquire_seg_after_bgc));
12982                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12983                 break;
12984             }
12985             case a_state_acquire_seg:
12986             {
12987                 BOOL can_get_new_seg_p = FALSE;
12988                 BOOL did_full_compacting_gc = FALSE;
12989
12990                 current_full_compact_gc_count = get_full_compact_gc_count();
12991
12992                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12993                 loh_alloc_state = (can_get_new_seg_p ? 
12994                                         a_state_try_fit_new_seg : 
12995                                         (did_full_compacting_gc ? 
12996                                             a_state_check_retry_seg :
12997                                             a_state_check_and_wait_for_bgc));
12998                 break;
12999             }
13000             case a_state_acquire_seg_after_cg:
13001             {
13002                 BOOL can_get_new_seg_p = FALSE;
13003                 BOOL did_full_compacting_gc = FALSE;
13004
13005                 current_full_compact_gc_count = get_full_compact_gc_count();
13006
13007                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
13008                 // Since we release the msl before we try to allocate a seg, other
13009                 // threads could have allocated a bunch of segments before us so
13010                 // we might need to retry.
13011                 loh_alloc_state = (can_get_new_seg_p ? 
13012                                         a_state_try_fit_new_seg_after_cg : 
13013                                         a_state_check_retry_seg);
13014                 break;
13015             }
13016             case a_state_acquire_seg_after_bgc:
13017             {
13018                 BOOL can_get_new_seg_p = FALSE;
13019                 BOOL did_full_compacting_gc = FALSE;
13020              
13021                 current_full_compact_gc_count = get_full_compact_gc_count();
13022
13023                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); 
13024                 loh_alloc_state = (can_get_new_seg_p ? 
13025                                         a_state_try_fit_new_seg : 
13026                                         (did_full_compacting_gc ? 
13027                                             a_state_check_retry_seg :
13028                                             a_state_trigger_full_compact_gc));
13029                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13030                 break;
13031             }
13032             case a_state_check_and_wait_for_bgc:
13033             {
13034                 BOOL bgc_in_progress_p = FALSE;
13035                 BOOL did_full_compacting_gc = FALSE;
13036
13037                 if (fgn_maxgen_percent)
13038                 {
13039                     dprintf (2, ("FGN: failed to acquire seg, may need to do a full blocking GC"));
13040                     send_full_gc_notification (max_generation, FALSE);
13041                 }
13042
13043                 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc);
13044                 loh_alloc_state = (!bgc_in_progress_p ?
13045                                         a_state_trigger_full_compact_gc : 
13046                                         (did_full_compacting_gc ? 
13047                                             a_state_try_fit_after_cg :
13048                                             a_state_try_fit_after_bgc));
13049                 break;
13050             }
13051             case a_state_trigger_full_compact_gc:
13052             {
13053                 BOOL got_full_compacting_gc = FALSE;
13054
13055                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
13056                 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
13057                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13058                 break;
13059             }
13060             case a_state_check_retry_seg:
13061             {
13062                 BOOL should_retry_gc = retry_full_compact_gc (size);
13063                 BOOL should_retry_get_seg = FALSE;
13064                 if (!should_retry_gc)
13065                 {
13066                     size_t last_full_compact_gc_count = current_full_compact_gc_count;
13067                     current_full_compact_gc_count = get_full_compact_gc_count();
13068
13069                     if (current_full_compact_gc_count > (last_full_compact_gc_count + 1))
13070                     {
13071                         should_retry_get_seg = TRUE;
13072                     }
13073                 }
13074     
13075                 loh_alloc_state = (should_retry_gc ? 
13076                                         a_state_trigger_full_compact_gc : 
13077                                         (should_retry_get_seg ?
13078                                             a_state_acquire_seg_after_cg :
13079                                             a_state_cant_allocate));
13080                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13081                 break;
13082             }
13083             default:
13084             {
13085                 assert (!"Invalid state!");
13086                 break;
13087             }
13088         }
13089     }
13090
13091 exit:
13092     if (loh_alloc_state == a_state_cant_allocate)
13093     {
13094         assert (oom_r != oom_no_failure);
13095         handle_oom (heap_number, 
13096                     oom_r, 
13097                     size,
13098                     0,
13099                     0);
13100
13101         add_saved_spinlock_info (me_release, mt_alloc_large_cant);
13102         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for loh oom", heap_number));
13103         leave_spin_lock (&more_space_lock);
13104     }
13105
13106     return (loh_alloc_state == a_state_can_allocate);
13107 }
13108
13109 int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
13110                                    int gen_number)
13111 {
13112     if (gc_heap::gc_started)
13113     {
13114         wait_for_gc_done();
13115         return -1;
13116     }
13117
13118 #ifdef SYNCHRONIZATION_STATS
13119     int64_t msl_acquire_start = GCToOSInterface::QueryPerformanceCounter();
13120 #endif //SYNCHRONIZATION_STATS
13121     enter_spin_lock (&more_space_lock);
13122     add_saved_spinlock_info (me_acquire, mt_try_alloc);
13123     dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
13124 #ifdef SYNCHRONIZATION_STATS
13125     int64_t msl_acquire = GCToOSInterface::QueryPerformanceCounter() - msl_acquire_start;
13126     total_msl_acquire += msl_acquire;
13127     num_msl_acquired++;
13128     if (msl_acquire > 200)
13129     {
13130         num_high_msl_acquire++;
13131     }
13132     else
13133     {
13134         num_low_msl_acquire++;
13135     }
13136 #endif //SYNCHRONIZATION_STATS
13137
13138     /*
13139     // We are commenting this out 'cause we don't see the point - we already
13140     // have checked gc_started when we were acquiring the msl - no need to check
13141     // again. This complicates the logic in bgc_suspend_EE 'cause that one would
13142     // need to release msl which causes all sorts of trouble.
13143     if (gc_heap::gc_started)
13144     {
13145 #ifdef SYNCHRONIZATION_STATS
13146         good_suspension++;
13147 #endif //SYNCHRONIZATION_STATS
13148         BOOL fStress = (g_pConfig->GetGCStressLevel() & GCConfig::GCSTRESS_TRANSITION) != 0;
13149         if (!fStress)
13150         {
13151             //Rendez vous early (MP scaling issue)
13152             //dprintf (1, ("[%d]waiting for gc", heap_number));
13153             wait_for_gc_done();
13154 #ifdef MULTIPLE_HEAPS
13155             return -1;
13156 #endif //MULTIPLE_HEAPS
13157         }
13158     }
13159     */
13160
13161     dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
13162
13163     int align_const = get_alignment_constant (gen_number != (max_generation+1));
13164
13165     if (fgn_maxgen_percent)
13166     {
13167         check_for_full_gc (gen_number, size);
13168     }
13169
13170     if (!(new_allocation_allowed (gen_number)))
13171     {
13172         if (fgn_maxgen_percent && (gen_number == 0))
13173         {
13174             // We only check gen0 every so often, so take this opportunity to check again.
13175             check_for_full_gc (gen_number, size);
13176         }
13177
13178 #ifdef BACKGROUND_GC
13179         wait_for_bgc_high_memory (awr_gen0_alloc);
13180 #endif //BACKGROUND_GC
13181
13182 #ifdef SYNCHRONIZATION_STATS
13183         bad_suspension++;
13184 #endif //SYNCHRONIZATION_STATS
13185         dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
13186
13187         if (!settings.concurrent || (gen_number == 0))
13188         {
13189             vm_heap->GarbageCollectGeneration (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh));
13190 #ifdef MULTIPLE_HEAPS
13191             enter_spin_lock (&more_space_lock);
13192             add_saved_spinlock_info (me_acquire, mt_try_budget);
13193             dprintf (SPINLOCK_LOG, ("[%d]Emsl out budget", heap_number));
13194 #endif //MULTIPLE_HEAPS
13195         }
13196     }
13197
13198     BOOL can_allocate = ((gen_number == 0) ?
13199         allocate_small (gen_number, size, acontext, align_const) :
13200         allocate_large (gen_number, size, acontext, align_const));
13201    
13202     if (can_allocate)
13203     {
13204         size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
13205         int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
13206
13207         etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
13208
13209
13210         if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
13211         {
13212 #ifdef FEATURE_REDHAWK
13213             FIRE_EVENT(GCAllocationTick_V1, (uint32_t)etw_allocation_running_amount[etw_allocation_index],
13214                                             (gen_number == 0) ? gc_etw_alloc_soh : gc_etw_alloc_loh);
13215 #else
13216             // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
13217             // The ones that do are much less efficient.
13218 #if defined(FEATURE_EVENT_TRACE)
13219             if (EVENT_ENABLED(GCAllocationTick_V3))
13220             {
13221                 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
13222             }
13223 #endif //FEATURE_EVENT_TRACE
13224 #endif //FEATURE_REDHAWK
13225             etw_allocation_running_amount[etw_allocation_index] = 0;
13226         }
13227     }
13228
13229     return (int)can_allocate;
13230 }
13231
13232 #ifdef MULTIPLE_HEAPS
13233 void gc_heap::balance_heaps (alloc_context* acontext)
13234 {
13235
13236     if (acontext->alloc_count < 4)
13237     {
13238         if (acontext->alloc_count == 0)
13239         {
13240             acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, 0) ));
13241             gc_heap* hp = acontext->get_home_heap()->pGenGCHeap;
13242             dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
13243             acontext->set_alloc_heap(acontext->get_home_heap());
13244             hp->alloc_context_count++;
13245         }
13246     }
13247     else
13248     {
13249         BOOL set_home_heap = FALSE;
13250         int hint = 0;
13251
13252         if (heap_select::can_find_heap_fast())
13253         {
13254             if (acontext->get_home_heap() != NULL)
13255                 hint = acontext->get_home_heap()->pGenGCHeap->heap_number;
13256             if (acontext->get_home_heap() != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
13257             {
13258                 set_home_heap = TRUE;
13259             }
13260         }
13261         else
13262         {
13263             // can't use gdt
13264             if ((acontext->alloc_count & 3) == 0)
13265                 set_home_heap = TRUE;
13266         }
13267
13268         if (set_home_heap)
13269         {
13270 /*
13271             // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
13272             if (n_heaps > MAX_SUPPORTED_CPUS)
13273             {
13274                 // on machines with many processors cache affinity is really king, so don't even try
13275                 // to balance on these.
13276                 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
13277                 acontext->alloc_heap = acontext->home_heap;
13278             }
13279             else
13280 */
13281             {
13282                 gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13283
13284                 dynamic_data* dd = org_hp->dynamic_data_of (0);
13285                 ptrdiff_t org_size = dd_new_allocation (dd);
13286                 int org_alloc_context_count;
13287                 int max_alloc_context_count;
13288                 gc_heap* max_hp;
13289                 ptrdiff_t max_size;
13290                 size_t delta = dd_min_size (dd)/4;
13291
13292                 int start, end, finish;
13293                 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13294                 finish = start + n_heaps;
13295
13296 try_again:
13297                 do
13298                 {
13299                     max_hp = org_hp;
13300                     max_size = org_size + delta;
13301                     acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, hint) ));
13302
13303                     if (org_hp == acontext->get_home_heap()->pGenGCHeap)
13304                         max_size = max_size + delta;
13305
13306                     org_alloc_context_count = org_hp->alloc_context_count;
13307                     max_alloc_context_count = org_alloc_context_count;
13308                     if (max_alloc_context_count > 1)
13309                         max_size /= max_alloc_context_count;
13310
13311                     for (int i = start; i < end; i++)
13312                     {
13313                         gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13314                         dd = hp->dynamic_data_of (0);
13315                         ptrdiff_t size = dd_new_allocation (dd);
13316                         if (hp == acontext->get_home_heap()->pGenGCHeap)
13317                             size = size + delta;
13318                         int hp_alloc_context_count = hp->alloc_context_count;
13319                         if (hp_alloc_context_count > 0)
13320                             size /= (hp_alloc_context_count + 1);
13321                         if (size > max_size)
13322                         {
13323                             max_hp = hp;
13324                             max_size = size;
13325                             max_alloc_context_count = hp_alloc_context_count;
13326                         }
13327                     }
13328                 }
13329                 while (org_alloc_context_count != org_hp->alloc_context_count ||
13330                        max_alloc_context_count != max_hp->alloc_context_count);
13331
13332                 if ((max_hp == org_hp) && (end < finish))
13333                 {   
13334                     start = end; end = finish; 
13335                     delta = dd_min_size(dd)/2; // Make it twice as hard to balance to remote nodes on NUMA.
13336                     goto try_again;
13337                 }
13338
13339                 if (max_hp != org_hp)
13340                 {
13341                     org_hp->alloc_context_count--;
13342                     max_hp->alloc_context_count++;
13343                     acontext->set_alloc_heap(GCHeap::GetHeap(max_hp->heap_number));
13344                     if (GCToOSInterface::CanEnableGCCPUGroups())
13345                     {   //only set ideal processor when max_hp and org_hp are in the same cpu
13346                         //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
13347                         uint16_t org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
13348                         uint16_t max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
13349                         if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
13350                         {   
13351                             uint16_t group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
13352
13353                             GCThreadAffinity affinity;
13354                             affinity.Processor = group_proc_no;
13355                             affinity.Group = org_gn;
13356                             if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13357                             {
13358                                 dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
13359                                             org_hp->heap_number));
13360                             }
13361                         }
13362                     }
13363                     else 
13364                     {
13365                         uint16_t proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
13366
13367                         GCThreadAffinity affinity;
13368                         affinity.Processor = proc_no;
13369                         affinity.Group = GCThreadAffinity::None;
13370
13371                         if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13372                         {
13373                             dprintf (3, ("Failed to set the ideal processor for heap %d.",
13374                                         org_hp->heap_number));
13375                         }
13376                     }
13377                     dprintf (3, ("Switching context %p (home heap %d) ", 
13378                                  acontext,
13379                         acontext->get_home_heap()->pGenGCHeap->heap_number));
13380                     dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ", 
13381                                  org_hp->heap_number,
13382                                  org_size,
13383                                  org_alloc_context_count));
13384                     dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n", 
13385                                  max_hp->heap_number,
13386                                  dd_new_allocation(max_hp->dynamic_data_of(0)),
13387                                                    max_alloc_context_count));
13388                 }
13389             }
13390         }
13391     }
13392     acontext->alloc_count++;
13393 }
13394
13395 gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t /*size*/)
13396 {
13397     gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13398     //dprintf (1, ("LA: %Id", size));
13399
13400     //if (size > 128*1024)
13401     if (1)
13402     {
13403         dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
13404
13405         ptrdiff_t org_size = dd_new_allocation (dd);
13406         gc_heap* max_hp;
13407         ptrdiff_t max_size;
13408         size_t delta = dd_min_size (dd) * 4;
13409
13410         int start, end, finish;
13411         heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13412         finish = start + n_heaps;
13413
13414 try_again:
13415         {
13416             max_hp = org_hp;
13417             max_size = org_size + delta;
13418             dprintf (3, ("orig hp: %d, max size: %d",
13419                 org_hp->heap_number,
13420                 max_size));
13421
13422             for (int i = start; i < end; i++)
13423             {
13424                 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13425                 dd = hp->dynamic_data_of (max_generation + 1);
13426                 ptrdiff_t size = dd_new_allocation (dd);
13427                 dprintf (3, ("hp: %d, size: %d",
13428                     hp->heap_number,
13429                     size));
13430                 if (size > max_size)
13431                 {
13432                     max_hp = hp;
13433                     max_size = size;
13434                     dprintf (3, ("max hp: %d, max size: %d",
13435                         max_hp->heap_number,
13436                         max_size));
13437                 }
13438             }
13439         }
13440
13441         if ((max_hp == org_hp) && (end < finish))
13442         {
13443             start = end; end = finish;
13444             delta = dd_min_size(dd) * 4;   // Need to tuning delta
13445             goto try_again;
13446         }
13447
13448         if (max_hp != org_hp)
13449         {
13450             dprintf (3, ("loh: %d(%Id)->%d(%Id)", 
13451                 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
13452                 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
13453         }
13454
13455         return max_hp;
13456     }
13457     else
13458     {
13459         return org_hp;
13460     }
13461 }
13462 #endif //MULTIPLE_HEAPS
13463
13464 BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
13465                                   int alloc_generation_number)
13466 {
13467     int status;
13468     do
13469     { 
13470 #ifdef MULTIPLE_HEAPS
13471         if (alloc_generation_number == 0)
13472         {
13473             balance_heaps (acontext);
13474             status = acontext->get_alloc_heap()->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
13475         }
13476         else
13477         {
13478             gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
13479             status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
13480         }
13481 #else
13482         status = try_allocate_more_space (acontext, size, alloc_generation_number);
13483 #endif //MULTIPLE_HEAPS
13484     }
13485     while (status == -1);
13486     
13487     return (status != 0);
13488 }
13489
13490 inline
13491 CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
13492 {
13493     size_t size = Align (jsize);
13494     assert (size >= Align (min_obj_size));
13495     {
13496     retry:
13497         uint8_t*  result = acontext->alloc_ptr;
13498         acontext->alloc_ptr+=size;
13499         if (acontext->alloc_ptr <= acontext->alloc_limit)
13500         {
13501             CObjectHeader* obj = (CObjectHeader*)result;
13502             assert (obj != 0);
13503             return obj;
13504         }
13505         else
13506         {
13507             acontext->alloc_ptr -= size;
13508
13509 #ifdef _MSC_VER
13510 #pragma inline_depth(0)
13511 #endif //_MSC_VER
13512
13513             if (! allocate_more_space (acontext, size, 0))
13514                 return 0;
13515
13516 #ifdef _MSC_VER
13517 #pragma inline_depth(20)
13518 #endif //_MSC_VER
13519
13520             goto retry;
13521         }
13522     }
13523 }
13524
13525 inline
13526 CObjectHeader* gc_heap::try_fast_alloc (size_t jsize)
13527 {
13528     size_t size = Align (jsize);
13529     assert (size >= Align (min_obj_size));
13530     generation* gen = generation_of (0);
13531     uint8_t*  result = generation_allocation_pointer (gen);
13532     generation_allocation_pointer (gen) += size;
13533     if (generation_allocation_pointer (gen) <=
13534         generation_allocation_limit (gen))
13535     {
13536         return (CObjectHeader*)result;
13537     }
13538     else
13539     {
13540         generation_allocation_pointer (gen) -= size;
13541         return 0;
13542     }
13543 }
13544 void  gc_heap::leave_allocation_segment (generation* gen)
13545 {
13546     adjust_limit (0, 0, gen, max_generation);
13547 }
13548
13549 void gc_heap::init_free_and_plug()
13550 {
13551 #ifdef FREE_USAGE_STATS
13552     for (int i = 0; i <= settings.condemned_generation; i++)
13553     {
13554         generation* gen = generation_of (i);
13555         memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
13556         memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13557         memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
13558     }
13559
13560     if (settings.condemned_generation != max_generation)
13561     {
13562         for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
13563         {
13564             generation* gen = generation_of (i);
13565             memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13566         }
13567     }
13568 #endif //FREE_USAGE_STATS
13569 }
13570
13571 void gc_heap::print_free_and_plug (const char* msg)
13572 {
13573 #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
13574     int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
13575     for (int i = 0; i <= older_gen; i++)
13576     {
13577         generation* gen = generation_of (i);
13578         for (int j = 0; j < NUM_GEN_POWER2; j++)
13579         {
13580             if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
13581             {
13582                 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id", 
13583                     msg, 
13584                     heap_number, 
13585                     (settings.concurrent ? "BGC" : "GC"),
13586                     settings.gc_index,
13587                     i,
13588                     (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
13589             }
13590         }
13591     }
13592 #else
13593     UNREFERENCED_PARAMETER(msg);
13594 #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
13595 }
13596
13597 void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
13598 {
13599 #ifdef FREE_USAGE_STATS
13600     dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
13601     generation* gen = generation_of (gen_number);
13602     size_t sz = BASE_GEN_SIZE;
13603     int i = 0;
13604
13605     for (; i < NUM_GEN_POWER2; i++)
13606     {
13607         if (plug_size < sz)
13608         {
13609             break;
13610         }
13611         sz = sz * 2;
13612     }
13613     
13614     (gen->gen_plugs[i])++;
13615 #else
13616     UNREFERENCED_PARAMETER(gen_number);
13617     UNREFERENCED_PARAMETER(plug_size);
13618 #endif //FREE_USAGE_STATS
13619 }
13620
13621 void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
13622 {
13623 #ifdef FREE_USAGE_STATS
13624     generation* gen = generation_of (gen_number);
13625     size_t sz = BASE_GEN_SIZE;
13626     int i = 0;
13627
13628     for (; i < NUM_GEN_POWER2; i++)
13629     {
13630         if (free_size < sz)
13631         {
13632             break;
13633         }
13634         sz = sz * 2;
13635     }
13636     
13637     (gen->gen_current_pinned_free_spaces[i])++;
13638     generation_pinned_free_obj_space (gen) += free_size;
13639     dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)", 
13640         free_size, (i + 10), gen_number, 
13641         generation_pinned_free_obj_space (gen),
13642         gen->gen_current_pinned_free_spaces[i]));
13643 #else
13644     UNREFERENCED_PARAMETER(gen_number);
13645     UNREFERENCED_PARAMETER(free_size);
13646 #endif //FREE_USAGE_STATS
13647 }
13648
13649 void gc_heap::add_gen_free (int gen_number, size_t free_size)
13650 {
13651 #ifdef FREE_USAGE_STATS
13652     dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13653     generation* gen = generation_of (gen_number);
13654     size_t sz = BASE_GEN_SIZE;
13655     int i = 0;
13656
13657     for (; i < NUM_GEN_POWER2; i++)
13658     {
13659         if (free_size < sz)
13660         {
13661             break;
13662         }
13663         sz = sz * 2;
13664     }
13665     
13666     (gen->gen_free_spaces[i])++;
13667 #else
13668     UNREFERENCED_PARAMETER(gen_number);
13669     UNREFERENCED_PARAMETER(free_size);
13670 #endif //FREE_USAGE_STATS
13671 }
13672
13673 void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13674 {
13675 #ifdef FREE_USAGE_STATS
13676     dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13677     generation* gen = generation_of (gen_number);
13678     size_t sz = BASE_GEN_SIZE;
13679     int i = 0;
13680
13681     for (; i < NUM_GEN_POWER2; i++)
13682     {
13683         if (free_size < sz)
13684         {
13685             break;
13686         }
13687         sz = sz * 2;
13688     }
13689     
13690     (gen->gen_free_spaces[i])--;
13691 #else
13692     UNREFERENCED_PARAMETER(gen_number);
13693     UNREFERENCED_PARAMETER(free_size);
13694 #endif //FREE_USAGE_STATS
13695 }
13696
13697 uint8_t* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13698                                              int from_gen_number,
13699                                              uint8_t* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13700 {
13701     size = Align (size);
13702     assert (size >= Align (min_obj_size));
13703     assert (from_gen_number < max_generation);
13704     assert (from_gen_number >= 0);
13705     assert (generation_of (from_gen_number + 1) == gen);
13706
13707     allocator* gen_allocator = generation_allocator (gen);
13708     BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13709     int pad_in_front = (old_loc != 0)? USE_PADDING_FRONT : 0;
13710
13711     size_t real_size = size + Align (min_obj_size);
13712     if (pad_in_front)
13713         real_size += Align (min_obj_size);
13714
13715     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13716                        generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13717     {
13718         size_t sz_list = gen_allocator->first_bucket_size();
13719         for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13720         {
13721             if ((real_size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13722             {
13723                 uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13724                 uint8_t* prev_free_item = 0;
13725                 while (free_list != 0)
13726                 {
13727                     dprintf (3, ("considering free list %Ix", (size_t)free_list));
13728
13729                     size_t free_list_size = unused_array_size (free_list);
13730
13731                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
13732                                     old_loc, USE_PADDING_TAIL | pad_in_front))
13733                     {
13734                         dprintf (4, ("F:%Ix-%Id",
13735                                      (size_t)free_list, free_list_size));
13736
13737                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
13738                         generation_free_list_space (gen) -= free_list_size;
13739                         remove_gen_free (gen->gen_num, free_list_size);
13740
13741                         adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
13742                         goto finished;
13743                     }
13744                     // We do first fit on bucket 0 because we are not guaranteed to find a fit there.
13745                     else if (discard_p || (a_l_idx == 0))
13746                     {
13747                         dprintf (3, ("couldn't use this free area, discarding"));
13748                         generation_free_obj_space (gen) += free_list_size;
13749
13750                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
13751                         generation_free_list_space (gen) -= free_list_size;
13752                         remove_gen_free (gen->gen_num, free_list_size);
13753                     }
13754                     else
13755                     {
13756                         prev_free_item = free_list;
13757                     }
13758                     free_list = free_list_slot (free_list); 
13759                 }
13760             }
13761             sz_list = sz_list * 2;
13762         }
13763         //go back to the beginning of the segment list 
13764         generation_allocate_end_seg_p (gen) = TRUE;
13765         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
13766         if (seg != generation_allocation_segment (gen))
13767         {
13768             leave_allocation_segment (gen);
13769             generation_allocation_segment (gen) = seg;
13770         }
13771         while (seg != ephemeral_heap_segment)
13772         {
13773             if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13774                            heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
13775             {
13776                 dprintf (3, ("using what's left in committed"));
13777                 adjust_limit (heap_segment_plan_allocated (seg),
13778                               heap_segment_committed (seg) -
13779                               heap_segment_plan_allocated (seg),
13780                               gen, from_gen_number+1);
13781                 // dformat (t, 3, "Expanding segment allocation");
13782                 heap_segment_plan_allocated (seg) =
13783                     heap_segment_committed (seg);
13784                 goto finished;
13785             }
13786             else
13787             {
13788                 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13789                                 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13790                     grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
13791                 {
13792                     dprintf (3, ("using what's left in reserved"));
13793                     adjust_limit (heap_segment_plan_allocated (seg),
13794                                   heap_segment_committed (seg) -
13795                                   heap_segment_plan_allocated (seg),
13796                                   gen, from_gen_number+1);
13797                     heap_segment_plan_allocated (seg) =
13798                         heap_segment_committed (seg);
13799
13800                     goto finished;
13801                 }
13802                 else
13803                 {
13804                     leave_allocation_segment (gen);
13805                     heap_segment*   next_seg = heap_segment_next_rw (seg);
13806                     if (next_seg)
13807                     {
13808                         dprintf (3, ("getting next segment"));
13809                         generation_allocation_segment (gen) = next_seg;
13810                         generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13811                         generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13812                     }
13813                     else
13814                     {
13815                         size = 0;
13816                         goto finished;
13817                     }
13818                 }
13819             }
13820             seg = generation_allocation_segment (gen);
13821         }
13822         //No need to fix the last region. Will be done later
13823         size = 0;
13824         goto finished;
13825     }
13826     finished:
13827     if (0 == size)
13828     {
13829         return 0;
13830     }
13831     else
13832     {
13833         uint8_t*  result = generation_allocation_pointer (gen);
13834         size_t pad = 0;
13835
13836 #ifdef SHORT_PLUGS
13837         if ((pad_in_front & USE_PADDING_FRONT) &&
13838             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13839              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13840         {
13841             pad = Align (min_obj_size);
13842             set_plug_padded (old_loc);
13843         }
13844 #endif //SHORT_PLUGS
13845
13846 #ifdef FEATURE_STRUCTALIGN
13847         _ASSERTE(!old_loc || alignmentOffset != 0);
13848         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13849         if (old_loc != 0)
13850         {
13851             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13852             set_node_aligninfo (old_loc, requiredAlignment, pad1);
13853             pad += pad1;
13854         }
13855 #else // FEATURE_STRUCTALIGN
13856         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13857         {
13858             pad += switch_alignment_size (is_plug_padded (old_loc));
13859             set_node_realigned (old_loc);
13860             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13861                          (size_t)old_loc, (size_t)(result+pad)));
13862             assert (same_large_alignment_p (result + pad, old_loc));
13863         }
13864 #endif // FEATURE_STRUCTALIGN
13865         dprintf (3, ("Allocate %Id bytes", size));
13866
13867         if ((old_loc == 0) || (pad != 0))
13868         {
13869             //allocating a non plug or a gap, so reset the start region
13870             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13871         }
13872
13873         generation_allocation_pointer (gen) += size + pad;
13874         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13875         if (generation_allocate_end_seg_p (gen))
13876         {
13877             generation_end_seg_allocated (gen) += size;
13878         }
13879         else
13880         {
13881             generation_free_list_allocated (gen) += size;
13882         }
13883         generation_allocation_size (gen) += size;
13884
13885         dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix", 
13886             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13887             generation_allocation_context_start_region (gen)));
13888
13889         return result + pad;;
13890     }
13891 }
13892
13893 void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
13894 {
13895     //make sure that every generation has a planned allocation start
13896     int  gen_number = max_generation - 1;
13897     while (gen_number>= 0)
13898     {
13899         generation* gen = generation_of (gen_number);
13900         if (0 == generation_plan_allocation_start (gen))
13901         {
13902             realloc_plan_generation_start (gen, consing_gen);
13903
13904             assert (generation_plan_allocation_start (gen));
13905         }
13906         gen_number--;
13907     }
13908
13909     // now we know the planned allocation size
13910     size_t  size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
13911     heap_segment* seg = generation_allocation_segment (consing_gen);
13912     if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
13913     {
13914         if (size != 0)
13915         {
13916             heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
13917         }
13918     }
13919     else
13920     {
13921         assert (settings.condemned_generation == max_generation);
13922         uint8_t* first_address = generation_allocation_limit (consing_gen);
13923         //look through the pinned plugs for relevant ones.
13924         //Look for the right pinned plug to start from.
13925         size_t mi = 0;
13926         mark* m = 0;
13927         while (mi != mark_stack_tos)
13928         {
13929             m = pinned_plug_of (mi);
13930             if ((pinned_plug (m) == first_address))
13931                 break;
13932             else
13933                 mi++;
13934         }
13935         assert (mi != mark_stack_tos);
13936         pinned_len (m) = size;
13937     }
13938 }
13939
13940 //tododefrag optimize for new segment (plan_allocated == mem)
13941 uint8_t* gc_heap::allocate_in_expanded_heap (generation* gen,
13942                                           size_t size,
13943                                           BOOL& adjacentp,
13944                                           uint8_t* old_loc,
13945 #ifdef SHORT_PLUGS
13946                                           BOOL set_padding_on_saved_p,
13947                                           mark* pinned_plug_entry,
13948 #endif //SHORT_PLUGS
13949                                           BOOL consider_bestfit,
13950                                           int active_new_gen_number
13951                                           REQD_ALIGN_AND_OFFSET_DCL)
13952 {
13953     UNREFERENCED_PARAMETER(active_new_gen_number);
13954     dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
13955
13956     size = Align (size);
13957     assert (size >= Align (min_obj_size));
13958     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
13959
13960     if (consider_bestfit && use_bestfit)
13961     {
13962         assert (bestfit_seg);
13963         dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id", 
13964                     old_loc, size));
13965         return bestfit_seg->fit (old_loc, 
13966 #ifdef SHORT_PLUGS
13967                                  set_padding_on_saved_p,
13968                                  pinned_plug_entry,
13969 #endif //SHORT_PLUGS
13970                                  size REQD_ALIGN_AND_OFFSET_ARG);
13971     }
13972
13973     heap_segment* seg = generation_allocation_segment (gen);
13974
13975     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13976                        generation_allocation_limit (gen), old_loc,
13977                        ((generation_allocation_limit (gen) !=
13978                           heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
13979     {
13980         dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
13981             generation_allocation_limit (gen)));
13982
13983         adjacentp = FALSE;
13984         uint8_t* first_address = (generation_allocation_limit (gen) ?
13985                                generation_allocation_limit (gen) :
13986                                heap_segment_mem (seg));
13987         assert (in_range_for_segment (first_address, seg));
13988
13989         uint8_t* end_address   = heap_segment_reserved (seg);
13990
13991         dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
13992             first_address, generation_allocation_limit (gen), end_address));
13993
13994         size_t mi = 0;
13995         mark* m = 0;
13996
13997         if (heap_segment_allocated (seg) != heap_segment_mem (seg))
13998         {
13999             assert (settings.condemned_generation == max_generation);
14000             //look through the pinned plugs for relevant ones.
14001             //Look for the right pinned plug to start from.
14002             while (mi != mark_stack_tos)
14003             {
14004                 m = pinned_plug_of (mi);
14005                 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
14006                 {
14007                     dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
14008                     break;
14009                 }
14010                 else
14011                     mi++;
14012             }
14013             if (mi != mark_stack_tos)
14014             {
14015                 //fix old free list.
14016                 size_t  hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
14017                 {
14018                     dprintf(3,("gc filling up hole"));
14019                     ptrdiff_t mi1 = (ptrdiff_t)mi;
14020                     while ((mi1 >= 0) &&
14021                            (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
14022                     {
14023                         dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
14024                         mi1--;
14025                     }
14026                     if (mi1 >= 0)
14027                     {
14028                         size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
14029                         pinned_len (pinned_plug_of(mi1)) = hsize;
14030                         dprintf (3, ("changing %Ix len %Ix->%Ix", 
14031                             pinned_plug (pinned_plug_of(mi1)), 
14032                             saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
14033                     }
14034                 }
14035             }
14036         }
14037         else
14038         {
14039             assert (generation_allocation_limit (gen) ==
14040                     generation_allocation_pointer (gen));
14041             mi = mark_stack_tos;
14042         }
14043
14044         while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
14045         {
14046             size_t len = pinned_len (m);
14047             uint8_t*  free_list = (pinned_plug (m) - len);
14048             dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)", 
14049                 free_list, (free_list + len), len));
14050             if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
14051             {
14052                 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
14053                             (size_t)free_list, len));
14054                 {
14055                     generation_allocation_pointer (gen) = free_list;
14056                     generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14057                     generation_allocation_limit (gen) = (free_list + len);
14058                 }
14059                 goto allocate_in_free;
14060             }
14061             mi++;
14062             m = pinned_plug_of (mi);
14063         }
14064
14065         //switch to the end of the segment.
14066         generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
14067         generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14068         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14069         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14070         dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)", 
14071             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14072             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
14073
14074         if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14075                          generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
14076         {
14077             dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
14078                 generation_allocation_limit (gen)));
14079             assert (!"Can't allocate if no free space");
14080             return 0;
14081         }
14082     }
14083     else
14084     {
14085         adjacentp = TRUE;
14086     }
14087
14088 allocate_in_free:
14089     {
14090         uint8_t*  result = generation_allocation_pointer (gen);
14091         size_t pad = 0;
14092
14093 #ifdef SHORT_PLUGS
14094         if ((pad_in_front & USE_PADDING_FRONT) &&
14095             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14096              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14097
14098         {
14099             pad = Align (min_obj_size);
14100             set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
14101         }
14102 #endif //SHORT_PLUGS
14103
14104 #ifdef FEATURE_STRUCTALIGN
14105         _ASSERTE(!old_loc || alignmentOffset != 0);
14106         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14107         if (old_loc != 0)
14108         {
14109             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14110             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14111             pad += pad1;
14112             adjacentp = FALSE;
14113         }
14114 #else // FEATURE_STRUCTALIGN
14115         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14116         {
14117             pad += switch_alignment_size (is_plug_padded (old_loc));
14118             set_node_realigned (old_loc);
14119             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14120                          (size_t)old_loc, (size_t)(result+pad)));
14121             assert (same_large_alignment_p (result + pad, old_loc));
14122             adjacentp = FALSE;
14123         }
14124 #endif // FEATURE_STRUCTALIGN
14125
14126         if ((old_loc == 0) || (pad != 0))
14127         {
14128             //allocating a non plug or a gap, so reset the start region
14129             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14130         }
14131
14132         generation_allocation_pointer (gen) += size + pad;
14133         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14134         dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
14135
14136         dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix", 
14137             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14138             generation_allocation_context_start_region (gen)));
14139
14140         return result + pad;
14141     }
14142 }
14143
14144 generation*  gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
14145 {
14146     heap_segment* seg = generation_allocation_segment (consing_gen);
14147     if (seg != ephemeral_heap_segment)
14148     {
14149         assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
14150         assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
14151
14152         //fix the allocated size of the segment.
14153         heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14154
14155         generation* new_consing_gen = generation_of (max_generation - 1);
14156         generation_allocation_pointer (new_consing_gen) =
14157                 heap_segment_mem (ephemeral_heap_segment);
14158         generation_allocation_limit (new_consing_gen) =
14159             generation_allocation_pointer (new_consing_gen);
14160         generation_allocation_context_start_region (new_consing_gen) = 
14161             generation_allocation_pointer (new_consing_gen);
14162         generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
14163
14164         return new_consing_gen;
14165     }
14166     else
14167         return consing_gen;
14168 }
14169
14170 uint8_t* gc_heap::allocate_in_condemned_generations (generation* gen,
14171                                                   size_t size,
14172                                                   int from_gen_number,
14173 #ifdef SHORT_PLUGS
14174                                                   BOOL* convert_to_pinned_p,
14175                                                   uint8_t* next_pinned_plug,
14176                                                   heap_segment* current_seg,
14177 #endif //SHORT_PLUGS
14178                                                   uint8_t* old_loc
14179                                                   REQD_ALIGN_AND_OFFSET_DCL)
14180 {
14181     // Make sure that the youngest generation gap hasn't been allocated
14182     if (settings.promotion)
14183     {
14184         assert (generation_plan_allocation_start (youngest_generation) == 0);
14185     }
14186
14187     size = Align (size);
14188     assert (size >= Align (min_obj_size));
14189     int to_gen_number = from_gen_number;
14190     if (from_gen_number != (int)max_generation)
14191     {
14192         to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
14193     }
14194
14195     dprintf (3, ("aic gen%d: s: %Id, %d->%d, %Ix->%Ix", gen->gen_num, size, from_gen_number, 
14196           to_gen_number, generation_allocation_pointer(gen), generation_allocation_limit(gen)));
14197
14198     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
14199
14200     if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
14201     {
14202         generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14203         generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14204     }
14205 retry:
14206     {
14207         heap_segment* seg = generation_allocation_segment (gen);
14208         if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14209                            generation_allocation_limit (gen), old_loc,
14210                            ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
14211         {
14212             if ((! (pinned_plug_que_empty_p()) &&
14213                  (generation_allocation_limit (gen) ==
14214                   pinned_plug (oldest_pin()))))
14215             {
14216                 size_t entry = deque_pinned_plug();
14217                 mark* pinned_plug_entry = pinned_plug_of (entry);
14218                 size_t len = pinned_len (pinned_plug_entry);
14219                 uint8_t* plug = pinned_plug (pinned_plug_entry);
14220                 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
14221
14222 #ifdef FREE_USAGE_STATS
14223                 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
14224                 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id", 
14225                     generation_allocated_since_last_pin (gen), 
14226                     plug,
14227                     generation_allocated_in_pinned_free (gen)));
14228                 generation_allocated_since_last_pin (gen) = 0;
14229
14230                 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
14231 #endif //FREE_USAGE_STATS
14232
14233                 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix", 
14234                     mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
14235
14236                 assert(mark_stack_array[entry].len == 0 ||
14237                        mark_stack_array[entry].len >= Align(min_obj_size));
14238                 generation_allocation_pointer (gen) = plug + len;
14239                 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14240                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14241                 set_allocator_next_pin (gen);
14242
14243                 //Add the size of the pinned plug to the right pinned allocations
14244                 //find out which gen this pinned plug came from 
14245                 int frgn = object_gennum (plug);
14246                 if ((frgn != (int)max_generation) && settings.promotion)
14247                 {
14248                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
14249                     int togn = object_gennum_plan (plug);
14250                     if (frgn < togn)
14251                     {
14252                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
14253                     }
14254                 }
14255                 goto retry;
14256             }
14257             
14258             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
14259             {
14260                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14261                 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
14262             }
14263             else
14264             {
14265                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
14266                 {
14267                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14268                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14269                     dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
14270                 }
14271                 else
14272                 {
14273 #ifndef RESPECT_LARGE_ALIGNMENT
14274                     assert (gen != youngest_generation);
14275 #endif //RESPECT_LARGE_ALIGNMENT
14276
14277                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14278                                     heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14279                         (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
14280                                             size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
14281                     {
14282                         dprintf (3, ("Expanded segment allocation by committing more memory"));
14283                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14284                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14285                     }
14286                     else
14287                     {
14288                         heap_segment*   next_seg = heap_segment_next (seg);
14289                         assert (generation_allocation_pointer (gen)>=
14290                                 heap_segment_mem (seg));
14291                         // Verify that all pinned plugs for this segment are consumed
14292                         if (!pinned_plug_que_empty_p() &&
14293                             ((pinned_plug (oldest_pin()) <
14294                               heap_segment_allocated (seg)) &&
14295                              (pinned_plug (oldest_pin()) >=
14296                               generation_allocation_pointer (gen))))
14297                         {
14298                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
14299                                          pinned_plug (oldest_pin())));
14300                             FATAL_GC_ERROR();
14301                         }
14302                         assert (generation_allocation_pointer (gen)>=
14303                                 heap_segment_mem (seg));
14304                         assert (generation_allocation_pointer (gen)<=
14305                                 heap_segment_committed (seg));
14306                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
14307
14308                         if (next_seg)
14309                         {
14310                             generation_allocation_segment (gen) = next_seg;
14311                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14312                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14313                             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14314                         }
14315                         else
14316                         {
14317                             return 0; //should only happen during allocation of generation 0 gap
14318                             // in that case we are going to grow the heap anyway
14319                         }
14320                     }
14321                 }
14322             }
14323             set_allocator_next_pin (gen);
14324
14325             goto retry;
14326         }
14327     }
14328
14329     {
14330         assert (generation_allocation_pointer (gen)>=
14331                 heap_segment_mem (generation_allocation_segment (gen)));
14332         uint8_t* result = generation_allocation_pointer (gen);
14333         size_t pad = 0;
14334 #ifdef SHORT_PLUGS
14335         if ((pad_in_front & USE_PADDING_FRONT) &&
14336             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14337              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14338         {
14339             ptrdiff_t dist = old_loc - result;
14340             if (dist == 0)
14341             {
14342                 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
14343                 pad = 0;
14344             }
14345             else
14346             {
14347                 if ((dist > 0) && (dist < (ptrdiff_t)Align (min_obj_size)))
14348                 {
14349                     dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
14350                     FATAL_GC_ERROR();
14351                 }
14352
14353                 pad = Align (min_obj_size);
14354                 set_plug_padded (old_loc);
14355             }
14356         }
14357 #endif //SHORT_PLUGS
14358 #ifdef FEATURE_STRUCTALIGN
14359         _ASSERTE(!old_loc || alignmentOffset != 0);
14360         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14361         if ((old_loc != 0))
14362         {
14363             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14364             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14365             pad += pad1;
14366         }
14367 #else // FEATURE_STRUCTALIGN
14368         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14369         {
14370             pad += switch_alignment_size (is_plug_padded (old_loc));
14371             set_node_realigned(old_loc);
14372             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14373                          (size_t)old_loc, (size_t)(result+pad)));
14374             assert (same_large_alignment_p (result + pad, old_loc));
14375         }
14376 #endif // FEATURE_STRUCTALIGN
14377
14378 #ifdef SHORT_PLUGS
14379         if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
14380         {
14381             assert (old_loc != 0);
14382             ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
14383             assert (dist_to_next_pin >= 0);
14384
14385             if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
14386             {
14387                 dprintf (3, ("%Ix->(%Ix,%Ix),%Ix(%Ix)(%Ix),NP->PP", 
14388                     old_loc, 
14389                     generation_allocation_pointer (gen),
14390                     generation_allocation_limit (gen),
14391                     next_pinned_plug,
14392                     size, 
14393                     dist_to_next_pin));
14394                 clear_plug_padded (old_loc);
14395                 pad = 0;
14396                 *convert_to_pinned_p = TRUE;
14397                 record_interesting_data_point (idp_converted_pin);
14398
14399                 return 0;
14400             }
14401         }
14402 #endif //SHORT_PLUGS
14403
14404         if ((old_loc == 0) || (pad != 0))
14405         {
14406             //allocating a non plug or a gap, so reset the start region
14407             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14408         }
14409
14410         generation_allocation_pointer (gen) += size + pad;
14411         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14412
14413 #ifdef FREE_USAGE_STATS
14414         generation_allocated_since_last_pin (gen) += size;
14415 #endif //FREE_USAGE_STATS
14416
14417         dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix", 
14418             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14419             generation_allocation_context_start_region (gen)));
14420
14421         assert (result + pad);
14422         return result + pad;
14423     }
14424 }
14425
14426 inline int power (int x, int y)
14427 {
14428     int z = 1;
14429     for (int i = 0; i < y; i++)
14430     {
14431         z = z*x;
14432     }
14433     return z;
14434 }
14435
14436 int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation, 
14437                                            int n_initial,
14438                                            BOOL* blocking_collection_p
14439                                            STRESS_HEAP_ARG(int n_original))
14440 {
14441     int n = n_initial;
14442 #ifdef MULTIPLE_HEAPS
14443     BOOL blocking_p = *blocking_collection_p;
14444     if (!blocking_p)
14445     {
14446         for (int i = 0; i < n_heaps; i++)
14447         {
14448             if (g_heaps[i]->last_gc_before_oom)
14449             {
14450                 dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
14451                 *blocking_collection_p = TRUE;
14452                 break;
14453             }
14454         }
14455     }
14456 #endif //MULTIPLE_HEAPS
14457
14458     if (should_evaluate_elevation && (n == max_generation))
14459     {
14460         dprintf (GTC_LOG, ("lock: %d(%d)", 
14461             (settings.should_lock_elevation ? 1 : 0), 
14462             settings.elevation_locked_count));
14463
14464         if (settings.should_lock_elevation)
14465         {
14466             settings.elevation_locked_count++;
14467             if (settings.elevation_locked_count == 6)
14468             {
14469                 settings.elevation_locked_count = 0;
14470             }
14471             else
14472             {
14473                 n = max_generation - 1;
14474                 settings.elevation_reduced = TRUE;
14475             }
14476         }
14477         else
14478         {
14479             settings.elevation_locked_count = 0;
14480         }
14481     }
14482     else
14483     {
14484         settings.should_lock_elevation = FALSE;
14485         settings.elevation_locked_count = 0;
14486     }
14487
14488 #ifdef STRESS_HEAP
14489 #ifdef BACKGROUND_GC
14490     // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14491     // generations to be collected,
14492
14493     // [LOCALGC TODO] STRESS_HEAP is not defined for a standalone GC so there are multiple
14494     // things that need to be fixed in this code block.
14495     if (n_original != max_generation &&
14496         g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
14497     {
14498 #ifndef FEATURE_REDHAWK
14499         // for the GC stress mix mode throttle down gen2 collections
14500         if (g_pConfig->IsGCStressMix())
14501         {
14502             size_t current_gc_count = 0;
14503
14504 #ifdef MULTIPLE_HEAPS
14505             current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
14506 #else
14507             current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
14508 #endif //MULTIPLE_HEAPS
14509             // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
14510             if ((current_gc_count % 10) == 0)
14511             {
14512                 n = max_generation;
14513             }
14514         }
14515         // for traditional GC stress
14516         else
14517 #endif // !FEATURE_REDHAWK
14518         if (*blocking_collection_p)
14519         {
14520             // We call StressHeap() a lot for Concurrent GC Stress. However,
14521             // if we can not do a concurrent collection, no need to stress anymore.
14522             // @TODO: Enable stress when the memory pressure goes down again
14523             GCStressPolicy::GlobalDisable();
14524         }
14525         else
14526         {
14527             n = max_generation;
14528         }
14529     }
14530 #endif //BACKGROUND_GC
14531 #endif //STRESS_HEAP
14532
14533     return n;
14534 }
14535
14536 inline
14537 size_t get_survived_size (gc_history_per_heap* hist)
14538 {
14539     size_t surv_size = 0;
14540     gc_generation_data* gen_data;
14541
14542     for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14543     {
14544         gen_data = &(hist->gen_data[gen_number]); 
14545         surv_size += (gen_data->size_after - 
14546                       gen_data->free_list_space_after - 
14547                       gen_data->free_obj_space_after);
14548     }
14549
14550     return surv_size;
14551 }
14552
14553 size_t gc_heap::get_total_survived_size()
14554 {
14555     size_t total_surv_size = 0;
14556 #ifdef MULTIPLE_HEAPS
14557     for (int i = 0; i < gc_heap::n_heaps; i++)
14558     {
14559         gc_heap* hp = gc_heap::g_heaps[i];
14560         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
14561         total_surv_size += get_survived_size (current_gc_data_per_heap);
14562     }
14563 #else
14564     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14565     total_surv_size = get_survived_size (current_gc_data_per_heap);
14566 #endif //MULTIPLE_HEAPS
14567     return total_surv_size;
14568 }
14569
14570 // Gets what's allocated on both SOH and LOH that hasn't been collected.
14571 size_t gc_heap::get_current_allocated()
14572 {
14573     dynamic_data* dd = dynamic_data_of (0);
14574     size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
14575     dd = dynamic_data_of (max_generation + 1);
14576     current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
14577
14578     return current_alloc;
14579 }
14580
14581 size_t gc_heap::get_total_allocated()
14582 {
14583     size_t total_current_allocated = 0;
14584 #ifdef MULTIPLE_HEAPS
14585     for (int i = 0; i < gc_heap::n_heaps; i++)
14586     {
14587         gc_heap* hp = gc_heap::g_heaps[i];
14588         total_current_allocated += hp->get_current_allocated();
14589     }
14590 #else
14591     total_current_allocated = get_current_allocated();
14592 #endif //MULTIPLE_HEAPS
14593     return total_current_allocated;
14594 }
14595
14596 size_t gc_heap::current_generation_size (int gen_number)
14597 {
14598     dynamic_data* dd = dynamic_data_of (gen_number);
14599     size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
14600                         - dd_new_allocation (dd));
14601
14602     return gen_size;
14603 }
14604
14605 #ifdef _PREFAST_
14606 #pragma warning(push)
14607 #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
14608 #endif //_PREFAST_
14609
14610 /*
14611     This is called by when we are actually doing a GC, or when we are just checking whether
14612     we would do a full blocking GC, in which case check_only_p is TRUE.
14613
14614     The difference between calling this with check_only_p TRUE and FALSE is that when it's
14615     TRUE: 
14616             settings.reason is ignored
14617             budgets are not checked (since they are checked before this is called)
14618             it doesn't change anything non local like generation_skip_ratio
14619 */
14620 int gc_heap::generation_to_condemn (int n_initial, 
14621                                     BOOL* blocking_collection_p, 
14622                                     BOOL* elevation_requested_p,
14623                                     BOOL check_only_p)
14624 {
14625     gc_mechanisms temp_settings = settings;
14626     gen_to_condemn_tuning temp_condemn_reasons;
14627     gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
14628     gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
14629     if (!check_only_p)
14630     {
14631         if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
14632         {
14633             assert (n_initial >= 1);
14634         }
14635
14636         assert (settings.reason != reason_empty);
14637     }
14638
14639     local_condemn_reasons->init();
14640
14641     int n = n_initial;
14642     int n_alloc = n;
14643     if (heap_number == 0)
14644     {
14645         dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
14646     }
14647     int i = 0;
14648     int temp_gen = 0;
14649     BOOL low_memory_detected = g_low_memory_status;
14650     uint32_t memory_load = 0;
14651     uint64_t available_physical = 0;
14652     uint64_t available_page_file = 0;
14653     BOOL check_memory = FALSE;
14654     BOOL high_fragmentation  = FALSE;
14655     BOOL v_high_memory_load  = FALSE;
14656     BOOL high_memory_load    = FALSE;
14657     BOOL low_ephemeral_space = FALSE;
14658     BOOL evaluate_elevation  = TRUE;
14659     *elevation_requested_p   = FALSE;
14660     *blocking_collection_p   = FALSE;
14661
14662     BOOL check_max_gen_alloc = TRUE;
14663
14664 #ifdef STRESS_HEAP
14665     int orig_gen = n;
14666 #endif //STRESS_HEAP
14667
14668     if (!check_only_p)
14669     {
14670         dd_fragmentation (dynamic_data_of (0)) = 
14671             generation_free_list_space (youngest_generation) + 
14672             generation_free_obj_space (youngest_generation);
14673
14674         dd_fragmentation (dynamic_data_of (max_generation + 1)) = 
14675             generation_free_list_space (large_object_generation) + 
14676             generation_free_obj_space (large_object_generation);
14677
14678         //save new_allocation
14679         for (i = 0; i <= max_generation+1; i++)
14680         {
14681             dynamic_data* dd = dynamic_data_of (i);
14682             dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)", 
14683                             heap_number, i,
14684                             dd_new_allocation (dd),
14685                             dd_desired_allocation (dd)));
14686             dd_gc_new_allocation (dd) = dd_new_allocation (dd);
14687         }
14688
14689         local_condemn_reasons->set_gen (gen_initial, n);
14690         temp_gen = n;
14691
14692 #ifdef BACKGROUND_GC
14693         if (recursive_gc_sync::background_running_p())
14694         {
14695             dprintf (GTC_LOG, ("bgc in prog, 1"));
14696             check_max_gen_alloc = FALSE;
14697         }
14698 #endif //BACKGROUND_GC
14699
14700         if (check_max_gen_alloc)
14701         {
14702             //figure out if large objects need to be collected.
14703             if (get_new_allocation (max_generation+1) <= 0)
14704             {
14705                 n = max_generation;
14706                 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14707             }
14708         }
14709
14710         //figure out which generation ran out of allocation
14711         for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
14712         {
14713             if (get_new_allocation (i) <= 0)
14714             {
14715                 n = i;
14716             }
14717             else
14718                 break;
14719         }
14720     }
14721
14722     if (n > temp_gen)
14723     {
14724         local_condemn_reasons->set_gen (gen_alloc_budget, n);
14725     }
14726
14727     dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
14728
14729     n_alloc = n;
14730
14731 #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
14732     //time based tuning
14733     // if enough time has elapsed since the last gc
14734     // and the number of gc is too low (1/10 of lower gen) then collect
14735     // This should also be enabled if we have memory concerns
14736     int n_time_max = max_generation;
14737
14738     if (!check_only_p)
14739     {
14740         if (recursive_gc_sync::background_running_p())
14741         {
14742             n_time_max = max_generation - 1;
14743         }
14744     }
14745
14746     if ((local_settings->pause_mode == pause_interactive) ||
14747         (local_settings->pause_mode == pause_sustained_low_latency))
14748     {
14749         dynamic_data* dd0 = dynamic_data_of (0);
14750         size_t now = GetHighPrecisionTimeStamp();
14751         temp_gen = n;
14752         for (i = (temp_gen+1); i <= n_time_max; i++)
14753         {
14754             dynamic_data* dd = dynamic_data_of (i);
14755             if ((now > dd_time_clock(dd) + dd_time_clock_interval(dd)) &&
14756                 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + dd_gc_clock_interval(dd))) &&
14757                 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
14758             {
14759                 n = min (i, n_time_max);
14760                 dprintf (GTC_LOG, ("time %d", n));
14761             }
14762         }
14763         if (n > temp_gen)
14764         {
14765             local_condemn_reasons->set_gen (gen_time_tuning, n);
14766         }
14767     }
14768
14769     if (n != n_alloc)
14770     {
14771         dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
14772     }
14773 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
14774
14775     if (n < (max_generation - 1))
14776     {
14777         if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
14778         {
14779             n = max (n, max_generation - 1);
14780             local_settings->promotion = TRUE;
14781             dprintf (GTC_LOG, ("h%d: skip %d, c %d",
14782                         heap_number, generation_skip_ratio, n));
14783             local_condemn_reasons->set_condition (gen_low_card_p);
14784         }
14785     }
14786
14787     if (!check_only_p)
14788     {
14789         generation_skip_ratio = 100;
14790     }
14791
14792     if (dt_low_ephemeral_space_p (check_only_p ? 
14793                                   tuning_deciding_full_gc : 
14794                                   tuning_deciding_condemned_gen))
14795     {
14796         low_ephemeral_space = TRUE;
14797
14798         n = max (n, max_generation - 1);
14799         local_condemn_reasons->set_condition (gen_low_ephemeral_p);
14800         dprintf (GTC_LOG, ("h%d: low eph", heap_number));
14801
14802 #ifdef BACKGROUND_GC
14803         if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
14804 #endif //BACKGROUND_GC
14805         {
14806             //It is better to defragment first if we are running out of space for
14807             //the ephemeral generation but we have enough fragmentation to make up for it
14808             //in the non ephemeral generation. Essentially we are trading a gen2 for 
14809             // having to expand heap in ephemeral collections.
14810             if (dt_high_frag_p (tuning_deciding_condemned_gen, 
14811                                 max_generation - 1, 
14812                                 TRUE))
14813             {
14814                 high_fragmentation = TRUE;
14815                 local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
14816                 dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
14817             }
14818         }
14819     }
14820
14821     //figure out which ephemeral generation is too fragramented
14822     temp_gen = n;
14823     for (i = n+1; i < max_generation; i++)
14824     {
14825         if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
14826         {
14827             dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
14828             n = i;
14829         }
14830         else
14831             break;
14832     }
14833
14834     if (low_ephemeral_space)
14835     {
14836         //enable promotion
14837         local_settings->promotion = TRUE;
14838     }
14839
14840     if (n > temp_gen)
14841     {
14842         local_condemn_reasons->set_condition (gen_eph_high_frag_p);
14843     }
14844
14845     if (!check_only_p)
14846     {
14847         if (settings.pause_mode == pause_low_latency)
14848         {
14849             if (!is_induced (settings.reason))
14850             {
14851                 n = min (n, max_generation - 1);
14852                 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
14853                 evaluate_elevation = FALSE;
14854                 goto exit;
14855             }
14856         }
14857     }
14858
14859     // It's hard to catch when we get to the point that the memory load is so high
14860     // we get an induced GC from the finalizer thread so we are checking the memory load
14861     // for every gen0 GC.
14862     check_memory = (check_only_p ? 
14863                     (n >= 0) : 
14864                     ((n >= 1) || low_memory_detected));
14865
14866     if (check_memory)
14867     {
14868         //find out if we are short on memory
14869         get_memory_info (&memory_load, &available_physical, &available_page_file);
14870         if (heap_number == 0)
14871         {
14872             dprintf (GTC_LOG, ("ml: %d", memory_load));
14873         }
14874         
14875         // Need to get it early enough for all heaps to use.
14876         entry_available_physical_mem = available_physical;
14877         local_settings->entry_memory_load = memory_load;
14878
14879         // @TODO: Force compaction more often under GCSTRESS
14880         if (memory_load >= high_memory_load_th || low_memory_detected)
14881         {
14882 #ifdef SIMPLE_DPRINTF
14883             // stress log can't handle any parameter that's bigger than a void*.
14884             if (heap_number == 0)
14885             {
14886                 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d", total_physical_mem, available_physical));
14887             }
14888 #endif //SIMPLE_DPRINTF
14889
14890             high_memory_load = TRUE;
14891
14892             if (memory_load >= v_high_memory_load_th || low_memory_detected)
14893             {
14894                 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
14895                 // gen1/gen0 may take a lot more memory than gen2.
14896                 if (!high_fragmentation)
14897                 {
14898                     high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation);
14899                 }
14900                 v_high_memory_load = TRUE;
14901             }
14902             else
14903             {
14904                 if (!high_fragmentation)
14905                 {
14906                     high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, available_physical);
14907                 }
14908             }
14909
14910             if (high_fragmentation)
14911             {
14912                 if (high_memory_load)
14913                 {
14914                     local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
14915                 }
14916                 else if (v_high_memory_load)
14917                 {
14918                     local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
14919                 }
14920             }
14921         }
14922     }
14923
14924     dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
14925                  heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
14926                  high_fragmentation));
14927
14928     if (should_expand_in_full_gc)
14929     {
14930         dprintf (GTC_LOG, ("h%d: expand_in_full - BLOCK", heap_number));
14931         *blocking_collection_p = TRUE;
14932         if (!check_only_p)
14933         {
14934             should_expand_in_full_gc = FALSE;
14935         }
14936         evaluate_elevation = FALSE;
14937         n = max_generation;
14938         local_condemn_reasons->set_condition (gen_expand_fullgc_p);
14939     }
14940
14941     if (last_gc_before_oom)
14942     {
14943         dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
14944         n = max_generation;
14945         *blocking_collection_p = TRUE;
14946         if ((local_settings->reason == reason_oos_loh) ||
14947             (local_settings->reason == reason_alloc_loh))
14948             evaluate_elevation = FALSE;
14949
14950         local_condemn_reasons->set_condition (gen_before_oom);
14951     }
14952
14953     if (!check_only_p)
14954     {
14955         if (is_induced_blocking (settings.reason) && 
14956             n_initial == max_generation
14957             IN_STRESS_HEAP( && !settings.stress_induced ))
14958         {
14959             if (heap_number == 0)
14960             {
14961                 dprintf (GTC_LOG, ("induced - BLOCK"));
14962             }
14963
14964             *blocking_collection_p = TRUE;
14965             local_condemn_reasons->set_condition (gen_induced_fullgc_p);
14966             evaluate_elevation = FALSE;
14967         }
14968
14969         if (settings.reason == reason_induced_noforce)
14970         {
14971             local_condemn_reasons->set_condition (gen_induced_noforce_p);
14972             evaluate_elevation = FALSE;
14973         }
14974     }
14975
14976     if (evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
14977     {
14978         *elevation_requested_p = TRUE;
14979 #ifdef BIT64
14980         // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
14981         if (high_memory_load || v_high_memory_load)
14982         {
14983             dynamic_data* dd_max = dynamic_data_of (max_generation);
14984             if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
14985             {
14986                 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)", 
14987                     dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
14988                 n = max_generation;
14989                 local_condemn_reasons->set_condition (gen_almost_max_alloc);
14990             }
14991         }
14992
14993         if (n <= max_generation)
14994         {
14995 #endif // BIT64
14996             if (high_fragmentation)
14997             {
14998                 //elevate to max_generation
14999                 n = max_generation;
15000                 dprintf (GTC_LOG, ("h%d: f full", heap_number));
15001
15002 #ifdef BACKGROUND_GC
15003                 if (high_memory_load || v_high_memory_load)
15004                 {
15005                     // For background GC we want to do blocking collections more eagerly because we don't
15006                     // want to get into the situation where the memory load becomes high while we are in
15007                     // a background GC and we'd have to wait for the background GC to finish to start
15008                     // a blocking collection (right now the implemenation doesn't handle converting 
15009                     // a background GC to a blocking collection midway.
15010                     dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
15011                     *blocking_collection_p = TRUE;
15012                 }
15013 #else
15014                 if (v_high_memory_load)
15015                 {
15016                     dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
15017                     *blocking_collection_p = TRUE;
15018                 }
15019 #endif //BACKGROUND_GC
15020             }
15021             else
15022             {
15023                 n = max (n, max_generation - 1);
15024                 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
15025             }
15026 #ifdef BIT64
15027         }
15028 #endif // BIT64
15029     }
15030
15031     if ((n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
15032     {
15033         dprintf (GTC_LOG, ("h%d: budget %d, check 2",
15034                       heap_number, n_alloc));
15035         if (get_new_allocation (max_generation) <= 0)
15036         {
15037             dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
15038             n = max_generation;
15039             local_condemn_reasons->set_condition (gen_max_gen1);
15040         }
15041     }
15042
15043     //figure out if max_generation is too fragmented -> blocking collection
15044     if (n == max_generation)
15045     {
15046         if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
15047         {
15048             dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
15049             local_condemn_reasons->set_condition (gen_max_high_frag_p);
15050             if (local_settings->pause_mode != pause_sustained_low_latency)
15051             {
15052                 *blocking_collection_p = TRUE;
15053             }
15054         }
15055     }
15056
15057 #ifdef BACKGROUND_GC
15058     if (n == max_generation)
15059     {
15060         if (heap_number == 0)
15061         {
15062             BOOL bgc_heap_too_small = TRUE;
15063             size_t gen2size = 0;
15064             size_t gen3size = 0;
15065 #ifdef MULTIPLE_HEAPS
15066             for (int i = 0; i < n_heaps; i++)
15067             {
15068                 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) || 
15069                     ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
15070                 {
15071                     bgc_heap_too_small = FALSE;
15072                     break;
15073                 }
15074             }
15075 #else //MULTIPLE_HEAPS
15076             if ((current_generation_size (max_generation) > bgc_min_per_heap) || 
15077                 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
15078             {
15079                 bgc_heap_too_small = FALSE;
15080             }            
15081 #endif //MULTIPLE_HEAPS
15082
15083             if (bgc_heap_too_small)
15084             {
15085                 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
15086
15087 #ifdef STRESS_HEAP
15088                 // do not turn stress-induced collections into blocking GCs
15089                 if (!settings.stress_induced)
15090 #endif //STRESS_HEAP
15091                 {
15092                     *blocking_collection_p = TRUE;
15093                 }
15094
15095                 local_condemn_reasons->set_condition (gen_gen2_too_small);
15096             }
15097         }
15098     }
15099 #endif //BACKGROUND_GC
15100
15101 exit:
15102     if (!check_only_p)
15103     {
15104 #ifdef STRESS_HEAP
15105 #ifdef BACKGROUND_GC
15106         // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
15107         // generations to be collected,
15108
15109         if (orig_gen != max_generation &&
15110             g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
15111         {
15112             *elevation_requested_p = FALSE;
15113         }
15114 #endif //BACKGROUND_GC
15115 #endif //STRESS_HEAP
15116
15117         if (check_memory)
15118         {
15119             fgm_result.available_pagefile_mb = (size_t)(available_page_file / (1024 * 1024));
15120         }
15121
15122         local_condemn_reasons->set_gen (gen_final_per_heap, n);
15123         get_gc_data_per_heap()->gen_to_condemn_reasons.init (local_condemn_reasons);
15124
15125 #ifdef DT_LOG
15126         local_condemn_reasons->print (heap_number);
15127 #endif //DT_LOG
15128
15129         if ((local_settings->reason == reason_oos_soh) || 
15130             (local_settings->reason == reason_oos_loh))
15131         {
15132             assert (n >= 1);
15133         }
15134     }
15135
15136     if (n == max_generation && GCToEEInterface::ForceFullGCToBeBlocking())
15137     {
15138 #ifdef BACKGROUND_GC
15139         // do not turn stress-induced collections into blocking GCs, unless there
15140         // have already been more full BGCs than full NGCs
15141 #if 0
15142         // This exposes DevDiv 94129, so we'll leave this out for now
15143         if (!settings.stress_induced ||
15144             full_gc_counts[gc_type_blocking] <= full_gc_counts[gc_type_background])
15145 #endif // 0
15146 #endif // BACKGROUND_GC
15147         {
15148             *blocking_collection_p = TRUE;
15149         }
15150     }
15151
15152     return n;
15153 }
15154
15155 #ifdef _PREFAST_
15156 #pragma warning(pop)
15157 #endif //_PREFAST_
15158
15159 inline
15160 size_t gc_heap::min_reclaim_fragmentation_threshold (uint32_t num_heaps)
15161 {
15162     // if the memory load is higher, the threshold we'd want to collect gets lower.
15163     size_t min_mem_based_on_available = 
15164         (500 - (settings.entry_memory_load - high_memory_load_th) * 40) * 1024 * 1024 / num_heaps;
15165     size_t ten_percent_size = (size_t)((float)generation_size (max_generation) * 0.10);
15166     uint64_t three_percent_mem = mem_one_percent * 3 / num_heaps;
15167
15168 #ifdef SIMPLE_DPRINTF
15169     dprintf (GTC_LOG, ("min av: %Id, 10%% gen2: %Id, 3%% mem: %I64d", 
15170         min_mem_based_on_available, ten_percent_size, three_percent_mem));
15171 #endif //SIMPLE_DPRINTF
15172     return (size_t)(min (min_mem_based_on_available, min (ten_percent_size, three_percent_mem)));
15173 }
15174
15175 inline
15176 uint64_t gc_heap::min_high_fragmentation_threshold(uint64_t available_mem, uint32_t num_heaps)
15177 {
15178     return min (available_mem, (256*1024*1024)) / num_heaps;
15179 }
15180
15181 enum {
15182 CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
15183 };
15184
15185
15186 #ifdef BACKGROUND_GC
15187 void gc_heap::init_background_gc ()
15188 {
15189     //reset the allocation so foreground gc can allocate into older (max_generation) generation
15190     generation* gen = generation_of (max_generation);
15191     generation_allocation_pointer (gen)= 0;
15192     generation_allocation_limit (gen) = 0;
15193     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
15194
15195     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
15196
15197     //reset the plan allocation for each segment
15198     for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
15199         seg = heap_segment_next_rw (seg))
15200     {
15201         heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
15202     }
15203
15204     if (heap_number == 0)
15205     {
15206         dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix", 
15207             heap_number,
15208             background_saved_lowest_address, 
15209             background_saved_highest_address));
15210     }
15211
15212     gc_lh_block_event.Reset();
15213 }
15214
15215 #endif //BACKGROUND_GC
15216
15217 inline
15218 void fire_drain_mark_list_event (size_t mark_list_objects)
15219 {
15220     FIRE_EVENT(BGCDrainMark, mark_list_objects);
15221 }
15222
15223 inline
15224 void fire_revisit_event (size_t dirtied_pages, 
15225                          size_t marked_objects,
15226                          BOOL large_objects_p)
15227 {
15228     FIRE_EVENT(BGCRevisit, dirtied_pages, marked_objects, large_objects_p);
15229 }
15230
15231 inline
15232 void fire_overflow_event (uint8_t* overflow_min,
15233                           uint8_t* overflow_max,
15234                           size_t marked_objects, 
15235                           int large_objects_p)
15236 {
15237     FIRE_EVENT(BGCOverflow, (uint64_t)overflow_min, (uint64_t)overflow_max, marked_objects, large_objects_p);
15238 }
15239
15240 void gc_heap::concurrent_print_time_delta (const char* msg)
15241 {
15242 #ifdef TRACE_GC
15243     size_t current_time = GetHighPrecisionTimeStamp();
15244     size_t elapsed_time = current_time - time_bgc_last;
15245     time_bgc_last = current_time;
15246
15247     dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
15248 #else
15249     UNREFERENCED_PARAMETER(msg);
15250 #endif //TRACE_GC
15251 }
15252
15253 void gc_heap::free_list_info (int gen_num, const char* msg)
15254 {
15255     UNREFERENCED_PARAMETER(gen_num);
15256 #if defined (BACKGROUND_GC) && defined (TRACE_GC)
15257     dprintf (3, ("h%d: %s", heap_number, msg));
15258     for (int i = 0; i <= (max_generation + 1); i++)
15259     {
15260         generation* gen = generation_of (i);
15261         if ((generation_allocation_size (gen) == 0) && 
15262             (generation_free_list_space (gen) == 0) && 
15263             (generation_free_obj_space (gen) == 0))
15264         {
15265             // don't print if everything is 0.
15266         }
15267         else
15268         {
15269             dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
15270                 heap_number, i, 
15271                 generation_allocation_size (gen), 
15272                 generation_free_list_space (gen), 
15273                 generation_free_obj_space (gen)));
15274         }
15275     }
15276 #else
15277     UNREFERENCED_PARAMETER(msg);
15278 #endif // BACKGROUND_GC && TRACE_GC
15279 }
15280
15281 void gc_heap::update_collection_counts_for_no_gc()
15282 {
15283     assert (settings.pause_mode == pause_no_gc);
15284
15285     settings.condemned_generation = max_generation;
15286 #ifdef MULTIPLE_HEAPS
15287     for (int i = 0; i < n_heaps; i++)
15288         g_heaps[i]->update_collection_counts();
15289 #else //MULTIPLE_HEAPS
15290     update_collection_counts();
15291 #endif //MULTIPLE_HEAPS
15292
15293     full_gc_counts[gc_type_blocking]++;
15294 }
15295
15296 BOOL gc_heap::should_proceed_with_gc()
15297 {
15298     if (gc_heap::settings.pause_mode == pause_no_gc)
15299     {
15300         if (current_no_gc_region_info.started)
15301         {
15302             // The no_gc mode was already in progress yet we triggered another GC,
15303             // this effectively exits the no_gc mode.
15304             restore_data_for_no_gc();
15305         }
15306         else
15307             return should_proceed_for_no_gc();
15308     }
15309
15310     return TRUE;
15311 }
15312
15313 //internal part of gc used by the serial and concurrent version
15314 void gc_heap::gc1()
15315 {
15316 #ifdef BACKGROUND_GC
15317     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15318 #endif //BACKGROUND_GC
15319
15320 #ifdef TIME_GC
15321     mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
15322 #endif //TIME_GC
15323
15324     verify_soh_segment_list();
15325
15326     int n = settings.condemned_generation;
15327
15328     update_collection_counts ();
15329
15330 #ifdef BACKGROUND_GC
15331     bgc_alloc_lock->check();
15332 #endif //BACKGROUND_GC
15333
15334     free_list_info (max_generation, "beginning");
15335
15336     vm_heap->GcCondemnedGeneration = settings.condemned_generation;
15337
15338     assert (g_gc_card_table == card_table);
15339
15340 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
15341     assert (g_gc_card_bundle_table == card_bundle_table);
15342 #endif    
15343
15344     {
15345         if (n == max_generation)
15346         {
15347             gc_low = lowest_address;
15348             gc_high = highest_address;
15349         }
15350         else
15351         {
15352             gc_low = generation_allocation_start (generation_of (n));
15353             gc_high = heap_segment_reserved (ephemeral_heap_segment);
15354         }   
15355 #ifdef BACKGROUND_GC
15356         if (settings.concurrent)
15357         {
15358 #ifdef TRACE_GC
15359             time_bgc_last = GetHighPrecisionTimeStamp();
15360 #endif //TRACE_GC
15361
15362             FIRE_EVENT(BGCBegin);
15363
15364             concurrent_print_time_delta ("BGC");
15365
15366 //#ifdef WRITE_WATCH
15367             //reset_write_watch (FALSE);
15368 //#endif //WRITE_WATCH
15369
15370             concurrent_print_time_delta ("RW");
15371             background_mark_phase();
15372             free_list_info (max_generation, "after mark phase");
15373             
15374             background_sweep();
15375             free_list_info (max_generation, "after sweep phase");
15376         }
15377         else
15378 #endif //BACKGROUND_GC
15379         {
15380             mark_phase (n, FALSE);
15381
15382             GCScan::GcRuntimeStructuresValid (FALSE);
15383             plan_phase (n);
15384             GCScan::GcRuntimeStructuresValid (TRUE);
15385         }
15386     }
15387
15388     size_t end_gc_time = GetHighPrecisionTimeStamp();
15389 //    printf ("generation: %d, elapsed time: %Id\n", n,  end_gc_time - dd_time_clock (dynamic_data_of (0)));
15390
15391     //adjust the allocation size from the pinned quantities. 
15392     for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
15393     {
15394         generation* gn = generation_of (gen_number);
15395         if (settings.compaction)
15396         {
15397             generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
15398             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
15399         }
15400         else
15401         {
15402             generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
15403             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
15404         }
15405         generation_pinned_allocation_sweep_size (gn) = 0;
15406         generation_pinned_allocation_compact_size (gn) = 0;
15407     }
15408
15409 #ifdef BACKGROUND_GC
15410     if (settings.concurrent)
15411     {
15412         dynamic_data* dd = dynamic_data_of (n);
15413         dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15414
15415         free_list_info (max_generation, "after computing new dynamic data");
15416
15417         gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
15418
15419         for (int gen_number = 0; gen_number < max_generation; gen_number++)
15420         {
15421             dprintf (2, ("end of BGC: gen%d new_alloc: %Id", 
15422                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15423             current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
15424             current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15425             current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15426         }
15427     }
15428     else
15429 #endif //BACKGROUND_GC
15430     {
15431         free_list_info (max_generation, "end");
15432         for (int gen_number = 0; gen_number <= n; gen_number++)
15433         {
15434             dynamic_data* dd = dynamic_data_of (gen_number);
15435             dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15436             compute_new_dynamic_data (gen_number);
15437         }
15438
15439         if (n != max_generation)
15440         {
15441             int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1));
15442             for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++)
15443             {
15444                 get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number);
15445                 get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15446                 get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15447             }
15448         }
15449
15450         get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100);
15451
15452         free_list_info (max_generation, "after computing new dynamic data");
15453         
15454         if (heap_number == 0)
15455         {
15456             dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms", 
15457                 dd_collection_count (dynamic_data_of (0)), 
15458                 settings.condemned_generation,
15459                 dd_gc_elapsed_time (dynamic_data_of (0))));
15460         }
15461
15462         for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
15463         {
15464             dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id", 
15465                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15466         }
15467     }
15468
15469     if (n < max_generation)
15470     {
15471         compute_promoted_allocation (1 + n);
15472
15473         dynamic_data* dd = dynamic_data_of (1 + n);
15474         size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) + 
15475                                    generation_free_obj_space (generation_of (1 + n));
15476
15477 #ifdef BACKGROUND_GC
15478         if (current_c_gc_state != c_gc_state_planning)
15479 #endif //BACKGROUND_GC
15480         {
15481             if (settings.promotion)
15482             {
15483                 dd_fragmentation (dd) = new_fragmentation;
15484             }
15485             else
15486             {
15487                 //assert (dd_fragmentation (dd) == new_fragmentation);
15488             }
15489         }
15490     }
15491
15492 #ifdef BACKGROUND_GC
15493     if (!settings.concurrent)
15494 #endif //BACKGROUND_GC
15495     {
15496 #ifndef FEATURE_REDHAWK
15497         // GCToEEInterface::IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
15498         assert(GCToEEInterface::IsGCThread());
15499 #endif // FEATURE_REDHAWK
15500         adjust_ephemeral_limits();
15501     }
15502
15503 #ifdef BACKGROUND_GC
15504     assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
15505     assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
15506 #endif //BACKGROUND_GC
15507
15508     if (fgn_maxgen_percent)
15509     {
15510         if (settings.condemned_generation == (max_generation - 1))
15511         {
15512             check_for_full_gc (max_generation - 1, 0);
15513         }
15514         else if (settings.condemned_generation == max_generation)
15515         {
15516             if (full_gc_approach_event_set 
15517 #ifdef MULTIPLE_HEAPS
15518                 && (heap_number == 0)
15519 #endif //MULTIPLE_HEAPS
15520                 )
15521             {
15522                 dprintf (2, ("FGN-GC: setting gen2 end event"));
15523
15524                 full_gc_approach_event.Reset();
15525 #ifdef BACKGROUND_GC
15526                 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
15527                 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
15528 #endif //BACKGROUND_GC
15529                 full_gc_end_event.Set();
15530                 full_gc_approach_event_set = false;            
15531             }
15532         }
15533     }
15534
15535 #ifdef BACKGROUND_GC
15536     if (!settings.concurrent)
15537 #endif //BACKGROUND_GC
15538     {
15539         //decide on the next allocation quantum
15540         if (alloc_contexts_used >= 1)
15541         {
15542             allocation_quantum = Align (min ((size_t)CLR_SIZE,
15543                                             (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
15544                                             get_alignment_constant(FALSE));
15545             dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
15546         }
15547     }
15548
15549     descr_generations (FALSE);
15550
15551     verify_soh_segment_list();
15552
15553 #ifdef BACKGROUND_GC
15554     add_to_history_per_heap();
15555     if (heap_number == 0)
15556     {
15557         add_to_history();
15558     }
15559 #endif // BACKGROUND_GC
15560
15561 #ifdef GC_STATS
15562     if (GCStatistics::Enabled() && heap_number == 0)
15563         g_GCStatistics.AddGCStats(settings, 
15564             dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
15565 #endif // GC_STATS
15566
15567 #ifdef TIME_GC
15568     fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
15569              n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
15570 #endif //TIME_GC
15571
15572 #ifdef BACKGROUND_GC
15573     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15574 #endif //BACKGROUND_GC
15575
15576 #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15577     if (FALSE 
15578 #ifdef VERIFY_HEAP
15579         // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same
15580         // value. If we ever allow randomly adjusting this as the process runs,
15581         // we cannot call it this way as joins need to match - we must have the same
15582         // value for all heaps like we do with bgc_heap_walk_for_etw_p.
15583         || (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15584 #endif
15585 #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
15586         || (bgc_heap_walk_for_etw_p && settings.concurrent)
15587 #endif
15588         )
15589     {
15590 #ifdef BACKGROUND_GC
15591         bool cooperative_mode = true;
15592
15593         if (settings.concurrent)
15594         {
15595             cooperative_mode = enable_preemptive ();
15596
15597 #ifdef MULTIPLE_HEAPS
15598             bgc_t_join.join(this, gc_join_suspend_ee_verify);
15599             if (bgc_t_join.joined())
15600             {
15601                 bgc_threads_sync_event.Reset();
15602
15603                 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
15604                 bgc_t_join.restart();
15605             }
15606             if (heap_number == 0)
15607             {
15608                 suspend_EE();
15609                 bgc_threads_sync_event.Set();
15610             }
15611             else
15612             {
15613                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15614                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15615             }
15616 #else
15617             suspend_EE();
15618 #endif //MULTIPLE_HEAPS
15619
15620             //fix the allocation area so verify_heap can proceed.
15621             fix_allocation_contexts (FALSE);
15622         }
15623 #endif //BACKGROUND_GC
15624
15625 #ifdef BACKGROUND_GC
15626         assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15627 #ifdef FEATURE_EVENT_TRACE
15628         if (bgc_heap_walk_for_etw_p && settings.concurrent)
15629         {
15630             GCToEEInterface::DiagWalkBGCSurvivors(__this);
15631
15632 #ifdef MULTIPLE_HEAPS
15633             bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
15634             if (bgc_t_join.joined())
15635             {
15636                 bgc_t_join.restart();
15637             }
15638 #endif // MULTIPLE_HEAPS
15639         }
15640 #endif // FEATURE_EVENT_TRACE
15641 #endif //BACKGROUND_GC
15642
15643 #ifdef VERIFY_HEAP
15644         if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15645             verify_heap (FALSE);
15646 #endif // VERIFY_HEAP
15647
15648 #ifdef BACKGROUND_GC
15649         if (settings.concurrent)
15650         {
15651             repair_allocation_contexts (TRUE);
15652
15653 #ifdef MULTIPLE_HEAPS
15654             bgc_t_join.join(this, gc_join_restart_ee_verify);
15655             if (bgc_t_join.joined())
15656             {
15657                 bgc_threads_sync_event.Reset();
15658
15659                 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
15660                 bgc_t_join.restart();
15661             }
15662             if (heap_number == 0)
15663             {
15664                 restart_EE();
15665                 bgc_threads_sync_event.Set();
15666             }
15667             else
15668             {
15669                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15670                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15671             }
15672 #else
15673             restart_EE();
15674 #endif //MULTIPLE_HEAPS
15675
15676             disable_preemptive (cooperative_mode);
15677         }
15678 #endif //BACKGROUND_GC
15679     }
15680 #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15681
15682 #ifdef MULTIPLE_HEAPS
15683     if (!settings.concurrent)
15684     {
15685         gc_t_join.join(this, gc_join_done);
15686         if (gc_t_join.joined ())
15687         {
15688             gc_heap::internal_gc_done = false;
15689
15690             //equalize the new desired size of the generations
15691             int limit = settings.condemned_generation;
15692             if (limit == max_generation)
15693             {
15694                 limit = max_generation+1;
15695             }
15696             for (int gen = 0; gen <= limit; gen++)
15697             {
15698                 size_t total_desired = 0;
15699
15700                 for (int i = 0; i < gc_heap::n_heaps; i++)
15701                 {
15702                     gc_heap* hp = gc_heap::g_heaps[i];
15703                     dynamic_data* dd = hp->dynamic_data_of (gen);
15704                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
15705                     if (temp_total_desired < total_desired)
15706                     {
15707                         // we overflowed.
15708                         total_desired = (size_t)MAX_PTR;
15709                         break;
15710                     }
15711                     total_desired = temp_total_desired;
15712                 }
15713
15714                 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
15715                                                     get_alignment_constant ((gen != (max_generation+1))));
15716
15717                 if (gen == 0)
15718                 {
15719 #if 1 //subsumed by the linear allocation model 
15720                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15721                     // apply some smoothing.
15722                     static size_t smoothed_desired_per_heap = 0;
15723                     size_t smoothing = 3; // exponential smoothing factor
15724                     if (smoothing  > VolatileLoad(&settings.gc_index))
15725                         smoothing  = VolatileLoad(&settings.gc_index);
15726                     smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
15727                     dprintf (1, ("sn = %Id  n = %Id", smoothed_desired_per_heap, desired_per_heap));
15728                     desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
15729 #endif //0
15730
15731                     // if desired_per_heap is close to min_gc_size, trim it
15732                     // down to min_gc_size to stay in the cache
15733                     gc_heap* hp = gc_heap::g_heaps[0];
15734                     dynamic_data* dd = hp->dynamic_data_of (gen);
15735                     size_t min_gc_size = dd_min_size(dd);
15736                     // if min GC size larger than true on die cache, then don't bother
15737                     // limiting the desired size
15738                     if ((min_gc_size <= GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)) &&
15739                         desired_per_heap <= 2*min_gc_size)
15740                     {
15741                         desired_per_heap = min_gc_size;
15742                     }
15743 #ifdef BIT64
15744                     desired_per_heap = joined_youngest_desired (desired_per_heap);
15745                     dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
15746 #endif // BIT64
15747
15748                     gc_data_global.final_youngest_desired = desired_per_heap;
15749                 }
15750 #if 1 //subsumed by the linear allocation model 
15751                 if (gen == (max_generation + 1))
15752                 {
15753                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15754                     // apply some smoothing.
15755                     static size_t smoothed_desired_per_heap_loh = 0;
15756                     size_t smoothing = 3; // exponential smoothing factor
15757                     size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
15758                     if (smoothing  > loh_count)
15759                         smoothing  = loh_count;
15760                     smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
15761                     dprintf( 2, ("smoothed_desired_per_heap_loh  = %Id  desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
15762                     desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
15763                 }
15764 #endif //0
15765                 for (int i = 0; i < gc_heap::n_heaps; i++)
15766                 {
15767                     gc_heap* hp = gc_heap::g_heaps[i];
15768                     dynamic_data* dd = hp->dynamic_data_of (gen);
15769                     dd_desired_allocation (dd) = desired_per_heap;
15770                     dd_gc_new_allocation (dd) = desired_per_heap;
15771                     dd_new_allocation (dd) = desired_per_heap;
15772
15773                     if (gen == 0)
15774                     {
15775                         hp->fgn_last_alloc = desired_per_heap;
15776                     }
15777                 }
15778             }
15779
15780 #ifdef FEATURE_LOH_COMPACTION
15781             BOOL all_heaps_compacted_p = TRUE;
15782 #endif //FEATURE_LOH_COMPACTION
15783             for (int i = 0; i < gc_heap::n_heaps; i++)
15784             {
15785                 gc_heap* hp = gc_heap::g_heaps[i];
15786                 hp->decommit_ephemeral_segment_pages();
15787                 hp->rearrange_large_heap_segments();
15788 #ifdef FEATURE_LOH_COMPACTION
15789                 all_heaps_compacted_p &= hp->loh_compacted_p;
15790 #endif //FEATURE_LOH_COMPACTION
15791             }
15792
15793 #ifdef FEATURE_LOH_COMPACTION
15794             check_loh_compact_mode (all_heaps_compacted_p);
15795 #endif //FEATURE_LOH_COMPACTION
15796
15797             fire_pevents();
15798
15799             gc_t_join.restart();
15800         }
15801         alloc_context_count = 0;
15802         heap_select::mark_heap (heap_number);
15803     }
15804
15805 #else
15806     gc_data_global.final_youngest_desired = 
15807         dd_desired_allocation (dynamic_data_of (0));
15808
15809     check_loh_compact_mode (loh_compacted_p);
15810
15811     decommit_ephemeral_segment_pages();
15812     fire_pevents();
15813
15814     if (!(settings.concurrent))
15815     {
15816         rearrange_large_heap_segments();
15817         do_post_gc();
15818     }
15819
15820 #ifdef BACKGROUND_GC
15821     recover_bgc_settings();
15822 #endif //BACKGROUND_GC
15823 #endif //MULTIPLE_HEAPS
15824 }
15825
15826 void gc_heap::save_data_for_no_gc()
15827 {
15828     current_no_gc_region_info.saved_pause_mode = settings.pause_mode;
15829 #ifdef MULTIPLE_HEAPS
15830     // This is to affect heap balancing. 
15831     for (int i = 0; i < n_heaps; i++)
15832     {
15833         current_no_gc_region_info.saved_gen0_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (0));
15834         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = min_balance_threshold;
15835         current_no_gc_region_info.saved_gen3_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1));
15836         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = 0;
15837     }
15838 #endif //MULTIPLE_HEAPS
15839 }
15840
15841 void gc_heap::restore_data_for_no_gc()
15842 {
15843     gc_heap::settings.pause_mode = current_no_gc_region_info.saved_pause_mode;
15844 #ifdef MULTIPLE_HEAPS
15845     for (int i = 0; i < n_heaps; i++)
15846     {
15847         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = current_no_gc_region_info.saved_gen0_min_size;
15848         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = current_no_gc_region_info.saved_gen3_min_size;
15849     }
15850 #endif //MULTIPLE_HEAPS
15851 }
15852
15853 start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size,
15854                                                              BOOL loh_size_known, 
15855                                                              uint64_t loh_size,
15856                                                              BOOL disallow_full_blocking)
15857 {
15858     if (current_no_gc_region_info.started)
15859     {
15860         return start_no_gc_in_progress;
15861     }
15862
15863     start_no_gc_region_status status = start_no_gc_success;
15864
15865     save_data_for_no_gc();
15866     settings.pause_mode = pause_no_gc;
15867     current_no_gc_region_info.start_status = start_no_gc_success;
15868
15869     uint64_t allocation_no_gc_loh = 0;
15870     uint64_t allocation_no_gc_soh = 0;
15871     assert(total_size != 0);
15872     if (loh_size_known)
15873     {
15874         assert(loh_size != 0);
15875         assert(loh_size <= total_size);
15876         allocation_no_gc_loh = loh_size;
15877         allocation_no_gc_soh = total_size - loh_size;
15878     }
15879     else
15880     {
15881         allocation_no_gc_soh = total_size;
15882         allocation_no_gc_loh = total_size;
15883     }
15884
15885     int soh_align_const = get_alignment_constant (TRUE);
15886     size_t max_soh_allocated = soh_segment_size - segment_info_size - eph_gen_starts_size;
15887     size_t size_per_heap = 0;
15888     const double scale_factor = 1.05;
15889
15890     int num_heaps = 1;
15891 #ifdef MULTIPLE_HEAPS
15892     num_heaps = n_heaps;
15893 #endif // MULTIPLE_HEAPS
15894
15895     uint64_t total_allowed_soh_allocation = max_soh_allocated * num_heaps;
15896     // [LOCALGC TODO]
15897     // In theory, the upper limit here is the physical memory of the machine, not
15898     // SIZE_T_MAX. This is not true today because total_physical_mem can be
15899     // larger than SIZE_T_MAX if running in wow64 on a machine with more than
15900     // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing
15901     // more freely between branches, it would be good to clean this up to use
15902     // total_physical_mem instead of SIZE_T_MAX.
15903     assert(total_allowed_soh_allocation <= SIZE_T_MAX);
15904     uint64_t total_allowed_loh_allocation = SIZE_T_MAX;
15905     uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast<uint64_t>(total_allowed_soh_allocation / scale_factor) : 0;
15906     uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast<uint64_t>(total_allowed_loh_allocation / scale_factor) : 0;
15907
15908     if (allocation_no_gc_soh > total_allowed_soh_alloc_scaled ||
15909         allocation_no_gc_loh > total_allowed_loh_alloc_scaled)
15910     {
15911         status = start_no_gc_too_large;
15912         goto done;
15913     }
15914
15915     if (allocation_no_gc_soh > 0)
15916     {
15917         allocation_no_gc_soh = static_cast<uint64_t>(allocation_no_gc_soh * scale_factor);
15918         allocation_no_gc_soh = min (allocation_no_gc_soh, total_allowed_soh_alloc_scaled);
15919     }
15920
15921     if (allocation_no_gc_loh > 0)
15922     {
15923         allocation_no_gc_loh = static_cast<uint64_t>(allocation_no_gc_loh * scale_factor);
15924         allocation_no_gc_loh = min (allocation_no_gc_loh, total_allowed_loh_alloc_scaled);
15925     }
15926
15927     if (disallow_full_blocking)
15928         current_no_gc_region_info.minimal_gc_p = TRUE;
15929
15930     if (allocation_no_gc_soh != 0)
15931     {
15932         current_no_gc_region_info.soh_allocation_size = static_cast<size_t>(allocation_no_gc_soh);
15933         size_per_heap = current_no_gc_region_info.soh_allocation_size;
15934 #ifdef MULTIPLE_HEAPS
15935         size_per_heap /= n_heaps;
15936         for (int i = 0; i < n_heaps; i++)
15937         {
15938             // due to heap balancing we need to allow some room before we even look to balance to another heap.
15939             g_heaps[i]->soh_allocation_no_gc = min (Align ((size_per_heap + min_balance_threshold), soh_align_const), max_soh_allocated);
15940         }
15941 #else //MULTIPLE_HEAPS
15942         soh_allocation_no_gc = min (Align (size_per_heap, soh_align_const), max_soh_allocated);
15943 #endif //MULTIPLE_HEAPS
15944     }
15945
15946     if (allocation_no_gc_loh != 0)
15947     {
15948         current_no_gc_region_info.loh_allocation_size = static_cast<size_t>(allocation_no_gc_loh);
15949         size_per_heap = current_no_gc_region_info.loh_allocation_size;
15950 #ifdef MULTIPLE_HEAPS
15951         size_per_heap /= n_heaps;
15952         for (int i = 0; i < n_heaps; i++)
15953             g_heaps[i]->loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
15954 #else //MULTIPLE_HEAPS
15955         loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
15956 #endif //MULTIPLE_HEAPS
15957     }
15958
15959 done:
15960     if (status != start_no_gc_success)
15961         restore_data_for_no_gc();
15962     return status;
15963 }
15964
15965 void gc_heap::handle_failure_for_no_gc()
15966 {
15967     gc_heap::restore_data_for_no_gc();
15968     // sets current_no_gc_region_info.started to FALSE here.
15969     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
15970 }
15971
15972 start_no_gc_region_status gc_heap::get_start_no_gc_region_status()
15973 {
15974     return current_no_gc_region_info.start_status;
15975 }
15976
15977 void gc_heap::record_gcs_during_no_gc()
15978 {
15979     if (current_no_gc_region_info.started)
15980     {
15981         current_no_gc_region_info.num_gcs++;
15982         if (is_induced (settings.reason))
15983             current_no_gc_region_info.num_gcs_induced++;
15984     }
15985 }
15986
15987 BOOL gc_heap::find_loh_free_for_no_gc()
15988 {
15989     allocator* loh_allocator = generation_allocator (generation_of (max_generation + 1));
15990     size_t sz_list = loh_allocator->first_bucket_size();
15991     size_t size = loh_allocation_no_gc;
15992     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
15993     {
15994         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
15995         {
15996             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
15997             while (free_list)
15998             {
15999                 size_t free_list_size = unused_array_size(free_list);
16000
16001                 if (free_list_size > loh_allocation_no_gc)
16002                 {
16003                     dprintf (3, ("free item %Ix(%Id) for no gc", (size_t)free_list, free_list_size));
16004                     return TRUE;
16005                 }
16006
16007                 free_list = free_list_slot (free_list); 
16008             }
16009         }
16010         sz_list = sz_list * 2;
16011     }
16012
16013     return FALSE;
16014 }
16015
16016 BOOL gc_heap::find_loh_space_for_no_gc()
16017 {
16018     saved_loh_segment_no_gc = 0;
16019
16020     if (find_loh_free_for_no_gc())
16021         return TRUE;
16022
16023     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16024
16025     while (seg)
16026     {
16027         size_t remaining = heap_segment_reserved (seg) - heap_segment_allocated (seg);
16028         if (remaining >= loh_allocation_no_gc)
16029         {
16030             saved_loh_segment_no_gc = seg;
16031             break;
16032         }
16033         seg = heap_segment_next (seg);
16034     }
16035
16036     if (!saved_loh_segment_no_gc && current_no_gc_region_info.minimal_gc_p)
16037     {
16038         // If no full GC is allowed, we try to get a new seg right away.
16039         saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc)
16040 #ifdef MULTIPLE_HEAPS
16041                                                       , this
16042 #endif //MULTIPLE_HEAPS
16043                                                       );
16044     }
16045
16046     return (saved_loh_segment_no_gc != 0);
16047 }
16048
16049 BOOL gc_heap::loh_allocated_for_no_gc()
16050 {
16051     if (!saved_loh_segment_no_gc)
16052         return FALSE;
16053
16054     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16055     do 
16056     {
16057         if (seg == saved_loh_segment_no_gc)
16058         {
16059             return FALSE;
16060         }
16061         seg = heap_segment_next (seg);
16062     } while (seg);
16063
16064     return TRUE;
16065 }
16066
16067 BOOL gc_heap::commit_loh_for_no_gc (heap_segment* seg)
16068 {
16069     uint8_t* end_committed = heap_segment_allocated (seg) + loh_allocation_no_gc;
16070     assert (end_committed <= heap_segment_reserved (seg));
16071     return (grow_heap_segment (seg, end_committed));
16072 }
16073
16074 void gc_heap::thread_no_gc_loh_segments()
16075 {
16076 #ifdef MULTIPLE_HEAPS
16077     for (int i = 0; i < n_heaps; i++)
16078     {
16079         gc_heap* hp = g_heaps[i];
16080         if (hp->loh_allocated_for_no_gc())
16081         {
16082             hp->thread_loh_segment (hp->saved_loh_segment_no_gc);
16083             hp->saved_loh_segment_no_gc = 0;
16084         }
16085     }
16086 #else //MULTIPLE_HEAPS
16087     if (loh_allocated_for_no_gc())
16088     {
16089         thread_loh_segment (saved_loh_segment_no_gc);
16090         saved_loh_segment_no_gc = 0;
16091     }
16092 #endif //MULTIPLE_HEAPS    
16093 }
16094
16095 void gc_heap::set_loh_allocations_for_no_gc()
16096 {
16097     if (current_no_gc_region_info.loh_allocation_size != 0)
16098     {
16099         dynamic_data* dd = dynamic_data_of (max_generation + 1);
16100         dd_new_allocation (dd) = loh_allocation_no_gc;
16101         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16102     }
16103 }
16104
16105 void gc_heap::set_soh_allocations_for_no_gc()
16106 {
16107     if (current_no_gc_region_info.soh_allocation_size != 0)
16108     {
16109         dynamic_data* dd = dynamic_data_of (0);
16110         dd_new_allocation (dd) = soh_allocation_no_gc;
16111         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16112 #ifdef MULTIPLE_HEAPS
16113         alloc_context_count = 0;
16114 #endif //MULTIPLE_HEAPS
16115     }
16116 }
16117
16118 void gc_heap::set_allocations_for_no_gc()
16119 {
16120 #ifdef MULTIPLE_HEAPS
16121     for (int i = 0; i < n_heaps; i++)
16122     {
16123         gc_heap* hp = g_heaps[i];
16124         hp->set_loh_allocations_for_no_gc();
16125         hp->set_soh_allocations_for_no_gc();
16126     }
16127 #else //MULTIPLE_HEAPS
16128     set_loh_allocations_for_no_gc();
16129     set_soh_allocations_for_no_gc();
16130 #endif //MULTIPLE_HEAPS
16131 }
16132
16133 BOOL gc_heap::should_proceed_for_no_gc()
16134 {
16135     BOOL gc_requested = FALSE;
16136     BOOL loh_full_gc_requested = FALSE;
16137     BOOL soh_full_gc_requested = FALSE;
16138     BOOL no_gc_requested = FALSE;
16139     BOOL get_new_loh_segments = FALSE;
16140
16141     if (current_no_gc_region_info.soh_allocation_size)
16142     {
16143 #ifdef MULTIPLE_HEAPS
16144         for (int i = 0; i < n_heaps; i++)
16145         {
16146             gc_heap* hp = g_heaps[i];
16147             if ((size_t)(heap_segment_reserved (hp->ephemeral_heap_segment) - hp->alloc_allocated) < hp->soh_allocation_no_gc)
16148             {
16149                 gc_requested = TRUE;
16150                 break;
16151             }
16152         }
16153 #else //MULTIPLE_HEAPS
16154         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated) < soh_allocation_no_gc)
16155             gc_requested = TRUE;
16156 #endif //MULTIPLE_HEAPS
16157
16158         if (!gc_requested)
16159         {
16160 #ifdef MULTIPLE_HEAPS
16161             for (int i = 0; i < n_heaps; i++)
16162             {
16163                 gc_heap* hp = g_heaps[i];
16164                 if (!(hp->grow_heap_segment (hp->ephemeral_heap_segment, (hp->alloc_allocated + hp->soh_allocation_no_gc))))
16165                 {
16166                     soh_full_gc_requested = TRUE;
16167                     break;
16168                 }
16169             }
16170 #else //MULTIPLE_HEAPS
16171             if (!grow_heap_segment (ephemeral_heap_segment, (alloc_allocated + soh_allocation_no_gc)))
16172                 soh_full_gc_requested = TRUE;
16173 #endif //MULTIPLE_HEAPS
16174         }
16175     }
16176
16177     if (!current_no_gc_region_info.minimal_gc_p && gc_requested)
16178     {
16179         soh_full_gc_requested = TRUE;
16180     }
16181
16182     no_gc_requested = !(soh_full_gc_requested || gc_requested);
16183
16184     if (soh_full_gc_requested && current_no_gc_region_info.minimal_gc_p)
16185     {
16186         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16187         goto done;
16188     }
16189
16190     if (!soh_full_gc_requested && current_no_gc_region_info.loh_allocation_size)
16191     {
16192         // Check to see if we have enough reserved space. 
16193 #ifdef MULTIPLE_HEAPS
16194         for (int i = 0; i < n_heaps; i++)
16195         {
16196             gc_heap* hp = g_heaps[i];
16197             if (!hp->find_loh_space_for_no_gc())
16198             {
16199                 loh_full_gc_requested = TRUE;
16200                 break;
16201             }
16202         }
16203 #else //MULTIPLE_HEAPS
16204         if (!find_loh_space_for_no_gc())
16205             loh_full_gc_requested = TRUE;
16206 #endif //MULTIPLE_HEAPS
16207
16208         // Check to see if we have committed space.
16209         if (!loh_full_gc_requested)
16210         {
16211 #ifdef MULTIPLE_HEAPS
16212             for (int i = 0; i < n_heaps; i++)
16213             {
16214                 gc_heap* hp = g_heaps[i];
16215                 if (hp->saved_loh_segment_no_gc &&!hp->commit_loh_for_no_gc (hp->saved_loh_segment_no_gc))
16216                 {
16217                     loh_full_gc_requested = TRUE;
16218                     break;
16219                 }
16220             }
16221 #else //MULTIPLE_HEAPS
16222             if (saved_loh_segment_no_gc && !commit_loh_for_no_gc (saved_loh_segment_no_gc))
16223                 loh_full_gc_requested = TRUE;
16224 #endif //MULTIPLE_HEAPS
16225         }
16226     }
16227
16228     if (loh_full_gc_requested || soh_full_gc_requested)
16229     {
16230         if (current_no_gc_region_info.minimal_gc_p)
16231             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16232     }
16233
16234     no_gc_requested = !(loh_full_gc_requested || soh_full_gc_requested || gc_requested);
16235
16236     if (current_no_gc_region_info.start_status == start_no_gc_success)
16237     {
16238         if (no_gc_requested)
16239             set_allocations_for_no_gc();
16240     }
16241
16242 done:
16243
16244     if ((current_no_gc_region_info.start_status == start_no_gc_success) && !no_gc_requested)
16245         return TRUE;
16246     else
16247     {
16248         // We are done with starting the no_gc_region.
16249         current_no_gc_region_info.started = TRUE;
16250         return FALSE;
16251     }
16252 }
16253
16254 end_no_gc_region_status gc_heap::end_no_gc_region()
16255 {
16256     dprintf (1, ("end no gc called"));
16257
16258     end_no_gc_region_status status = end_no_gc_success;
16259
16260     if (!(current_no_gc_region_info.started))
16261         status = end_no_gc_not_in_progress;
16262     if (current_no_gc_region_info.num_gcs_induced)
16263         status = end_no_gc_induced;
16264     else if (current_no_gc_region_info.num_gcs)
16265         status = end_no_gc_alloc_exceeded;
16266
16267     if (settings.pause_mode == pause_no_gc)
16268         restore_data_for_no_gc();
16269
16270     // sets current_no_gc_region_info.started to FALSE here.
16271     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16272
16273     return status;
16274 }
16275
16276 //update counters
16277 void gc_heap::update_collection_counts ()
16278 {
16279     dynamic_data* dd0 = dynamic_data_of (0);
16280     dd_gc_clock (dd0) += 1;
16281
16282     size_t now = GetHighPrecisionTimeStamp();
16283
16284     for (int i = 0; i <= settings.condemned_generation;i++)
16285     {
16286         dynamic_data* dd = dynamic_data_of (i);
16287         dd_collection_count (dd)++;
16288         //this is needed by the linear allocation model
16289         if (i == max_generation)
16290             dd_collection_count (dynamic_data_of (max_generation+1))++;
16291         dd_gc_clock (dd) = dd_gc_clock (dd0);
16292         dd_time_clock (dd) = now;
16293     }
16294 }
16295
16296 #ifdef HEAP_ANALYZE
16297 inline
16298 BOOL AnalyzeSurvivorsRequested(int condemnedGeneration)
16299 {
16300 #ifndef BUILD_AS_STANDALONE
16301     // Is the list active?
16302     GcNotifications gn(g_pGcNotificationTable);
16303     if (gn.IsActive())
16304     {
16305         GcEvtArgs gea = { GC_MARK_END, { (1<<condemnedGeneration) } };
16306         if (gn.GetNotification(gea) != 0)
16307         {
16308             return TRUE;
16309         }
16310     }
16311 #endif // BUILD_AS_STANDALONE
16312     return FALSE;
16313 }
16314
16315 void DACNotifyGcMarkEnd(int condemnedGeneration)
16316 {
16317 #ifndef BUILD_AS_STANDALONE
16318     // Is the list active?
16319     GcNotifications gn(g_pGcNotificationTable);
16320     if (gn.IsActive())
16321     {
16322         GcEvtArgs gea = { GC_MARK_END, { (1<<condemnedGeneration) } };
16323         if (gn.GetNotification(gea) != 0)
16324         {
16325             DACNotify::DoGCNotification(gea);
16326         }
16327     }
16328 #endif // BUILD_AS_STANDALONE
16329 }
16330 #endif // HEAP_ANALYZE
16331
16332 BOOL gc_heap::expand_soh_with_minimal_gc()
16333 {
16334     if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) >= soh_allocation_no_gc)
16335         return TRUE;
16336
16337     heap_segment* new_seg = soh_get_segment_to_expand();
16338     if (new_seg)
16339     {
16340         if (g_gc_card_table != card_table)
16341             copy_brick_card_table();
16342
16343         settings.promotion = TRUE;
16344         settings.demotion = FALSE;
16345         ephemeral_promotion = TRUE;
16346         int condemned_gen_number = max_generation - 1;
16347
16348         generation* gen = 0;
16349         int align_const = get_alignment_constant (TRUE);
16350
16351         for (int i = 0; i <= condemned_gen_number; i++)
16352         {
16353             gen = generation_of (i);
16354             saved_ephemeral_plan_start[i] = generation_allocation_start (gen);
16355             saved_ephemeral_plan_start_size[i] = Align (size (generation_allocation_start (gen)), align_const);
16356         }
16357
16358         // We do need to clear the bricks here as we are converting a bunch of ephemeral objects to gen2
16359         // and need to make sure that there are no left over bricks from the previous GCs for the space 
16360         // we just used for gen0 allocation. We will need to go through the bricks for these objects for 
16361         // ephemeral GCs later.
16362         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
16363              b < brick_of (align_on_brick (heap_segment_allocated (ephemeral_heap_segment)));
16364              b++)
16365         {
16366             set_brick (b, -1);
16367         }
16368
16369         size_t ephemeral_size = (heap_segment_allocated (ephemeral_heap_segment) - 
16370                                 generation_allocation_start (generation_of (max_generation - 1)));
16371         heap_segment_next (ephemeral_heap_segment) = new_seg;
16372         ephemeral_heap_segment = new_seg;
16373         uint8_t*  start = heap_segment_mem (ephemeral_heap_segment);
16374
16375         for (int i = condemned_gen_number; i >= 0; i--)
16376         {
16377             gen = generation_of (i);
16378             size_t gen_start_size = Align (min_obj_size);
16379             make_generation (generation_table[i], ephemeral_heap_segment, start, 0);
16380             generation_plan_allocation_start (gen) = start;
16381             generation_plan_allocation_start_size (gen) = gen_start_size;
16382             start += gen_start_size;
16383         }
16384         heap_segment_used (ephemeral_heap_segment) = start - plug_skew;
16385         heap_segment_plan_allocated (ephemeral_heap_segment) = start;
16386
16387         fix_generation_bounds (condemned_gen_number, generation_of (0));
16388
16389         dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
16390         dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));
16391
16392         adjust_ephemeral_limits();
16393         return TRUE;
16394     }
16395     else
16396         return FALSE;
16397 }
16398
16399 // Only to be done on the thread that calls restart in a join for server GC
16400 // and reset the oom status per heap.
16401 void gc_heap::check_and_set_no_gc_oom()
16402 {
16403 #ifdef MULTIPLE_HEAPS
16404     for (int i = 0; i < n_heaps; i++)
16405     {
16406         gc_heap* hp = g_heaps[i];
16407         if (hp->no_gc_oom_p)
16408         {
16409             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16410             hp->no_gc_oom_p = false;
16411         }
16412     }
16413 #else
16414     if (no_gc_oom_p)
16415     {
16416         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16417         no_gc_oom_p = false;
16418     }
16419 #endif //MULTIPLE_HEAPS
16420 }
16421
16422 void gc_heap::allocate_for_no_gc_after_gc()
16423 {
16424     if (current_no_gc_region_info.minimal_gc_p)
16425         repair_allocation_contexts (TRUE);
16426
16427     no_gc_oom_p = false;
16428
16429     if (current_no_gc_region_info.start_status != start_no_gc_no_memory)
16430     {
16431         if (current_no_gc_region_info.soh_allocation_size != 0)
16432         {
16433             if (((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) < soh_allocation_no_gc) ||
16434                 (!grow_heap_segment (ephemeral_heap_segment, (heap_segment_allocated (ephemeral_heap_segment) + soh_allocation_no_gc))))
16435             {
16436                 no_gc_oom_p = true;
16437             }
16438
16439 #ifdef MULTIPLE_HEAPS
16440             gc_t_join.join(this, gc_join_after_commit_soh_no_gc);
16441             if (gc_t_join.joined())
16442             {
16443 #endif //MULTIPLE_HEAPS
16444
16445                 check_and_set_no_gc_oom();
16446
16447 #ifdef MULTIPLE_HEAPS
16448                 gc_t_join.restart();
16449             }
16450 #endif //MULTIPLE_HEAPS
16451         }
16452
16453         if ((current_no_gc_region_info.start_status == start_no_gc_success) &&
16454             !(current_no_gc_region_info.minimal_gc_p) && 
16455             (current_no_gc_region_info.loh_allocation_size != 0))
16456         {
16457             gc_policy = policy_compact;
16458             saved_loh_segment_no_gc = 0;
16459
16460             if (!find_loh_free_for_no_gc())
16461             {
16462                 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16463                 BOOL found_seg_p = FALSE;
16464                 while (seg)
16465                 {
16466                     if ((size_t)(heap_segment_reserved (seg) - heap_segment_allocated (seg)) >= loh_allocation_no_gc)
16467                     {
16468                         found_seg_p = TRUE;
16469                         if (!commit_loh_for_no_gc (seg))
16470                         {
16471                             no_gc_oom_p = true;
16472                             break;
16473                         }
16474                     }
16475                     seg = heap_segment_next (seg);
16476                 }
16477
16478                 if (!found_seg_p)
16479                     gc_policy = policy_expand;
16480             }
16481
16482 #ifdef MULTIPLE_HEAPS
16483             gc_t_join.join(this, gc_join_expand_loh_no_gc);
16484             if (gc_t_join.joined())
16485             {
16486                 check_and_set_no_gc_oom();
16487
16488                 if (current_no_gc_region_info.start_status == start_no_gc_success)
16489                 {
16490                     for (int i = 0; i < n_heaps; i++)
16491                     {
16492                         gc_heap* hp = g_heaps[i];
16493                         if (hp->gc_policy == policy_expand)
16494                         {
16495                             hp->saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc), hp);
16496                             if (!(hp->saved_loh_segment_no_gc))
16497                             {
16498                                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16499                                 break;
16500                             }
16501                         }
16502                     }
16503                 }
16504
16505                 gc_t_join.restart();
16506             }
16507 #else //MULTIPLE_HEAPS
16508             check_and_set_no_gc_oom();
16509
16510             if ((current_no_gc_region_info.start_status == start_no_gc_success) && (gc_policy == policy_expand))
16511             {
16512                 saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc));
16513                 if (!saved_loh_segment_no_gc)
16514                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16515             }
16516 #endif //MULTIPLE_HEAPS
16517
16518             if ((current_no_gc_region_info.start_status == start_no_gc_success) && saved_loh_segment_no_gc)
16519             {
16520                 if (!commit_loh_for_no_gc (saved_loh_segment_no_gc))
16521                 {
16522                     no_gc_oom_p = true;
16523                 }
16524             }
16525         }
16526     }
16527
16528 #ifdef MULTIPLE_HEAPS
16529     gc_t_join.join(this, gc_join_final_no_gc);
16530     if (gc_t_join.joined())
16531     {
16532 #endif //MULTIPLE_HEAPS
16533
16534         check_and_set_no_gc_oom();
16535
16536         if (current_no_gc_region_info.start_status == start_no_gc_success)
16537         {
16538             set_allocations_for_no_gc();
16539             current_no_gc_region_info.started = TRUE;
16540         }
16541
16542 #ifdef MULTIPLE_HEAPS
16543         gc_t_join.restart();
16544     }
16545 #endif //MULTIPLE_HEAPS
16546 }
16547
16548 void gc_heap::init_records()
16549 {
16550     memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
16551     gc_data_per_heap.heap_index = heap_number;
16552     if (heap_number == 0)
16553         memset (&gc_data_global, 0, sizeof (gc_data_global));
16554
16555 #ifdef GC_CONFIG_DRIVEN
16556     memset (interesting_data_per_gc, 0, sizeof (interesting_data_per_gc));
16557 #endif //GC_CONFIG_DRIVEN
16558 }
16559
16560 int gc_heap::garbage_collect (int n)
16561 {
16562     //reset the number of alloc contexts
16563     alloc_contexts_used = 0;
16564
16565     fix_allocation_contexts (TRUE);
16566 #ifdef MULTIPLE_HEAPS
16567 #ifdef JOIN_STATS
16568     gc_t_join.start_ts(this);
16569 #endif //JOIN_STATS
16570     clear_gen0_bricks();
16571 #endif //MULTIPLE_HEAPS
16572
16573     if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p)
16574     {
16575 #ifdef MULTIPLE_HEAPS
16576         gc_t_join.join(this, gc_join_minimal_gc);
16577         if (gc_t_join.joined())
16578         {
16579 #endif //MULTIPLE_HEAPS
16580
16581 #ifdef MULTIPLE_HEAPS
16582             // this is serialized because we need to get a segment
16583             for (int i = 0; i < n_heaps; i++)
16584             {
16585                 if (!(g_heaps[i]->expand_soh_with_minimal_gc()))
16586                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16587             }
16588 #else
16589             if (!expand_soh_with_minimal_gc())
16590                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16591 #endif //MULTIPLE_HEAPS
16592
16593             update_collection_counts_for_no_gc();
16594
16595 #ifdef MULTIPLE_HEAPS
16596             gc_t_join.restart();
16597         }
16598 #endif //MULTIPLE_HEAPS
16599
16600         goto done;
16601     }
16602
16603     init_records();
16604     memset (&fgm_result, 0, sizeof (fgm_result));
16605
16606     settings.reason = gc_trigger_reason;
16607     verify_pinned_queue_p = FALSE;
16608
16609 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
16610         num_pinned_objects = 0;
16611 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
16612
16613 #ifdef STRESS_HEAP
16614     if (settings.reason == reason_gcstress)
16615     {
16616         settings.reason = reason_induced;
16617         settings.stress_induced = TRUE;
16618     }
16619 #endif // STRESS_HEAP
16620
16621 #ifdef MULTIPLE_HEAPS
16622     //align all heaps on the max generation to condemn
16623     dprintf (3, ("Joining for max generation to condemn"));
16624     condemned_generation_num = generation_to_condemn (n, 
16625                                                       &blocking_collection, 
16626                                                       &elevation_requested, 
16627                                                       FALSE);
16628     gc_t_join.join(this, gc_join_generation_determined);
16629     if (gc_t_join.joined())
16630 #endif //MULTIPLE_HEAPS
16631     {
16632 #ifdef MULTIPLE_HEAPS
16633 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16634         //delete old slots from the segment table
16635         seg_table->delete_old_slots();
16636 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16637         for (int i = 0; i < n_heaps; i++)
16638         {
16639             //copy the card and brick tables
16640             if (g_gc_card_table != g_heaps[i]->card_table)
16641             {
16642                 g_heaps[i]->copy_brick_card_table();
16643             }
16644
16645             g_heaps[i]->rearrange_large_heap_segments();
16646             if (!recursive_gc_sync::background_running_p())
16647             {
16648                 g_heaps[i]->rearrange_small_heap_segments();
16649             }
16650         }
16651 #else //MULTIPLE_HEAPS
16652 #ifdef BACKGROUND_GC
16653             //delete old slots from the segment table
16654 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16655             seg_table->delete_old_slots();
16656 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16657             rearrange_large_heap_segments();
16658             if (!recursive_gc_sync::background_running_p())
16659             {
16660                 rearrange_small_heap_segments();
16661             }
16662 #endif //BACKGROUND_GC
16663         // check for card table growth
16664         if (g_gc_card_table != card_table)
16665             copy_brick_card_table();
16666
16667 #endif //MULTIPLE_HEAPS
16668
16669         BOOL should_evaluate_elevation = FALSE;
16670         BOOL should_do_blocking_collection = FALSE;
16671
16672 #ifdef MULTIPLE_HEAPS
16673         int gen_max = condemned_generation_num;
16674         for (int i = 0; i < n_heaps; i++)
16675         {
16676             if (gen_max < g_heaps[i]->condemned_generation_num)
16677                 gen_max = g_heaps[i]->condemned_generation_num;
16678             if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested))
16679                 should_evaluate_elevation = TRUE;
16680             if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
16681                 should_do_blocking_collection = TRUE;
16682         }
16683
16684         settings.condemned_generation = gen_max;
16685 #else //MULTIPLE_HEAPS
16686         settings.condemned_generation = generation_to_condemn (n, 
16687                                                             &blocking_collection, 
16688                                                             &elevation_requested, 
16689                                                             FALSE);
16690         should_evaluate_elevation = elevation_requested;
16691         should_do_blocking_collection = blocking_collection;
16692 #endif //MULTIPLE_HEAPS
16693
16694         settings.condemned_generation = joined_generation_to_condemn (
16695                                             should_evaluate_elevation, 
16696                                             settings.condemned_generation,
16697                                             &should_do_blocking_collection
16698                                             STRESS_HEAP_ARG(n)
16699                                             );
16700
16701         STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, 
16702                 "condemned generation num: %d\n", settings.condemned_generation);
16703
16704         record_gcs_during_no_gc();
16705
16706         if (settings.condemned_generation > 1)
16707             settings.promotion = TRUE;
16708
16709 #ifdef HEAP_ANALYZE
16710         // At this point we've decided what generation is condemned
16711         // See if we've been requested to analyze survivors after the mark phase
16712         if (AnalyzeSurvivorsRequested(settings.condemned_generation))
16713         {
16714             heap_analyze_enabled = TRUE;
16715         }
16716 #endif // HEAP_ANALYZE
16717
16718         GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced);
16719
16720 #ifdef BACKGROUND_GC
16721         if ((settings.condemned_generation == max_generation) &&
16722             (recursive_gc_sync::background_running_p()))
16723         {
16724             //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work
16725             // because we have to collect 0 and 1 properly
16726             // in particular, the allocation contexts are gone.
16727             // For now, it is simpler to collect max_generation-1
16728             settings.condemned_generation = max_generation - 1;
16729             dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
16730         }
16731
16732         if ((settings.condemned_generation == max_generation) &&
16733             (should_do_blocking_collection == FALSE) &&
16734             gc_can_use_concurrent &&
16735             !temp_disable_concurrent_p &&                 
16736             ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
16737         {
16738             keep_bgc_threads_p = TRUE;
16739             c_write (settings.concurrent,  TRUE);
16740         }
16741 #endif //BACKGROUND_GC
16742
16743         settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1;
16744
16745         // Call the EE for start of GC work
16746         // just one thread for MP GC
16747         GCToEEInterface::GcStartWork (settings.condemned_generation,
16748                                  max_generation);            
16749
16750         // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
16751         // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
16752         // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
16753         // fired in gc1.
16754         do_pre_gc();
16755
16756 #ifdef MULTIPLE_HEAPS
16757         gc_start_event.Reset();
16758         //start all threads on the roots.
16759         dprintf(3, ("Starting all gc threads for gc"));
16760         gc_t_join.restart();
16761 #endif //MULTIPLE_HEAPS
16762     }
16763
16764     {
16765         int gen_num_for_data = max_generation + 1;
16766         for (int i = 0; i <= gen_num_for_data; i++)
16767         {
16768             gc_data_per_heap.gen_data[i].size_before = generation_size (i);
16769             generation* gen = generation_of (i);
16770             gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
16771             gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
16772         }
16773     }
16774     descr_generations (TRUE);
16775 //    descr_card_table();
16776
16777 #ifdef VERIFY_HEAP
16778     if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
16779        !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_POST_GC_ONLY))
16780     {
16781         verify_heap (TRUE);
16782     }
16783     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK)
16784         checkGCWriteBarrier();
16785
16786 #endif // VERIFY_HEAP
16787
16788 #ifdef BACKGROUND_GC
16789     if (settings.concurrent)
16790     {
16791         // We need to save the settings because we'll need to restore it after each FGC.
16792         assert (settings.condemned_generation == max_generation);
16793         settings.compaction = FALSE;
16794         saved_bgc_settings = settings;
16795
16796 #ifdef MULTIPLE_HEAPS
16797         if (heap_number == 0)
16798         {
16799             for (int i = 0; i < n_heaps; i++)
16800             {
16801                 prepare_bgc_thread (g_heaps[i]);
16802             }
16803             dprintf (2, ("setting bgc_threads_sync_event"));
16804             bgc_threads_sync_event.Set();
16805         }
16806         else
16807         {
16808             bgc_threads_sync_event.Wait(INFINITE, FALSE);
16809             dprintf (2, ("bgc_threads_sync_event is signalled"));
16810         }
16811 #else
16812         prepare_bgc_thread(0);
16813 #endif //MULTIPLE_HEAPS
16814
16815 #ifdef MULTIPLE_HEAPS
16816         gc_t_join.join(this, gc_join_start_bgc);
16817         if (gc_t_join.joined())
16818 #endif //MULTIPLE_HEAPS
16819         {
16820             do_concurrent_p = TRUE;
16821             do_ephemeral_gc_p = FALSE;
16822 #ifdef MULTIPLE_HEAPS
16823             dprintf(2, ("Joined to perform a background GC"));
16824
16825             for (int i = 0; i < n_heaps; i++)
16826             {
16827                 gc_heap* hp = g_heaps[i];
16828                 if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
16829                 {
16830                     do_concurrent_p = FALSE;
16831                     break;
16832                 }
16833                 else
16834                 {
16835                     hp->background_saved_lowest_address = hp->lowest_address;
16836                     hp->background_saved_highest_address = hp->highest_address;
16837                 }
16838             }
16839 #else
16840             do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
16841             if (do_concurrent_p)
16842             {
16843                 background_saved_lowest_address = lowest_address;
16844                 background_saved_highest_address = highest_address;
16845             }
16846 #endif //MULTIPLE_HEAPS
16847
16848             if (do_concurrent_p)
16849             {
16850 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
16851                 SoftwareWriteWatch::EnableForGCHeap();
16852 #endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
16853
16854 #ifdef MULTIPLE_HEAPS
16855                 for (int i = 0; i < n_heaps; i++)
16856                     g_heaps[i]->current_bgc_state = bgc_initialized;
16857 #else
16858                 current_bgc_state = bgc_initialized;
16859 #endif //MULTIPLE_HEAPS
16860
16861                 int gen = check_for_ephemeral_alloc();
16862                 // always do a gen1 GC before we start BGC. 
16863                 // This is temporary for testing purpose.
16864                 //int gen = max_generation - 1;
16865                 dont_restart_ee_p = TRUE;
16866                 if (gen == -1)
16867                 {
16868                     // If we decide to not do a GC before the BGC we need to 
16869                     // restore the gen0 alloc context.
16870 #ifdef MULTIPLE_HEAPS
16871                     for (int i = 0; i < n_heaps; i++)
16872                     {
16873                         generation_allocation_pointer (g_heaps[i]->generation_of (0)) =  0;
16874                         generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
16875                     }
16876 #else
16877                     generation_allocation_pointer (youngest_generation) =  0;
16878                     generation_allocation_limit (youngest_generation) = 0;
16879 #endif //MULTIPLE_HEAPS
16880                 }
16881                 else
16882                 {
16883                     do_ephemeral_gc_p = TRUE;
16884
16885                     settings.init_mechanisms();
16886                     settings.condemned_generation = gen;
16887                     settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2;
16888                     do_pre_gc();
16889
16890                     // TODO BACKGROUND_GC need to add the profiling stuff here.
16891                     dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
16892                 }
16893
16894                 //clear the cards so they don't bleed in gen 1 during collection
16895                 // shouldn't this always be done at the beginning of any GC?
16896                 //clear_card_for_addresses (
16897                 //    generation_allocation_start (generation_of (0)),
16898                 //    heap_segment_allocated (ephemeral_heap_segment));
16899
16900                 if (!do_ephemeral_gc_p)
16901                 {
16902                     do_background_gc();
16903                 }
16904             }
16905             else
16906             {
16907                 settings.compaction = TRUE;
16908                 c_write (settings.concurrent, FALSE);
16909             }
16910
16911 #ifdef MULTIPLE_HEAPS
16912             gc_t_join.restart();
16913 #endif //MULTIPLE_HEAPS
16914         }
16915
16916         if (do_concurrent_p)
16917         {
16918             // At this point we are sure we'll be starting a BGC, so save its per heap data here.
16919             // global data is only calculated at the end of the GC so we don't need to worry about
16920             // FGCs overwriting it.
16921             memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap));
16922             memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
16923
16924             if (do_ephemeral_gc_p)
16925             {
16926                 dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
16927
16928                 gen_to_condemn_reasons.init();
16929                 gen_to_condemn_reasons.set_condition (gen_before_bgc);
16930                 gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
16931                 gc1();
16932 #ifdef MULTIPLE_HEAPS
16933                 gc_t_join.join(this, gc_join_bgc_after_ephemeral);
16934                 if (gc_t_join.joined())
16935 #endif //MULTIPLE_HEAPS
16936                 {
16937 #ifdef MULTIPLE_HEAPS
16938                     do_post_gc();
16939 #endif //MULTIPLE_HEAPS
16940                     settings = saved_bgc_settings;
16941                     assert (settings.concurrent);
16942
16943                     do_background_gc();
16944
16945 #ifdef MULTIPLE_HEAPS
16946                     gc_t_join.restart();
16947 #endif //MULTIPLE_HEAPS
16948                 }
16949             }
16950         }
16951         else
16952         {
16953             dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
16954             gc1();
16955         }
16956     }
16957     else
16958 #endif //BACKGROUND_GC
16959     {
16960         gc1();
16961     }
16962 #ifndef MULTIPLE_HEAPS
16963     allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp();
16964     allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
16965     fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
16966 #endif //MULTIPLE_HEAPS
16967
16968 done:
16969     if (settings.pause_mode == pause_no_gc)
16970         allocate_for_no_gc_after_gc();
16971
16972     int gn = settings.condemned_generation;
16973     return gn;
16974 }
16975
16976 #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
16977
16978 inline
16979 size_t& gc_heap::promoted_bytes(int thread)
16980 {
16981 #ifdef MULTIPLE_HEAPS
16982     return g_promoted [thread*16];
16983 #else //MULTIPLE_HEAPS
16984     UNREFERENCED_PARAMETER(thread);
16985     return g_promoted;
16986 #endif //MULTIPLE_HEAPS
16987 }
16988
16989 #ifdef INTERIOR_POINTERS
16990 heap_segment* gc_heap::find_segment (uint8_t* interior, BOOL small_segment_only_p)
16991 {
16992 #ifdef SEG_MAPPING_TABLE
16993     heap_segment* seg = seg_mapping_table_segment_of (interior);
16994     if (seg)
16995     {
16996         if (small_segment_only_p && heap_segment_loh_p (seg))
16997             return 0;
16998     }
16999     return seg;
17000 #else //SEG_MAPPING_TABLE
17001 #ifdef MULTIPLE_HEAPS
17002     for (int i = 0; i < gc_heap::n_heaps; i++)
17003     {
17004         gc_heap* h = gc_heap::g_heaps [i];
17005         hs = h->find_segment_per_heap (o, small_segment_only_p);
17006         if (hs)
17007         {
17008             break;
17009         }        
17010     }
17011 #else
17012     {
17013         gc_heap* h = pGenGCHeap;
17014         hs = h->find_segment_per_heap (o, small_segment_only_p);
17015     }
17016 #endif //MULTIPLE_HEAPS
17017 #endif //SEG_MAPPING_TABLE
17018 }
17019
17020 heap_segment* gc_heap::find_segment_per_heap (uint8_t* interior, BOOL small_segment_only_p)
17021 {
17022 #ifdef SEG_MAPPING_TABLE
17023     return find_segment (interior, small_segment_only_p);
17024 #else //SEG_MAPPING_TABLE
17025     if (in_range_for_segment (interior, ephemeral_heap_segment))
17026     {
17027         return ephemeral_heap_segment;
17028     }
17029     else
17030     {
17031         heap_segment* found_seg = 0;
17032
17033         {
17034             heap_segment* seg = generation_start_segment (generation_of (max_generation));
17035             do
17036             {
17037                 if (in_range_for_segment (interior, seg))
17038                 {
17039                     found_seg = seg;
17040                     goto end_find_segment;
17041                 }
17042
17043             } while ((seg = heap_segment_next (seg)) != 0);
17044         }
17045         if (!small_segment_only_p)
17046         {
17047 #ifdef BACKGROUND_GC
17048             {
17049                 ptrdiff_t delta = 0;
17050                 heap_segment* seg = segment_of (interior, delta);
17051                 if (seg && in_range_for_segment (interior, seg))
17052                 {
17053                     found_seg = seg;
17054                 }
17055                 goto end_find_segment;
17056             }
17057 #else //BACKGROUND_GC
17058             heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
17059             do
17060             {
17061                 if (in_range_for_segment(interior, seg))
17062                 {
17063                     found_seg = seg;
17064                     goto end_find_segment;
17065                 }
17066
17067             } while ((seg = heap_segment_next (seg)) != 0);
17068 #endif //BACKGROUND_GC
17069         }
17070 end_find_segment:
17071
17072         return found_seg;
17073     }
17074 #endif //SEG_MAPPING_TABLE
17075 }
17076 #endif //INTERIOR_POINTERS
17077
17078 #if !defined(_DEBUG) && !defined(__GNUC__)
17079 inline // This causes link errors if global optimization is off
17080 #endif //!_DEBUG && !__GNUC__
17081 gc_heap* gc_heap::heap_of (uint8_t* o)
17082 {
17083 #ifdef MULTIPLE_HEAPS
17084     if (o == 0)
17085         return g_heaps [0];
17086 #ifdef SEG_MAPPING_TABLE
17087     gc_heap* hp = seg_mapping_table_heap_of (o);
17088     return (hp ? hp : g_heaps[0]);
17089 #else //SEG_MAPPING_TABLE
17090     ptrdiff_t delta = 0;
17091     heap_segment* seg = segment_of (o, delta);
17092     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17093 #endif //SEG_MAPPING_TABLE
17094 #else //MULTIPLE_HEAPS
17095     UNREFERENCED_PARAMETER(o);
17096     return __this;
17097 #endif //MULTIPLE_HEAPS
17098 }
17099
17100 inline
17101 gc_heap* gc_heap::heap_of_gc (uint8_t* o)
17102 {
17103 #ifdef MULTIPLE_HEAPS
17104     if (o == 0)
17105         return g_heaps [0];
17106 #ifdef SEG_MAPPING_TABLE
17107     gc_heap* hp = seg_mapping_table_heap_of_gc (o);
17108     return (hp ? hp : g_heaps[0]);
17109 #else //SEG_MAPPING_TABLE
17110     ptrdiff_t delta = 0;
17111     heap_segment* seg = segment_of (o, delta);
17112     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17113 #endif //SEG_MAPPING_TABLE
17114 #else //MULTIPLE_HEAPS
17115     UNREFERENCED_PARAMETER(o);
17116     return __this;
17117 #endif //MULTIPLE_HEAPS
17118 }
17119
17120 #ifdef INTERIOR_POINTERS
17121 // will find all heap objects (large and small)
17122 uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
17123 {
17124     if (!gen0_bricks_cleared)
17125     {
17126 #ifdef MULTIPLE_HEAPS
17127         assert (!"Should have already been done in server GC");
17128 #endif //MULTIPLE_HEAPS
17129         gen0_bricks_cleared = TRUE;
17130         //initialize brick table for gen 0
17131         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
17132              b < brick_of (align_on_brick
17133                            (heap_segment_allocated (ephemeral_heap_segment)));
17134              b++)
17135         {
17136             set_brick (b, -1);
17137         }
17138     }
17139 #ifdef FFIND_OBJECT
17140     //indicate that in the future this needs to be done during allocation
17141 #ifdef MULTIPLE_HEAPS
17142     gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
17143 #else
17144     gen0_must_clear_bricks = FFIND_DECAY;
17145 #endif //MULTIPLE_HEAPS
17146 #endif //FFIND_OBJECT
17147
17148     int brick_entry = get_brick_entry(brick_of (interior));
17149     if (brick_entry == 0)
17150     {
17151         // this is a pointer to a large object
17152         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17153         if (seg
17154 #ifdef FEATURE_CONSERVATIVE_GC
17155             && (GCConfig::GetConservativeGC() || interior <= heap_segment_allocated(seg))
17156 #endif
17157             )
17158         {
17159             // If interior falls within the first free object at the beginning of a generation,
17160             // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
17161             int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
17162 #ifdef FEATURE_CONSERVATIVE_GC
17163                                                        || (GCConfig::GetConservativeGC() && !heap_segment_loh_p (seg))
17164 #endif
17165                                                       );
17166             //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
17167             assert (interior < heap_segment_allocated (seg));
17168
17169             uint8_t* o = heap_segment_mem (seg);
17170             while (o < heap_segment_allocated (seg))
17171             {
17172                 uint8_t* next_o = o + Align (size (o), align_const);
17173                 assert (next_o > o);
17174                 if ((o <= interior) && (interior < next_o))
17175                 return o;
17176                 o = next_o;
17177             }
17178             return 0;
17179         }
17180         else
17181         {
17182             return 0;
17183         }
17184     }
17185     else if (interior >= low)
17186     {
17187         heap_segment* seg = find_segment_per_heap (interior, TRUE);
17188         if (seg)
17189         {
17190 #ifdef FEATURE_CONSERVATIVE_GC
17191             if (interior >= heap_segment_allocated (seg))
17192                 return 0;
17193 #else
17194             assert (interior < heap_segment_allocated (seg));
17195 #endif
17196             uint8_t* o = find_first_object (interior, heap_segment_mem (seg));
17197             return o;
17198         }
17199         else
17200             return 0;
17201     }
17202     else
17203         return 0;
17204 }
17205
17206 uint8_t*
17207 gc_heap::find_object_for_relocation (uint8_t* interior, uint8_t* low, uint8_t* high)
17208 {
17209     uint8_t* old_address = interior;
17210     if (!((old_address >= low) && (old_address < high)))
17211         return 0;
17212     uint8_t* plug = 0;
17213     size_t  brick = brick_of (old_address);
17214     int    brick_entry =  brick_table [ brick ];
17215     if (brick_entry != 0)
17216     {
17217     retry:
17218         {
17219             while (brick_entry < 0)
17220             {
17221                 brick = (brick + brick_entry);
17222                 brick_entry =  brick_table [ brick ];
17223             }
17224             uint8_t* old_loc = old_address;
17225             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
17226                                       old_loc);
17227             if (node <= old_loc)
17228                 plug = node;
17229             else
17230             {
17231                 brick = brick - 1;
17232                 brick_entry =  brick_table [ brick ];
17233                 goto retry;
17234             }
17235
17236         }
17237         assert (plug);
17238         //find the object by going along the plug
17239         uint8_t* o = plug;
17240         while (o <= interior)
17241         {
17242             uint8_t* next_o = o + Align (size (o));
17243             assert (next_o > o);
17244             if (next_o > interior)
17245             {
17246                 break;
17247             }
17248             o = next_o;
17249         }
17250         assert ((o <= interior) && ((o + Align (size (o))) > interior));
17251         return o;
17252     }
17253     else
17254     {
17255         // this is a pointer to a large object
17256         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17257         if (seg)
17258         {
17259             assert (interior < heap_segment_allocated (seg));
17260
17261             uint8_t* o = heap_segment_mem (seg);
17262             while (o < heap_segment_allocated (seg))
17263             {
17264                 uint8_t* next_o = o + Align (size (o));
17265                 assert (next_o > o);
17266                 if ((o < interior) && (interior < next_o))
17267                 return o;
17268                 o = next_o;
17269             }
17270             return 0;
17271         }
17272         else
17273             {
17274             return 0;
17275         }
17276     }
17277 }
17278 #else //INTERIOR_POINTERS
17279 inline
17280 uint8_t* gc_heap::find_object (uint8_t* o, uint8_t* low)
17281 {
17282     return o;
17283 }
17284 #endif //INTERIOR_POINTERS
17285
17286 #ifdef MARK_LIST
17287 #ifdef GC_CONFIG_DRIVEN
17288 #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;}
17289 #else
17290 #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;}
17291 #endif //GC_CONFIG_DRIVEN
17292 #else //MARK_LIST
17293 #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17294 #endif //MARK_LIST
17295
17296 #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17297
17298 #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
17299
17300 inline
17301 BOOL gc_heap::gc_mark1 (uint8_t* o)
17302 {
17303     BOOL marked = !marked (o);
17304     set_marked (o);
17305     dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
17306     return marked;
17307 }
17308
17309 inline
17310 BOOL gc_heap::gc_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17311 {
17312     BOOL marked = FALSE;
17313     if ((o >= low) && (o < high))
17314         marked = gc_mark1 (o);
17315 #ifdef MULTIPLE_HEAPS
17316     else if (o)
17317     {
17318         //find the heap
17319         gc_heap* hp = heap_of_gc (o);
17320         assert (hp);
17321         if ((o >= hp->gc_low) && (o < hp->gc_high))
17322             marked = gc_mark1 (o);
17323     }
17324 #ifdef SNOOP_STATS
17325     snoop_stat.objects_checked_count++;
17326
17327     if (marked)
17328     {
17329         snoop_stat.objects_marked_count++;
17330     }
17331     if (!o)
17332     {
17333         snoop_stat.zero_ref_count++;
17334     }
17335
17336 #endif //SNOOP_STATS
17337 #endif //MULTIPLE_HEAPS
17338     return marked;
17339 }
17340
17341 #ifdef BACKGROUND_GC
17342
17343 inline
17344 BOOL gc_heap::background_marked (uint8_t* o)
17345 {
17346     return mark_array_marked (o);
17347 }
17348 inline
17349 BOOL gc_heap::background_mark1 (uint8_t* o)
17350 {
17351     BOOL to_mark = !mark_array_marked (o);
17352
17353     dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
17354     if (to_mark)
17355     {
17356         mark_array_set_marked (o);
17357         dprintf (4, ("n*%Ix*n", (size_t)o));
17358         return TRUE;
17359     }
17360     else
17361         return FALSE;
17362 }
17363
17364 // TODO: we could consider filtering out NULL's here instead of going to 
17365 // look for it on other heaps
17366 inline
17367 BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17368 {
17369     BOOL marked = FALSE;
17370     if ((o >= low) && (o < high))
17371         marked = background_mark1 (o);
17372 #ifdef MULTIPLE_HEAPS
17373     else if (o)
17374     {
17375         //find the heap
17376         gc_heap* hp = heap_of (o);
17377         assert (hp);
17378         if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
17379             marked = background_mark1 (o);
17380     }
17381 #endif //MULTIPLE_HEAPS
17382     return marked;
17383 }
17384
17385 #endif //BACKGROUND_GC
17386
17387 inline
17388 uint8_t* gc_heap::next_end (heap_segment* seg, uint8_t* f)
17389 {
17390     if (seg == ephemeral_heap_segment)
17391         return  f;
17392     else
17393         return  heap_segment_allocated (seg);
17394 }
17395
17396 #define new_start() {if (ppstop <= start) {break;} else {parm = start}}
17397 #define ignore_start 0
17398 #define use_start 1
17399
17400 #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp)      \
17401 {                                                                           \
17402     CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt));           \
17403     CGCDescSeries* cur = map->GetHighestSeries();                           \
17404     ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries();                        \
17405                                                                             \
17406     if (cnt >= 0)                                                           \
17407     {                                                                       \
17408         CGCDescSeries* last = map->GetLowestSeries();                       \
17409         uint8_t** parm = 0;                                                 \
17410         do                                                                  \
17411         {                                                                   \
17412             assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset()));     \
17413             parm = (uint8_t**)((o) + cur->GetSeriesOffset());               \
17414             uint8_t** ppstop =                                              \
17415                 (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\
17416             if (!start_useful || (uint8_t*)ppstop > (start))                \
17417             {                                                               \
17418                 if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\
17419                 while (parm < ppstop)                                       \
17420                 {                                                           \
17421                    {exp}                                                    \
17422                    parm++;                                                  \
17423                 }                                                           \
17424             }                                                               \
17425             cur--;                                                          \
17426                                                                             \
17427         } while (cur >= last);                                              \
17428     }                                                                       \
17429     else                                                                    \
17430     {                                                                       \
17431         /* Handle the repeating case - array of valuetypes */               \
17432         uint8_t** parm = (uint8_t**)((o) + cur->startoffset);               \
17433         if (start_useful && start > (uint8_t*)parm)                         \
17434         {                                                                   \
17435             ptrdiff_t cs = mt->RawGetComponentSize();                         \
17436             parm = (uint8_t**)((uint8_t*)parm + (((start) - (uint8_t*)parm)/cs)*cs); \
17437         }                                                                   \
17438         while ((uint8_t*)parm < ((o)+(size)-plug_skew))                     \
17439         {                                                                   \
17440             for (ptrdiff_t __i = 0; __i > cnt; __i--)                         \
17441             {                                                               \
17442                 HALF_SIZE_T skip =  cur->val_serie[__i].skip;               \
17443                 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs;              \
17444                 uint8_t** ppstop = parm + nptrs;                            \
17445                 if (!start_useful || (uint8_t*)ppstop > (start))            \
17446                 {                                                           \
17447                     if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);      \
17448                     do                                                      \
17449                     {                                                       \
17450                        {exp}                                                \
17451                        parm++;                                              \
17452                     } while (parm < ppstop);                                \
17453                 }                                                           \
17454                 parm = (uint8_t**)((uint8_t*)ppstop + skip);                \
17455             }                                                               \
17456         }                                                                   \
17457     }                                                                       \
17458 }
17459
17460 #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
17461
17462 // 1 thing to note about this macro:
17463 // 1) you can use *parm safely but in general you don't want to use parm 
17464 // because for the collectible types it's not an address on the managed heap.
17465 #ifndef COLLECTIBLE_CLASS
17466 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17467 {                                                                           \
17468     if (header(o)->ContainsPointers())                                      \
17469     {                                                                       \
17470         go_through_object_nostart(mt,o,size,parm,exp);                      \
17471     }                                                                       \
17472 }
17473 #else //COLLECTIBLE_CLASS
17474 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17475 {                                                                           \
17476     if (header(o)->Collectible())                                           \
17477     {                                                                       \
17478         uint8_t* class_obj = get_class_object (o);                             \
17479         uint8_t** parm = &class_obj;                                           \
17480         do {exp} while (false);                                             \
17481     }                                                                       \
17482     if (header(o)->ContainsPointers())                                      \
17483     {                                                                       \
17484         go_through_object_nostart(mt,o,size,parm,exp);                      \
17485     }                                                                       \
17486 }
17487 #endif //COLLECTIBLE_CLASS
17488
17489 // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
17490 void gc_heap::enque_pinned_plug (uint8_t* plug,
17491                                  BOOL save_pre_plug_info_p, 
17492                                  uint8_t* last_object_in_last_plug)
17493 {
17494     if (mark_stack_array_length <= mark_stack_tos)
17495     {
17496         if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
17497         {
17498             // we don't want to continue here due to security
17499             // risks. This happens very rarely and fixing it in the
17500             // way so that we can continue is a bit involved and will
17501             // not be done in Dev10.
17502             GCToEEInterface::HandleFatalError(CORINFO_EXCEPTION_GC);
17503         }
17504     }
17505
17506     dprintf (3, ("enqueuing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d", 
17507         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)));
17508     mark& m = mark_stack_array[mark_stack_tos];
17509     m.first = plug;
17510     // Must be set now because if we have a short object we'll need the value of saved_pre_p.
17511     m.saved_pre_p = save_pre_plug_info_p;
17512
17513     if (save_pre_plug_info_p)
17514     {
17515 #ifdef SHORT_PLUGS
17516         BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17517         if (is_padded)
17518             clear_plug_padded (last_object_in_last_plug);
17519 #endif //SHORT_PLUGS
17520         memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17521 #ifdef SHORT_PLUGS
17522         if (is_padded)
17523             set_plug_padded (last_object_in_last_plug);
17524 #endif //SHORT_PLUGS
17525
17526         memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17527
17528         // If the last object in the last plug is too short, it requires special handling.
17529         size_t last_obj_size = plug - last_object_in_last_plug;
17530         if (last_obj_size < min_pre_pin_obj_size)
17531         {
17532             record_interesting_data_point (idp_pre_short);
17533 #ifdef SHORT_PLUGS
17534             if (is_padded)
17535                 record_interesting_data_point (idp_pre_short_padded);
17536 #endif //SHORT_PLUGS
17537             dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!", 
17538                          last_object_in_last_plug, plug));
17539             // Need to set the short bit regardless of having refs or not because we need to 
17540             // indicate that this object is not walkable.
17541             m.set_pre_short();
17542
17543 #ifdef COLLECTIBLE_CLASS
17544             if (is_collectible (last_object_in_last_plug))
17545             {
17546                 m.set_pre_short_collectible();
17547             }
17548 #endif //COLLECTIBLE_CLASS
17549
17550             if (contain_pointers (last_object_in_last_plug))
17551             {
17552                 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17553
17554                 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17555                     {
17556                         size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17557                         dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17558                         m.set_pre_short_bit (gap_offset);
17559                     }
17560                 );
17561             }
17562         }
17563     }
17564
17565     m.saved_post_p = FALSE;
17566 }
17567
17568 void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_object_in_last_plug, uint8_t* post_plug)
17569 {
17570     UNREFERENCED_PARAMETER(last_pinned_plug);
17571
17572     mark& m = mark_stack_array[mark_stack_tos - 1];
17573     assert (last_pinned_plug == m.first);
17574     m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]);
17575
17576 #ifdef SHORT_PLUGS
17577     BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17578     if (is_padded)
17579         clear_plug_padded (last_object_in_last_plug);
17580 #endif //SHORT_PLUGS
17581     memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17582 #ifdef SHORT_PLUGS
17583     if (is_padded)
17584         set_plug_padded (last_object_in_last_plug);
17585 #endif //SHORT_PLUGS
17586
17587     memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17588
17589     // This is important - we need to clear all bits here except the last one.
17590     m.saved_post_p = TRUE;
17591
17592 #ifdef _DEBUG
17593     m.saved_post_plug_debug.gap = 1;
17594 #endif //_DEBUG
17595
17596     dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
17597
17598     size_t last_obj_size = post_plug - last_object_in_last_plug;
17599     if (last_obj_size < min_pre_pin_obj_size)
17600     {
17601         dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
17602         record_interesting_data_point (idp_post_short);
17603 #ifdef SHORT_PLUGS
17604         if (is_padded)
17605             record_interesting_data_point (idp_post_short_padded);
17606 #endif //SHORT_PLUGS
17607         m.set_post_short();
17608         verify_pinned_queue_p = TRUE;
17609
17610 #ifdef COLLECTIBLE_CLASS
17611         if (is_collectible (last_object_in_last_plug))
17612         {
17613             m.set_post_short_collectible();
17614         }
17615 #endif //COLLECTIBLE_CLASS
17616
17617         if (contain_pointers (last_object_in_last_plug))
17618         {
17619             dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17620
17621             // TODO: since we won't be able to walk this object in relocation, we still need to
17622             // take care of collectible assemblies here.
17623             go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17624                 {
17625                     size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17626                     dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17627                     m.set_post_short_bit (gap_offset);
17628                 }
17629             );
17630         }
17631     }
17632 }
17633
17634 //#define PREFETCH
17635 #ifdef PREFETCH
17636 __declspec(naked) void __fastcall Prefetch(void* addr)
17637 {
17638    __asm {
17639        PREFETCHT0 [ECX]
17640         ret
17641     };
17642 }
17643 #else //PREFETCH
17644 inline void Prefetch (void* addr)
17645 {
17646     UNREFERENCED_PARAMETER(addr);
17647 }
17648 #endif //PREFETCH
17649 #ifdef MH_SC_MARK
17650 inline
17651 VOLATILE(uint8_t*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
17652 {
17653     return ((VOLATILE(uint8_t*)*)(hp->mark_stack_array))[index];
17654 }
17655
17656 #endif //MH_SC_MARK
17657
17658 #define stolen 2
17659 #define partial 1
17660 #define partial_object 3
17661 inline 
17662 uint8_t* ref_from_slot (uint8_t* r)
17663 {
17664     return (uint8_t*)((size_t)r & ~(stolen | partial));
17665 }
17666 inline
17667 BOOL stolen_p (uint8_t* r)
17668 {
17669     return (((size_t)r&2) && !((size_t)r&1));
17670 }
17671 inline 
17672 BOOL ready_p (uint8_t* r)
17673 {
17674     return ((size_t)r != 1);
17675 }
17676 inline
17677 BOOL partial_p (uint8_t* r)
17678 {
17679     return (((size_t)r&1) && !((size_t)r&2));
17680 }
17681 inline 
17682 BOOL straight_ref_p (uint8_t* r)
17683 {
17684     return (!stolen_p (r) && !partial_p (r));
17685 }
17686 inline 
17687 BOOL partial_object_p (uint8_t* r)
17688 {
17689     return (((size_t)r & partial_object) == partial_object);
17690 }
17691 inline
17692 BOOL ref_p (uint8_t* r)
17693 {
17694     return (straight_ref_p (r) || partial_object_p (r));
17695 }
17696
17697 void gc_heap::mark_object_simple1 (uint8_t* oo, uint8_t* start THREAD_NUMBER_DCL)
17698 {
17699     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)mark_stack_array;
17700     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)&mark_stack_array[mark_stack_array_length];
17701     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_base = mark_stack_tos;
17702 #ifdef SORT_MARK_STACK
17703     SERVER_SC_MARK_VOLATILE(uint8_t*)* sorted_tos = mark_stack_base;
17704 #endif //SORT_MARK_STACK
17705
17706     // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't 
17707     // update mark list.
17708     BOOL  full_p = (settings.condemned_generation == max_generation);
17709
17710     assert ((start >= oo) && (start < oo+size(oo)));
17711
17712 #ifndef MH_SC_MARK
17713     *mark_stack_tos = oo;
17714 #endif //!MH_SC_MARK
17715
17716     while (1)
17717     {
17718 #ifdef MULTIPLE_HEAPS
17719 #else  //MULTIPLE_HEAPS
17720         const int thread = 0;
17721 #endif //MULTIPLE_HEAPS
17722
17723         if (oo && ((size_t)oo != 4))
17724         {
17725             size_t s = 0; 
17726             if (stolen_p (oo))
17727             {
17728                 --mark_stack_tos;
17729                 goto next_level;
17730             }
17731             else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
17732             {
17733                 BOOL overflow_p = FALSE;
17734
17735                 if (mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit  - 1))
17736                 {
17737                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
17738                     if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
17739                     {
17740                         overflow_p = TRUE;
17741                     }
17742                 }
17743                 
17744                 if (overflow_p == FALSE)
17745                 {
17746                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17747
17748                     go_through_object_cl (method_table(oo), oo, s, ppslot,
17749                                           {
17750                                               uint8_t* o = *ppslot;
17751                                               Prefetch(o);
17752                                               if (gc_mark (o, gc_low, gc_high))
17753                                               {
17754                                                   if (full_p)
17755                                                   {
17756                                                       m_boundary_fullgc (o);
17757                                                   }
17758                                                   else
17759                                                   {
17760                                                       m_boundary (o);
17761                                                   }
17762                                                   size_t obj_size = size (o);
17763                                                   promoted_bytes (thread) += obj_size;
17764                                                   if (contain_pointers_or_collectible (o))
17765                                                   {
17766                                                       *(mark_stack_tos++) = o;
17767                                                   }
17768                                               }
17769                                           }
17770                         );
17771                 }
17772                 else
17773                 {
17774                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17775                     min_overflow_address = min (min_overflow_address, oo);
17776                     max_overflow_address = max (max_overflow_address, oo);
17777                 }
17778             }
17779             else
17780             {
17781                 if (partial_p (oo))
17782                 {
17783                     start = ref_from_slot (oo);
17784                     oo = ref_from_slot (*(--mark_stack_tos));
17785                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
17786                     assert ((oo < start) && (start < (oo + size (oo))));
17787                 }
17788 #ifdef COLLECTIBLE_CLASS
17789                 else
17790                 {
17791                     // If there's a class object, push it now. We are guaranteed to have the slot since
17792                     // we just popped one object off.
17793                     if (is_collectible (oo))
17794                     {
17795                         uint8_t* class_obj = get_class_object (oo);
17796                         if (gc_mark (class_obj, gc_low, gc_high))
17797                         {
17798                             if (full_p)
17799                             {
17800                                 m_boundary_fullgc (class_obj);
17801                             }
17802                             else
17803                             {
17804                                 m_boundary (class_obj);
17805                             }
17806
17807                             size_t obj_size = size (class_obj);
17808                             promoted_bytes (thread) += obj_size;
17809                             *(mark_stack_tos++) = class_obj;
17810                         }
17811                     }
17812                 }
17813 #endif //COLLECTIBLE_CLASS
17814
17815                 s = size (oo);
17816                 
17817                 BOOL overflow_p = FALSE;
17818             
17819                 if (mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
17820                 {
17821                     overflow_p = TRUE;
17822                 }
17823                 if (overflow_p == FALSE)
17824                 {
17825                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17826
17827                     //push the object and its current 
17828                     SERVER_SC_MARK_VOLATILE(uint8_t*)* place = ++mark_stack_tos;
17829                     mark_stack_tos++;
17830 #ifdef MH_SC_MARK
17831                     *(place-1) = 0;
17832                     *(place) = (uint8_t*)partial;
17833 #endif //MH_SC_MARK
17834                     int i = num_partial_refs; 
17835                     uint8_t* ref_to_continue = 0;
17836
17837                     go_through_object (method_table(oo), oo, s, ppslot,
17838                                        start, use_start, (oo + s),
17839                                        {
17840                                            uint8_t* o = *ppslot;
17841                                            Prefetch(o);
17842                                            if (gc_mark (o, gc_low, gc_high))
17843                                            {
17844                                                 if (full_p)
17845                                                 {
17846                                                     m_boundary_fullgc (o);
17847                                                 }
17848                                                 else
17849                                                 {
17850                                                     m_boundary (o);
17851                                                 }
17852                                                 size_t obj_size = size (o);
17853                                                 promoted_bytes (thread) += obj_size;
17854                                                 if (contain_pointers_or_collectible (o))
17855                                                 {
17856                                                     *(mark_stack_tos++) = o;
17857                                                     if (--i == 0)
17858                                                     {
17859                                                         ref_to_continue = (uint8_t*)((size_t)(ppslot+1) | partial);
17860                                                         goto more_to_do;
17861                                                     }
17862
17863                                                 }
17864                                            }
17865
17866                                        }
17867                         );
17868                     //we are finished with this object
17869                     assert (ref_to_continue == 0);
17870 #ifdef MH_SC_MARK
17871                     assert ((*(place-1)) == (uint8_t*)0);
17872 #else //MH_SC_MARK
17873                     *(place-1) = 0;
17874 #endif //MH_SC_MARK
17875                     *place = 0; 
17876                     // shouldn't we decrease tos by 2 here??
17877
17878 more_to_do:
17879                     if (ref_to_continue)
17880                     {
17881                         //update the start
17882 #ifdef MH_SC_MARK
17883                         assert ((*(place-1)) == (uint8_t*)0);
17884                         *(place-1) = (uint8_t*)((size_t)oo | partial_object);
17885                         assert (((*place) == (uint8_t*)1) || ((*place) == (uint8_t*)2));
17886 #endif //MH_SC_MARK
17887                         *place = ref_to_continue;
17888                     }
17889                 }
17890                 else
17891                 {
17892                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17893                     min_overflow_address = min (min_overflow_address, oo);
17894                     max_overflow_address = max (max_overflow_address, oo);
17895                 }
17896             }
17897 #ifdef SORT_MARK_STACK
17898             if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
17899             {
17900                 rqsort1 (sorted_tos, mark_stack_tos-1);
17901                 sorted_tos = mark_stack_tos-1;
17902             }
17903 #endif //SORT_MARK_STACK
17904         }
17905     next_level:
17906         if (!(mark_stack_empty_p()))
17907         {
17908             oo = *(--mark_stack_tos);
17909             start = oo;
17910
17911 #ifdef SORT_MARK_STACK
17912             sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
17913 #endif //SORT_MARK_STACK
17914         }
17915         else
17916             break;
17917     }
17918 }
17919
17920 #ifdef MH_SC_MARK
17921 BOOL same_numa_node_p (int hn1, int hn2)
17922 {
17923     return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
17924 }
17925
17926 int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
17927 {
17928     int hn = (current_buddy+1)%n_heaps;
17929     while (hn != current_buddy)
17930     {
17931         if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
17932             return hn;
17933         hn = (hn+1)%n_heaps;
17934     }
17935     return current_buddy;
17936 }
17937
17938 void 
17939 gc_heap::mark_steal()
17940 {
17941     mark_stack_busy() = 0;
17942     //clear the mark stack in the snooping range
17943     for (int i = 0; i < max_snoop_level; i++)
17944     {
17945         ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
17946     }
17947
17948     //pick the next heap as our buddy
17949     int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
17950
17951 #ifdef SNOOP_STATS
17952         dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
17953         uint32_t begin_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
17954 #endif //SNOOP_STATS
17955
17956     int idle_loop_count = 0; 
17957     int first_not_ready_level = 0;
17958
17959     while (1)
17960     {
17961         gc_heap* hp = g_heaps [thpn];
17962         int level = first_not_ready_level;
17963         first_not_ready_level = 0; 
17964
17965         while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
17966         {
17967             idle_loop_count = 0; 
17968 #ifdef SNOOP_STATS
17969             snoop_stat.busy_count++;
17970             dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix", 
17971                                  heap_number, level, (int)((uint8_t**)(hp->mark_stack_array))[level]));
17972 #endif //SNOOP_STATS
17973
17974             uint8_t* o = ref_mark_stack (hp, level);
17975
17976             uint8_t* start = o;
17977             if (ref_p (o))
17978             {
17979                 mark_stack_busy() = 1;
17980
17981                 BOOL success = TRUE;
17982                 uint8_t* next = (ref_mark_stack (hp, level+1));
17983                 if (ref_p (next))
17984                 {
17985                     if (((size_t)o > 4) && !partial_object_p (o))
17986                     {
17987                         //this is a normal object, not a partial mark tuple
17988                         //success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
17989                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), (uint8_t*)4, o)==o);
17990 #ifdef SNOOP_STATS
17991                         snoop_stat.interlocked_count++;
17992                         if (success)
17993                             snoop_stat.normal_count++;
17994 #endif //SNOOP_STATS
17995                     }
17996                     else
17997                     {
17998                         //it is a stolen entry, or beginning/ending of a partial mark
17999                         level++;
18000 #ifdef SNOOP_STATS
18001                         snoop_stat.stolen_or_pm_count++;
18002 #endif //SNOOP_STATS
18003                         success = FALSE;
18004                     }
18005                 }
18006                 else if (stolen_p (next))
18007                 {
18008                     //ignore the stolen guy and go to the next level
18009                     success = FALSE;
18010                     level+=2;
18011 #ifdef SNOOP_STATS
18012                     snoop_stat.stolen_entry_count++;
18013 #endif //SNOOP_STATS
18014                 }
18015                 else
18016                 {
18017                     assert (partial_p (next));
18018                     start = ref_from_slot (next);
18019                     //re-read the object
18020                     o = ref_from_slot (ref_mark_stack (hp, level));
18021                     if (o && start)
18022                     {
18023                         //steal the object
18024                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level+1), (uint8_t*)stolen, next)==next);
18025 #ifdef SNOOP_STATS
18026                         snoop_stat.interlocked_count++;
18027                         if (success)
18028                         {
18029                             snoop_stat.partial_mark_parent_count++;                    
18030                         }
18031 #endif //SNOOP_STATS
18032                     }
18033                     else
18034                     {
18035                         // stack is not ready, or o is completely different from the last time we read from this stack level.
18036                         // go up 2 levels to steal children or totally unrelated objects.
18037                         success = FALSE;
18038                         if (first_not_ready_level == 0)
18039                         {
18040                             first_not_ready_level = level;
18041                         }
18042                         level+=2;
18043 #ifdef SNOOP_STATS
18044                         snoop_stat.pm_not_ready_count++;
18045 #endif //SNOOP_STATS                        
18046                     }
18047                 }
18048                 if (success)
18049                 {
18050
18051 #ifdef SNOOP_STATS
18052                     dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
18053                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18054                             (GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18055                     uint32_t start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
18056 #endif //SNOOP_STATS
18057
18058                     mark_object_simple1 (o, start, heap_number);
18059
18060 #ifdef SNOOP_STATS
18061                     dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
18062                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18063                             (GCToOSInterface::GetLowPrecisionTimeStamp()-start_tick),(GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18064 #endif //SNOOP_STATS
18065
18066                     mark_stack_busy() = 0;
18067
18068                     //clear the mark stack in snooping range
18069                     for (int i = 0; i < max_snoop_level; i++)
18070                     {
18071                         if (((uint8_t**)mark_stack_array)[i] != 0)
18072                         {
18073                             ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18074 #ifdef SNOOP_STATS
18075                             snoop_stat.stack_bottom_clear_count++;
18076 #endif //SNOOP_STATS
18077                         }
18078                     }
18079
18080                     level = 0; 
18081                 }
18082                 mark_stack_busy() = 0;
18083             }
18084             else
18085             {
18086                 //slot is either partial or stolen
18087                 level++;
18088             }
18089         }
18090         if ((first_not_ready_level != 0) && hp->mark_stack_busy())
18091         {
18092             continue;
18093         } 
18094         if (!hp->mark_stack_busy())
18095         {
18096             first_not_ready_level = 0; 
18097             idle_loop_count++;
18098
18099             if ((idle_loop_count % (6) )==1)
18100             {
18101 #ifdef SNOOP_STATS
18102                 snoop_stat.switch_to_thread_count++;
18103 #endif //SNOOP_STATS
18104                 GCToOSInterface::Sleep(1);
18105             }
18106             int free_count = 1;
18107 #ifdef SNOOP_STATS
18108             snoop_stat.stack_idle_count++;
18109             //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
18110 #endif //SNOOP_STATS
18111             for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
18112             {
18113                 if (!((g_heaps [hpn])->mark_stack_busy()))
18114                 {
18115                     free_count++;
18116 #ifdef SNOOP_STATS
18117                 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
18118 #endif //SNOOP_STATS
18119                 }
18120                 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
18121                 {
18122                     thpn = hpn;
18123                     break;
18124                 }
18125                 hpn = (hpn+1)%n_heaps;
18126                 YieldProcessor();
18127             }
18128             if (free_count == n_heaps)
18129             {
18130                 break;
18131             }
18132         }
18133     }
18134 }
18135
18136 inline
18137 BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
18138 {
18139 #ifdef SNOOP_STATS
18140     snoop_stat.check_level_count++;
18141 #endif //SNOOP_STATS
18142     return (next_heap->mark_stack_busy()>=1);
18143 }
18144 #endif //MH_SC_MARK
18145
18146 #ifdef SNOOP_STATS
18147 void gc_heap::print_snoop_stat()
18148 {
18149     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18150         "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
18151     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
18152         snoop_stat.heap_index,
18153         snoop_stat.objects_checked_count,
18154         snoop_stat.zero_ref_count,
18155         snoop_stat.objects_marked_count,
18156         snoop_stat.stolen_stack_count,
18157         snoop_stat.partial_stack_count,
18158         snoop_stat.normal_stack_count,
18159         snoop_stat.non_stack_count));
18160     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18161         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
18162     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18163         snoop_stat.heap_index,
18164         snoop_stat.check_level_count,
18165         snoop_stat.busy_count,
18166         snoop_stat.interlocked_count,
18167         snoop_stat.partial_mark_parent_count,
18168         snoop_stat.stolen_or_pm_count,
18169         snoop_stat.stolen_entry_count,
18170         snoop_stat.pm_not_ready_count,
18171         snoop_stat.normal_count,
18172         snoop_stat.stack_bottom_clear_count));
18173
18174     printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n", 
18175         "heap", "check", "zero", "mark", "idle", "switch");
18176     printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
18177         snoop_stat.heap_index,
18178         snoop_stat.objects_checked_count,
18179         snoop_stat.zero_ref_count,
18180         snoop_stat.objects_marked_count,
18181         snoop_stat.stack_idle_count,
18182         snoop_stat.switch_to_thread_count);
18183     printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
18184         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18185     printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18186         snoop_stat.heap_index,
18187         snoop_stat.check_level_count,
18188         snoop_stat.busy_count,
18189         snoop_stat.interlocked_count,
18190         snoop_stat.partial_mark_parent_count,
18191         snoop_stat.stolen_or_pm_count,
18192         snoop_stat.stolen_entry_count,
18193         snoop_stat.pm_not_ready_count,
18194         snoop_stat.normal_count,
18195         snoop_stat.stack_bottom_clear_count);
18196 }
18197 #endif //SNOOP_STATS
18198
18199 #ifdef HEAP_ANALYZE
18200 void
18201 gc_heap::ha_mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18202 {
18203     if (!internal_root_array)
18204     {
18205         internal_root_array = new (nothrow) uint8_t* [internal_root_array_length];
18206         if (!internal_root_array)
18207         {
18208             heap_analyze_success = FALSE;
18209         }
18210     }
18211
18212     if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
18213     {
18214         size_t new_size = 2*internal_root_array_length;
18215
18216         uint64_t available_physical = 0;
18217         get_memory_info (NULL, &available_physical);
18218         if (new_size > (size_t)(available_physical / 10))
18219         {
18220             heap_analyze_success = FALSE;
18221         }
18222         else
18223         {
18224             uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18225             if (tmp)
18226             {
18227                 memcpy (tmp, internal_root_array,
18228                         internal_root_array_length*sizeof (uint8_t*));
18229                 delete[] internal_root_array;
18230                 internal_root_array = tmp;
18231                 internal_root_array_length = new_size;
18232             }
18233             else
18234             {
18235                 heap_analyze_success = FALSE;
18236             }
18237         }
18238     }
18239
18240     if (heap_analyze_success)
18241     {
18242         PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
18243
18244         uint8_t* ref = (uint8_t*)po;
18245         if (!current_obj || 
18246             !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
18247         {
18248             gc_heap* hp = gc_heap::heap_of (ref);
18249             current_obj = hp->find_object (ref, hp->lowest_address);
18250             current_obj_size = size (current_obj);
18251
18252             internal_root_array[internal_root_array_index] = current_obj;
18253             internal_root_array_index++;
18254         }
18255     }
18256
18257     mark_object_simple (po THREAD_NUMBER_ARG);
18258 }
18259 #endif //HEAP_ANALYZE
18260
18261 //this method assumes that *po is in the [low. high[ range
18262 void
18263 gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18264 {
18265     uint8_t* o = *po;
18266 #ifdef MULTIPLE_HEAPS
18267 #else  //MULTIPLE_HEAPS
18268     const int thread = 0;
18269 #endif //MULTIPLE_HEAPS
18270     {
18271 #ifdef SNOOP_STATS
18272         snoop_stat.objects_checked_count++;
18273 #endif //SNOOP_STATS
18274
18275         if (gc_mark1 (o))
18276         {
18277             m_boundary (o);
18278             size_t s = size (o);
18279             promoted_bytes (thread) += s;
18280             {
18281                 go_through_object_cl (method_table(o), o, s, poo,
18282                                         {
18283                                             uint8_t* oo = *poo;
18284                                             if (gc_mark (oo, gc_low, gc_high))
18285                                             {
18286                                                 m_boundary (oo);
18287                                                 size_t obj_size = size (oo);
18288                                                 promoted_bytes (thread) += obj_size;
18289
18290                                                 if (contain_pointers_or_collectible (oo))
18291                                                     mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
18292                                             }
18293                                         }
18294                     );
18295             }
18296         }
18297     }
18298 }
18299
18300 inline
18301 uint8_t* gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL)
18302 {
18303     if ((o >= gc_low) && (o < gc_high))
18304         mark_object_simple (&o THREAD_NUMBER_ARG);
18305 #ifdef MULTIPLE_HEAPS
18306     else if (o)
18307     {
18308         //find the heap
18309         gc_heap* hp = heap_of (o);
18310         assert (hp);
18311         if ((o >= hp->gc_low) && (o < hp->gc_high))
18312             mark_object_simple (&o THREAD_NUMBER_ARG);
18313     }
18314 #endif //MULTIPLE_HEAPS
18315
18316     return o;
18317 }
18318
18319 #ifdef BACKGROUND_GC
18320
18321 void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL)
18322 {
18323     uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
18324
18325 #ifdef SORT_MARK_STACK
18326     uint8_t** sorted_tos = background_mark_stack_array;
18327 #endif //SORT_MARK_STACK
18328
18329     background_mark_stack_tos = background_mark_stack_array;
18330
18331     while (1)
18332     {
18333 #ifdef MULTIPLE_HEAPS
18334 #else  //MULTIPLE_HEAPS
18335         const int thread = 0;
18336 #endif //MULTIPLE_HEAPS
18337         if (oo)
18338         {
18339             size_t s = 0; 
18340             if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18341             {
18342                 BOOL overflow_p = FALSE;
18343             
18344                 if (background_mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1))
18345                 {
18346                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18347                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18348                     if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
18349                     {
18350                         dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs", 
18351                             heap_number,
18352                             (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
18353                             method_table(oo), 
18354                             num_pointers));
18355
18356                         bgc_overflow_count++;
18357                         overflow_p = TRUE;
18358                     }
18359                 }
18360             
18361                 if (overflow_p == FALSE)
18362                 {
18363                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18364
18365                     go_through_object_cl (method_table(oo), oo, s, ppslot,
18366                     {
18367                         uint8_t* o = *ppslot;
18368                         Prefetch(o);
18369                         if (background_mark (o, 
18370                                              background_saved_lowest_address, 
18371                                              background_saved_highest_address))
18372                         {
18373                             //m_boundary (o);
18374                             size_t obj_size = size (o);
18375                             bpromoted_bytes (thread) += obj_size;
18376                             if (contain_pointers_or_collectible (o))
18377                             {
18378                                 *(background_mark_stack_tos++) = o;
18379
18380                             }
18381                         }
18382                     }
18383                         );
18384                 }
18385                 else
18386                 {
18387                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18388                     background_min_overflow_address = min (background_min_overflow_address, oo);
18389                     background_max_overflow_address = max (background_max_overflow_address, oo);
18390                 }
18391             }
18392             else 
18393             {
18394                 uint8_t* start = oo;
18395                 if ((size_t)oo & 1)
18396                 {
18397                     oo = (uint8_t*)((size_t)oo & ~1);
18398                     start = *(--background_mark_stack_tos);
18399                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18400                 }
18401 #ifdef COLLECTIBLE_CLASS
18402                 else
18403                 {
18404                     // If there's a class object, push it now. We are guaranteed to have the slot since
18405                     // we just popped one object off.
18406                     if (is_collectible (oo))
18407                     {
18408                         uint8_t* class_obj = get_class_object (oo);
18409                         if (background_mark (class_obj, 
18410                                             background_saved_lowest_address, 
18411                                             background_saved_highest_address))
18412                         {
18413                             size_t obj_size = size (class_obj);
18414                             bpromoted_bytes (thread) += obj_size;
18415
18416                             *(background_mark_stack_tos++) = class_obj;
18417                         }
18418                     }
18419                 }
18420 #endif //COLLECTIBLE_CLASS
18421
18422                 s = size (oo);
18423                 
18424                 BOOL overflow_p = FALSE;
18425             
18426                 if (background_mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
18427                 {
18428                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18429                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18430
18431                     dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id", 
18432                         heap_number,
18433                         (size_t)(mark_stack_limit - background_mark_stack_tos),
18434                         oo,
18435                         method_table(oo), 
18436                         start,
18437                         num_pointers));
18438
18439                     bgc_overflow_count++;
18440                     overflow_p = TRUE;
18441                 }
18442                 if (overflow_p == FALSE)
18443                 {
18444                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18445
18446                     //push the object and its current 
18447                     uint8_t** place = background_mark_stack_tos++;
18448                     *(place) = start;
18449                     *(background_mark_stack_tos++) = (uint8_t*)((size_t)oo | 1);
18450
18451                     int i = num_partial_refs; 
18452
18453                     go_through_object (method_table(oo), oo, s, ppslot,
18454                                        start, use_start, (oo + s),
18455                     {
18456                         uint8_t* o = *ppslot;
18457                         Prefetch(o);
18458
18459                         if (background_mark (o, 
18460                                             background_saved_lowest_address, 
18461                                             background_saved_highest_address))
18462                         {
18463                             //m_boundary (o);
18464                             size_t obj_size = size (o);
18465                             bpromoted_bytes (thread) += obj_size;
18466                             if (contain_pointers_or_collectible (o))
18467                             {
18468                                 *(background_mark_stack_tos++) = o;
18469                                 if (--i == 0)
18470                                 {
18471                                     //update the start
18472                                     *place = (uint8_t*)(ppslot+1);
18473                                     goto more_to_do;
18474                                 }
18475
18476                             }
18477                         }
18478
18479                     }
18480                         );
18481                     //we are finished with this object
18482                     *place = 0; 
18483                     *(place+1) = 0;
18484
18485                 more_to_do:;
18486                 }
18487                 else
18488                 {
18489                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18490                     background_min_overflow_address = min (background_min_overflow_address, oo);
18491                     background_max_overflow_address = max (background_max_overflow_address, oo);
18492                 }
18493             }
18494         }
18495 #ifdef SORT_MARK_STACK
18496         if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18497         {
18498             rqsort1 (sorted_tos, background_mark_stack_tos-1);
18499             sorted_tos = background_mark_stack_tos-1;
18500         }
18501 #endif //SORT_MARK_STACK
18502
18503         allow_fgc();
18504
18505         if (!(background_mark_stack_tos == background_mark_stack_array))
18506         {
18507             oo = *(--background_mark_stack_tos);
18508
18509 #ifdef SORT_MARK_STACK
18510             sorted_tos = (uint8_t**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
18511 #endif //SORT_MARK_STACK
18512         }
18513         else
18514             break;
18515     }
18516
18517     assert (background_mark_stack_tos == background_mark_stack_array);
18518
18519
18520 }
18521
18522 //this version is different than the foreground GC because
18523 //it can't keep pointers to the inside of an object
18524 //while calling background_mark_simple1. The object could be moved
18525 //by an intervening foreground gc.
18526 //this method assumes that *po is in the [low. high[ range
18527 void
18528 gc_heap::background_mark_simple (uint8_t* o THREAD_NUMBER_DCL)
18529 {
18530 #ifdef MULTIPLE_HEAPS
18531 #else  //MULTIPLE_HEAPS
18532     const int thread = 0;
18533 #endif //MULTIPLE_HEAPS
18534     {
18535         dprintf (3, ("bmarking %Ix", o));
18536         
18537         if (background_mark1 (o))
18538         {
18539             //m_boundary (o);
18540             size_t s = size (o);
18541             bpromoted_bytes (thread) += s;
18542
18543             if (contain_pointers_or_collectible (o))
18544             {
18545                 background_mark_simple1 (o THREAD_NUMBER_ARG);
18546             }
18547         }
18548     }
18549 }
18550
18551 inline
18552 uint8_t* gc_heap::background_mark_object (uint8_t* o THREAD_NUMBER_DCL)
18553 {
18554     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
18555     {
18556         background_mark_simple (o THREAD_NUMBER_ARG);
18557     }
18558     else
18559     {
18560         if (o)
18561         {
18562             dprintf (3, ("or-%Ix", o));
18563         }
18564     }
18565     return o;
18566 }
18567
18568 void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, uint32_t flags)
18569 {
18570     UNREFERENCED_PARAMETER(sc);
18571
18572     assert (settings.concurrent);
18573     uint8_t* o = (uint8_t*)object;
18574
18575     gc_heap* hp = gc_heap::heap_of (o);
18576 #ifdef INTERIOR_POINTERS
18577     if (flags & GC_CALL_INTERIOR)
18578     {
18579         o = hp->find_object (o, background_saved_lowest_address);
18580     }
18581 #endif //INTERIOR_POINTERS
18582
18583     if (!background_object_marked (o, FALSE))
18584     {
18585         FATAL_GC_ERROR();
18586     }
18587 }
18588
18589 void gc_heap::background_promote (Object** ppObject, ScanContext* sc, uint32_t flags)
18590 {
18591     UNREFERENCED_PARAMETER(sc);
18592     //in order to save space on the array, mark the object,
18593     //knowing that it will be visited later
18594     assert (settings.concurrent);
18595
18596     THREAD_NUMBER_FROM_CONTEXT;
18597 #ifndef MULTIPLE_HEAPS
18598     const int thread = 0;
18599 #endif //!MULTIPLE_HEAPS
18600
18601     uint8_t* o = (uint8_t*)*ppObject;
18602
18603     if (o == 0)
18604         return;
18605
18606 #ifdef DEBUG_DestroyedHandleValue
18607     // we can race with destroy handle during concurrent scan
18608     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
18609         return;
18610 #endif //DEBUG_DestroyedHandleValue
18611
18612     HEAP_FROM_THREAD;
18613
18614     gc_heap* hp = gc_heap::heap_of (o);
18615
18616     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
18617     {
18618         return;
18619     }
18620
18621 #ifdef INTERIOR_POINTERS
18622     if (flags & GC_CALL_INTERIOR)
18623     {
18624         o = hp->find_object (o, hp->background_saved_lowest_address);
18625         if (o == 0)
18626             return;
18627     }
18628 #endif //INTERIOR_POINTERS
18629
18630 #ifdef FEATURE_CONSERVATIVE_GC
18631     // For conservative GC, a value on stack may point to middle of a free object.
18632     // In this case, we don't need to promote the pointer.
18633     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
18634     {
18635         return;
18636     }
18637 #endif //FEATURE_CONSERVATIVE_GC
18638
18639 #ifdef _DEBUG
18640     ((CObjectHeader*)o)->Validate();
18641 #endif //_DEBUG
18642
18643     dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
18644
18645     //needs to be called before the marking because it is possible for a foreground
18646     //gc to take place during the mark and move the object
18647     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
18648
18649     hpt->background_mark_simple (o THREAD_NUMBER_ARG);
18650 }
18651
18652 //used by the ephemeral collection to scan the local background structures
18653 //containing references.
18654 void
18655 gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
18656 {
18657     ScanContext sc;
18658     if (pSC == 0)
18659         pSC = &sc;
18660
18661     pSC->thread_number = hn;
18662
18663 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
18664     pSC->pCurrentDomain = 0;
18665 #endif
18666
18667     BOOL relocate_p = (fn == &GCHeap::Relocate);
18668
18669     dprintf (3, ("Scanning background mark list"));
18670
18671     //scan mark_list
18672     size_t mark_list_finger = 0;
18673     while (mark_list_finger < c_mark_list_index)
18674     {
18675         uint8_t** o = &c_mark_list [mark_list_finger];
18676         if (!relocate_p)
18677         {
18678             // We may not be able to calculate the size during relocate as POPO
18679             // may have written over the object.
18680             size_t s = size (*o);
18681             assert (Align (s) >= Align (min_obj_size));
18682             dprintf(3,("background root %Ix", (size_t)*o));
18683         }
18684         (*fn) ((Object**)o, pSC, 0);
18685         mark_list_finger++;
18686     }
18687
18688     //scan the mark stack
18689     dprintf (3, ("Scanning background mark stack"));
18690
18691     uint8_t** finger = background_mark_stack_array;
18692     while (finger < background_mark_stack_tos)
18693     {
18694         if ((finger + 1) < background_mark_stack_tos)
18695         {
18696             // We need to check for the partial mark case here.
18697             uint8_t* parent_obj = *(finger + 1);
18698             if ((size_t)parent_obj & 1)
18699             {
18700                 uint8_t* place = *finger;
18701                 size_t place_offset = 0;
18702                 uint8_t* real_parent_obj = (uint8_t*)((size_t)parent_obj & ~1);
18703
18704                 if (relocate_p)
18705                 {
18706                     *(finger + 1) = real_parent_obj;
18707                     place_offset = place - real_parent_obj;
18708                     dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
18709                     (*fn) ((Object**)(finger + 1), pSC, 0);
18710                     real_parent_obj = *(finger + 1);
18711                     *finger = real_parent_obj + place_offset;
18712                     *(finger + 1) = (uint8_t*)((size_t)real_parent_obj | 1);
18713                     dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
18714                 }
18715                 else
18716                 {
18717                     uint8_t** temp = &real_parent_obj;
18718                     dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
18719                     (*fn) ((Object**)temp, pSC, 0);
18720                 }
18721
18722                 finger += 2;
18723                 continue;
18724             }
18725         }
18726         dprintf(3,("background root %Ix", (size_t)*finger));
18727         (*fn) ((Object**)finger, pSC, 0);
18728         finger++;
18729     }
18730 }
18731
18732 inline
18733 void gc_heap::background_mark_through_object (uint8_t* oo THREAD_NUMBER_DCL)
18734 {
18735     if (contain_pointers (oo))
18736     {
18737         size_t total_refs = 0;
18738         size_t s = size (oo);
18739         go_through_object_nostart (method_table(oo), oo, s, po,
18740                           {
18741                             uint8_t* o = *po;
18742                             total_refs++;
18743                             background_mark_object (o THREAD_NUMBER_ARG);
18744                           }
18745             );
18746
18747         dprintf (3,("Background marking through %Ix went through %Id refs", 
18748                           (size_t)oo,
18749                            total_refs));
18750     }
18751 }
18752
18753 uint8_t* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
18754 {
18755     if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
18756     {
18757         // for now we stop at where gen1 started when we started processing 
18758         return background_min_soh_overflow_address;
18759     }
18760     else
18761     {
18762         return heap_segment_allocated (seg);
18763     }
18764 }
18765
18766 uint8_t* gc_heap::background_first_overflow (uint8_t* min_add,
18767                                           heap_segment* seg,
18768                                           BOOL concurrent_p, 
18769                                           BOOL small_object_p)
18770 {
18771     uint8_t* o = 0;
18772
18773     if (small_object_p)
18774     {
18775         if (in_range_for_segment (min_add, seg))
18776         {
18777             // min_add was the beginning of gen1 when we did the concurrent
18778             // overflow. Now we could be in a situation where min_add is
18779             // actually the same as allocated for that segment (because
18780             // we expanded heap), in which case we can not call 
18781             // find first on this address or we will AV.
18782             if (min_add >= heap_segment_allocated (seg))
18783             {
18784                 return min_add;
18785             }
18786             else
18787             {
18788                 if (concurrent_p && 
18789                     ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
18790                 {
18791                     return background_min_soh_overflow_address;
18792                 }
18793                 else
18794                 {
18795                     o = find_first_object (min_add, heap_segment_mem (seg));
18796                     return o;
18797                 }
18798             }
18799         }
18800     }
18801
18802     o = max (heap_segment_mem (seg), min_add);
18803     return o;
18804 }
18805
18806 void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
18807                                                          uint8_t* min_add, uint8_t* max_add,
18808                                                          BOOL concurrent_p)
18809 {
18810     if (concurrent_p)
18811     {
18812         current_bgc_state = bgc_overflow_soh;
18813     }
18814
18815     size_t total_marked_objects = 0;
18816
18817 #ifdef MULTIPLE_HEAPS
18818     int thread = heap_number;
18819 #endif //MULTIPLE_HEAPS
18820
18821     exclusive_sync* loh_alloc_lock = 0;
18822
18823     dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
18824 #ifdef MULTIPLE_HEAPS
18825     // We don't have each heap scan all heaps concurrently because we are worried about
18826     // multiple threads calling things like find_first_object.
18827     int h_start = (concurrent_p ? heap_number : 0);
18828     int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
18829     for (int hi = h_start; hi < h_end; hi++)
18830     {
18831         gc_heap*  hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
18832
18833 #else
18834     {
18835         gc_heap*  hp = 0;
18836
18837 #endif //MULTIPLE_HEAPS
18838         BOOL small_object_segments = TRUE;
18839         int align_const = get_alignment_constant (small_object_segments);
18840         generation* gen = hp->generation_of (condemned_gen_number);
18841         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
18842         PREFIX_ASSUME(seg != NULL);
18843         loh_alloc_lock = hp->bgc_alloc_lock;
18844
18845         uint8_t* o = hp->background_first_overflow (min_add,
18846                                                     seg, 
18847                                                     concurrent_p, 
18848                                                     small_object_segments);
18849
18850         while (1)
18851         {
18852             while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
18853             {
18854                 dprintf (3, ("considering %Ix", (size_t)o));
18855
18856                 size_t s;
18857
18858                 if (concurrent_p && !small_object_segments)
18859                 {
18860                     loh_alloc_lock->bgc_mark_set (o);
18861
18862                     if (((CObjectHeader*)o)->IsFree())
18863                     {
18864                         s = unused_array_size (o);
18865                     }
18866                     else
18867                     {
18868                         s = size (o);
18869                     }
18870                 }
18871                 else
18872                 {
18873                     s = size (o);
18874                 }
18875
18876                 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
18877                 {
18878                     total_marked_objects++;
18879                     go_through_object_cl (method_table(o), o, s, poo,
18880                                           uint8_t* oo = *poo;
18881                                           background_mark_object (oo THREAD_NUMBER_ARG);
18882                                          );
18883                 }
18884
18885                 if (concurrent_p && !small_object_segments)
18886                 {
18887                     loh_alloc_lock->bgc_mark_done ();
18888                 }
18889
18890                 o = o + Align (s, align_const);
18891
18892                 if (concurrent_p)
18893                 {
18894                     allow_fgc();
18895                 }
18896             }
18897
18898             dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)", 
18899                 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
18900
18901             if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
18902                 (seg = heap_segment_next_in_range (seg)) == 0)
18903             {
18904                 if (small_object_segments)
18905                 {
18906                     if (concurrent_p)
18907                     {
18908                         current_bgc_state = bgc_overflow_loh;
18909                     }
18910
18911                     dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
18912                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
18913                     concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
18914                     total_marked_objects = 0;
18915                     small_object_segments = FALSE;
18916                     align_const = get_alignment_constant (small_object_segments);
18917                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
18918
18919                     PREFIX_ASSUME(seg != NULL);
18920
18921                     o = max (heap_segment_mem (seg), min_add);
18922                     continue;
18923                 }
18924                 else
18925                 {
18926                     dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
18927                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
18928                     break;
18929                 }
18930             } 
18931             else
18932             {
18933                 o = hp->background_first_overflow (min_add, 
18934                                                    seg, 
18935                                                    concurrent_p, 
18936                                                    small_object_segments);
18937                 continue;
18938             }
18939         }
18940     }
18941 }
18942
18943 BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
18944 {
18945     BOOL grow_mark_array_p = TRUE;
18946
18947     if (concurrent_p)
18948     {
18949         assert (!processed_soh_overflow_p);
18950
18951         if ((background_max_overflow_address != 0) &&
18952             (background_min_overflow_address != MAX_PTR))
18953         {
18954             // We have overflow to process but we know we can't process the ephemeral generations
18955             // now (we actually could process till the current gen1 start but since we are going to 
18956             // make overflow per segment, for now I'll just stop at the saved gen1 start.
18957             saved_overflow_ephemeral_seg = ephemeral_heap_segment;
18958             background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
18959             background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
18960         }
18961     }
18962     else
18963     {
18964         assert ((saved_overflow_ephemeral_seg == 0) || 
18965                 ((background_max_soh_overflow_address != 0) &&
18966                  (background_min_soh_overflow_address != MAX_PTR)));
18967         
18968         if (!processed_soh_overflow_p)
18969         {
18970             // if there was no more overflow we just need to process what we didn't process 
18971             // on the saved ephemeral segment.
18972             if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
18973             {
18974                 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
18975                 grow_mark_array_p = FALSE;
18976             }
18977
18978             background_min_overflow_address = min (background_min_overflow_address, 
18979                                                 background_min_soh_overflow_address);
18980             background_max_overflow_address = max (background_max_overflow_address,
18981                                                 background_max_soh_overflow_address);
18982             processed_soh_overflow_p = TRUE;
18983         }
18984     }
18985
18986     BOOL  overflow_p = FALSE;
18987 recheck:
18988     if ((! ((background_max_overflow_address == 0)) ||
18989          ! ((background_min_overflow_address == MAX_PTR))))
18990     {
18991         overflow_p = TRUE;
18992
18993         if (grow_mark_array_p)
18994         {
18995             // Try to grow the array.
18996             size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
18997
18998             if ((new_size * sizeof(mark)) > 100*1024)
18999             {
19000                 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19001
19002                 new_size = min(new_max_size, new_size);
19003             }
19004
19005             if ((background_mark_stack_array_length < new_size) && 
19006                 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
19007             {
19008                 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
19009
19010                 uint8_t** tmp = new (nothrow) uint8_t* [new_size];
19011                 if (tmp)
19012                 {
19013                     delete background_mark_stack_array;
19014                     background_mark_stack_array = tmp;
19015                     background_mark_stack_array_length = new_size;
19016                     background_mark_stack_tos = background_mark_stack_array;
19017                 }
19018             }
19019         }
19020         else
19021         {
19022             grow_mark_array_p = TRUE;
19023         }
19024
19025         uint8_t*  min_add = background_min_overflow_address;
19026         uint8_t*  max_add = background_max_overflow_address;
19027
19028         background_max_overflow_address = 0;
19029         background_min_overflow_address = MAX_PTR;
19030
19031         background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
19032         if (!concurrent_p)
19033         {        
19034             goto recheck;
19035         }
19036     }
19037
19038     return overflow_p;
19039 }
19040
19041 #endif //BACKGROUND_GC
19042
19043 inline
19044 void gc_heap::mark_through_object (uint8_t* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
19045 {
19046 #ifndef COLLECTIBLE_CLASS
19047     UNREFERENCED_PARAMETER(mark_class_object_p);
19048     BOOL to_mark_class_object = FALSE;
19049 #else //COLLECTIBLE_CLASS
19050     BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
19051 #endif //COLLECTIBLE_CLASS
19052     if (contain_pointers (oo) || to_mark_class_object)
19053     {
19054         dprintf(3,( "Marking through %Ix", (size_t)oo));
19055         size_t s = size (oo);
19056
19057 #ifdef COLLECTIBLE_CLASS
19058         if (to_mark_class_object)
19059         {
19060             uint8_t* class_obj = get_class_object (oo);
19061             mark_object (class_obj THREAD_NUMBER_ARG);
19062         }
19063 #endif //COLLECTIBLE_CLASS
19064
19065         if (contain_pointers (oo))
19066         {
19067             go_through_object_nostart (method_table(oo), oo, s, po,
19068                                 uint8_t* o = *po;
19069                                 mark_object (o THREAD_NUMBER_ARG);
19070                                 );
19071         }
19072     }
19073 }
19074
19075 size_t gc_heap::get_total_heap_size()
19076 {
19077     size_t total_heap_size = 0;
19078
19079 #ifdef MULTIPLE_HEAPS
19080     int hn = 0;
19081
19082     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19083     {
19084         gc_heap* hp2 = gc_heap::g_heaps [hn];
19085         total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
19086     }
19087 #else
19088     total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
19089 #endif //MULTIPLE_HEAPS
19090
19091     return total_heap_size;
19092 }
19093
19094 size_t gc_heap::get_total_fragmentation()
19095 {
19096     size_t total_fragmentation = 0;
19097
19098 #ifdef MULTIPLE_HEAPS
19099     for (int i = 0; i < gc_heap::n_heaps; i++)
19100     {
19101         gc_heap* hp = gc_heap::g_heaps[i];
19102 #else //MULTIPLE_HEAPS
19103     {
19104         gc_heap* hp = pGenGCHeap;
19105 #endif //MULTIPLE_HEAPS
19106         for (int i = 0; i <= (max_generation + 1); i++)
19107         {
19108             generation* gen = hp->generation_of (i);
19109             total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen));
19110         }
19111     }
19112
19113     return total_fragmentation;
19114 }
19115
19116 size_t gc_heap::committed_size()
19117 {
19118     generation* gen = generation_of (max_generation);
19119     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19120     size_t total_committed = 0;
19121
19122     while (1)
19123     {
19124         total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19125
19126         seg = heap_segment_next (seg);
19127         if (!seg)
19128         {
19129             if (gen != large_object_generation)
19130             {
19131                 gen = generation_of (max_generation + 1);
19132                 seg = generation_start_segment (gen);
19133             }
19134             else
19135                 break;
19136         }
19137     }
19138
19139     return total_committed;
19140 }
19141
19142 size_t gc_heap::get_total_committed_size()
19143 {
19144     size_t total_committed = 0;
19145
19146 #ifdef MULTIPLE_HEAPS
19147     int hn = 0;
19148
19149     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19150     {
19151         gc_heap* hp = gc_heap::g_heaps [hn];
19152         total_committed += hp->committed_size();
19153     }
19154 #else
19155     total_committed = committed_size();
19156 #endif //MULTIPLE_HEAPS
19157
19158     return total_committed;
19159 }
19160
19161 void gc_heap::get_memory_info (uint32_t* memory_load, 
19162                                uint64_t* available_physical,
19163                                uint64_t* available_page_file)
19164 {
19165     GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file);
19166 }
19167
19168 void fire_mark_event (int heap_num, int root_type, size_t bytes_marked)
19169 {
19170     dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id", heap_num, root_type, bytes_marked));
19171     FIRE_EVENT(GCMarkWithType, heap_num, root_type, bytes_marked);
19172 }
19173
19174 //returns TRUE is an overflow happened.
19175 BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
19176 {
19177     size_t last_promoted_bytes = promoted_bytes (heap_number);
19178     BOOL  overflow_p = FALSE;
19179 recheck:
19180     if ((! (max_overflow_address == 0) ||
19181          ! (min_overflow_address == MAX_PTR)))
19182     {
19183         overflow_p = TRUE;
19184         // Try to grow the array.
19185         size_t new_size =
19186             max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
19187
19188         if ((new_size * sizeof(mark)) > 100*1024)
19189         {
19190             size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19191
19192             new_size = min(new_max_size, new_size);
19193         }
19194
19195         if ((mark_stack_array_length < new_size) && 
19196             ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
19197         {
19198             mark* tmp = new (nothrow) mark [new_size];
19199             if (tmp)
19200             {
19201                 delete mark_stack_array;
19202                 mark_stack_array = tmp;
19203                 mark_stack_array_length = new_size;
19204             }
19205         }
19206
19207         uint8_t*  min_add = min_overflow_address;
19208         uint8_t*  max_add = max_overflow_address;
19209         max_overflow_address = 0;
19210         min_overflow_address = MAX_PTR;
19211         process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
19212         goto recheck;
19213     }
19214
19215     size_t current_promoted_bytes = promoted_bytes (heap_number);
19216
19217     if (current_promoted_bytes != last_promoted_bytes)
19218         fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes));
19219     return overflow_p;
19220 }
19221
19222 void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
19223                                               uint8_t* min_add, uint8_t* max_add)
19224 {
19225 #ifdef MULTIPLE_HEAPS
19226     int thread = heap_number;
19227 #endif //MULTIPLE_HEAPS
19228     BOOL  full_p = (condemned_gen_number == max_generation);
19229
19230         dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19231 #ifdef MULTIPLE_HEAPS
19232             for (int hi = 0; hi < n_heaps; hi++)
19233             {
19234                 gc_heap*  hp = g_heaps [(heap_number + hi) % n_heaps];
19235
19236 #else
19237             {
19238                 gc_heap*  hp = 0;
19239
19240 #endif //MULTIPLE_HEAPS
19241         BOOL small_object_segments = TRUE;
19242         int align_const = get_alignment_constant (small_object_segments);
19243         generation* gen = hp->generation_of (condemned_gen_number);
19244         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19245         
19246         PREFIX_ASSUME(seg != NULL);
19247         uint8_t*  o = max (heap_segment_mem (seg), min_add);
19248         while (1)
19249         {
19250             uint8_t*  end = heap_segment_allocated (seg);
19251
19252             while ((o < end) && (o <= max_add))
19253             {
19254                 assert ((min_add <= o) && (max_add >= o));
19255                 dprintf (3, ("considering %Ix", (size_t)o));
19256                 if (marked (o))
19257                 {
19258                     mark_through_object (o, TRUE THREAD_NUMBER_ARG);
19259                 }
19260
19261                 o = o + Align (size (o), align_const);
19262             }
19263
19264             if (( seg = heap_segment_next_in_range (seg)) == 0)
19265             {
19266                 if (small_object_segments && full_p)
19267                 {
19268                     small_object_segments = FALSE;
19269                     align_const = get_alignment_constant (small_object_segments);
19270                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19271
19272                     PREFIX_ASSUME(seg != NULL);
19273
19274                     o = max (heap_segment_mem (seg), min_add);
19275                     continue;
19276                 }
19277                 else
19278                 {
19279                     break;
19280                 } 
19281             } 
19282             else
19283             {
19284                 o = max (heap_segment_mem (seg), min_add);
19285                 continue;
19286             }
19287         }
19288     }
19289 }
19290
19291 // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
19292 // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
19293 // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
19294 // promotion scan multiple times.
19295 // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
19296 // also has the effect of processing any mark stack overflow.
19297
19298 #ifdef MULTIPLE_HEAPS
19299 // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
19300 // worker threads synchronized. The algorithms are sufficiently divergent that we have different
19301 // implementations based on whether MULTIPLE_HEAPS is defined or not.
19302 //
19303 // Define some static variables used for synchronization in the method below. These should really be defined
19304 // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
19305 //
19306 // A note about the synchronization used within this method. Communication between the worker threads is
19307 // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
19308 // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
19309 // protection of a join.
19310 static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
19311 static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
19312 static VOLATILE(BOOL) s_fScanRequired;
19313 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19314 {
19315     // Whenever we call this method there may have been preceding object promotions. So set
19316     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19317     // based on the how the scanning proceeded).
19318     s_fUnscannedPromotions = TRUE;
19319
19320     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
19321     // the state of this thread's portion of the dependent handle table. That's because promotions on other
19322     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
19323     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
19324     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
19325     // as all the others or they'll get out of step).
19326     while (true)
19327     {
19328         // The various worker threads are all currently racing in this code. We need to work out if at least
19329         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
19330         // dependent handle table when both of the following conditions apply:
19331         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
19332         //     object happens to correspond to a primary in one of our handles we might potentially have to
19333         //     promote the associated secondary).
19334         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
19335         //
19336         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
19337         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
19338         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
19339         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
19340         // follows below. Note that we can't read this outside of the join since on any iteration apart from
19341         // the first threads will be racing between reading this value and completing their previous
19342         // iteration's table scan.
19343         //
19344         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
19345         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
19346         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
19347         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
19348         // we're safely joined.
19349         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19350             s_fUnpromotedHandles = TRUE;
19351
19352         // Synchronize all the threads so we can read our state variables safely. The shared variable
19353         // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
19354         // a single thread inside the join.
19355         gc_t_join.join(this, gc_join_scan_dependent_handles);
19356         if (gc_t_join.joined())
19357         {
19358             // We're synchronized so it's safe to read our shared state variables. We update another shared
19359             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
19360             // the loop. We scan if there has been at least one object promotion since last time and at least
19361             // one thread has a dependent handle table with a potential handle promotion possible.
19362             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
19363
19364             // Reset our shared state variables (ready to be set again on this scan or with a good initial
19365             // value for the next call if we're terminating the loop).
19366             s_fUnscannedPromotions = FALSE;
19367             s_fUnpromotedHandles = FALSE;
19368
19369             if (!s_fScanRequired)
19370             {
19371                 // We're terminating the loop. Perform any last operations that require single threaded access.
19372                 if (!initial_scan_p)
19373                 {
19374                     // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
19375                     // load balance if some of the heaps have an abnormally large workload.
19376                     uint8_t* all_heaps_max = 0;
19377                     uint8_t* all_heaps_min = MAX_PTR;
19378                     int i;
19379                     for (i = 0; i < n_heaps; i++)
19380                     {
19381                         if (all_heaps_max < g_heaps[i]->max_overflow_address)
19382                             all_heaps_max = g_heaps[i]->max_overflow_address;
19383                         if (all_heaps_min > g_heaps[i]->min_overflow_address)
19384                             all_heaps_min = g_heaps[i]->min_overflow_address;
19385                     }
19386                     for (i = 0; i < n_heaps; i++)
19387                     {
19388                         g_heaps[i]->max_overflow_address = all_heaps_max;
19389                         g_heaps[i]->min_overflow_address = all_heaps_min;
19390                     }
19391                 }
19392             }
19393
19394             // Restart all the workers.
19395             dprintf(3, ("Starting all gc thread mark stack overflow processing"));
19396             gc_t_join.restart();
19397         }
19398
19399         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19400         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
19401         // global flag indicating that at least one object promotion may have occurred (the usual comment
19402         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
19403         // exit the method since we unconditionally set this variable on method entry anyway).
19404         if (process_mark_overflow(condemned_gen_number))
19405             s_fUnscannedPromotions = TRUE;
19406
19407         // If we decided that no scan was required we can terminate the loop now.
19408         if (!s_fScanRequired)
19409             break;
19410
19411         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
19412         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
19413         // could miss noting the promotion of some primary objects).
19414         gc_t_join.join(this, gc_join_rescan_dependent_handles);
19415         if (gc_t_join.joined())
19416         {
19417             // Restart all the workers.
19418             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
19419             gc_t_join.restart();
19420         }
19421
19422         // If the portion of the dependent handle table managed by this worker has handles that could still be
19423         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
19424         // could require a rescan of handles on this or other workers.
19425         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19426             if (GCScan::GcDhReScan(sc))
19427                 s_fUnscannedPromotions = TRUE;
19428     }
19429 }
19430 #else //MULTIPLE_HEAPS
19431 // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
19432 // threads synchronized.
19433 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19434 {
19435     UNREFERENCED_PARAMETER(initial_scan_p);
19436
19437     // Whenever we call this method there may have been preceding object promotions. So set
19438     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19439     // based on the how the scanning proceeded).
19440     bool fUnscannedPromotions = true;
19441
19442     // Loop until there are either no more dependent handles that can have their secondary promoted or we've
19443     // managed to perform a scan without promoting anything new.
19444     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
19445     {
19446         // On each iteration of the loop start with the assumption that no further objects have been promoted.
19447         fUnscannedPromotions = false;
19448
19449         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19450         // being visible. If there was an overflow (process_mark_overflow returned true) then additional
19451         // objects now appear to be promoted and we should set the flag.
19452         if (process_mark_overflow(condemned_gen_number))
19453             fUnscannedPromotions = true;
19454
19455         // Perform the scan and set the flag if any promotions resulted.
19456         if (GCScan::GcDhReScan(sc))
19457             fUnscannedPromotions = true;
19458     }
19459
19460     // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
19461     // scan any handles at all this is the processing of overflows that may have occurred prior to this method
19462     // invocation).
19463     process_mark_overflow(condemned_gen_number);
19464 }
19465 #endif //MULTIPLE_HEAPS
19466
19467 void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
19468 {
19469     assert (settings.concurrent == FALSE);
19470
19471     ScanContext sc;
19472     sc.thread_number = heap_number;
19473     sc.promotion = TRUE;
19474     sc.concurrent = FALSE;
19475
19476     dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
19477     BOOL  full_p = (condemned_gen_number == max_generation);
19478
19479 #ifdef TIME_GC
19480     unsigned start;
19481     unsigned finish;
19482     start = GetCycleCount32();
19483 #endif //TIME_GC
19484
19485     int gen_to_init = condemned_gen_number;
19486     if (condemned_gen_number == max_generation)
19487     {
19488         gen_to_init = max_generation + 1;
19489     }
19490     for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
19491     {
19492         dynamic_data* dd = dynamic_data_of (gen_idx);
19493         dd_begin_data_size (dd) = generation_size (gen_idx) - 
19494                                    dd_fragmentation (dd) -
19495                                    Align (size (generation_allocation_start (generation_of (gen_idx))));
19496         dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
19497         dd_survived_size (dd) = 0;
19498         dd_pinned_survived_size (dd) = 0;
19499         dd_artificial_pinned_survived_size (dd) = 0;
19500         dd_added_pinned_size (dd) = 0;
19501 #ifdef SHORT_PLUGS
19502         dd_padding_size (dd) = 0;
19503 #endif //SHORT_PLUGS
19504 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
19505         dd_num_npinned_plugs (dd) = 0;
19506 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
19507     }
19508
19509 #ifdef FFIND_OBJECT
19510     if (gen0_must_clear_bricks > 0)
19511         gen0_must_clear_bricks--;
19512 #endif //FFIND_OBJECT
19513
19514     size_t last_promoted_bytes = 0;
19515
19516     promoted_bytes (heap_number) = 0;
19517     reset_mark_stack();
19518
19519 #ifdef SNOOP_STATS
19520     memset (&snoop_stat, 0, sizeof(snoop_stat));
19521     snoop_stat.heap_index = heap_number;
19522 #endif //SNOOP_STATS
19523
19524 #ifdef MH_SC_MARK
19525     if (full_p)
19526     {
19527         //initialize the mark stack
19528         for (int i = 0; i < max_snoop_level; i++)
19529         {
19530             ((uint8_t**)(mark_stack_array))[i] = 0;
19531         }
19532
19533         mark_stack_busy() = 1;
19534     }
19535 #endif //MH_SC_MARK
19536
19537     static uint32_t num_sizedrefs = 0;
19538
19539 #ifdef MH_SC_MARK
19540     static BOOL do_mark_steal_p = FALSE;
19541 #endif //MH_SC_MARK
19542
19543 #ifdef MULTIPLE_HEAPS
19544     gc_t_join.join(this, gc_join_begin_mark_phase);
19545     if (gc_t_join.joined())
19546     {
19547 #endif //MULTIPLE_HEAPS
19548
19549         num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
19550
19551 #ifdef MULTIPLE_HEAPS
19552
19553 #ifdef MH_SC_MARK
19554         if (full_p)
19555         {
19556             size_t total_heap_size = get_total_heap_size();
19557
19558             if (total_heap_size > (100 * 1024 * 1024))
19559             {
19560                 do_mark_steal_p = TRUE;
19561             }
19562             else
19563             {
19564                 do_mark_steal_p = FALSE;
19565             }
19566         }
19567         else
19568         {
19569             do_mark_steal_p = FALSE;
19570         }
19571 #endif //MH_SC_MARK
19572
19573         gc_t_join.restart();
19574     }
19575 #endif //MULTIPLE_HEAPS
19576
19577     {
19578
19579 #ifdef MARK_LIST
19580         //set up the mark lists from g_mark_list
19581         assert (g_mark_list);
19582 #ifdef MULTIPLE_HEAPS
19583         mark_list = &g_mark_list [heap_number*mark_list_size];
19584 #else
19585         mark_list = g_mark_list;
19586 #endif //MULTIPLE_HEAPS
19587         //dont use the mark list for full gc
19588         //because multiple segments are more complex to handle and the list
19589         //is likely to overflow
19590         if (condemned_gen_number != max_generation)
19591             mark_list_end = &mark_list [mark_list_size-1];
19592         else
19593             mark_list_end = &mark_list [0];
19594         mark_list_index = &mark_list [0];
19595 #endif //MARK_LIST
19596
19597         shigh = (uint8_t*) 0;
19598         slow  = MAX_PTR;
19599
19600         //%type%  category = quote (mark);
19601
19602         if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
19603         {
19604             GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19605             fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes));
19606             last_promoted_bytes = promoted_bytes (heap_number);
19607
19608 #ifdef MULTIPLE_HEAPS
19609             gc_t_join.join(this, gc_join_scan_sizedref_done);
19610             if (gc_t_join.joined())
19611             {
19612                 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
19613                 gc_t_join.restart();
19614             }
19615 #endif //MULTIPLE_HEAPS
19616         }
19617     
19618         dprintf(3,("Marking Roots"));
19619
19620         GCScan::GcScanRoots(GCHeap::Promote,
19621                                 condemned_gen_number, max_generation,
19622                                 &sc);
19623
19624         fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes));
19625         last_promoted_bytes = promoted_bytes (heap_number);
19626
19627 #ifdef BACKGROUND_GC
19628         if (recursive_gc_sync::background_running_p())
19629         {
19630             scan_background_roots (GCHeap::Promote, heap_number, &sc);
19631         }
19632 #endif //BACKGROUND_GC
19633
19634 #ifdef FEATURE_PREMORTEM_FINALIZATION
19635         dprintf(3, ("Marking finalization data"));
19636         finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
19637 #endif // FEATURE_PREMORTEM_FINALIZATION
19638
19639         fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes));
19640         last_promoted_bytes = promoted_bytes (heap_number);
19641
19642 // MTHTS
19643         {
19644
19645             dprintf(3,("Marking handle table"));
19646             GCScan::GcScanHandles(GCHeap::Promote,
19647                                       condemned_gen_number, max_generation,
19648                                       &sc);
19649             fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes));
19650             last_promoted_bytes = promoted_bytes (heap_number);
19651         }
19652
19653 #ifdef TRACE_GC
19654         size_t promoted_before_cards = promoted_bytes (heap_number);
19655 #endif //TRACE_GC
19656
19657         dprintf (3, ("before cards: %Id", promoted_before_cards));
19658         if (!full_p)
19659         {
19660 #ifdef CARD_BUNDLE
19661 #ifdef MULTIPLE_HEAPS
19662             if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
19663             {
19664 #endif //MULTIPLE_HEAPS
19665
19666 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
19667                 // If we are manually managing card bundles, every write to the card table should already be
19668                 // accounted for in the card bundle table so there's nothing to update here.
19669                 update_card_table_bundle();
19670 #endif
19671                 if (card_bundles_enabled())
19672                 {
19673                     verify_card_bundles();
19674                 }
19675
19676 #ifdef MULTIPLE_HEAPS
19677                 gc_t_join.r_restart();
19678             }
19679 #endif //MULTIPLE_HEAPS
19680 #endif //CARD_BUNDLE
19681
19682             card_fn mark_object_fn = &gc_heap::mark_object_simple;
19683 #ifdef HEAP_ANALYZE
19684             heap_analyze_success = TRUE;
19685             if (heap_analyze_enabled)
19686             {
19687                 internal_root_array_index = 0;
19688                 current_obj = 0;
19689                 current_obj_size = 0;
19690                 mark_object_fn = &gc_heap::ha_mark_object_simple;
19691             }
19692 #endif //HEAP_ANALYZE
19693
19694             dprintf(3,("Marking cross generation pointers"));
19695             mark_through_cards_for_segments (mark_object_fn, FALSE);
19696
19697             dprintf(3,("Marking cross generation pointers for large objects"));
19698             mark_through_cards_for_large_objects (mark_object_fn, FALSE);
19699
19700             dprintf (3, ("marked by cards: %Id", 
19701                 (promoted_bytes (heap_number) - promoted_before_cards)));
19702             fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes));
19703             last_promoted_bytes = promoted_bytes (heap_number);
19704         }
19705     }
19706
19707 #ifdef MH_SC_MARK
19708     if (do_mark_steal_p)
19709     {
19710         mark_steal();
19711     }
19712 #endif //MH_SC_MARK
19713
19714     // Dependent handles need to be scanned with a special algorithm (see the header comment on
19715     // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
19716     // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
19717     // but in a common case (where there are no dependent handles that are due to be collected) it allows us
19718     // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
19719     // iterations if required and will also perform processing of any mark stack overflow once the dependent
19720     // handle table has been fully promoted.
19721     GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19722     scan_dependent_handles(condemned_gen_number, &sc, true);
19723
19724 #ifdef MULTIPLE_HEAPS
19725     dprintf(3, ("Joining for short weak handle scan"));
19726     gc_t_join.join(this, gc_join_null_dead_short_weak);
19727     if (gc_t_join.joined())
19728 #endif //MULTIPLE_HEAPS
19729     {
19730 #ifdef HEAP_ANALYZE
19731         heap_analyze_enabled = FALSE;
19732         DACNotifyGcMarkEnd(condemned_gen_number);
19733 #endif // HEAP_ANALYZE
19734         GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
19735
19736 #ifdef MULTIPLE_HEAPS
19737         if (!full_p)
19738         {
19739             // we used r_join and need to reinitialize states for it here.
19740             gc_t_join.r_init();
19741         }
19742
19743         //start all threads on the roots.
19744         dprintf(3, ("Starting all gc thread for short weak handle scan"));
19745         gc_t_join.restart();
19746 #endif //MULTIPLE_HEAPS
19747
19748     }
19749
19750     // null out the target of short weakref that were not promoted.
19751     GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
19752
19753 // MTHTS: keep by single thread
19754 #ifdef MULTIPLE_HEAPS
19755     dprintf(3, ("Joining for finalization"));
19756     gc_t_join.join(this, gc_join_scan_finalization);
19757     if (gc_t_join.joined())
19758 #endif //MULTIPLE_HEAPS
19759
19760     {
19761 #ifdef MULTIPLE_HEAPS
19762         //start all threads on the roots.
19763         dprintf(3, ("Starting all gc thread for Finalization"));
19764         gc_t_join.restart();
19765 #endif //MULTIPLE_HEAPS
19766     }
19767
19768     //Handle finalization.
19769     size_t promoted_bytes_live = promoted_bytes (heap_number);
19770
19771 #ifdef FEATURE_PREMORTEM_FINALIZATION
19772     dprintf (3, ("Finalize marking"));
19773     finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
19774
19775     GCToEEInterface::DiagWalkFReachableObjects(__this);
19776 #endif // FEATURE_PREMORTEM_FINALIZATION
19777
19778     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
19779     // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
19780     scan_dependent_handles(condemned_gen_number, &sc, false);
19781
19782 #ifdef MULTIPLE_HEAPS
19783     dprintf(3, ("Joining for weak pointer deletion"));
19784     gc_t_join.join(this, gc_join_null_dead_long_weak);
19785     if (gc_t_join.joined())
19786     {
19787         //start all threads on the roots.
19788         dprintf(3, ("Starting all gc thread for weak pointer deletion"));
19789         gc_t_join.restart();
19790     }
19791 #endif //MULTIPLE_HEAPS
19792
19793     // null out the target of long weakref that were not promoted.
19794     GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19795
19796 // MTHTS: keep by single thread
19797 #ifdef MULTIPLE_HEAPS
19798 #ifdef MARK_LIST
19799 #ifdef PARALLEL_MARK_LIST_SORT
19800 //    unsigned long start = GetCycleCount32();
19801     sort_mark_list();
19802 //    printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
19803 #endif //PARALLEL_MARK_LIST_SORT
19804 #endif //MARK_LIST
19805
19806     dprintf (3, ("Joining for sync block cache entry scanning"));
19807     gc_t_join.join(this, gc_join_null_dead_syncblk);
19808     if (gc_t_join.joined())
19809 #endif //MULTIPLE_HEAPS
19810     {
19811         // scan for deleted entries in the syncblk cache
19812         GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
19813
19814 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
19815         if (g_fEnableARM)
19816         {
19817             size_t promoted_all_heaps = 0;
19818 #ifdef MULTIPLE_HEAPS
19819             for (int i = 0; i < n_heaps; i++)
19820             {
19821                 promoted_all_heaps += promoted_bytes (i);
19822             }
19823 #else
19824             promoted_all_heaps = promoted_bytes (heap_number);
19825 #endif //MULTIPLE_HEAPS
19826             SystemDomain::RecordTotalSurvivedBytes (promoted_all_heaps);
19827         }
19828 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
19829
19830 #ifdef MULTIPLE_HEAPS
19831
19832 #ifdef MARK_LIST
19833 #ifndef PARALLEL_MARK_LIST_SORT
19834         //compact g_mark_list and sort it.
19835         combine_mark_lists();
19836 #endif //PARALLEL_MARK_LIST_SORT
19837 #endif //MARK_LIST
19838
19839         //decide on promotion
19840         if (!settings.promotion)
19841         {
19842             size_t m = 0;
19843             for (int n = 0; n <= condemned_gen_number;n++)
19844             {
19845                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.1);
19846             }
19847
19848             for (int i = 0; i < n_heaps; i++)
19849             {
19850                 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
19851                                                                      max_generation));
19852                 size_t older_gen_size = (dd_current_size (dd) +
19853                                          (dd_desired_allocation (dd) -
19854                                          dd_new_allocation (dd)));
19855
19856                 if ((m > (older_gen_size)) ||
19857                     (promoted_bytes (i) > m))
19858                 {
19859                     settings.promotion = TRUE;
19860                 }
19861             }
19862         }
19863
19864 #ifdef SNOOP_STATS
19865         if (do_mark_steal_p)
19866         {
19867             size_t objects_checked_count = 0;
19868             size_t zero_ref_count = 0;
19869             size_t objects_marked_count = 0;
19870             size_t check_level_count = 0;
19871             size_t busy_count = 0;
19872             size_t interlocked_count = 0;
19873             size_t partial_mark_parent_count = 0;
19874             size_t stolen_or_pm_count = 0; 
19875             size_t stolen_entry_count = 0; 
19876             size_t pm_not_ready_count = 0; 
19877             size_t normal_count = 0;
19878             size_t stack_bottom_clear_count = 0;
19879
19880             for (int i = 0; i < n_heaps; i++)
19881             {
19882                 gc_heap* hp = g_heaps[i];
19883                 hp->print_snoop_stat();
19884                 objects_checked_count += hp->snoop_stat.objects_checked_count;
19885                 zero_ref_count += hp->snoop_stat.zero_ref_count;
19886                 objects_marked_count += hp->snoop_stat.objects_marked_count;
19887                 check_level_count += hp->snoop_stat.check_level_count;
19888                 busy_count += hp->snoop_stat.busy_count;
19889                 interlocked_count += hp->snoop_stat.interlocked_count;
19890                 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
19891                 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
19892                 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
19893                 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
19894                 normal_count += hp->snoop_stat.normal_count;
19895                 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
19896             }
19897
19898             fflush (stdout);
19899
19900             printf ("-------total stats-------\n");
19901             printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
19902                 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
19903             printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
19904                 objects_checked_count,
19905                 zero_ref_count,
19906                 objects_marked_count,
19907                 check_level_count,
19908                 busy_count,
19909                 interlocked_count,
19910                 partial_mark_parent_count,
19911                 stolen_or_pm_count,
19912                 stolen_entry_count,
19913                 pm_not_ready_count,
19914                 normal_count,
19915                 stack_bottom_clear_count);
19916         }
19917 #endif //SNOOP_STATS
19918
19919         //start all threads.
19920         dprintf(3, ("Starting all threads for end of mark phase"));
19921         gc_t_join.restart();
19922 #else //MULTIPLE_HEAPS
19923
19924         //decide on promotion
19925         if (!settings.promotion)
19926         {
19927             size_t m = 0;
19928             for (int n = 0; n <= condemned_gen_number;n++)
19929             {
19930                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06);
19931             }
19932             dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
19933                                                      max_generation));
19934             size_t older_gen_size = (dd_current_size (dd) +
19935                                      (dd_desired_allocation (dd) -
19936                                      dd_new_allocation (dd)));
19937
19938             dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
19939                          m, promoted_bytes (heap_number), older_gen_size));
19940
19941             if ((m > older_gen_size) ||
19942                     (promoted_bytes (heap_number) > m))
19943             {
19944                 settings.promotion = TRUE;
19945             }
19946         }
19947
19948 #endif //MULTIPLE_HEAPS
19949     }
19950
19951 #ifdef MULTIPLE_HEAPS
19952 #ifdef MARK_LIST
19953 #ifdef PARALLEL_MARK_LIST_SORT
19954 //    start = GetCycleCount32();
19955     merge_mark_lists();
19956 //    printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
19957 #endif //PARALLEL_MARK_LIST_SORT
19958 #endif //MARK_LIST
19959 #endif //MULTIPLE_HEAPS
19960
19961 #ifdef BACKGROUND_GC
19962     total_promoted_bytes = promoted_bytes (heap_number);
19963 #endif //BACKGROUND_GC
19964
19965     promoted_bytes (heap_number) -= promoted_bytes_live;
19966
19967 #ifdef TIME_GC
19968         finish = GetCycleCount32();
19969         mark_time = finish - start;
19970 #endif //TIME_GC
19971
19972     dprintf(2,("---- End of mark phase ----"));
19973 }
19974
19975 inline
19976 void gc_heap::pin_object (uint8_t* o, uint8_t** ppObject, uint8_t* low, uint8_t* high)
19977 {
19978     dprintf (3, ("Pinning %Ix", (size_t)o));
19979     if ((o >= low) && (o < high))
19980     {
19981         dprintf(3,("^%Ix^", (size_t)o));
19982         set_pinned (o);
19983
19984 #ifdef FEATURE_EVENT_TRACE        
19985         if(EVENT_ENABLED(PinObjectAtGCTime))
19986         {
19987             fire_etw_pin_object_event(o, ppObject);
19988         }
19989 #endif // FEATURE_EVENT_TRACE
19990
19991 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
19992         num_pinned_objects++;
19993 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
19994     }
19995 }
19996
19997 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
19998 size_t gc_heap::get_total_pinned_objects()
19999 {
20000 #ifdef MULTIPLE_HEAPS
20001     size_t total_num_pinned_objects = 0;
20002     for (int i = 0; i < gc_heap::n_heaps; i++)
20003     {
20004         gc_heap* hp = gc_heap::g_heaps[i];
20005         total_num_pinned_objects += hp->num_pinned_objects;
20006     }
20007     return total_num_pinned_objects;
20008 #else //MULTIPLE_HEAPS
20009     return num_pinned_objects;
20010 #endif //MULTIPLE_HEAPS
20011 }
20012 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
20013
20014 void gc_heap::reset_mark_stack ()
20015 {
20016     reset_pinned_queue();
20017     max_overflow_address = 0;
20018     min_overflow_address = MAX_PTR;
20019 }
20020
20021 #ifdef FEATURE_STRUCTALIGN
20022 //
20023 // The word with left child, right child, and align info is laid out as follows:
20024 //
20025 //      |   upper short word   |   lower short word   |
20026 //      |<------------> <----->|<------------> <----->|
20027 //      |  left child   info hi| right child   info lo|
20028 // x86: |    10 bits     6 bits|   10 bits      6 bits|
20029 //
20030 // where left/right child are signed values and concat(info hi, info lo) is unsigned.
20031 //
20032 // The "align info" encodes two numbers: the required alignment (a power of two)
20033 // and the misalignment (the number of machine words the destination address needs
20034 // to be adjusted by to provide alignment - so this number is always smaller than
20035 // the required alignment).  Thus, the two can be represented as the "logical or"
20036 // of the two numbers.  Note that the actual pad is computed from the misalignment
20037 // by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
20038 //
20039
20040 // The number of bits in a brick.
20041 #if defined (_TARGET_AMD64_)
20042 #define brick_bits (12)
20043 #else
20044 #define brick_bits (11)
20045 #endif //_TARGET_AMD64_
20046 C_ASSERT(brick_size == (1 << brick_bits));
20047
20048 // The number of bits needed to represent the offset to a child node.
20049 // "brick_bits + 1" allows us to represent a signed offset within a brick.
20050 #define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
20051
20052 // The number of bits in each of the pad hi, pad lo fields.
20053 #define pad_bits (sizeof(short) * 8 - child_bits)
20054
20055 #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
20056 #define pad_mask ((1 << pad_bits) - 1)
20057 #define pad_from_short(w) ((size_t)(w) & pad_mask)
20058 #else // FEATURE_STRUCTALIGN
20059 #define child_from_short(w) (w)
20060 #endif // FEATURE_STRUCTALIGN
20061
20062 inline
20063 short node_left_child(uint8_t* node)
20064 {
20065     return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20066 }
20067
20068 inline
20069 void set_node_left_child(uint8_t* node, ptrdiff_t val)
20070 {
20071     assert (val > -(ptrdiff_t)brick_size);
20072     assert (val < (ptrdiff_t)brick_size);
20073     assert (Aligned (val));
20074 #ifdef FEATURE_STRUCTALIGN
20075     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20076     ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20077 #else // FEATURE_STRUCTALIGN
20078     ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
20079 #endif // FEATURE_STRUCTALIGN
20080     assert (node_left_child (node) == val);
20081 }
20082
20083 inline
20084 short node_right_child(uint8_t* node)
20085 {
20086     return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20087 }
20088
20089 inline
20090 void set_node_right_child(uint8_t* node, ptrdiff_t val)
20091 {
20092     assert (val > -(ptrdiff_t)brick_size);
20093     assert (val < (ptrdiff_t)brick_size);
20094     assert (Aligned (val));
20095 #ifdef FEATURE_STRUCTALIGN
20096     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20097     ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20098 #else // FEATURE_STRUCTALIGN
20099     ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
20100 #endif // FEATURE_STRUCTALIGN
20101     assert (node_right_child (node) == val);
20102 }
20103
20104 #ifdef FEATURE_STRUCTALIGN
20105 void node_aligninfo (uint8_t* node, int& requiredAlignment, ptrdiff_t& pad)
20106 {
20107     // Extract the single-number aligninfo from the fields.
20108     short left = ((plug_and_pair*)node)[-1].m_pair.left;
20109     short right = ((plug_and_pair*)node)[-1].m_pair.right;
20110     ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
20111     ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
20112
20113     // Replicate the topmost bit into all lower bits.
20114     ptrdiff_t x = aligninfo;
20115     x |= x >> 8;
20116     x |= x >> 4;
20117     x |= x >> 2;
20118     x |= x >> 1;
20119
20120     // Clear all bits but the highest.
20121     requiredAlignment = (int)(x ^ (x >> 1));
20122     pad = aligninfo - requiredAlignment;
20123     pad += AdjustmentForMinPadSize(pad, requiredAlignment);
20124 }
20125
20126 inline
20127 ptrdiff_t node_alignpad (uint8_t* node)
20128 {
20129     int requiredAlignment;
20130     ptrdiff_t alignpad;
20131     node_aligninfo (node, requiredAlignment, alignpad);
20132     return alignpad;
20133 }
20134
20135 void clear_node_aligninfo (uint8_t* node)
20136 {
20137     ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
20138     ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
20139 }
20140
20141 void set_node_aligninfo (uint8_t* node, int requiredAlignment, ptrdiff_t pad)
20142 {
20143     // Encode the alignment requirement and alignment offset as a single number
20144     // as described above.
20145     ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
20146     assert (Aligned (aligninfo));
20147     ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
20148     assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
20149
20150     ptrdiff_t hi = aligninfo_shifted >> pad_bits;
20151     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
20152     ((plug_and_pair*)node)[-1].m_pair.left |= hi;
20153
20154     ptrdiff_t lo = aligninfo_shifted & pad_mask;
20155     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
20156     ((plug_and_pair*)node)[-1].m_pair.right |= lo;
20157
20158 #ifdef _DEBUG
20159     int requiredAlignment2;
20160     ptrdiff_t pad2;
20161     node_aligninfo (node, requiredAlignment2, pad2);
20162     assert (requiredAlignment == requiredAlignment2);
20163     assert (pad == pad2);
20164 #endif // _DEBUG
20165 }
20166 #endif // FEATURE_STRUCTALIGN
20167
20168 inline
20169 void loh_set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20170 {
20171     ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
20172     *place = val;
20173 }
20174
20175 inline
20176 ptrdiff_t loh_node_relocation_distance(uint8_t* node)
20177 {
20178     return (((loh_obj_and_pad*)node)[-1].reloc);
20179 }
20180
20181 inline
20182 ptrdiff_t node_relocation_distance (uint8_t* node)
20183 {
20184     return (((plug_and_reloc*)(node))[-1].reloc & ~3);
20185 }
20186
20187 inline
20188 void set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20189 {
20190     assert (val == (val & ~3));
20191     ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
20192     //clear the left bit and the relocation field
20193     *place &= 1;
20194     // store the value
20195     *place |= val;
20196 }
20197
20198 #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
20199
20200 #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
20201
20202 #ifndef FEATURE_STRUCTALIGN
20203 void set_node_realigned(uint8_t* node)
20204 {
20205     ((plug_and_reloc*)(node))[-1].reloc |= 1;
20206 }
20207
20208 void clear_node_realigned(uint8_t* node)
20209 {
20210 #ifdef RESPECT_LARGE_ALIGNMENT
20211     ((plug_and_reloc*)(node))[-1].reloc &= ~1;
20212 #else
20213     UNREFERENCED_PARAMETER(node);
20214 #endif //RESPECT_LARGE_ALIGNMENT
20215 }
20216 #endif // FEATURE_STRUCTALIGN
20217
20218 inline
20219 size_t  node_gap_size (uint8_t* node)
20220 {
20221     return ((plug_and_gap *)node)[-1].gap;
20222 }
20223
20224 void set_gap_size (uint8_t* node, size_t size)
20225 {
20226     assert (Aligned (size));
20227
20228     // clear the 2 uint32_t used by the node.
20229     ((plug_and_gap *)node)[-1].reloc = 0;
20230     ((plug_and_gap *)node)[-1].lr =0;
20231     ((plug_and_gap *)node)[-1].gap = size;
20232
20233     assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
20234
20235 }
20236
20237 uint8_t* gc_heap::insert_node (uint8_t* new_node, size_t sequence_number,
20238                    uint8_t* tree, uint8_t* last_node)
20239 {
20240     dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
20241                  (size_t)new_node, brick_of(new_node), 
20242                  (size_t)tree, brick_of(tree), 
20243                  (size_t)last_node, brick_of(last_node),
20244                  sequence_number));
20245     if (power_of_two_p (sequence_number))
20246     {
20247         set_node_left_child (new_node, (tree - new_node));
20248         dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
20249         tree = new_node;
20250     }
20251     else
20252     {
20253         if (oddp (sequence_number))
20254         {
20255             set_node_right_child (last_node, (new_node - last_node));
20256             dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
20257         }
20258         else
20259         {
20260             uint8_t*  earlier_node = tree;
20261             size_t imax = logcount(sequence_number) - 2;
20262             for (size_t i = 0; i != imax; i++)
20263             {
20264                 earlier_node = earlier_node + node_right_child (earlier_node);
20265             }
20266             int tmp_offset = node_right_child (earlier_node);
20267             assert (tmp_offset); // should never be empty
20268             set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
20269             set_node_right_child (earlier_node, (new_node - earlier_node));
20270
20271             dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix", 
20272                 new_node, ((earlier_node + tmp_offset ) - new_node),
20273                 earlier_node, (new_node - earlier_node)));
20274         }
20275     }
20276     return tree;
20277 }
20278
20279 size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick,
20280                                     uint8_t* x, uint8_t* plug_end)
20281 {
20282     dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
20283         tree, current_brick, x, plug_end));
20284
20285     if (tree != NULL)
20286     {
20287         dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix", 
20288             current_brick, (size_t)(tree - brick_address (current_brick)), tree));
20289         set_brick (current_brick, (tree - brick_address (current_brick)));
20290     }
20291     else
20292     {
20293         dprintf (3, ("b- %Ix->-1", current_brick));
20294         set_brick (current_brick, -1);
20295     }
20296     size_t  b = 1 + current_brick;
20297     ptrdiff_t  offset = 0;
20298     size_t last_br = brick_of (plug_end-1);
20299     current_brick = brick_of (x-1);
20300     dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
20301     while (b <= current_brick)
20302     {
20303         if (b <= last_br)
20304         {
20305             set_brick (b, --offset);
20306         }
20307         else
20308         {
20309             set_brick (b,-1);
20310         }
20311         b++;
20312     }
20313     return brick_of (x);
20314 }
20315
20316 void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate)
20317 {
20318 #ifdef BIT64
20319     // We should never demote big plugs to gen0.
20320     if (gen == youngest_generation)
20321     {
20322         heap_segment* seg = ephemeral_heap_segment;
20323         size_t mark_stack_large_bos = mark_stack_bos;
20324         size_t large_plug_pos = 0;
20325         while (mark_stack_large_bos < mark_stack_tos)
20326         {
20327             if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
20328             {
20329                 while (mark_stack_bos <= mark_stack_large_bos)
20330                 {
20331                     size_t entry = deque_pinned_plug();
20332                     size_t len = pinned_len (pinned_plug_of (entry));
20333                     uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20334                     if (len > demotion_plug_len_th)
20335                     {
20336                         dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
20337                     }
20338                     pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
20339                     assert(mark_stack_array[entry].len == 0 ||
20340                             mark_stack_array[entry].len >= Align(min_obj_size));
20341                     generation_allocation_pointer (consing_gen) = plug + len;
20342                     generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
20343                     set_allocator_next_pin (consing_gen);
20344                 }
20345             }
20346
20347             mark_stack_large_bos++;
20348         }
20349     }
20350 #endif // BIT64
20351
20352     generation_plan_allocation_start (gen) =
20353         allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
20354     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20355     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20356     if (next_plug_to_allocate)
20357     {
20358         size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
20359         if (allocation_left > dist_to_next_plug)
20360         {
20361             allocation_left = dist_to_next_plug;
20362         }
20363     }
20364     if (allocation_left < Align (min_obj_size))
20365     {
20366         generation_plan_allocation_start_size (gen) += allocation_left;
20367         generation_allocation_pointer (consing_gen) += allocation_left;
20368     }
20369
20370     dprintf (1, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)", gen->gen_num, 
20371         generation_plan_allocation_start (gen),
20372         generation_plan_allocation_start_size (gen),
20373         generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen),
20374         next_plug_to_allocate));
20375 }
20376
20377 void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
20378 {
20379     BOOL adjacentp = FALSE;
20380
20381     generation_plan_allocation_start (gen) =  
20382         allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0, 
20383 #ifdef SHORT_PLUGS
20384                                    FALSE, NULL, 
20385 #endif //SHORT_PLUGS
20386                                    FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
20387
20388     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20389     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20390     if ((allocation_left < Align (min_obj_size)) && 
20391          (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
20392     {
20393         generation_plan_allocation_start_size (gen) += allocation_left;
20394         generation_allocation_pointer (consing_gen) += allocation_left;
20395     }
20396
20397     dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num, 
20398         generation_plan_allocation_start (consing_gen),
20399         generation_allocation_pointer (consing_gen), 
20400         generation_allocation_limit (consing_gen))); 
20401 }
20402
20403 void gc_heap::plan_generation_starts (generation*& consing_gen)
20404 {
20405     //make sure that every generation has a planned allocation start
20406     int  gen_number = settings.condemned_generation;
20407     while (gen_number >= 0)
20408     {
20409         if (gen_number < max_generation)
20410         {
20411             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20412         }
20413         generation* gen = generation_of (gen_number);
20414         if (0 == generation_plan_allocation_start (gen))
20415         {
20416             plan_generation_start (gen, consing_gen, 0);
20417             assert (generation_plan_allocation_start (gen));
20418         }
20419         gen_number--;
20420     }
20421     // now we know the planned allocation size
20422     heap_segment_plan_allocated (ephemeral_heap_segment) =
20423         generation_allocation_pointer (consing_gen);
20424 }
20425
20426 void gc_heap::advance_pins_for_demotion (generation* gen)
20427 {
20428     uint8_t* original_youngest_start = generation_allocation_start (youngest_generation);
20429     heap_segment* seg = ephemeral_heap_segment;
20430
20431     if ((!(pinned_plug_que_empty_p())))
20432     {
20433         size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
20434         size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
20435         size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
20436         float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
20437         float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
20438         if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
20439         {
20440             while (!pinned_plug_que_empty_p() &&
20441                     (pinned_plug (oldest_pin()) < original_youngest_start))
20442             {
20443                 size_t entry = deque_pinned_plug();
20444                 size_t len = pinned_len (pinned_plug_of (entry));
20445                 uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20446                 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
20447                 assert(mark_stack_array[entry].len == 0 ||
20448                         mark_stack_array[entry].len >= Align(min_obj_size));
20449                 generation_allocation_pointer (gen) = plug + len;
20450                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20451                 set_allocator_next_pin (gen);
20452
20453                 //Add the size of the pinned plug to the right pinned allocations
20454                 //find out which gen this pinned plug came from 
20455                 int frgn = object_gennum (plug);
20456                 if ((frgn != (int)max_generation) && settings.promotion)
20457                 {
20458                     int togn = object_gennum_plan (plug);
20459                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20460                     if (frgn < togn)
20461                     {
20462                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
20463                     }
20464                 }
20465
20466                 dprintf (2, ("skipping gap %d, pin %Ix (%Id)", 
20467                     pinned_len (pinned_plug_of (entry)), plug, len));
20468             }
20469         }
20470         dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d", 
20471             gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
20472     }
20473 }
20474
20475 void gc_heap::process_ephemeral_boundaries (uint8_t* x,
20476                                             int& active_new_gen_number,
20477                                             int& active_old_gen_number,
20478                                             generation*& consing_gen,
20479                                             BOOL& allocate_in_condemned)
20480 {
20481 retry:
20482     if ((active_old_gen_number > 0) &&
20483         (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
20484     {
20485         dprintf (1, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
20486
20487         if (!pinned_plug_que_empty_p())
20488         {
20489             dprintf (1, ("oldest pin: %Ix(%Id)",
20490                 pinned_plug (oldest_pin()), 
20491                 (x - pinned_plug (oldest_pin()))));
20492         }
20493
20494         if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
20495         {
20496             active_new_gen_number--;
20497         }
20498
20499         active_old_gen_number--;
20500         assert ((!settings.promotion) || (active_new_gen_number>0));
20501
20502         if (active_new_gen_number == (max_generation - 1))
20503         {
20504 #ifdef FREE_USAGE_STATS
20505             if (settings.condemned_generation == max_generation)
20506             {
20507                 // We need to do this before we skip the rest of the pinned plugs.
20508                 generation* gen_2 = generation_of (max_generation);
20509                 generation* gen_1 = generation_of (max_generation - 1);
20510
20511                 size_t total_num_pinned_free_spaces_left = 0;
20512
20513                 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
20514                 for (int j = 0; j < NUM_GEN_POWER2; j++)
20515                 {
20516                     dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)", 
20517                         heap_number, 
20518                         settings.gc_index,
20519                         (j + 10), 
20520                         gen_2->gen_current_pinned_free_spaces[j],
20521                         gen_2->gen_plugs[j], gen_1->gen_plugs[j],
20522                         (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
20523
20524                     total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
20525                 }
20526
20527                 float pinned_free_list_efficiency = 0;
20528                 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
20529                 if (total_pinned_free_space != 0)
20530                 {
20531                     pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
20532                 }
20533
20534                 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
20535                             heap_number,
20536                             generation_allocated_in_pinned_free (gen_2),
20537                             total_pinned_free_space, 
20538                             (int)(pinned_free_list_efficiency * 100),
20539                             generation_pinned_free_obj_space (gen_2),
20540                             total_num_pinned_free_spaces_left));
20541             }
20542 #endif //FREE_USAGE_STATS
20543
20544             //Go past all of the pinned plugs for this generation.
20545             while (!pinned_plug_que_empty_p() &&
20546                    (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
20547             {
20548                 size_t  entry = deque_pinned_plug();
20549                 mark*  m = pinned_plug_of (entry);
20550                 uint8_t*  plug = pinned_plug (m);
20551                 size_t  len = pinned_len (m);
20552                 // detect pinned block in different segment (later) than
20553                 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
20554                 // adjust the allocation segment along the way (at the end it will
20555                 // be the ephemeral segment.
20556                 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
20557
20558                 PREFIX_ASSUME(nseg != NULL);
20559
20560                 while (!((plug >= generation_allocation_pointer (consing_gen))&&
20561                         (plug < heap_segment_allocated (nseg))))
20562                 {
20563                     //adjust the end of the segment to be the end of the plug
20564                     assert (generation_allocation_pointer (consing_gen)>=
20565                             heap_segment_mem (nseg));
20566                     assert (generation_allocation_pointer (consing_gen)<=
20567                             heap_segment_committed (nseg));
20568
20569                     heap_segment_plan_allocated (nseg) =
20570                         generation_allocation_pointer (consing_gen);
20571                     //switch allocation segment
20572                     nseg = heap_segment_next_rw (nseg);
20573                     generation_allocation_segment (consing_gen) = nseg;
20574                     //reset the allocation pointer and limits
20575                     generation_allocation_pointer (consing_gen) =
20576                         heap_segment_mem (nseg);
20577                 }
20578                 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
20579                 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
20580                 generation_allocation_pointer (consing_gen) = plug + len;
20581                 generation_allocation_limit (consing_gen) =
20582                     generation_allocation_pointer (consing_gen);
20583             }
20584             allocate_in_condemned = TRUE;
20585             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20586         }
20587
20588         if (active_new_gen_number != max_generation)
20589         {
20590             if (active_new_gen_number == (max_generation - 1))
20591             {
20592                 maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
20593                 if (!demote_gen1_p)
20594                     advance_pins_for_demotion (consing_gen);
20595             }
20596
20597             plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
20598                 
20599             dprintf (1, ("process eph: allocated gen%d start at %Ix", 
20600                 active_new_gen_number,
20601                 generation_plan_allocation_start (generation_of (active_new_gen_number))));
20602
20603             if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
20604             {
20605                 uint8_t* pplug = pinned_plug (oldest_pin());
20606                 if (object_gennum (pplug) > 0)
20607                 {
20608                     demotion_low = pplug;
20609                     dprintf (3, ("process eph: dlow->%Ix", demotion_low));
20610                 }
20611             }
20612
20613             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
20614         }
20615
20616         goto retry;
20617     }
20618 }
20619
20620 inline
20621 void gc_heap::seg_clear_mark_bits (heap_segment* seg)
20622 {
20623     uint8_t* o = heap_segment_mem (seg);
20624     while (o < heap_segment_allocated (seg))
20625     {
20626         if (marked (o))
20627         {
20628             clear_marked (o);
20629         }
20630         o = o  + Align (size (o));
20631     }
20632 }
20633
20634 #ifdef FEATURE_BASICFREEZE
20635 void gc_heap::sweep_ro_segments (heap_segment* start_seg)
20636 {
20637     //go through all of the segment in range and reset the mark bit
20638     //TODO works only on small object segments
20639
20640     heap_segment* seg = start_seg;
20641
20642     while (seg)
20643     {
20644         if (heap_segment_read_only_p (seg) &&
20645             heap_segment_in_range_p (seg))
20646         {
20647 #ifdef BACKGROUND_GC
20648             if (settings.concurrent)
20649             {
20650                 seg_clear_mark_array_bits_soh (seg);
20651             }
20652             else
20653             {
20654                 seg_clear_mark_bits (seg);
20655             }
20656 #else //BACKGROUND_GC
20657
20658 #ifdef MARK_ARRAY
20659             if(gc_can_use_concurrent)
20660             {
20661                 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
20662                               min (heap_segment_allocated (seg), highest_address),
20663                               FALSE); // read_only segments need the mark clear
20664             }
20665 #else //MARK_ARRAY
20666             seg_clear_mark_bits (seg);
20667 #endif //MARK_ARRAY
20668
20669 #endif //BACKGROUND_GC
20670         }
20671         seg = heap_segment_next (seg);
20672     }
20673 }
20674 #endif // FEATURE_BASICFREEZE
20675
20676 #ifdef FEATURE_LOH_COMPACTION
20677 inline
20678 BOOL gc_heap::loh_pinned_plug_que_empty_p()
20679 {
20680     return (loh_pinned_queue_bos == loh_pinned_queue_tos);
20681 }
20682
20683 void gc_heap::loh_set_allocator_next_pin()
20684 {
20685     if (!(loh_pinned_plug_que_empty_p()))
20686     {
20687         mark*  oldest_entry = loh_oldest_pin();
20688         uint8_t* plug = pinned_plug (oldest_entry);
20689         generation* gen = large_object_generation;
20690         if ((plug >= generation_allocation_pointer (gen)) &&
20691             (plug <  generation_allocation_limit (gen)))
20692         {
20693             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
20694         }
20695         else
20696             assert (!((plug < generation_allocation_pointer (gen)) &&
20697                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
20698     }
20699 }
20700
20701 size_t gc_heap::loh_deque_pinned_plug ()
20702 {
20703     size_t m = loh_pinned_queue_bos;
20704     loh_pinned_queue_bos++;
20705     return m;
20706 }
20707
20708 inline
20709 mark* gc_heap::loh_pinned_plug_of (size_t bos)
20710 {
20711     return &loh_pinned_queue[bos];
20712 }
20713
20714 inline
20715 mark* gc_heap::loh_oldest_pin()
20716 {
20717     return loh_pinned_plug_of (loh_pinned_queue_bos);
20718 }
20719
20720 // If we can't grow the queue, then don't compact.
20721 BOOL gc_heap::loh_enque_pinned_plug (uint8_t* plug, size_t len)
20722 {
20723     assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
20724
20725     if (loh_pinned_queue_length <= loh_pinned_queue_tos)
20726     {
20727         if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
20728         {
20729             return FALSE;
20730         }
20731     }
20732     dprintf (3, (" P: %Ix(%Id)", plug, len));
20733     mark& m = loh_pinned_queue[loh_pinned_queue_tos];
20734     m.first = plug;
20735     m.len = len;
20736     loh_pinned_queue_tos++;
20737     loh_set_allocator_next_pin();
20738     return TRUE;
20739 }
20740
20741 inline
20742 BOOL gc_heap::loh_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit)
20743 {
20744     dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)", 
20745         size, 
20746         (2* AlignQword (loh_padding_obj_size) +  size),
20747         alloc_pointer,
20748         alloc_limit,
20749         (alloc_limit - alloc_pointer)));
20750
20751     return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) +  size) <= alloc_limit);
20752 }
20753
20754 uint8_t* gc_heap::loh_allocate_in_condemned (uint8_t* old_loc, size_t size)
20755 {
20756     UNREFERENCED_PARAMETER(old_loc);
20757
20758     generation* gen = large_object_generation;
20759     dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id", 
20760         generation_allocation_pointer (gen),
20761         generation_allocation_limit (gen),
20762         size));
20763
20764 retry:
20765     {
20766         heap_segment* seg = generation_allocation_segment (gen);
20767         if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
20768         {
20769             if ((!(loh_pinned_plug_que_empty_p()) &&
20770                  (generation_allocation_limit (gen) ==
20771                   pinned_plug (loh_oldest_pin()))))
20772             {
20773                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
20774                 size_t len = pinned_len (m);
20775                 uint8_t* plug = pinned_plug (m);
20776                 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
20777                 pinned_len (m) = plug - generation_allocation_pointer (gen);
20778                 generation_allocation_pointer (gen) = plug + len;
20779                 
20780                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20781                 loh_set_allocator_next_pin();
20782                 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)", 
20783                     generation_allocation_pointer (gen), 
20784                     generation_allocation_limit (gen),
20785                     (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20786
20787                 goto retry;
20788             }
20789
20790             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
20791             {
20792                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20793                 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
20794             }
20795             else
20796             {
20797                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
20798                 {
20799                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
20800                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20801                     dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
20802                 }
20803                 else
20804                 {
20805                     if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
20806                         (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
20807                     {
20808                         dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
20809                                          (generation_allocation_pointer (gen) + size)));
20810
20811                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
20812                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20813
20814                         dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)", 
20815                             generation_allocation_pointer (gen), 
20816                             generation_allocation_limit (gen),
20817                             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20818                     }
20819                     else
20820                     {
20821                         heap_segment* next_seg = heap_segment_next (seg);
20822                         assert (generation_allocation_pointer (gen)>=
20823                                 heap_segment_mem (seg));
20824                         // Verify that all pinned plugs for this segment are consumed
20825                         if (!loh_pinned_plug_que_empty_p() &&
20826                             ((pinned_plug (loh_oldest_pin()) <
20827                               heap_segment_allocated (seg)) &&
20828                              (pinned_plug (loh_oldest_pin()) >=
20829                               generation_allocation_pointer (gen))))
20830                         {
20831                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
20832                                          pinned_plug (loh_oldest_pin())));
20833                             dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
20834                             FATAL_GC_ERROR();
20835                         }
20836                         assert (generation_allocation_pointer (gen)>=
20837                                 heap_segment_mem (seg));
20838                         assert (generation_allocation_pointer (gen)<=
20839                                 heap_segment_committed (seg));
20840                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
20841
20842                         if (next_seg)
20843                         {
20844                             // for LOH do we want to try starting from the first LOH every time though?
20845                             generation_allocation_segment (gen) = next_seg;
20846                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
20847                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
20848
20849                             dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)", 
20850                                 generation_allocation_pointer (gen), 
20851                                 generation_allocation_limit (gen),
20852                                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20853                         }
20854                         else
20855                         {
20856                             dprintf (1, ("We ran out of space compacting, shouldn't happen"));
20857                             FATAL_GC_ERROR();
20858                         }
20859                     }
20860                 }
20861             }
20862             loh_set_allocator_next_pin();
20863
20864             dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)", 
20865                 generation_allocation_pointer (gen), 
20866                 generation_allocation_limit (gen),
20867                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20868
20869             goto retry;
20870         }
20871     }
20872
20873     {
20874         assert (generation_allocation_pointer (gen)>=
20875                 heap_segment_mem (generation_allocation_segment (gen)));
20876         uint8_t* result = generation_allocation_pointer (gen);
20877         size_t loh_pad = AlignQword (loh_padding_obj_size);
20878
20879         generation_allocation_pointer (gen) += size + loh_pad;
20880         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
20881
20882         dprintf (1235, ("p: %Ix, l: %Ix (%Id)", 
20883             generation_allocation_pointer (gen), 
20884             generation_allocation_limit (gen),
20885             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20886
20887         assert (result + loh_pad);
20888         return result + loh_pad;
20889     }
20890 }
20891
20892 BOOL gc_heap::should_compact_loh()
20893 {
20894     return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
20895 }
20896
20897 inline
20898 void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
20899 {
20900     if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
20901     {
20902         if (all_heaps_compacted_p)
20903         {
20904             // If the compaction mode says to compact once and we are going to compact LOH, 
20905             // we need to revert it back to no compaction.
20906             loh_compaction_mode = loh_compaction_default;
20907         }
20908     }
20909 }
20910
20911 BOOL gc_heap::plan_loh()
20912 {
20913     if (!loh_pinned_queue)
20914     {
20915         loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
20916         if (!loh_pinned_queue)
20917         {
20918             dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction", 
20919                          LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
20920             return FALSE;
20921         }
20922
20923         loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
20924     }
20925
20926     if (heap_number == 0)
20927         loh_pinned_queue_decay = LOH_PIN_DECAY;
20928
20929     loh_pinned_queue_tos = 0;
20930     loh_pinned_queue_bos = 0;
20931     
20932     generation* gen        = large_object_generation;
20933     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
20934     PREFIX_ASSUME(start_seg != NULL);
20935     heap_segment* seg      = start_seg;
20936     uint8_t* o             = generation_allocation_start (gen);
20937
20938     dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n", 
20939         generation_size (max_generation + 1), 
20940         generation_free_list_space (gen),
20941         generation_free_obj_space (gen)));
20942
20943     while (seg)
20944     {
20945         heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
20946         seg = heap_segment_next (seg);
20947     }
20948
20949     seg = start_seg;
20950
20951     //Skip the generation gap object
20952     o = o + AlignQword (size (o));
20953     // We don't need to ever realloc gen3 start so don't touch it.
20954     heap_segment_plan_allocated (seg) = o;
20955     generation_allocation_pointer (gen) = o;
20956     generation_allocation_limit (gen) = generation_allocation_pointer (gen);
20957     generation_allocation_segment (gen) = start_seg;
20958
20959     uint8_t* free_space_start = o;
20960     uint8_t* free_space_end = o;
20961     uint8_t* new_address = 0;
20962
20963     while (1)
20964     {
20965         if (o >= heap_segment_allocated (seg))
20966         {
20967             seg = heap_segment_next (seg);
20968             if (seg == 0)
20969             {
20970                 break;
20971             }
20972
20973             o = heap_segment_mem (seg);
20974         }
20975
20976         if (marked (o))
20977         {
20978             free_space_end = o;
20979             size_t size = AlignQword (size (o));
20980             dprintf (1235, ("%Ix(%Id) M", o, size));
20981
20982             if (pinned (o))
20983             {
20984                 // We don't clear the pinned bit yet so we can check in 
20985                 // compact phase how big a free object we should allocate
20986                 // in front of the pinned object. We use the reloc address
20987                 // field to store this.
20988                 if (!loh_enque_pinned_plug (o, size))
20989                 {
20990                     return FALSE;
20991                 }
20992                 new_address = o;
20993             }
20994             else
20995             {
20996                 new_address = loh_allocate_in_condemned (o, size);
20997             }
20998
20999             loh_set_node_relocation_distance (o, (new_address - o));
21000             dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
21001
21002             o = o + size;
21003             free_space_start = o;
21004             if (o < heap_segment_allocated (seg))
21005             {
21006                 assert (!marked (o));
21007             }
21008         }
21009         else
21010         {
21011             while (o < heap_segment_allocated (seg) && !marked (o))
21012             {
21013                 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_gc_pFreeObjectMethodTable) ? 1 : 0)));
21014                 o = o + AlignQword (size (o));
21015             }
21016         }
21017     }
21018
21019     while (!loh_pinned_plug_que_empty_p())
21020     {
21021         mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21022         size_t len = pinned_len (m);
21023         uint8_t* plug = pinned_plug (m);
21024
21025         // detect pinned block in different segment (later) than
21026         // allocation segment
21027         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
21028
21029         while ((plug < generation_allocation_pointer (gen)) ||
21030                (plug >= heap_segment_allocated (nseg)))
21031         {
21032             assert ((plug < heap_segment_mem (nseg)) ||
21033                     (plug > heap_segment_reserved (nseg)));
21034             //adjust the end of the segment to be the end of the plug
21035             assert (generation_allocation_pointer (gen)>=
21036                     heap_segment_mem (nseg));
21037             assert (generation_allocation_pointer (gen)<=
21038                     heap_segment_committed (nseg));
21039
21040             heap_segment_plan_allocated (nseg) =
21041                 generation_allocation_pointer (gen);
21042             //switch allocation segment
21043             nseg = heap_segment_next_rw (nseg);
21044             generation_allocation_segment (gen) = nseg;
21045             //reset the allocation pointer and limits
21046             generation_allocation_pointer (gen) =
21047                 heap_segment_mem (nseg);
21048         }
21049
21050         dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21051         pinned_len (m) = plug - generation_allocation_pointer (gen);
21052         generation_allocation_pointer (gen) = plug + len;
21053     }
21054
21055     heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
21056     generation_allocation_pointer (gen) = 0;
21057     generation_allocation_limit (gen) = 0;
21058
21059     return TRUE;
21060 }
21061
21062 void gc_heap::compact_loh()
21063 {
21064     assert (should_compact_loh());
21065
21066     generation* gen        = large_object_generation;
21067     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21068     PREFIX_ASSUME(start_seg != NULL);
21069     heap_segment* seg      = start_seg;
21070     heap_segment* prev_seg = 0;
21071     uint8_t* o             = generation_allocation_start (gen);
21072
21073     //Skip the generation gap object
21074     o = o + AlignQword (size (o));
21075     // We don't need to ever realloc gen3 start so don't touch it.
21076     uint8_t* free_space_start = o;
21077     uint8_t* free_space_end = o;
21078     generation_allocator (gen)->clear();
21079     generation_free_list_space (gen) = 0;
21080     generation_free_obj_space (gen) = 0;
21081
21082     loh_pinned_queue_bos = 0;
21083
21084     while (1)
21085     {
21086         if (o >= heap_segment_allocated (seg))
21087         {
21088             heap_segment* next_seg = heap_segment_next (seg);
21089
21090             if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
21091                 (seg != start_seg) && !heap_segment_read_only_p (seg))
21092             {
21093                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
21094                 assert (prev_seg);
21095                 heap_segment_next (prev_seg) = next_seg;
21096                 heap_segment_next (seg) = freeable_large_heap_segment;
21097                 freeable_large_heap_segment = seg;
21098             }
21099             else
21100             {
21101                 if (!heap_segment_read_only_p (seg))
21102                 {
21103                     // We grew the segment to accommodate allocations.
21104                     if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
21105                     {
21106                         if ((heap_segment_plan_allocated (seg) - plug_skew)  > heap_segment_used (seg))
21107                         {
21108                             heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
21109                         }
21110                     }
21111
21112                     heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
21113                     dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
21114                     decommit_heap_segment_pages (seg, 0);
21115                     dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
21116                         seg, 
21117                         heap_segment_allocated (seg),
21118                         heap_segment_used (seg),
21119                         heap_segment_committed (seg)));
21120                     //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
21121                     dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
21122                 }
21123                 prev_seg = seg;
21124             }
21125
21126             seg = next_seg;
21127             if (seg == 0)
21128                 break;
21129             else
21130             {
21131                 o = heap_segment_mem (seg);
21132             }
21133         }
21134
21135         if (marked (o))
21136         {
21137             free_space_end = o;
21138             size_t size = AlignQword (size (o));
21139
21140             size_t loh_pad;
21141             uint8_t* reloc = o;
21142             clear_marked (o);
21143
21144             if (pinned (o))
21145             {
21146                 // We are relying on the fact the pinned objects are always looked at in the same order 
21147                 // in plan phase and in compact phase.
21148                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21149                 uint8_t* plug = pinned_plug (m);
21150                 assert (plug == o);
21151
21152                 loh_pad = pinned_len (m);
21153                 clear_pinned (o);
21154             }
21155             else
21156             {
21157                 loh_pad = AlignQword (loh_padding_obj_size);
21158
21159                 reloc += loh_node_relocation_distance (o);
21160                 gcmemcopy (reloc, o, size, TRUE);
21161             }
21162
21163             thread_gap ((reloc - loh_pad), loh_pad, gen);
21164
21165             o = o + size;
21166             free_space_start = o;
21167             if (o < heap_segment_allocated (seg))
21168             {
21169                 assert (!marked (o));
21170             }
21171         }
21172         else
21173         {
21174             while (o < heap_segment_allocated (seg) && !marked (o))
21175             {
21176                 o = o + AlignQword (size (o));
21177             }
21178         }
21179     }
21180
21181     assert (loh_pinned_plug_que_empty_p());
21182
21183     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21184         generation_size (max_generation + 1), 
21185         generation_free_list_space (gen),
21186         generation_free_obj_space (gen)));
21187 }
21188
21189 void gc_heap::relocate_in_loh_compact()
21190 {
21191     generation* gen        = large_object_generation;
21192     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21193     uint8_t* o             = generation_allocation_start (gen);
21194
21195     //Skip the generation gap object
21196     o = o + AlignQword (size (o));
21197
21198     relocate_args args;
21199     args.low = gc_low;
21200     args.high = gc_high;
21201     args.last_plug = 0;
21202
21203     while (1)
21204     {
21205         if (o >= heap_segment_allocated (seg))
21206         {
21207             seg = heap_segment_next (seg);
21208             if (seg == 0)
21209             {
21210                 break;
21211             }
21212
21213             o = heap_segment_mem (seg);
21214         }
21215
21216         if (marked (o))
21217         {
21218             size_t size = AlignQword (size (o));
21219
21220             check_class_object_demotion (o);
21221             if (contain_pointers (o))
21222             {
21223                 go_through_object_nostart (method_table (o), o, size(o), pval,
21224                 {
21225                     reloc_survivor_helper (pval);
21226                 });
21227             }
21228
21229             o = o + size;
21230             if (o < heap_segment_allocated (seg))
21231             {
21232                 assert (!marked (o));
21233             }
21234         }
21235         else
21236         {
21237             while (o < heap_segment_allocated (seg) && !marked (o))
21238             {
21239                 o = o + AlignQword (size (o));
21240             }
21241         }
21242     }
21243
21244     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21245         generation_size (max_generation + 1), 
21246         generation_free_list_space (gen),
21247         generation_free_obj_space (gen)));
21248 }
21249
21250 void gc_heap::walk_relocation_for_loh (void* profiling_context, record_surv_fn fn)
21251 {
21252     generation* gen        = large_object_generation;
21253     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21254     uint8_t* o             = generation_allocation_start (gen);
21255
21256     //Skip the generation gap object
21257     o = o + AlignQword (size (o));
21258
21259     while (1)
21260     {
21261         if (o >= heap_segment_allocated (seg))
21262         {
21263             seg = heap_segment_next (seg);
21264             if (seg == 0)
21265             {
21266                 break;
21267             }
21268
21269             o = heap_segment_mem (seg);
21270         }
21271
21272         if (marked (o))
21273         {
21274             size_t size = AlignQword (size (o));
21275
21276             ptrdiff_t reloc = loh_node_relocation_distance (o);
21277
21278             STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
21279
21280             fn (o, (o + size), reloc, profiling_context, !!settings.compaction, false);
21281
21282             o = o + size;
21283             if (o < heap_segment_allocated (seg))
21284             {
21285                 assert (!marked (o));
21286             }
21287         }
21288         else
21289         {
21290             while (o < heap_segment_allocated (seg) && !marked (o))
21291             {
21292                 o = o + AlignQword (size (o));
21293             }
21294         }
21295     }
21296 }
21297
21298 BOOL gc_heap::loh_object_p (uint8_t* o)
21299 {
21300 #ifdef MULTIPLE_HEAPS
21301     gc_heap* hp = gc_heap::g_heaps [0];
21302     int brick_entry = hp->brick_table[hp->brick_of (o)];
21303 #else //MULTIPLE_HEAPS
21304     int brick_entry = brick_table[brick_of (o)];
21305 #endif //MULTIPLE_HEAPS
21306
21307     return (brick_entry == 0);
21308 }
21309 #endif //FEATURE_LOH_COMPACTION
21310
21311 void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p, 
21312                                       BOOL& last_pinned_plug_p, 
21313                                       BOOL& pinned_plug_p,
21314                                       size_t ps,
21315                                       size_t& artificial_pinned_size)
21316 {
21317     last_npinned_plug_p = FALSE;
21318     last_pinned_plug_p = TRUE;
21319     pinned_plug_p = TRUE;
21320     artificial_pinned_size = ps;
21321 }
21322
21323 // Because we have the artificial pinning, we can't guarantee that pinned and npinned
21324 // plugs are always interleaved.
21325 void gc_heap::store_plug_gap_info (uint8_t* plug_start,
21326                                    uint8_t* plug_end,
21327                                    BOOL& last_npinned_plug_p, 
21328                                    BOOL& last_pinned_plug_p, 
21329                                    uint8_t*& last_pinned_plug,
21330                                    BOOL& pinned_plug_p,
21331                                    uint8_t* last_object_in_last_plug,
21332                                    BOOL& merge_with_last_pin_p,
21333                                    // this is only for verification purpose
21334                                    size_t last_plug_len)
21335 {
21336     UNREFERENCED_PARAMETER(last_plug_len);
21337
21338     if (!last_npinned_plug_p && !last_pinned_plug_p)
21339     {
21340         //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
21341         dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
21342         assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
21343         set_gap_size (plug_start, plug_start - plug_end);
21344     }
21345
21346     if (pinned (plug_start))
21347     {
21348         BOOL save_pre_plug_info_p = FALSE;
21349
21350         if (last_npinned_plug_p || last_pinned_plug_p)
21351         {
21352             //if (last_plug_len == Align (min_obj_size))
21353             //{
21354             //    dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
21355             //    GCToOSInterface::DebugBreak();
21356             //}
21357             save_pre_plug_info_p = TRUE;
21358         }
21359
21360         pinned_plug_p = TRUE;
21361         last_npinned_plug_p = FALSE;
21362
21363         if (last_pinned_plug_p)
21364         {
21365             dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
21366             merge_with_last_pin_p = TRUE;
21367         }
21368         else
21369         {
21370             last_pinned_plug_p = TRUE;
21371             last_pinned_plug = plug_start;
21372                 
21373             enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
21374
21375             if (save_pre_plug_info_p)
21376             {
21377                 set_gap_size (plug_start, sizeof (gap_reloc_pair));
21378             }
21379         }
21380     }
21381     else
21382     {
21383         if (last_pinned_plug_p)
21384         {
21385             //if (Align (last_plug_len) < min_pre_pin_obj_size)
21386             //{
21387             //    dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
21388             //    GCToOSInterface::DebugBreak();
21389             //}
21390
21391             save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
21392             set_gap_size (plug_start, sizeof (gap_reloc_pair));
21393
21394             verify_pins_with_post_plug_info("after saving post plug info");
21395         }
21396         last_npinned_plug_p = TRUE;
21397         last_pinned_plug_p = FALSE;
21398     }
21399 }
21400
21401 void gc_heap::record_interesting_data_point (interesting_data_point idp)
21402 {
21403 #ifdef GC_CONFIG_DRIVEN
21404     (interesting_data_per_gc[idp])++;
21405 #else
21406     UNREFERENCED_PARAMETER(idp);
21407 #endif //GC_CONFIG_DRIVEN
21408 }
21409
21410 #ifdef _PREFAST_
21411 #pragma warning(push)
21412 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
21413 #endif //_PREFAST_
21414 void gc_heap::plan_phase (int condemned_gen_number)
21415 {
21416     size_t old_gen2_allocated = 0;
21417     size_t old_gen2_size = 0;
21418
21419     if (condemned_gen_number == (max_generation - 1))
21420     {
21421         old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
21422         old_gen2_size = generation_size (max_generation);
21423     }
21424
21425     assert (settings.concurrent == FALSE);
21426
21427     // %type%  category = quote (plan);
21428 #ifdef TIME_GC
21429     unsigned start;
21430     unsigned finish;
21431     start = GetCycleCount32();
21432 #endif //TIME_GC
21433
21434     dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
21435                 condemned_gen_number, settings.promotion ? 1 : 0));
21436
21437     generation*  condemned_gen1 = generation_of (condemned_gen_number);
21438
21439 #ifdef MARK_LIST
21440     BOOL use_mark_list = FALSE;
21441     uint8_t** mark_list_next = &mark_list[0];
21442 #ifdef GC_CONFIG_DRIVEN
21443     dprintf (3, ("total number of marked objects: %Id (%Id)",
21444                  (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0]))));
21445 #else
21446     dprintf (3, ("mark_list length: %Id",
21447                  (mark_list_index - &mark_list[0])));
21448 #endif //GC_CONFIG_DRIVEN
21449
21450     if ((condemned_gen_number < max_generation) &&
21451         (mark_list_index <= mark_list_end) 
21452 #ifdef BACKGROUND_GC        
21453         && (!recursive_gc_sync::background_running_p())
21454 #endif //BACKGROUND_GC
21455         )
21456     {
21457 #ifndef MULTIPLE_HEAPS
21458         _sort (&mark_list[0], mark_list_index-1, 0);
21459         //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
21460         //verify_qsort_array (&mark_list[0], mark_list_index-1);
21461 #endif //!MULTIPLE_HEAPS
21462         use_mark_list = TRUE;
21463         get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit);
21464     }
21465     else
21466     {
21467         dprintf (3, ("mark_list not used"));
21468     }
21469
21470 #endif //MARK_LIST
21471
21472 #ifdef FEATURE_BASICFREEZE
21473     if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
21474         ro_segments_in_range)
21475     {
21476         sweep_ro_segments (generation_start_segment (condemned_gen1));
21477     }
21478 #endif // FEATURE_BASICFREEZE
21479
21480 #ifndef MULTIPLE_HEAPS
21481     if (shigh != (uint8_t*)0)
21482     {
21483         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21484
21485         PREFIX_ASSUME(seg != NULL);
21486
21487         heap_segment* fseg = seg;
21488         do
21489         {
21490             if (slow > heap_segment_mem (seg) &&
21491                 slow < heap_segment_reserved (seg))
21492             {
21493                 if (seg == fseg)
21494                 {
21495                     uint8_t* o = generation_allocation_start (condemned_gen1) +
21496                         Align (size (generation_allocation_start (condemned_gen1)));
21497                     if (slow > o)
21498                     {
21499                         assert ((slow - o) >= (int)Align (min_obj_size));
21500 #ifdef BACKGROUND_GC
21501                         if (current_c_gc_state == c_gc_state_marking)
21502                         {
21503                             bgc_clear_batch_mark_array_bits (o, slow);
21504                         }
21505 #endif //BACKGROUND_GC
21506                         make_unused_array (o, slow - o);
21507                     }
21508                 } 
21509                 else
21510                 {
21511                     assert (condemned_gen_number == max_generation);
21512                     make_unused_array (heap_segment_mem (seg),
21513                                        slow - heap_segment_mem (seg));
21514                 }
21515             }
21516             if (in_range_for_segment (shigh, seg))
21517             {
21518 #ifdef BACKGROUND_GC
21519                 if (current_c_gc_state == c_gc_state_marking)
21520                 {
21521                     bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
21522                 }
21523 #endif //BACKGROUND_GC
21524                 heap_segment_allocated (seg) = shigh + Align (size (shigh));
21525             }
21526             // test if the segment is in the range of [slow, shigh]
21527             if (!((heap_segment_reserved (seg) >= slow) &&
21528                   (heap_segment_mem (seg) <= shigh)))
21529             {
21530                 // shorten it to minimum
21531                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
21532             }
21533             seg = heap_segment_next_rw (seg);
21534         } while (seg);
21535     }
21536     else
21537     {
21538         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21539
21540         PREFIX_ASSUME(seg != NULL);
21541
21542         heap_segment* sseg = seg;
21543         do
21544         {
21545             // shorten it to minimum
21546             if (seg == sseg)
21547             {
21548                 // no survivors make all generations look empty
21549                 uint8_t* o = generation_allocation_start (condemned_gen1) +
21550                     Align (size (generation_allocation_start (condemned_gen1)));
21551 #ifdef BACKGROUND_GC
21552                 if (current_c_gc_state == c_gc_state_marking)
21553                 {
21554                     bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
21555                 }
21556 #endif //BACKGROUND_GC
21557                 heap_segment_allocated (seg) = o;
21558             }
21559             else
21560             {
21561                 assert (condemned_gen_number == max_generation);
21562 #ifdef BACKGROUND_GC
21563                 if (current_c_gc_state == c_gc_state_marking)
21564                 {
21565                     bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
21566                 }
21567 #endif //BACKGROUND_GC
21568                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
21569             }
21570             seg = heap_segment_next_rw (seg);
21571         } while (seg);
21572     }
21573
21574 #endif //MULTIPLE_HEAPS
21575
21576     heap_segment*  seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
21577
21578     PREFIX_ASSUME(seg1 != NULL);
21579
21580     uint8_t*  end = heap_segment_allocated (seg1);
21581     uint8_t*  first_condemned_address = generation_allocation_start (condemned_gen1);
21582     uint8_t*  x = first_condemned_address;
21583
21584     assert (!marked (x));
21585     uint8_t*  plug_end = x;
21586     uint8_t*  tree = 0;
21587     size_t  sequence_number = 0;
21588     uint8_t*  last_node = 0;
21589     size_t  current_brick = brick_of (x);
21590     BOOL  allocate_in_condemned = ((condemned_gen_number == max_generation)||
21591                                    (settings.promotion == FALSE));
21592     int  active_old_gen_number = condemned_gen_number;
21593     int  active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
21594                                   (1 + condemned_gen_number));
21595     generation*  older_gen = 0;
21596     generation* consing_gen = condemned_gen1;
21597     alloc_list  r_free_list [MAX_BUCKET_COUNT];
21598
21599     size_t r_free_list_space = 0;
21600     size_t r_free_obj_space = 0;
21601     size_t r_older_gen_free_list_allocated = 0;
21602     size_t r_older_gen_condemned_allocated = 0;
21603     size_t r_older_gen_end_seg_allocated = 0;
21604     uint8_t*  r_allocation_pointer = 0;
21605     uint8_t*  r_allocation_limit = 0;
21606     uint8_t* r_allocation_start_region = 0;
21607     heap_segment*  r_allocation_segment = 0;
21608 #ifdef FREE_USAGE_STATS
21609     size_t r_older_gen_free_space[NUM_GEN_POWER2];
21610 #endif //FREE_USAGE_STATS
21611
21612     if ((condemned_gen_number < max_generation))
21613     {
21614         older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
21615         generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
21616
21617         r_free_list_space = generation_free_list_space (older_gen);
21618         r_free_obj_space = generation_free_obj_space (older_gen);
21619 #ifdef FREE_USAGE_STATS
21620         memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
21621 #endif //FREE_USAGE_STATS
21622         generation_allocate_end_seg_p (older_gen) = FALSE;
21623         r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
21624         r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
21625         r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
21626         r_allocation_limit = generation_allocation_limit (older_gen);
21627         r_allocation_pointer = generation_allocation_pointer (older_gen);
21628         r_allocation_start_region = generation_allocation_context_start_region (older_gen);
21629         r_allocation_segment = generation_allocation_segment (older_gen);
21630         heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
21631
21632         PREFIX_ASSUME(start_seg != NULL);
21633
21634         if (start_seg != ephemeral_heap_segment)
21635         {
21636             assert (condemned_gen_number == (max_generation - 1));
21637             while (start_seg && (start_seg != ephemeral_heap_segment))
21638             {
21639                 assert (heap_segment_allocated (start_seg) >=
21640                         heap_segment_mem (start_seg));
21641                 assert (heap_segment_allocated (start_seg) <=
21642                         heap_segment_reserved (start_seg));
21643                 heap_segment_plan_allocated (start_seg) =
21644                     heap_segment_allocated (start_seg);
21645                 start_seg = heap_segment_next_rw (start_seg);
21646             }
21647         }
21648     }
21649
21650     //reset all of the segment allocated sizes
21651     {
21652         heap_segment*  seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
21653
21654         PREFIX_ASSUME(seg2 != NULL);
21655
21656         while (seg2)
21657         {
21658             heap_segment_plan_allocated (seg2) =
21659                 heap_segment_mem (seg2);
21660             seg2 = heap_segment_next_rw (seg2);
21661         }
21662     }
21663     int  condemned_gn = condemned_gen_number;
21664
21665     int bottom_gen = 0;
21666     init_free_and_plug();
21667
21668     while (condemned_gn >= bottom_gen)
21669     {
21670         generation*  condemned_gen2 = generation_of (condemned_gn);
21671         generation_allocator (condemned_gen2)->clear();
21672         generation_free_list_space (condemned_gen2) = 0;
21673         generation_free_obj_space (condemned_gen2) = 0;
21674         generation_allocation_size (condemned_gen2) = 0;
21675         generation_condemned_allocated (condemned_gen2) = 0; 
21676         generation_pinned_allocated (condemned_gen2) = 0; 
21677         generation_free_list_allocated(condemned_gen2) = 0; 
21678         generation_end_seg_allocated (condemned_gen2) = 0; 
21679         generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
21680         generation_pinned_allocation_compact_size (condemned_gen2) = 0;
21681 #ifdef FREE_USAGE_STATS
21682         generation_pinned_free_obj_space (condemned_gen2) = 0;
21683         generation_allocated_in_pinned_free (condemned_gen2) = 0;
21684         generation_allocated_since_last_pin (condemned_gen2) = 0;
21685 #endif //FREE_USAGE_STATS
21686         generation_plan_allocation_start (condemned_gen2) = 0;
21687         generation_allocation_segment (condemned_gen2) =
21688             heap_segment_rw (generation_start_segment (condemned_gen2));
21689
21690         PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
21691
21692         if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
21693         {
21694             generation_allocation_pointer (condemned_gen2) =
21695                 heap_segment_mem (generation_allocation_segment (condemned_gen2));
21696         }
21697         else
21698         {
21699             generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
21700         }
21701
21702         generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21703         generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21704
21705         condemned_gn--;
21706     }
21707
21708     BOOL allocate_first_generation_start = FALSE;
21709     
21710     if (allocate_in_condemned)
21711     {
21712         allocate_first_generation_start = TRUE;
21713     }
21714
21715     dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21716
21717     demotion_low = MAX_PTR;
21718     demotion_high = heap_segment_allocated (ephemeral_heap_segment);
21719
21720     // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
21721     // from gen1. They should get promoted to gen2.
21722     demote_gen1_p = !(settings.promotion && 
21723                       (settings.condemned_generation == (max_generation - 1)) && 
21724                       gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
21725
21726     total_ephemeral_size = 0;
21727
21728     print_free_and_plug ("BP");
21729
21730     for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
21731     {
21732         generation* temp_gen = generation_of (gen_idx);
21733
21734         dprintf (2, ("gen%d start %Ix, plan start %Ix",
21735             gen_idx, 
21736             generation_allocation_start (temp_gen),
21737             generation_plan_allocation_start (temp_gen)));
21738     }
21739
21740     BOOL fire_pinned_plug_events_p = ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PinPlugAtGCTime);
21741     size_t last_plug_len = 0;
21742
21743     while (1)
21744     {
21745         if (x >= end)
21746         {
21747             assert (x == end);
21748             assert (heap_segment_allocated (seg1) == end);
21749             heap_segment_allocated (seg1) = plug_end;
21750
21751             current_brick = update_brick_table (tree, current_brick, x, plug_end);
21752             dprintf (3, ("end of seg: new tree, sequence# 0"));
21753             sequence_number = 0;
21754             tree = 0;
21755
21756             if (heap_segment_next_rw (seg1))
21757             {
21758                 seg1 = heap_segment_next_rw (seg1);
21759                 end = heap_segment_allocated (seg1);
21760                 plug_end = x = heap_segment_mem (seg1);
21761                 current_brick = brick_of (x);
21762                 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21763                 continue;
21764             }
21765             else
21766             {
21767                 break;
21768             }
21769         }
21770
21771         BOOL last_npinned_plug_p = FALSE;
21772         BOOL last_pinned_plug_p = FALSE;
21773
21774         // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
21775         // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
21776         // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
21777         uint8_t* last_pinned_plug = 0;
21778         size_t num_pinned_plugs_in_plug = 0;
21779
21780         uint8_t* last_object_in_plug = 0;
21781
21782         while ((x < end) && marked (x))
21783         {
21784             uint8_t*  plug_start = x;
21785             uint8_t*  saved_plug_end = plug_end;
21786             BOOL   pinned_plug_p = FALSE;
21787             BOOL   npin_before_pin_p = FALSE;
21788             BOOL   saved_last_npinned_plug_p = last_npinned_plug_p;
21789             uint8_t*  saved_last_object_in_plug = last_object_in_plug;
21790             BOOL   merge_with_last_pin_p = FALSE;
21791
21792             size_t added_pinning_size = 0;
21793             size_t artificial_pinned_size = 0;
21794
21795             store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p, 
21796                                  last_pinned_plug, pinned_plug_p, last_object_in_plug, 
21797                                  merge_with_last_pin_p, last_plug_len);
21798
21799 #ifdef FEATURE_STRUCTALIGN
21800             int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
21801             size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
21802 #endif // FEATURE_STRUCTALIGN
21803
21804             {
21805                 uint8_t* xl = x;
21806                 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
21807                 {
21808                     assert (xl < end);
21809                     if (pinned(xl))
21810                     {
21811                         clear_pinned (xl);
21812                     }
21813 #ifdef FEATURE_STRUCTALIGN
21814                     else
21815                     {
21816                         int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
21817                         if (obj_requiredAlignment > requiredAlignment)
21818                         {
21819                             requiredAlignment = obj_requiredAlignment;
21820                             alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
21821                         }
21822                     }
21823 #endif // FEATURE_STRUCTALIGN
21824
21825                     clear_marked (xl);
21826
21827                     dprintf(4, ("+%Ix+", (size_t)xl));
21828                     assert ((size (xl) > 0));
21829                     assert ((size (xl) <= LARGE_OBJECT_SIZE));
21830
21831                     last_object_in_plug = xl;
21832
21833                     xl = xl + Align (size (xl));
21834                     Prefetch (xl);
21835                 }
21836
21837                 BOOL next_object_marked_p = ((xl < end) && marked (xl));
21838
21839                 if (pinned_plug_p)
21840                 {
21841                     // If it is pinned we need to extend to the next marked object as we can't use part of
21842                     // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
21843                     // references but for now I am just using the next non pinned object for that).
21844                     if (next_object_marked_p) 
21845                     {
21846                         clear_marked (xl);
21847                         last_object_in_plug = xl;
21848                         size_t extra_size = Align (size (xl));
21849                         xl = xl + extra_size;
21850                         added_pinning_size = extra_size;
21851                     }
21852                 }
21853                 else
21854                 {
21855                     if (next_object_marked_p)
21856                         npin_before_pin_p = TRUE;
21857                 }
21858
21859                 assert (xl <= end);
21860                 x = xl;
21861             }
21862             dprintf (3, ( "%Ix[", (size_t)x));
21863             plug_end = x;
21864             size_t ps = plug_end - plug_start;
21865             last_plug_len = ps;
21866             dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
21867             uint8_t*  new_address = 0;
21868
21869             if (!pinned_plug_p)
21870             {
21871                 if (allocate_in_condemned &&
21872                     (settings.condemned_generation == max_generation) &&
21873                     (ps > OS_PAGE_SIZE))
21874                 {
21875                     ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
21876                     //reloc should >=0 except when we relocate
21877                     //across segments and the dest seg is higher then the src
21878
21879                     if ((ps > (8*OS_PAGE_SIZE)) &&
21880                         (reloc > 0) &&
21881                         ((size_t)reloc < (ps/16)))
21882                     {
21883                         dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
21884                                      (size_t)plug_start, reloc));
21885                         // The last plug couldn't have been a npinned plug or it would have
21886                         // included this plug.
21887                         assert (!saved_last_npinned_plug_p);
21888
21889                         if (last_pinned_plug)
21890                         {
21891                             dprintf (3, ("artificially pinned plug merged with last pinned plug"));
21892                             merge_with_last_pin_p = TRUE;
21893                         }
21894                         else
21895                         {
21896                             enque_pinned_plug (plug_start, FALSE, 0);
21897                             last_pinned_plug = plug_start;
21898                         }
21899
21900                         convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
21901                                                 ps, artificial_pinned_size);
21902                     }
21903                 }
21904             }
21905
21906             if (allocate_first_generation_start)
21907             {
21908                 allocate_first_generation_start = FALSE;
21909                 plan_generation_start (condemned_gen1, consing_gen, plug_start);
21910                 assert (generation_plan_allocation_start (condemned_gen1));
21911             }
21912
21913             if (seg1 == ephemeral_heap_segment)
21914             {
21915                 process_ephemeral_boundaries (plug_start, active_new_gen_number,
21916                                               active_old_gen_number,
21917                                               consing_gen,
21918                                               allocate_in_condemned);
21919             }
21920
21921             dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
21922
21923             dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
21924             dd_survived_size (dd_active_old) += ps;
21925
21926             BOOL convert_to_pinned_p = FALSE;
21927
21928             if (!pinned_plug_p)
21929             {
21930 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
21931                 dd_num_npinned_plugs (dd_active_old)++;
21932 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
21933
21934                 add_gen_plug (active_old_gen_number, ps);
21935
21936                 if (allocate_in_condemned)
21937                 {
21938                     verify_pins_with_post_plug_info("before aic");
21939
21940                     new_address =
21941                         allocate_in_condemned_generations (consing_gen,
21942                                                            ps,
21943                                                            active_old_gen_number,
21944 #ifdef SHORT_PLUGS
21945                                                            &convert_to_pinned_p,
21946                                                            (npin_before_pin_p ? plug_end : 0),
21947                                                            seg1,
21948 #endif //SHORT_PLUGS
21949                                                            plug_start REQD_ALIGN_AND_OFFSET_ARG);
21950                     verify_pins_with_post_plug_info("after aic");
21951                 }
21952                 else
21953                 {
21954                     new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
21955
21956                     if (new_address != 0)
21957                     {
21958                         if (settings.condemned_generation == (max_generation - 1))
21959                         {
21960                             dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
21961                                 plug_start, plug_end,
21962                                 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
21963                                 (size_t)(plug_end - plug_start)));
21964                         }
21965                     }
21966                     else
21967                     {
21968                         allocate_in_condemned = TRUE;
21969
21970                         new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number, 
21971 #ifdef SHORT_PLUGS
21972                                                                          &convert_to_pinned_p,
21973                                                                          (npin_before_pin_p ? plug_end : 0),
21974                                                                          seg1,
21975 #endif //SHORT_PLUGS
21976                                                                          plug_start REQD_ALIGN_AND_OFFSET_ARG);
21977                     }
21978                 }
21979
21980                 if (convert_to_pinned_p)
21981                 {
21982                     assert (last_npinned_plug_p != FALSE);
21983                     assert (last_pinned_plug_p == FALSE);
21984                     convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
21985                                             ps, artificial_pinned_size);
21986                     enque_pinned_plug (plug_start, FALSE, 0);
21987                     last_pinned_plug = plug_start;
21988                 }
21989                 else
21990                 {
21991                     if (!new_address)
21992                     {
21993                         //verify that we are at then end of the ephemeral segment
21994                         assert (generation_allocation_segment (consing_gen) ==
21995                                 ephemeral_heap_segment);
21996                         //verify that we are near the end
21997                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
21998                                 heap_segment_allocated (ephemeral_heap_segment));
21999                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
22000                                 (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
22001                     }
22002                     else
22003                     {
22004 #ifdef SIMPLE_DPRINTF
22005                         dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)",
22006                             (size_t)(node_gap_size (plug_start)), 
22007                             plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
22008                                 (size_t)new_address + ps, ps, 
22009                                 (is_plug_padded (plug_start) ? 1 : 0)));
22010 #endif //SIMPLE_DPRINTF
22011
22012 #ifdef SHORT_PLUGS
22013                         if (is_plug_padded (plug_start))
22014                         {
22015                             dprintf (3, ("%Ix was padded", plug_start));
22016                             dd_padding_size (dd_active_old) += Align (min_obj_size);
22017                         }
22018 #endif //SHORT_PLUGS
22019                     }
22020                 }
22021             }
22022
22023             if (pinned_plug_p)
22024             {
22025                 if (fire_pinned_plug_events_p)
22026                     FireEtwPinPlugAtGCTime(plug_start, plug_end, 
22027                                            (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start)),
22028                                            GetClrInstanceId());
22029
22030                 if (merge_with_last_pin_p)
22031                 {
22032                     merge_with_last_pinned_plug (last_pinned_plug, ps);
22033                 }
22034                 else
22035                 {
22036                     assert (last_pinned_plug == plug_start);
22037                     set_pinned_info (plug_start, ps, consing_gen);
22038                 }
22039
22040                 new_address = plug_start;
22041
22042                 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)",
22043                             (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
22044                             (size_t)plug_end, ps,
22045                             (merge_with_last_pin_p ? 1 : 0)));
22046
22047                 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
22048                 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
22049                 dd_added_pinned_size (dd_active_old) += added_pinning_size;
22050                 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
22051
22052                 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
22053                 {
22054                     last_gen1_pin_end = plug_end;
22055                 }
22056             }
22057
22058 #ifdef _DEBUG
22059             // detect forward allocation in the same segment
22060             assert (!((new_address > plug_start) &&
22061                 (new_address < heap_segment_reserved (seg1))));
22062 #endif //_DEBUG
22063
22064             if (!merge_with_last_pin_p)
22065             {
22066                 if (current_brick != brick_of (plug_start))
22067                 {
22068                     current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
22069                     sequence_number = 0;
22070                     tree = 0;
22071                 }
22072
22073                 set_node_relocation_distance (plug_start, (new_address - plug_start));
22074                 if (last_node && (node_relocation_distance (last_node) ==
22075                                   (node_relocation_distance (plug_start) +
22076                                    node_gap_size (plug_start))))
22077                 {
22078                     //dprintf(3,( " Lb"));
22079                     dprintf (3, ("%Ix Lb", plug_start));
22080                     set_node_left (plug_start);
22081                 }
22082                 if (0 == sequence_number)
22083                 {
22084                     dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
22085                     tree = plug_start;
22086                 }
22087
22088                 verify_pins_with_post_plug_info("before insert node");
22089
22090                 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
22091                 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
22092                 last_node = plug_start;
22093
22094 #ifdef _DEBUG
22095                 // If we detect if the last plug is pinned plug right before us, we should save this gap info
22096                 if (!pinned_plug_p)
22097                 {
22098                     if (mark_stack_tos > 0)
22099                     {
22100                         mark& m = mark_stack_array[mark_stack_tos - 1];
22101                         if (m.has_post_plug_info())
22102                         {
22103                             uint8_t* post_plug_info_start = m.saved_post_plug_info_start;
22104                             size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
22105                             if ((uint8_t*)current_plug_gap_start == post_plug_info_start)
22106                             {
22107                                 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
22108                                     *current_plug_gap_start, *(current_plug_gap_start + 1),
22109                                     *(current_plug_gap_start + 2)));
22110                                 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
22111                             }
22112                         }
22113                     }
22114                 }
22115 #endif //_DEBUG
22116
22117                 verify_pins_with_post_plug_info("after insert node");
22118             }
22119         }
22120         
22121         if (num_pinned_plugs_in_plug > 1)
22122         {
22123             dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
22124         }
22125
22126         {
22127 #ifdef MARK_LIST
22128             if (use_mark_list)
22129             {
22130                while ((mark_list_next < mark_list_index) &&
22131                       (*mark_list_next <= x))
22132                {
22133                    mark_list_next++;
22134                }
22135                if ((mark_list_next < mark_list_index)
22136 #ifdef MULTIPLE_HEAPS
22137                    && (*mark_list_next < end) //for multiple segments
22138 #endif //MULTIPLE_HEAPS
22139                    )
22140                    x = *mark_list_next;
22141                else
22142                    x = end;
22143             }
22144             else
22145 #endif //MARK_LIST
22146             {
22147                 uint8_t* xl = x;
22148 #ifdef BACKGROUND_GC
22149                 if (current_c_gc_state == c_gc_state_marking)
22150                 {
22151                     assert (recursive_gc_sync::background_running_p());
22152                     while ((xl < end) && !marked (xl))
22153                     {
22154                         dprintf (4, ("-%Ix-", (size_t)xl));
22155                         assert ((size (xl) > 0));
22156                         background_object_marked (xl, TRUE);
22157                         xl = xl + Align (size (xl));
22158                         Prefetch (xl);
22159                     }
22160                 }
22161                 else
22162 #endif //BACKGROUND_GC
22163                 {
22164                     while ((xl < end) && !marked (xl))
22165                     {
22166                         dprintf (4, ("-%Ix-", (size_t)xl));
22167                         assert ((size (xl) > 0));
22168                         xl = xl + Align (size (xl));
22169                         Prefetch (xl);
22170                     }
22171                 }
22172                 assert (xl <= end);
22173                 x = xl;
22174             }
22175         }
22176     }
22177
22178     while (!pinned_plug_que_empty_p())
22179     {
22180         if (settings.promotion)
22181         {
22182             uint8_t* pplug = pinned_plug (oldest_pin());
22183             if (in_range_for_segment (pplug, ephemeral_heap_segment))
22184             {
22185                 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
22186                 //allocate all of the generation gaps
22187                 while (active_new_gen_number > 0)
22188                 {
22189                     active_new_gen_number--;
22190
22191                     if (active_new_gen_number == (max_generation - 1))
22192                     {
22193                         maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
22194                         if (!demote_gen1_p)
22195                             advance_pins_for_demotion (consing_gen);
22196                     }
22197
22198                     generation* gen = generation_of (active_new_gen_number);
22199                     plan_generation_start (gen, consing_gen, 0);
22200
22201                     if (demotion_low == MAX_PTR)
22202                     {
22203                         demotion_low = pplug;
22204                         dprintf (3, ("end plan: dlow->%Ix", demotion_low));
22205                     }
22206
22207                     dprintf (2, ("(%d)gen%d plan start: %Ix", 
22208                                   heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
22209                     assert (generation_plan_allocation_start (gen));
22210                 }
22211             }
22212         }
22213
22214         if (pinned_plug_que_empty_p())
22215             break;
22216
22217         size_t  entry = deque_pinned_plug();
22218         mark*  m = pinned_plug_of (entry);
22219         uint8_t*  plug = pinned_plug (m);
22220         size_t  len = pinned_len (m);
22221
22222         // detect pinned block in different segment (later) than
22223         // allocation segment
22224         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
22225
22226         while ((plug < generation_allocation_pointer (consing_gen)) ||
22227                (plug >= heap_segment_allocated (nseg)))
22228         {
22229             assert ((plug < heap_segment_mem (nseg)) ||
22230                     (plug > heap_segment_reserved (nseg)));
22231             //adjust the end of the segment to be the end of the plug
22232             assert (generation_allocation_pointer (consing_gen)>=
22233                     heap_segment_mem (nseg));
22234             assert (generation_allocation_pointer (consing_gen)<=
22235                     heap_segment_committed (nseg));
22236
22237             heap_segment_plan_allocated (nseg) =
22238                 generation_allocation_pointer (consing_gen);
22239             //switch allocation segment
22240             nseg = heap_segment_next_rw (nseg);
22241             generation_allocation_segment (consing_gen) = nseg;
22242             //reset the allocation pointer and limits
22243             generation_allocation_pointer (consing_gen) =
22244                 heap_segment_mem (nseg);
22245         }
22246
22247         set_new_pin_info (m, generation_allocation_pointer (consing_gen));
22248         dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
22249             (size_t)(brick_table[brick_of (plug)])));
22250
22251         generation_allocation_pointer (consing_gen) = plug + len;
22252         generation_allocation_limit (consing_gen) =
22253             generation_allocation_pointer (consing_gen);
22254         //Add the size of the pinned plug to the right pinned allocations
22255         //find out which gen this pinned plug came from 
22256         int frgn = object_gennum (plug);
22257         if ((frgn != (int)max_generation) && settings.promotion)
22258         {
22259             generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
22260         }
22261
22262     }
22263
22264     plan_generation_starts (consing_gen);
22265     print_free_and_plug ("AP");
22266
22267     {
22268 #ifdef SIMPLE_DPRINTF
22269         for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22270         {
22271             generation* temp_gen = generation_of (gen_idx);
22272             dynamic_data* temp_dd = dynamic_data_of (gen_idx);
22273
22274             int added_pinning_ratio = 0;
22275             int artificial_pinned_ratio = 0;
22276
22277             if (dd_pinned_survived_size (temp_dd) != 0)
22278             {
22279                 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22280                 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22281             }
22282
22283             size_t padding_size = 
22284 #ifdef SHORT_PLUGS
22285                 dd_padding_size (temp_dd);
22286 #else
22287                 0;
22288 #endif //SHORT_PLUGS
22289             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",
22290                 gen_idx, 
22291                 generation_allocation_start (temp_gen),
22292                 generation_plan_allocation_start (temp_gen),
22293                 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
22294                 generation_allocation_size (temp_gen),
22295                 generation_pinned_allocation_compact_size (temp_gen),
22296                 generation_pinned_allocation_sweep_size (temp_gen),
22297                 dd_survived_size (temp_dd),
22298                 dd_pinned_survived_size (temp_dd),
22299                 added_pinning_ratio,
22300                 artificial_pinned_ratio,
22301                 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
22302                 padding_size));
22303         }
22304 #endif //SIMPLE_DPRINTF
22305     }
22306
22307     if (settings.condemned_generation == (max_generation - 1 ))
22308     {
22309         size_t plan_gen2_size = generation_plan_size (max_generation);
22310         size_t growth = plan_gen2_size - old_gen2_size;
22311
22312         if (growth > 0)
22313         {
22314             dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
22315                 growth, generation_end_seg_allocated (generation_of (max_generation)), 
22316                 generation_condemned_allocated (generation_of (max_generation - 1))));
22317         }
22318         else
22319         {
22320             dprintf (1, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
22321                 (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)), 
22322                 generation_condemned_allocated (generation_of (max_generation - 1))));
22323         }
22324
22325         generation* older_gen = generation_of (settings.condemned_generation + 1);
22326         size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
22327         size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
22328         size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated;
22329         size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated;
22330
22331         dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
22332                     r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
22333                     r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen), 
22334                     r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
22335
22336         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", 
22337             free_list_allocated, rejected_free_space, end_seg_allocated,
22338             condemned_allocated, generation_condemned_allocated (generation_of (settings.condemned_generation))));
22339
22340         maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info);
22341         maxgen_size_info->free_list_allocated = free_list_allocated;
22342         maxgen_size_info->free_list_rejected = rejected_free_space;
22343         maxgen_size_info->end_seg_allocated = end_seg_allocated;
22344         maxgen_size_info->condemned_allocated = condemned_allocated;
22345         maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance;
22346         maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance;
22347
22348 #ifdef FREE_USAGE_STATS
22349         int free_list_efficiency = 0;
22350         if ((free_list_allocated + rejected_free_space) != 0)
22351             free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100);
22352
22353         int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100);
22354
22355         dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%",
22356                     older_gen->gen_num,
22357                     free_list_efficiency, running_free_list_efficiency));
22358
22359         dprintf (1, ("gen2 free list change"));
22360         for (int j = 0; j < NUM_GEN_POWER2; j++)
22361         {
22362             dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id", 
22363                 heap_number, 
22364                 settings.gc_index,
22365                 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j], 
22366                 (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
22367                 (generation_of(max_generation - 1))->gen_plugs[j]));
22368         }
22369 #endif //FREE_USAGE_STATS
22370     }
22371
22372     size_t fragmentation =
22373         generation_fragmentation (generation_of (condemned_gen_number),
22374                                   consing_gen,
22375                                   heap_segment_allocated (ephemeral_heap_segment));
22376
22377     dprintf (2,("Fragmentation: %Id", fragmentation));
22378     dprintf (2,("---- End of Plan phase ----"));
22379
22380 #ifdef TIME_GC
22381     finish = GetCycleCount32();
22382     plan_time = finish - start;
22383 #endif //TIME_GC
22384
22385     // We may update write barrier code.  We assume here EE has been suspended if we are on a GC thread.
22386     assert(IsGCInProgress());
22387
22388     BOOL should_expand = FALSE;
22389     BOOL should_compact= FALSE;
22390     ephemeral_promotion = FALSE;
22391
22392 #ifdef BIT64
22393     if ((!settings.concurrent) &&
22394         ((condemned_gen_number < max_generation) && 
22395          ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
22396     {
22397         dprintf (2, ("gen0 reduction count is %d, condemning %d, mem load %d",
22398                      settings.gen0_reduction_count,
22399                      condemned_gen_number,
22400                      settings.entry_memory_load));
22401         should_compact = TRUE;
22402
22403         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, 
22404             ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load));
22405
22406         if ((condemned_gen_number >= (max_generation - 1)) && 
22407             dt_low_ephemeral_space_p (tuning_deciding_expansion))
22408         {
22409             dprintf (2, ("Not enough space for all ephemeral generations with compaction"));
22410             should_expand = TRUE;
22411         }
22412     }
22413     else
22414     {
22415 #endif // BIT64
22416         should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
22417 #ifdef BIT64
22418     }
22419 #endif // BIT64
22420
22421 #ifdef FEATURE_LOH_COMPACTION
22422     loh_compacted_p = FALSE;
22423 #endif //FEATURE_LOH_COMPACTION
22424
22425     if (condemned_gen_number == max_generation)
22426     {
22427 #ifdef FEATURE_LOH_COMPACTION
22428         if (settings.loh_compaction)
22429         {
22430             if (plan_loh())
22431             {
22432                 should_compact = TRUE;
22433                 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced);
22434                 loh_compacted_p = TRUE;
22435             }
22436         }
22437         else
22438         {
22439             if ((heap_number == 0) && (loh_pinned_queue))
22440             {
22441                 loh_pinned_queue_decay--;
22442
22443                 if (!loh_pinned_queue_decay)
22444                 {
22445                     delete loh_pinned_queue;
22446                     loh_pinned_queue = 0;
22447                 }
22448             }
22449         }
22450
22451         if (!loh_compacted_p)
22452 #endif //FEATURE_LOH_COMPACTION
22453         {
22454             GCToEEInterface::DiagWalkLOHSurvivors(__this);
22455             sweep_large_objects();
22456         }
22457     }
22458     else
22459     {
22460         settings.loh_compaction = FALSE;
22461     }
22462
22463 #ifdef MULTIPLE_HEAPS
22464
22465     new_heap_segment = NULL;
22466
22467     if (should_compact && should_expand)
22468         gc_policy = policy_expand;
22469     else if (should_compact)
22470         gc_policy = policy_compact;
22471     else
22472         gc_policy = policy_sweep;
22473
22474     //vote for result of should_compact
22475     dprintf (3, ("Joining for compaction decision"));
22476     gc_t_join.join(this, gc_join_decide_on_compaction);
22477     if (gc_t_join.joined())
22478     {
22479         //safe place to delete large heap segments
22480         if (condemned_gen_number == max_generation)
22481         {
22482             for (int i = 0; i < n_heaps; i++)
22483             {
22484                 g_heaps [i]->rearrange_large_heap_segments ();
22485             }
22486         }
22487
22488         settings.demotion = FALSE;
22489         int pol_max = policy_sweep;
22490 #ifdef GC_CONFIG_DRIVEN
22491         BOOL is_compaction_mandatory = FALSE;
22492 #endif //GC_CONFIG_DRIVEN
22493
22494         int i;
22495         for (i = 0; i < n_heaps; i++)
22496         {
22497             if (pol_max < g_heaps[i]->gc_policy)
22498                 pol_max = policy_compact;
22499             // set the demotion flag is any of the heap has demotion
22500             if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
22501             {
22502                 (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit);
22503                 settings.demotion = TRUE;
22504             }
22505
22506 #ifdef GC_CONFIG_DRIVEN
22507             if (!is_compaction_mandatory)
22508             {
22509                 int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact);
22510                 if (compact_reason >= 0)
22511                 {
22512                     if (gc_heap_compact_reason_mandatory_p[compact_reason])
22513                         is_compaction_mandatory = TRUE;
22514                 }
22515             }
22516 #endif //GC_CONFIG_DRIVEN
22517         }
22518
22519 #ifdef GC_CONFIG_DRIVEN
22520         if (!is_compaction_mandatory)
22521         {
22522             // If compaction is not mandatory we can feel free to change it to a sweeping GC.
22523             // Note that we may want to change this to only checking every so often instead of every single GC.
22524             if (should_do_sweeping_gc (pol_max >= policy_compact))
22525             {
22526                 pol_max = policy_sweep;
22527             }
22528             else
22529             {
22530                 if (pol_max == policy_sweep)
22531                     pol_max = policy_compact;
22532             }
22533         }
22534 #endif //GC_CONFIG_DRIVEN
22535
22536         for (i = 0; i < n_heaps; i++)
22537         {
22538             if (pol_max > g_heaps[i]->gc_policy)
22539                 g_heaps[i]->gc_policy = pol_max;
22540             //get the segment while we are serialized
22541             if (g_heaps[i]->gc_policy == policy_expand)
22542             {
22543                 g_heaps[i]->new_heap_segment =
22544                      g_heaps[i]->soh_get_segment_to_expand();
22545                 if (!g_heaps[i]->new_heap_segment)
22546                 {
22547                     set_expand_in_full_gc (condemned_gen_number);
22548                     //we are out of memory, cancel the expansion
22549                     g_heaps[i]->gc_policy = policy_compact;
22550                 }
22551             }
22552         }
22553
22554         BOOL is_full_compacting_gc = FALSE;
22555
22556         if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
22557         {
22558             full_gc_counts[gc_type_compacting]++;
22559             is_full_compacting_gc = TRUE;
22560         }
22561
22562         for (i = 0; i < n_heaps; i++)
22563         {
22564             //copy the card and brick tables
22565             if (g_gc_card_table!= g_heaps[i]->card_table)
22566             {
22567                 g_heaps[i]->copy_brick_card_table();
22568             }
22569
22570             if (is_full_compacting_gc)
22571             {
22572                 g_heaps[i]->loh_alloc_since_cg = 0;
22573             }
22574         }
22575
22576         //start all threads on the roots.
22577         dprintf(3, ("Starting all gc threads after compaction decision"));
22578         gc_t_join.restart();
22579     }
22580
22581     //reset the local variable accordingly
22582     should_compact = (gc_policy >= policy_compact);
22583     should_expand  = (gc_policy >= policy_expand);
22584
22585 #else //MULTIPLE_HEAPS
22586
22587     //safe place to delete large heap segments
22588     if (condemned_gen_number == max_generation)
22589     {
22590         rearrange_large_heap_segments ();
22591     }
22592
22593     settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
22594     if (settings.demotion)
22595         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
22596
22597 #ifdef GC_CONFIG_DRIVEN
22598     BOOL is_compaction_mandatory = FALSE;
22599     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
22600     if (compact_reason >= 0)
22601         is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason];
22602
22603     if (!is_compaction_mandatory)
22604     {
22605         if (should_do_sweeping_gc (should_compact))
22606             should_compact = FALSE;
22607         else
22608             should_compact = TRUE;
22609     }
22610 #endif //GC_CONFIG_DRIVEN
22611
22612     if (should_compact && (condemned_gen_number == max_generation))
22613     {
22614         full_gc_counts[gc_type_compacting]++;
22615         loh_alloc_since_cg = 0;
22616     }
22617 #endif //MULTIPLE_HEAPS
22618
22619     if (should_compact)
22620     {
22621         dprintf (2,( "**** Doing Compacting GC ****"));
22622
22623         if (should_expand)
22624         {
22625 #ifndef MULTIPLE_HEAPS
22626             heap_segment* new_heap_segment = soh_get_segment_to_expand();
22627 #endif //!MULTIPLE_HEAPS
22628             if (new_heap_segment)
22629             {
22630                 consing_gen = expand_heap(condemned_gen_number,
22631                                           consing_gen,
22632                                           new_heap_segment);
22633             }
22634
22635             // If we couldn't get a new segment, or we were able to 
22636             // reserve one but no space to commit, we couldn't
22637             // expand heap.
22638             if (ephemeral_heap_segment != new_heap_segment)
22639             {
22640                 set_expand_in_full_gc (condemned_gen_number);
22641                 should_expand = FALSE;
22642             }
22643         }
22644         generation_allocation_limit (condemned_gen1) =
22645             generation_allocation_pointer (condemned_gen1);
22646         if ((condemned_gen_number < max_generation))
22647         {
22648             generation_allocator (older_gen)->commit_alloc_list_changes();
22649
22650             // Fix the allocation area of the older generation
22651             fix_older_allocation_area (older_gen);
22652         }
22653         assert (generation_allocation_segment (consing_gen) ==
22654                 ephemeral_heap_segment);
22655
22656         GCToEEInterface::DiagWalkSurvivors(__this);
22657
22658         relocate_phase (condemned_gen_number, first_condemned_address);
22659         compact_phase (condemned_gen_number, first_condemned_address,
22660                        (!settings.demotion && settings.promotion));
22661         fix_generation_bounds (condemned_gen_number, consing_gen);
22662         assert (generation_allocation_limit (youngest_generation) ==
22663                 generation_allocation_pointer (youngest_generation));
22664         if (condemned_gen_number >= (max_generation -1))
22665         {
22666 #ifdef MULTIPLE_HEAPS
22667             // this needs be serialized just because we have one
22668             // segment_standby_list/seg_table for all heaps. We should make it at least
22669             // so that when hoarding is not on we don't need this join because
22670             // decommitting memory can take a long time.
22671             //must serialize on deleting segments
22672             gc_t_join.join(this, gc_join_rearrange_segs_compaction);
22673             if (gc_t_join.joined())
22674             {
22675                 for (int i = 0; i < n_heaps; i++)
22676                 {
22677                     g_heaps[i]->rearrange_heap_segments(TRUE);
22678                 }
22679                 gc_t_join.restart();
22680             }
22681 #else
22682             rearrange_heap_segments(TRUE);
22683 #endif //MULTIPLE_HEAPS
22684
22685             if (should_expand)
22686             {
22687                 //fix the start_segment for the ephemeral generations
22688                 for (int i = 0; i < max_generation; i++)
22689                 {
22690                     generation* gen = generation_of (i);
22691                     generation_start_segment (gen) = ephemeral_heap_segment;
22692                     generation_allocation_segment (gen) = ephemeral_heap_segment;
22693                 }
22694             }
22695         }
22696
22697         {
22698 #ifdef FEATURE_PREMORTEM_FINALIZATION
22699             finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
22700                                                        (!settings.demotion && settings.promotion));
22701 #endif // FEATURE_PREMORTEM_FINALIZATION
22702
22703 #ifdef MULTIPLE_HEAPS
22704             dprintf(3, ("Joining after end of compaction"));
22705             gc_t_join.join(this, gc_join_adjust_handle_age_compact);
22706             if (gc_t_join.joined())
22707 #endif //MULTIPLE_HEAPS
22708             {
22709 #ifdef MULTIPLE_HEAPS
22710                 //join all threads to make sure they are synchronized
22711                 dprintf(3, ("Restarting after Promotion granted"));
22712                 gc_t_join.restart();
22713 #endif //MULTIPLE_HEAPS
22714             }
22715
22716             ScanContext sc;
22717             sc.thread_number = heap_number;
22718             sc.promotion = FALSE;
22719             sc.concurrent = FALSE;
22720             // new generations bounds are set can call this guy
22721             if (settings.promotion && !settings.demotion)
22722             {
22723                 dprintf (2, ("Promoting EE roots for gen %d",
22724                              condemned_gen_number));
22725                 GCScan::GcPromotionsGranted(condemned_gen_number,
22726                                                 max_generation, &sc);
22727             }
22728             else if (settings.demotion)
22729             {
22730                 dprintf (2, ("Demoting EE roots for gen %d",
22731                              condemned_gen_number));
22732                 GCScan::GcDemote (condemned_gen_number, max_generation, &sc);
22733             }
22734         }
22735
22736         {
22737             gen0_big_free_spaces = 0;
22738
22739             reset_pinned_queue_bos();
22740             unsigned int  gen_number = min (max_generation, 1 + condemned_gen_number);
22741             generation*  gen = generation_of (gen_number);
22742             uint8_t*  low = generation_allocation_start (generation_of (gen_number-1));
22743             uint8_t*  high =  heap_segment_allocated (ephemeral_heap_segment);
22744             
22745             while (!pinned_plug_que_empty_p())
22746             {
22747                 mark*  m = pinned_plug_of (deque_pinned_plug());
22748                 size_t len = pinned_len (m);
22749                 uint8_t*  arr = (pinned_plug (m) - len);
22750                 dprintf(3,("free [%Ix %Ix[ pin",
22751                             (size_t)arr, (size_t)arr + len));
22752                 if (len != 0)
22753                 {
22754                     assert (len >= Align (min_obj_size));
22755                     make_unused_array (arr, len);
22756                     // fix fully contained bricks + first one
22757                     // if the array goes beyond the first brick
22758                     size_t start_brick = brick_of (arr);
22759                     size_t end_brick = brick_of (arr + len);
22760                     if (end_brick != start_brick)
22761                     {
22762                         dprintf (3,
22763                                     ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
22764                                     start_brick, end_brick, (size_t)arr));
22765                         set_brick (start_brick,
22766                                     arr - brick_address (start_brick));
22767                         size_t brick = start_brick+1;
22768                         while (brick < end_brick)
22769                         {
22770                             set_brick (brick, start_brick - brick);
22771                             brick++;
22772                         }
22773                     }
22774
22775                     //when we take an old segment to make the new
22776                     //ephemeral segment. we can have a bunch of
22777                     //pinned plugs out of order going to the new ephemeral seg
22778                     //and then the next plugs go back to max_generation
22779                     if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
22780                         (heap_segment_reserved (ephemeral_heap_segment) > arr))
22781                     {
22782
22783                         while ((low <= arr) && (high > arr))
22784                         {
22785                             gen_number--;
22786                             assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
22787                                     settings.demotion || !settings.promotion);
22788                             dprintf (3, ("new free list generation %d", gen_number));
22789
22790                             gen = generation_of (gen_number);
22791                             if (gen_number >= 1)
22792                                 low = generation_allocation_start (generation_of (gen_number-1));
22793                             else
22794                                 low = high;
22795                         }
22796                     }
22797                     else
22798                     {
22799                         dprintf (3, ("new free list generation %d", max_generation));
22800                         gen_number = max_generation;
22801                         gen = generation_of (gen_number);
22802                     }
22803
22804                     dprintf(3,("threading it into generation %d", gen_number));
22805                     thread_gap (arr, len, gen);
22806                     add_gen_free (gen_number, len);
22807                 }
22808             }
22809         }
22810
22811 #ifdef _DEBUG
22812         for (int x = 0; x <= max_generation; x++)
22813         {
22814             assert (generation_allocation_start (generation_of (x)));
22815         }
22816 #endif //_DEBUG
22817
22818         if (!settings.demotion && settings.promotion)
22819         {
22820             //clear card for generation 1. generation 0 is empty
22821             clear_card_for_addresses (
22822                 generation_allocation_start (generation_of (1)),
22823                 generation_allocation_start (generation_of (0)));
22824         }
22825         if (settings.promotion && !settings.demotion)
22826         {
22827             uint8_t* start = generation_allocation_start (youngest_generation);
22828             MAYBE_UNUSED_VAR(start);
22829             assert (heap_segment_allocated (ephemeral_heap_segment) ==
22830                     (start + Align (size (start))));
22831         }
22832     }
22833     else
22834     {
22835         //force promotion for sweep
22836         settings.promotion = TRUE;
22837         settings.compaction = FALSE;
22838
22839         ScanContext sc;
22840         sc.thread_number = heap_number;
22841         sc.promotion = FALSE;
22842         sc.concurrent = FALSE;
22843
22844         dprintf (2, ("**** Doing Mark and Sweep GC****"));
22845
22846         if ((condemned_gen_number < max_generation))
22847         {
22848             generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
22849             generation_free_list_space (older_gen) = r_free_list_space;
22850             generation_free_obj_space (older_gen) = r_free_obj_space;
22851             generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
22852             generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
22853             generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
22854             generation_allocation_limit (older_gen) = r_allocation_limit;
22855             generation_allocation_pointer (older_gen) = r_allocation_pointer;
22856             generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
22857             generation_allocation_segment (older_gen) = r_allocation_segment;
22858         }
22859
22860         if ((condemned_gen_number < max_generation))
22861         {
22862             // Fix the allocation area of the older generation
22863             fix_older_allocation_area (older_gen);
22864         }
22865
22866         GCToEEInterface::DiagWalkSurvivors(__this);
22867
22868         gen0_big_free_spaces = 0;
22869         make_free_lists (condemned_gen_number);
22870         recover_saved_pinned_info();
22871
22872 #ifdef FEATURE_PREMORTEM_FINALIZATION
22873         finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
22874 #endif // FEATURE_PREMORTEM_FINALIZATION
22875 // MTHTS: leave single thread for HT processing on plan_phase
22876 #ifdef MULTIPLE_HEAPS
22877         dprintf(3, ("Joining after end of sweep"));
22878         gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
22879         if (gc_t_join.joined())
22880 #endif //MULTIPLE_HEAPS
22881         {
22882             GCScan::GcPromotionsGranted(condemned_gen_number,
22883                                             max_generation, &sc);
22884             if (condemned_gen_number >= (max_generation -1))
22885             {
22886 #ifdef MULTIPLE_HEAPS
22887                 for (int i = 0; i < n_heaps; i++)
22888                 {
22889                     g_heaps[i]->rearrange_heap_segments(FALSE);
22890                 }
22891 #else
22892                 rearrange_heap_segments(FALSE);
22893 #endif //MULTIPLE_HEAPS
22894             }
22895
22896 #ifdef MULTIPLE_HEAPS
22897             //join all threads to make sure they are synchronized
22898             dprintf(3, ("Restarting after Promotion granted"));
22899             gc_t_join.restart();
22900 #endif //MULTIPLE_HEAPS
22901         }
22902
22903 #ifdef _DEBUG
22904         for (int x = 0; x <= max_generation; x++)
22905         {
22906             assert (generation_allocation_start (generation_of (x)));
22907         }
22908 #endif //_DEBUG
22909
22910         //clear card for generation 1. generation 0 is empty
22911         clear_card_for_addresses (
22912             generation_allocation_start (generation_of (1)),
22913             generation_allocation_start (generation_of (0)));
22914         assert ((heap_segment_allocated (ephemeral_heap_segment) ==
22915                  (generation_allocation_start (youngest_generation) +
22916                   Align (min_obj_size))));
22917     }
22918
22919     //verify_partial();
22920 }
22921 #ifdef _PREFAST_
22922 #pragma warning(pop)
22923 #endif //_PREFAST_
22924
22925
22926 /*****************************
22927 Called after compact phase to fix all generation gaps
22928 ********************************/
22929 void gc_heap::fix_generation_bounds (int condemned_gen_number,
22930                                      generation* consing_gen)
22931 {
22932     UNREFERENCED_PARAMETER(consing_gen);
22933
22934     assert (generation_allocation_segment (consing_gen) ==
22935             ephemeral_heap_segment);
22936
22937     //assign the planned allocation start to the generation
22938     int gen_number = condemned_gen_number;
22939     int bottom_gen = 0;
22940
22941     while (gen_number >= bottom_gen)
22942     {
22943         generation*  gen = generation_of (gen_number);
22944         dprintf(3,("Fixing generation pointers for %Ix", gen_number));
22945         if ((gen_number < max_generation) && ephemeral_promotion)
22946         {
22947             make_unused_array (saved_ephemeral_plan_start[gen_number], 
22948                                saved_ephemeral_plan_start_size[gen_number]);
22949         }
22950         reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
22951         make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
22952         dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
22953         gen_number--;
22954     }
22955 #ifdef MULTIPLE_HEAPS
22956     if (ephemeral_promotion)
22957     {
22958         //we are creating a generation fault. set the cards.
22959         // and we are only doing this for multiple heaps because in the single heap scenario the 
22960         // new ephemeral generations will be empty and there'll be no need to set cards for the
22961         // old ephemeral generations that got promoted into max_generation.
22962         ptrdiff_t delta = 0;
22963 #ifdef SEG_MAPPING_TABLE
22964         heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
22965 #else //SEG_MAPPING_TABLE
22966         heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
22967 #endif //SEG_MAPPING_TABLE
22968
22969         assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
22970         size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
22971         size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
22972         while (card != end_card)
22973         {
22974             set_card (card);
22975             card++;
22976         }
22977     }
22978 #endif //MULTIPLE_HEAPS
22979     {
22980         alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
22981         //reset the allocated size
22982         uint8_t* start = generation_allocation_start (youngest_generation);
22983         MAYBE_UNUSED_VAR(start);
22984         if (settings.promotion && !settings.demotion)
22985         {
22986             assert ((start + Align (size (start))) ==
22987                     heap_segment_plan_allocated(ephemeral_heap_segment));
22988         }
22989
22990         heap_segment_allocated(ephemeral_heap_segment)=
22991             heap_segment_plan_allocated(ephemeral_heap_segment);
22992     }
22993 }
22994
22995 uint8_t* gc_heap::generation_limit (int gen_number)
22996 {
22997     if (settings.promotion)
22998     {
22999         if (gen_number <= 1)
23000             return heap_segment_reserved (ephemeral_heap_segment);
23001         else
23002             return generation_allocation_start (generation_of ((gen_number - 2)));
23003     }
23004     else
23005     {
23006         if (gen_number <= 0)
23007             return heap_segment_reserved (ephemeral_heap_segment);
23008         else
23009             return generation_allocation_start (generation_of ((gen_number - 1)));
23010     }
23011 }
23012
23013 BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
23014 {
23015     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23016     size_t size = Align (min_obj_size)*(condemned_gen_number+1);
23017     assert ((start + size) <=
23018             heap_segment_reserved (ephemeral_heap_segment));
23019     if ((start + size) >
23020         heap_segment_committed (ephemeral_heap_segment))
23021     {
23022         if (!grow_heap_segment (ephemeral_heap_segment, start + size))
23023         {
23024             return FALSE;
23025         }
23026     }
23027     return TRUE;
23028 }
23029
23030 uint8_t* gc_heap::allocate_at_end (size_t size)
23031 {
23032     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23033     size = Align (size);
23034     uint8_t* result = start;
23035     // only called to allocate a min obj so can't overflow here.
23036     assert ((start + size) <=
23037             heap_segment_reserved (ephemeral_heap_segment));
23038     //ensure_gap_allocation took care of it
23039     assert ((start + size) <=
23040             heap_segment_committed (ephemeral_heap_segment));
23041     heap_segment_allocated (ephemeral_heap_segment) += size;
23042     return result;
23043 }
23044
23045
23046 void gc_heap::make_free_lists (int condemned_gen_number)
23047 {
23048 #ifdef TIME_GC
23049     unsigned start;
23050     unsigned finish;
23051     start = GetCycleCount32();
23052 #endif //TIME_GC
23053
23054     //Promotion has to happen in sweep case.
23055     assert (settings.promotion);
23056
23057     generation* condemned_gen = generation_of (condemned_gen_number);
23058     uint8_t* start_address = generation_allocation_start (condemned_gen);
23059
23060     size_t  current_brick = brick_of (start_address);
23061     heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23062
23063     PREFIX_ASSUME(current_heap_segment != NULL);
23064
23065     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
23066     size_t  end_brick = brick_of (end_address-1);
23067     make_free_args args;
23068     args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
23069     args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
23070                               MAX_PTR :
23071                               (generation_limit (args.free_list_gen_number)));
23072     args.free_list_gen = generation_of (args.free_list_gen_number);
23073     args.highest_plug = 0;
23074
23075     if ((start_address < end_address) ||
23076         (condemned_gen_number == max_generation))
23077     {
23078         while (1)
23079         {
23080             if ((current_brick > end_brick))
23081             {
23082                 if (args.current_gen_limit == MAX_PTR)
23083                 {
23084                     //We had an empty segment
23085                     //need to allocate the generation start
23086
23087                     generation* gen = generation_of (max_generation);
23088
23089                     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
23090
23091                     PREFIX_ASSUME(start_seg != NULL);
23092
23093                     uint8_t* gap = heap_segment_mem (start_seg);
23094
23095                     generation_allocation_start (gen) = gap;
23096                     heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
23097                     make_unused_array (gap, Align (min_obj_size));
23098                     reset_allocation_pointers (gen, gap);
23099                     dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
23100                                  max_generation, (size_t)gap));
23101                     args.current_gen_limit = generation_limit (args.free_list_gen_number);
23102                 }
23103                 if (heap_segment_next_rw (current_heap_segment))
23104                 {
23105                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
23106                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
23107                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23108
23109                     continue;
23110                 }
23111                 else
23112                 {
23113                     break;
23114                 }
23115             }
23116             {
23117                 int brick_entry =  brick_table [ current_brick ];
23118                 if ((brick_entry >= 0))
23119                 {
23120                     make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
23121                     dprintf(3,("Fixing brick entry %Ix to %Ix",
23122                                current_brick, (size_t)args.highest_plug));
23123                     set_brick (current_brick,
23124                                (args.highest_plug - brick_address (current_brick)));
23125                 }
23126                 else
23127                 {
23128                     if ((brick_entry > -32768))
23129                     {
23130
23131 #ifdef _DEBUG
23132                         ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
23133                         if ((brick_entry != -32767) && (! ((offset == brick_entry))))
23134                         {
23135                             assert ((brick_entry == -1));
23136                         }
23137 #endif //_DEBUG
23138                         //init to -1 for faster find_first_object
23139                         set_brick (current_brick, -1);
23140                     }
23141                 }
23142             }
23143             current_brick++;
23144         }
23145     }
23146     {
23147         int bottom_gen = 0;
23148         args.free_list_gen_number--;
23149         while (args.free_list_gen_number >= bottom_gen)
23150         {
23151             uint8_t*  gap = 0;
23152             generation* gen2 = generation_of (args.free_list_gen_number);
23153             gap = allocate_at_end (Align(min_obj_size));
23154             generation_allocation_start (gen2) = gap;
23155             reset_allocation_pointers (gen2, gap);
23156             dprintf(3,("Fixing generation start of %d to: %Ix",
23157                        args.free_list_gen_number, (size_t)gap));
23158             PREFIX_ASSUME(gap != NULL);
23159             make_unused_array (gap, Align (min_obj_size));
23160
23161             args.free_list_gen_number--;
23162         }
23163
23164         //reset the allocated size
23165         uint8_t* start2 = generation_allocation_start (youngest_generation);
23166         alloc_allocated = start2 + Align (size (start2));
23167     }
23168
23169 #ifdef TIME_GC
23170     finish = GetCycleCount32();
23171     sweep_time = finish - start;
23172 #endif //TIME_GC
23173 }
23174
23175 void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args)
23176 {
23177     assert ((tree != NULL));
23178     {
23179         int  right_node = node_right_child (tree);
23180         int left_node = node_left_child (tree);
23181         args->highest_plug = 0;
23182         if (! (0 == tree))
23183         {
23184             if (! (0 == left_node))
23185             {
23186                 make_free_list_in_brick (tree + left_node, args);
23187
23188             }
23189             {
23190                 uint8_t*  plug = tree;
23191                 size_t  gap_size = node_gap_size (tree);
23192                 uint8_t*  gap = (plug - gap_size);
23193                 dprintf (3,("Making free list %Ix len %d in %d",
23194                 //dprintf (3,("F: %Ix len %Ix in %d",
23195                         (size_t)gap, gap_size, args->free_list_gen_number));
23196                 args->highest_plug = tree;
23197 #ifdef SHORT_PLUGS
23198                 if (is_plug_padded (plug))
23199                 {
23200                     dprintf (3, ("%Ix padded", plug));
23201                     clear_plug_padded (plug);
23202                 }
23203 #endif //SHORT_PLUGS
23204             gen_crossing:
23205                 {
23206                     if ((args->current_gen_limit == MAX_PTR) ||
23207                         ((plug >= args->current_gen_limit) &&
23208                          ephemeral_pointer_p (plug)))
23209                     {
23210                         dprintf(3,(" Crossing Generation boundary at %Ix",
23211                                (size_t)args->current_gen_limit));
23212                         if (!(args->current_gen_limit == MAX_PTR))
23213                         {
23214                             args->free_list_gen_number--;
23215                             args->free_list_gen = generation_of (args->free_list_gen_number);
23216                         }
23217                         dprintf(3,( " Fixing generation start of %d to: %Ix",
23218                                 args->free_list_gen_number, (size_t)gap));
23219
23220                         reset_allocation_pointers (args->free_list_gen, gap);
23221                         args->current_gen_limit = generation_limit (args->free_list_gen_number);
23222
23223                         if ((gap_size >= (2*Align (min_obj_size))))
23224                         {
23225                             dprintf(3,(" Splitting the gap in two %Id left",
23226                                    gap_size));
23227                             make_unused_array (gap, Align(min_obj_size));
23228                             gap_size = (gap_size - Align(min_obj_size));
23229                             gap = (gap + Align(min_obj_size));
23230                         }
23231                         else
23232                         {
23233                             make_unused_array (gap, gap_size);
23234                             gap_size = 0;
23235                         }
23236                         goto gen_crossing;
23237                     }
23238                 }
23239
23240                 thread_gap (gap, gap_size, args->free_list_gen);
23241                 add_gen_free (args->free_list_gen->gen_num, gap_size);
23242             }
23243             if (! (0 == right_node))
23244             {
23245                 make_free_list_in_brick (tree + right_node, args);
23246             }
23247         }
23248     }
23249 }
23250
23251 void gc_heap::thread_gap (uint8_t* gap_start, size_t size, generation*  gen)
23252 {
23253     assert (generation_allocation_start (gen));
23254     if ((size > 0))
23255     {
23256         if ((gen->gen_num == 0) && (size > CLR_SIZE))
23257         {
23258             gen0_big_free_spaces += size;
23259         }
23260
23261         assert ((heap_segment_rw (generation_start_segment (gen))!=
23262                  ephemeral_heap_segment) ||
23263                 (gap_start > generation_allocation_start (gen)));
23264         // The beginning of a segment gap is not aligned
23265         assert (size >= Align (min_obj_size));
23266         make_unused_array (gap_start, size, 
23267                           (!settings.concurrent && (gen != youngest_generation)),
23268                           (gen->gen_num == max_generation));
23269         dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
23270
23271         if ((size >= min_free_list))
23272         {
23273             generation_free_list_space (gen) += size;
23274             generation_allocator (gen)->thread_item (gap_start, size);
23275         }
23276         else
23277         {
23278             generation_free_obj_space (gen) += size;
23279         }
23280     }
23281 }
23282
23283 void gc_heap::loh_thread_gap_front (uint8_t* gap_start, size_t size, generation*  gen)
23284 {
23285     assert (generation_allocation_start (gen));
23286     if (size >= min_free_list)
23287     {
23288         generation_free_list_space (gen) += size;
23289         generation_allocator (gen)->thread_item_front (gap_start, size);
23290     }
23291 }
23292
23293 void gc_heap::make_unused_array (uint8_t* x, size_t size, BOOL clearp, BOOL resetp)
23294 {
23295     dprintf (3, ("Making unused array [%Ix, %Ix[",
23296         (size_t)x, (size_t)(x+size)));
23297     assert (size >= Align (min_obj_size));
23298
23299 //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
23300 //    check_batch_mark_array_bits (x, x+size);
23301 //#endif //VERIFY_HEAP && BACKGROUND_GC
23302
23303     if (resetp)
23304         reset_memory (x, size);
23305
23306     ((CObjectHeader*)x)->SetFree(size);
23307
23308 #ifdef BIT64
23309
23310 #if BIGENDIAN
23311 #error "This won't work on big endian platforms"
23312 #endif
23313
23314     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23315
23316     if (size_as_object < size)
23317     {
23318         //
23319         // If the size is more than 4GB, we need to create multiple objects because of
23320         // the Array::m_NumComponents is uint32_t and the high 32 bits of unused array
23321         // size is ignored in regular object size computation.
23322         //
23323         uint8_t * tmp = x + size_as_object;
23324         size_t remaining_size = size - size_as_object;
23325
23326         while (remaining_size > UINT32_MAX)
23327         {
23328             // Make sure that there will be at least Align(min_obj_size) left
23329             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23330                 - Align (min_obj_size, get_alignment_constant (FALSE));
23331
23332             ((CObjectHeader*)tmp)->SetFree(current_size);
23333
23334             remaining_size -= current_size;
23335             tmp += current_size;
23336         }
23337
23338         ((CObjectHeader*)tmp)->SetFree(remaining_size);
23339     }
23340 #endif
23341
23342     if (clearp)
23343         clear_card_for_addresses (x, x + Align(size));
23344 }
23345
23346 // Clear memory set by make_unused_array.
23347 void gc_heap::clear_unused_array (uint8_t* x, size_t size)
23348 {
23349     // Also clear the sync block
23350     *(((PTR_PTR)x)-1) = 0;
23351
23352     ((CObjectHeader*)x)->UnsetFree();
23353
23354 #ifdef BIT64
23355
23356 #if BIGENDIAN
23357 #error "This won't work on big endian platforms"
23358 #endif
23359
23360     // The memory could have been cleared in the meantime. We have to mirror the algorithm
23361     // from make_unused_array since we cannot depend on the object sizes in memory.
23362     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23363
23364     if (size_as_object < size)
23365     {
23366         uint8_t * tmp = x + size_as_object;
23367         size_t remaining_size = size - size_as_object;
23368
23369         while (remaining_size > UINT32_MAX)
23370         {
23371             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23372                 - Align (min_obj_size, get_alignment_constant (FALSE));
23373
23374             ((CObjectHeader*)tmp)->UnsetFree();
23375
23376             remaining_size -= current_size;
23377             tmp += current_size;
23378         }
23379
23380         ((CObjectHeader*)tmp)->UnsetFree();
23381     }
23382 #else
23383     UNREFERENCED_PARAMETER(size);
23384 #endif
23385 }
23386
23387 inline
23388 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address)
23389 {
23390     uint8_t* candidate = 0;
23391     int cn;
23392     while (1)
23393     {
23394         if (tree < old_address)
23395         {
23396             if ((cn = node_right_child (tree)) != 0)
23397             {
23398                 assert (candidate < tree);
23399                 candidate = tree;
23400                 tree = tree + cn;
23401                 Prefetch (tree - 8);
23402                 continue;
23403             }
23404             else
23405                 break;
23406         }
23407         else if (tree > old_address)
23408         {
23409             if ((cn = node_left_child (tree)) != 0)
23410             {
23411                 tree = tree + cn;
23412                 Prefetch (tree - 8);
23413                 continue;
23414             }
23415             else
23416                 break;
23417         } else
23418             break;
23419     }
23420     if (tree <= old_address)
23421         return tree;
23422     else if (candidate)
23423         return candidate;
23424     else
23425         return tree;
23426 }
23427
23428 #ifdef FEATURE_BASICFREEZE
23429 bool gc_heap::frozen_object_p (Object* obj)
23430 {
23431     heap_segment* pSegment = gc_heap::find_segment ((uint8_t*)obj, FALSE);
23432     _ASSERTE(pSegment);
23433
23434     return heap_segment_read_only_p(pSegment);
23435 }
23436 #endif // FEATURE_BASICFREEZE
23437
23438 #ifdef FEATURE_REDHAWK
23439 // TODO: this was added on RH, we have not done perf runs to see if this is the right
23440 // thing to do for other versions of the CLR.
23441 inline
23442 #endif // FEATURE_REDHAWK
23443 void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL)
23444 {
23445     uint8_t* old_address = *pold_address;
23446     if (!((old_address >= gc_low) && (old_address < gc_high)))
23447 #ifdef MULTIPLE_HEAPS
23448     {
23449         UNREFERENCED_PARAMETER(thread);
23450         if (old_address == 0)
23451             return;
23452         gc_heap* hp = heap_of (old_address);
23453         if ((hp == this) ||
23454             !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
23455             return;
23456     }
23457 #else //MULTIPLE_HEAPS
23458         return ;
23459 #endif //MULTIPLE_HEAPS
23460     // delta translates old_address into address_gc (old_address);
23461     size_t  brick = brick_of (old_address);
23462     int    brick_entry =  brick_table [ brick ];
23463     uint8_t*  new_address = old_address;
23464     if (! ((brick_entry == 0)))
23465     {
23466     retry:
23467         {
23468             while (brick_entry < 0)
23469             {
23470                 brick = (brick + brick_entry);
23471                 brick_entry =  brick_table [ brick ];
23472             }
23473             uint8_t* old_loc = old_address;
23474
23475             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
23476                                       old_loc);
23477             if ((node <= old_loc))
23478                 new_address = (old_address + node_relocation_distance (node));
23479             else
23480             {
23481                 if (node_left_p (node))
23482                 {
23483                     dprintf(3,(" L: %Ix", (size_t)node));
23484                     new_address = (old_address +
23485                                    (node_relocation_distance (node) +
23486                                     node_gap_size (node)));
23487                 }
23488                 else
23489                 {
23490                     brick = brick - 1;
23491                     brick_entry =  brick_table [ brick ];
23492                     goto retry;
23493                 }
23494             }
23495         }
23496
23497         *pold_address = new_address;
23498         return;
23499     }
23500
23501 #ifdef FEATURE_LOH_COMPACTION
23502     if (loh_compacted_p
23503 #ifdef FEATURE_BASICFREEZE
23504         && !frozen_object_p((Object*)old_address)
23505 #endif // FEATURE_BASICFREEZE
23506         )
23507     {
23508         *pold_address = old_address + loh_node_relocation_distance (old_address);
23509     }
23510     else
23511 #endif //FEATURE_LOH_COMPACTION
23512     {
23513         *pold_address = new_address;
23514     }
23515 }
23516
23517 inline void 
23518 gc_heap::check_class_object_demotion (uint8_t* obj)
23519 {
23520 #ifdef COLLECTIBLE_CLASS
23521     if (is_collectible(obj))
23522     {
23523         check_class_object_demotion_internal (obj);
23524     }
23525 #else
23526     UNREFERENCED_PARAMETER(obj);
23527 #endif //COLLECTIBLE_CLASS
23528 }
23529
23530 #ifdef COLLECTIBLE_CLASS
23531 NOINLINE void 
23532 gc_heap::check_class_object_demotion_internal (uint8_t* obj)
23533 {
23534     if (settings.demotion)
23535     {
23536 #ifdef MULTIPLE_HEAPS
23537         // We set the card without checking the demotion range 'cause at this point
23538         // the handle that points to the loader allocator object may or may not have
23539         // been relocated by other GC threads. 
23540         set_card (card_of (obj));
23541 #else
23542         THREAD_FROM_HEAP;
23543         uint8_t* class_obj = get_class_object (obj);
23544         dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
23545         uint8_t* temp_class_obj = class_obj;
23546         uint8_t** temp = &temp_class_obj;
23547         relocate_address (temp THREAD_NUMBER_ARG);
23548
23549         check_demotion_helper (temp, obj);
23550 #endif //MULTIPLE_HEAPS
23551     }
23552 }
23553
23554 #endif //COLLECTIBLE_CLASS
23555
23556 inline void
23557 gc_heap::check_demotion_helper (uint8_t** pval, uint8_t* parent_obj)
23558 {
23559     // detect if we are demoting an object
23560     if ((*pval < demotion_high) &&
23561         (*pval >= demotion_low))
23562     {
23563         dprintf(3, ("setting card %Ix:%Ix",
23564                     card_of((uint8_t*)pval),
23565                     (size_t)pval));
23566
23567         set_card (card_of (parent_obj));
23568     }
23569 #ifdef MULTIPLE_HEAPS
23570     else if (settings.demotion)
23571     {
23572         dprintf (4, ("Demotion active, computing heap_of object"));
23573         gc_heap* hp = heap_of (*pval);
23574         if ((*pval < hp->demotion_high) &&
23575             (*pval >= hp->demotion_low))
23576         {
23577             dprintf(3, ("setting card %Ix:%Ix",
23578                         card_of((uint8_t*)pval),
23579                         (size_t)pval));
23580
23581             set_card (card_of (parent_obj));
23582         }
23583     }
23584 #endif //MULTIPLE_HEAPS
23585 }
23586
23587 inline void
23588 gc_heap::reloc_survivor_helper (uint8_t** pval)
23589 {
23590     THREAD_FROM_HEAP;
23591     relocate_address (pval THREAD_NUMBER_ARG);
23592
23593     check_demotion_helper (pval, (uint8_t*)pval);
23594 }
23595
23596 inline void
23597 gc_heap::relocate_obj_helper (uint8_t* x, size_t s)
23598 {
23599     THREAD_FROM_HEAP;
23600     if (contain_pointers (x))
23601     {
23602         dprintf (3, ("$%Ix$", (size_t)x));
23603
23604         go_through_object_nostart (method_table(x), x, s, pval,
23605                             {
23606                                 uint8_t* child = *pval;
23607                                 reloc_survivor_helper (pval);
23608                                 if (child)
23609                                 {
23610                                     dprintf (3, ("%Ix->%Ix->%Ix", (uint8_t*)pval, child, *pval));
23611                                 }
23612                             });
23613
23614     }
23615     check_class_object_demotion (x);
23616 }
23617
23618 inline 
23619 void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc)
23620 {
23621     THREAD_FROM_HEAP;
23622
23623     uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0);
23624     relocate_address (address_to_reloc THREAD_NUMBER_ARG);
23625     if (address_to_reloc)
23626     {
23627         dprintf (3, ("SR %Ix: %Ix->%Ix", (uint8_t*)address_to_reloc, old_val, *address_to_reloc));
23628     }
23629
23630     //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval);
23631     uint8_t* relocated_addr = *address_to_reloc;
23632     if ((relocated_addr < demotion_high) &&
23633         (relocated_addr >= demotion_low))
23634     {
23635         dprintf (3, ("set card for location %Ix(%Ix)",
23636                     (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23637
23638         set_card (card_of ((uint8_t*)address_to_set_card));
23639     }
23640 #ifdef MULTIPLE_HEAPS
23641     else if (settings.demotion)
23642     {
23643         gc_heap* hp = heap_of (relocated_addr);
23644         if ((relocated_addr < hp->demotion_high) &&
23645             (relocated_addr >= hp->demotion_low))
23646         {
23647             dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)",
23648                         relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23649
23650             set_card (card_of ((uint8_t*)address_to_set_card));
23651         }
23652     }
23653 #endif //MULTIPLE_HEAPS
23654 }
23655
23656 void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
23657 {
23658     THREAD_FROM_HEAP;
23659     uint8_t* plug = pinned_plug (pinned_plug_entry);
23660     uint8_t* pre_plug_start = plug - sizeof (plug_and_gap);
23661     // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
23662     // address. Consider this scenario: 
23663     // gen1 start | 3-ptr sized NP | PP
23664     // 0          | 0x18           | 0x30
23665     // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
23666     // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
23667     // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree). 
23668     pre_plug_start += sizeof (uint8_t*);
23669     uint8_t** old_address = &pre_plug_start;
23670
23671     uint8_t* old_val = (old_address ? *old_address : 0);
23672     relocate_address (old_address THREAD_NUMBER_ARG);
23673     if (old_address)
23674     {
23675         dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix", 
23676             (uint8_t*)old_address, old_val, *old_address, (pre_plug_start - sizeof (uint8_t*))));
23677     }
23678
23679     pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (uint8_t*));
23680 }
23681
23682 inline
23683 void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned)
23684 {
23685     THREAD_FROM_HEAP;
23686     uint8_t* plug = pinned_plug (pinned_plug_entry);
23687
23688     if (!is_pinned)
23689     {
23690         //// Temporary - we just wanna make sure we are doing things right when padding is needed.
23691         //if ((x + s) < plug)
23692         //{
23693         //    dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix", 
23694         //        x, (x + s), (plug- (x + s)), plug));
23695         //    GCToOSInterface::DebugBreak();
23696         //}
23697
23698         relocate_pre_plug_info (pinned_plug_entry);
23699     }
23700
23701     verify_pins_with_post_plug_info("after relocate_pre_plug_info");
23702
23703     uint8_t* saved_plug_info_start = 0;
23704     uint8_t** saved_info_to_relocate = 0;
23705
23706     if (is_pinned)
23707     {
23708         saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start());
23709         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23710     }
23711     else
23712     {
23713         saved_plug_info_start = (plug - sizeof (plug_and_gap));
23714         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
23715     }
23716     
23717     uint8_t** current_saved_info_to_relocate = 0;
23718     uint8_t* child = 0;
23719
23720     dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
23721
23722     if (contain_pointers (x))
23723     {
23724         dprintf (3,("$%Ix$", (size_t)x));
23725
23726         go_through_object_nostart (method_table(x), x, s, pval,
23727         {
23728             dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (uint8_t*)pval, *pval));
23729
23730             if ((uint8_t*)pval >= end)
23731             {
23732                 current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**);
23733                 child = *current_saved_info_to_relocate;
23734                 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
23735                 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
23736                     (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
23737             }
23738             else
23739             {
23740                 reloc_survivor_helper (pval);
23741             }
23742         });
23743     }
23744
23745     check_class_object_demotion (x);
23746 }
23747
23748 void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end)
23749 {
23750     uint8_t*  x = plug;
23751     while (x < plug_end)
23752     {
23753         size_t s = size (x);
23754         uint8_t* next_obj = x + Align (s);
23755         Prefetch (next_obj);
23756         relocate_obj_helper (x, s);
23757         assert (s > 0);
23758         x = next_obj;
23759     }
23760 }
23761
23762 // if we expanded, right now we are not handling it as We are not saving the new reloc info.
23763 void gc_heap::verify_pins_with_post_plug_info (const char* msg)
23764 {
23765 #if defined  (_DEBUG) && defined (VERIFY_HEAP)
23766     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
23767     {
23768         if (!verify_pinned_queue_p)
23769             return;
23770
23771         if (settings.heap_expansion)
23772             return;
23773
23774         for (size_t i = 0; i < mark_stack_tos; i++)
23775         {
23776             mark& m = mark_stack_array[i];
23777
23778             mark* pinned_plug_entry = pinned_plug_of(i);
23779
23780             if (pinned_plug_entry->has_post_plug_info() && 
23781                 pinned_plug_entry->post_short_p() && 
23782                 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
23783             {
23784                 uint8_t* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
23785                 // object after pin
23786                 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d", 
23787                     next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
23788                     (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
23789
23790                 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
23791
23792                 if (node_gap_size (next_obj) != *post_plug_debug)
23793                 {
23794                     dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix", 
23795                         next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
23796                     FATAL_GC_ERROR();
23797                 }
23798                 post_plug_debug++;
23799                 // can't do node_relocation_distance here as it clears the left bit.
23800                 //if (node_relocation_distance (next_obj) != *post_plug_debug)
23801                 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
23802                 {
23803                     dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix", 
23804                         next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
23805                     FATAL_GC_ERROR();
23806                 }
23807                 if (node_left_child (next_obj) > 0)
23808                 {
23809                     dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
23810                     FATAL_GC_ERROR();
23811                 }
23812             }
23813         }
23814
23815         dprintf (3, ("%s verified", msg));
23816     }
23817 #else // _DEBUG && VERIFY_HEAP
23818     UNREFERENCED_PARAMETER(msg);
23819 #endif // _DEBUG && VERIFY_HEAP
23820 }
23821
23822 #ifdef COLLECTIBLE_CLASS
23823 // We don't want to burn another ptr size space for pinned plugs to record this so just 
23824 // set the card unconditionally for collectible objects if we are demoting.
23825 inline void
23826 gc_heap::unconditional_set_card_collectible (uint8_t* obj)
23827 {
23828     if (settings.demotion)
23829     {
23830         set_card (card_of (obj));
23831     }
23832 }
23833 #endif //COLLECTIBLE_CLASS
23834
23835 void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry)
23836 {
23837     uint8_t*  x = plug;
23838     uint8_t* p_plug = pinned_plug (pinned_plug_entry);
23839     BOOL is_pinned = (plug == p_plug);
23840     BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
23841
23842     plug_end += sizeof (gap_reloc_pair);
23843
23844     //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
23845     dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
23846
23847     verify_pins_with_post_plug_info("begin reloc short surv");
23848
23849     while (x < plug_end)
23850     {
23851         if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
23852         {
23853             dprintf (3, ("last obj %Ix is short", x));
23854
23855             if (is_pinned)
23856             {
23857 #ifdef COLLECTIBLE_CLASS
23858                 if (pinned_plug_entry->post_short_collectible_p())
23859                     unconditional_set_card_collectible (x);
23860 #endif //COLLECTIBLE_CLASS
23861
23862                 // Relocate the saved references based on bits set.
23863                 uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start());
23864                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23865                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
23866                 {
23867                     if (pinned_plug_entry->post_short_bit_p (i))
23868                     {
23869                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
23870                     }
23871                 }
23872             }
23873             else
23874             {
23875 #ifdef COLLECTIBLE_CLASS
23876                 if (pinned_plug_entry->pre_short_collectible_p())
23877                     unconditional_set_card_collectible (x);
23878 #endif //COLLECTIBLE_CLASS
23879
23880                 relocate_pre_plug_info (pinned_plug_entry);
23881
23882                 // Relocate the saved references based on bits set.
23883                 uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap));
23884                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
23885                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
23886                 {
23887                     if (pinned_plug_entry->pre_short_bit_p (i))
23888                     {
23889                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
23890                     }
23891                 }
23892             }
23893
23894             break;
23895         }
23896
23897         size_t s = size (x);
23898         uint8_t* next_obj = x + Align (s);
23899         Prefetch (next_obj);
23900
23901         if (next_obj >= plug_end) 
23902         {
23903             dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix", 
23904                 next_obj, plug, plug_end));
23905
23906             verify_pins_with_post_plug_info("before reloc short obj");
23907
23908             relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
23909         }
23910         else
23911         {
23912             relocate_obj_helper (x, s);
23913         }
23914
23915         assert (s > 0);
23916         x = next_obj;
23917     }
23918
23919     verify_pins_with_post_plug_info("end reloc short surv");
23920 }
23921
23922 void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end,
23923                                           BOOL check_last_object_p, 
23924                                           mark* pinned_plug_entry)
23925 {
23926     //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
23927     dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
23928
23929     if (check_last_object_p)
23930     {
23931         relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
23932     }
23933     else
23934     {
23935         relocate_survivor_helper (plug, plug_end);
23936     }
23937 }
23938
23939 void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args)
23940 {
23941     assert ((tree != NULL));
23942
23943     dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
23944         tree, args->last_plug, 
23945         (tree + node_left_child (tree)),
23946         (tree + node_right_child (tree)),
23947         node_gap_size (tree)));
23948
23949     if (node_left_child (tree))
23950     {
23951         relocate_survivors_in_brick (tree + node_left_child (tree), args);
23952     }
23953     {
23954         uint8_t*  plug = tree;
23955         BOOL   has_post_plug_info_p = FALSE;
23956         BOOL   has_pre_plug_info_p = FALSE;
23957
23958         if (tree == oldest_pinned_plug)
23959         {
23960             args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
23961                                                                &has_post_plug_info_p);
23962             assert (tree == pinned_plug (args->pinned_plug_entry));
23963
23964             dprintf (3, ("tree is the oldest pin: %Ix", tree));
23965         }
23966         if (args->last_plug)
23967         {
23968             size_t  gap_size = node_gap_size (tree);
23969             uint8_t*  gap = (plug - gap_size);
23970             dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
23971             assert (gap_size >= Align (min_obj_size));
23972             uint8_t*  last_plug_end = gap;
23973
23974             BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
23975
23976             {
23977                 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
23978             }
23979         }
23980         else
23981         {
23982             assert (!has_pre_plug_info_p);
23983         }
23984
23985         args->last_plug = plug;
23986         args->is_shortened = has_post_plug_info_p;
23987         if (has_post_plug_info_p)
23988         {
23989             dprintf (3, ("setting %Ix as shortened", plug));
23990         }
23991         dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
23992     }
23993     if (node_right_child (tree))
23994     {
23995         relocate_survivors_in_brick (tree + node_right_child (tree), args);
23996     }
23997 }
23998
23999 inline
24000 void gc_heap::update_oldest_pinned_plug()
24001 {
24002     oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
24003 }
24004
24005 void gc_heap::relocate_survivors (int condemned_gen_number,
24006                                   uint8_t* first_condemned_address)
24007 {
24008     generation* condemned_gen = generation_of (condemned_gen_number);
24009     uint8_t*  start_address = first_condemned_address;
24010     size_t  current_brick = brick_of (start_address);
24011     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24012
24013     PREFIX_ASSUME(current_heap_segment != NULL);
24014
24015     uint8_t*  end_address = 0;
24016
24017     reset_pinned_queue_bos();
24018     update_oldest_pinned_plug();
24019     
24020     end_address = heap_segment_allocated (current_heap_segment);
24021
24022     size_t  end_brick = brick_of (end_address - 1);
24023     relocate_args args;
24024     args.low = gc_low;
24025     args.high = gc_high;
24026     args.is_shortened = FALSE;
24027     args.pinned_plug_entry = 0;
24028     args.last_plug = 0;
24029     while (1)
24030     {
24031         if (current_brick > end_brick)
24032         {
24033             if (args.last_plug)
24034             {
24035                 {
24036                     assert (!(args.is_shortened));
24037                     relocate_survivors_in_plug (args.last_plug,
24038                                                 heap_segment_allocated (current_heap_segment),
24039                                                 args.is_shortened, 
24040                                                 args.pinned_plug_entry);
24041                 }
24042
24043                 args.last_plug = 0;
24044             }
24045
24046             if (heap_segment_next_rw (current_heap_segment))
24047             {
24048                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24049                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24050                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24051                 continue;
24052             }
24053             else
24054             {
24055                 break;
24056             }
24057         }
24058         {
24059             int brick_entry =  brick_table [ current_brick ];
24060
24061             if (brick_entry >= 0)
24062             {
24063                 relocate_survivors_in_brick (brick_address (current_brick) +
24064                                              brick_entry -1,
24065                                              &args);
24066             }
24067         }
24068         current_brick++;
24069     }
24070 }
24071
24072 void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args)
24073 {
24074     if (check_last_object_p)
24075     {
24076         size += sizeof (gap_reloc_pair);
24077         mark* entry = args->pinned_plug_entry;
24078
24079         if (args->is_shortened)
24080         {
24081             assert (entry->has_post_plug_info());
24082             entry->swap_post_plug_and_saved_for_profiler();
24083         }
24084         else
24085         {
24086             assert (entry->has_pre_plug_info());
24087             entry->swap_pre_plug_and_saved_for_profiler();
24088         }
24089     }
24090
24091     ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
24092     STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
24093     ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
24094
24095     (args->fn) (plug, (plug + size), reloc, args->profiling_context, !!settings.compaction, false);
24096
24097     if (check_last_object_p)
24098     {
24099         mark* entry = args->pinned_plug_entry;
24100
24101         if (args->is_shortened)
24102         {
24103             entry->swap_post_plug_and_saved_for_profiler();
24104         }
24105         else
24106         {
24107             entry->swap_pre_plug_and_saved_for_profiler();
24108         }
24109     }
24110 }
24111
24112 void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args)
24113 {
24114     assert ((tree != NULL));
24115     if (node_left_child (tree))
24116     {
24117         walk_relocation_in_brick (tree + node_left_child (tree), args);
24118     }
24119
24120     uint8_t*  plug = tree;
24121     BOOL   has_pre_plug_info_p = FALSE;
24122     BOOL   has_post_plug_info_p = FALSE;
24123
24124     if (tree == oldest_pinned_plug)
24125     {
24126         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24127                                                            &has_post_plug_info_p);
24128         assert (tree == pinned_plug (args->pinned_plug_entry));
24129     }
24130
24131     if (args->last_plug != 0)
24132     {
24133         size_t gap_size = node_gap_size (tree);
24134         uint8_t*  gap = (plug - gap_size);
24135         uint8_t*  last_plug_end = gap;
24136         size_t last_plug_size = (last_plug_end - args->last_plug);
24137         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
24138             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24139         
24140         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24141         if (!check_last_object_p)
24142         {
24143             assert (last_plug_size >= Align (min_obj_size));
24144         }
24145
24146         walk_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24147     }
24148     else
24149     {
24150         assert (!has_pre_plug_info_p);
24151     }
24152
24153     dprintf (3, ("set args last plug to plug: %Ix", plug));
24154     args->last_plug = plug;
24155     args->is_shortened = has_post_plug_info_p;
24156
24157     if (node_right_child (tree))
24158     {
24159         walk_relocation_in_brick (tree + node_right_child (tree), args);
24160     }
24161 }
24162
24163 void gc_heap::walk_relocation (void* profiling_context, record_surv_fn fn)
24164 {
24165     generation* condemned_gen = generation_of (settings.condemned_generation);
24166     uint8_t*  start_address = generation_allocation_start (condemned_gen);
24167     size_t  current_brick = brick_of (start_address);
24168     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24169
24170     PREFIX_ASSUME(current_heap_segment != NULL);
24171
24172     reset_pinned_queue_bos();
24173     update_oldest_pinned_plug();
24174     size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24175     walk_relocate_args args;
24176     args.is_shortened = FALSE;
24177     args.pinned_plug_entry = 0;
24178     args.last_plug = 0;
24179     args.profiling_context = profiling_context;
24180     args.fn = fn;
24181
24182     while (1)
24183     {
24184         if (current_brick > end_brick)
24185         {
24186             if (args.last_plug)
24187             {
24188                 walk_plug (args.last_plug, 
24189                            (heap_segment_allocated (current_heap_segment) - args.last_plug), 
24190                            args.is_shortened,
24191                            &args);
24192                 args.last_plug = 0;
24193             }
24194             if (heap_segment_next_rw (current_heap_segment))
24195             {
24196                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24197                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24198                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24199                 continue;
24200             }
24201             else
24202             {
24203                 break;
24204             }
24205         }
24206         {
24207             int brick_entry =  brick_table [ current_brick ];
24208             if (brick_entry >= 0)
24209             {
24210                 walk_relocation_in_brick (brick_address (current_brick) +
24211                                           brick_entry - 1,
24212                                           &args);
24213             }
24214         }
24215         current_brick++;
24216     }
24217 }
24218
24219 void gc_heap::walk_survivors (record_surv_fn fn, void* context, walk_surv_type type)
24220 {
24221     if (type == walk_for_gc)
24222         walk_survivors_relocation (context, fn);
24223 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24224     else if (type == walk_for_bgc)
24225         walk_survivors_for_bgc (context, fn);
24226 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
24227     else if (type == walk_for_loh)
24228         walk_survivors_for_loh (context, fn);
24229     else
24230         assert (!"unknown type!");
24231 }
24232
24233 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24234 void gc_heap::walk_survivors_for_bgc (void* profiling_context, record_surv_fn fn)
24235 {
24236     // This should only be called for BGCs
24237     assert(settings.concurrent);
24238
24239     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24240
24241     BOOL small_object_segments = TRUE;
24242     int align_const = get_alignment_constant (small_object_segments);
24243
24244     while (1)
24245     {
24246         if (seg == 0)
24247         {
24248             if (small_object_segments)
24249             {
24250                 //switch to large segment
24251                 small_object_segments = FALSE;
24252
24253                 align_const = get_alignment_constant (small_object_segments);
24254                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24255
24256                 PREFIX_ASSUME(seg != NULL);
24257
24258                 continue;
24259             }
24260             else 
24261                 break;
24262         }
24263
24264         uint8_t* o = heap_segment_mem (seg);
24265         uint8_t* end = heap_segment_allocated (seg);
24266
24267         while (o < end)
24268         {
24269             if (method_table(o) == g_gc_pFreeObjectMethodTable)
24270             {
24271                 o += Align (size (o), align_const);
24272                 continue;
24273             }
24274
24275             // It's survived. Make a fake plug, starting at o,
24276             // and send the event
24277
24278             uint8_t* plug_start = o;
24279
24280             while (method_table(o) != g_gc_pFreeObjectMethodTable)
24281             {
24282                 o += Align (size (o), align_const);
24283                 if (o >= end)
24284                 {
24285                     break;
24286                 }
24287             }
24288                 
24289             uint8_t* plug_end = o;
24290
24291             fn (plug_start, 
24292                 plug_end,
24293                 0,              // Reloc distance == 0 as this is non-compacting
24294                 profiling_context,
24295                 false,          // Non-compacting
24296                 true);          // BGC
24297         }
24298
24299         seg = heap_segment_next (seg);
24300     }
24301 }
24302 #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24303
24304 void gc_heap::relocate_phase (int condemned_gen_number,
24305                               uint8_t* first_condemned_address)
24306 {
24307     ScanContext sc;
24308     sc.thread_number = heap_number;
24309     sc.promotion = FALSE;
24310     sc.concurrent = FALSE;
24311
24312
24313 #ifdef TIME_GC
24314         unsigned start;
24315         unsigned finish;
24316         start = GetCycleCount32();
24317 #endif //TIME_GC
24318
24319 //  %type%  category = quote (relocate);
24320     dprintf (2,("---- Relocate phase -----"));
24321
24322 #ifdef MULTIPLE_HEAPS
24323     //join all threads to make sure they are synchronized
24324     dprintf(3, ("Joining after end of plan"));
24325     gc_t_join.join(this, gc_join_begin_relocate_phase);
24326     if (gc_t_join.joined())
24327 #endif //MULTIPLE_HEAPS
24328
24329     {
24330 #ifdef MULTIPLE_HEAPS
24331
24332         //join all threads to make sure they are synchronized
24333         dprintf(3, ("Restarting for relocation"));
24334         gc_t_join.restart();
24335 #endif //MULTIPLE_HEAPS
24336     }
24337
24338     dprintf(3,("Relocating roots"));
24339     GCScan::GcScanRoots(GCHeap::Relocate,
24340                             condemned_gen_number, max_generation, &sc);
24341
24342     verify_pins_with_post_plug_info("after reloc stack");
24343
24344 #ifdef BACKGROUND_GC
24345     if (recursive_gc_sync::background_running_p())
24346     {
24347         scan_background_roots (GCHeap::Relocate, heap_number, &sc);
24348     }
24349 #endif //BACKGROUND_GC
24350
24351     if (condemned_gen_number != max_generation)
24352     {
24353         dprintf(3,("Relocating cross generation pointers"));
24354         mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
24355         verify_pins_with_post_plug_info("after reloc cards");
24356     }
24357     if (condemned_gen_number != max_generation)
24358     {
24359         dprintf(3,("Relocating cross generation pointers for large objects"));
24360         mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
24361     }
24362     else
24363     {
24364 #ifdef FEATURE_LOH_COMPACTION
24365         if (loh_compacted_p)
24366         {
24367             assert (settings.condemned_generation == max_generation);
24368             relocate_in_loh_compact();
24369         }
24370         else
24371 #endif //FEATURE_LOH_COMPACTION
24372         {
24373             relocate_in_large_objects ();
24374         }
24375     }
24376     {
24377         dprintf(3,("Relocating survivors"));
24378         relocate_survivors (condemned_gen_number,
24379                             first_condemned_address);
24380     }
24381
24382 #ifdef FEATURE_PREMORTEM_FINALIZATION
24383         dprintf(3,("Relocating finalization data"));
24384         finalize_queue->RelocateFinalizationData (condemned_gen_number,
24385                                                        __this);
24386 #endif // FEATURE_PREMORTEM_FINALIZATION
24387
24388
24389 // MTHTS
24390     {
24391         dprintf(3,("Relocating handle table"));
24392         GCScan::GcScanHandles(GCHeap::Relocate,
24393                                   condemned_gen_number, max_generation, &sc);
24394     }
24395
24396 #ifdef MULTIPLE_HEAPS
24397     //join all threads to make sure they are synchronized
24398     dprintf(3, ("Joining after end of relocation"));
24399     gc_t_join.join(this, gc_join_relocate_phase_done);
24400
24401 #endif //MULTIPLE_HEAPS
24402
24403 #ifdef TIME_GC
24404         finish = GetCycleCount32();
24405         reloc_time = finish - start;
24406 #endif //TIME_GC
24407
24408     dprintf(2,( "---- End of Relocate phase ----"));
24409 }
24410
24411 // This compares to see if tree is the current pinned plug and returns info
24412 // for this pinned plug. Also advances the pinned queue if that's the case.
24413 //
24414 // We don't change the values of the plug info if tree is not the same as 
24415 // the current pinned plug - the caller is responsible for setting the right
24416 // values to begin with.
24417 //
24418 // POPO TODO: We are keeping this temporarily as this is also used by realloc 
24419 // where it passes FALSE to deque_p, change it to use the same optimization 
24420 // as relocate. Not as essential since realloc is already a slow path.
24421 mark* gc_heap::get_next_pinned_entry (uint8_t* tree,
24422                                       BOOL* has_pre_plug_info_p, 
24423                                       BOOL* has_post_plug_info_p,
24424                                       BOOL deque_p)
24425 {
24426     if (!pinned_plug_que_empty_p())
24427     {
24428         mark* oldest_entry = oldest_pin();
24429         uint8_t* oldest_plug = pinned_plug (oldest_entry);
24430         if (tree == oldest_plug)
24431         {
24432             *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24433             *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24434
24435             if (deque_p)
24436             {
24437                 deque_pinned_plug();
24438             }
24439
24440             dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d", 
24441                 tree, 
24442                 (*has_pre_plug_info_p ? 1 : 0),
24443                 (*has_post_plug_info_p ? 1 : 0)));
24444
24445             return oldest_entry;
24446         }
24447     }
24448
24449     return NULL;
24450 }
24451
24452 // This also deques the oldest entry and update the oldest plug
24453 mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p, 
24454                                         BOOL* has_post_plug_info_p)
24455 {
24456     mark* oldest_entry = oldest_pin();
24457     *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24458     *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24459
24460     deque_pinned_plug();
24461     update_oldest_pinned_plug();
24462     return oldest_entry;
24463 }
24464
24465 inline
24466 void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24467 {
24468     if (copy_cards_p)
24469         copy_cards_for_addresses (dest, src, len);
24470     else
24471         clear_card_for_addresses (dest, dest + len);
24472 }
24473
24474 // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
24475 // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
24476 // we won't need to individually recover each overwritten part of plugs.
24477 inline
24478 void  gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24479 {
24480     if (dest != src)
24481     {
24482 #ifdef BACKGROUND_GC
24483         if (current_c_gc_state == c_gc_state_marking) 
24484         {
24485             //TODO: should look to see whether we should consider changing this
24486             // to copy a consecutive region of the mark array instead.
24487             copy_mark_bits_for_addresses (dest, src, len);
24488         }
24489 #endif //BACKGROUND_GC
24490         //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24491         dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24492         memcopy (dest - plug_skew, src - plug_skew, len);
24493 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24494         if (SoftwareWriteWatch::IsEnabledForGCHeap())
24495         {
24496             // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC
24497             // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the
24498             // object at (src + len), so it can be ignored anyway.
24499             SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew);
24500         }
24501 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24502         copy_cards_range (dest, src, len, copy_cards_p);
24503     }
24504 }
24505
24506 void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args)
24507 {
24508     args->print();
24509     uint8_t* reloc_plug = plug + args->last_plug_relocation;
24510
24511     if (check_last_object_p)
24512     {
24513         size += sizeof (gap_reloc_pair);
24514         mark* entry = args->pinned_plug_entry;
24515
24516         if (args->is_shortened)
24517         {
24518             assert (entry->has_post_plug_info());
24519             entry->swap_post_plug_and_saved();
24520         }
24521         else
24522         {
24523             assert (entry->has_pre_plug_info());
24524             entry->swap_pre_plug_and_saved();
24525         }
24526     }
24527
24528     int  old_brick_entry =  brick_table [brick_of (plug)];
24529
24530     assert (node_relocation_distance (plug) == args->last_plug_relocation);
24531
24532 #ifdef FEATURE_STRUCTALIGN
24533     ptrdiff_t alignpad = node_alignpad(plug);
24534     if (alignpad)
24535     {
24536         make_unused_array (reloc_plug - alignpad, alignpad);
24537         if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
24538         {
24539             // The alignment padding is straddling one or more bricks;
24540             // it has to be the last "object" of its first brick.
24541             fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
24542         }
24543     }
24544 #else // FEATURE_STRUCTALIGN
24545     size_t unused_arr_size = 0; 
24546     BOOL  already_padded_p = FALSE;
24547 #ifdef SHORT_PLUGS
24548     if (is_plug_padded (plug))
24549     {
24550         already_padded_p = TRUE;
24551         clear_plug_padded (plug);
24552         unused_arr_size = Align (min_obj_size);
24553     }
24554 #endif //SHORT_PLUGS
24555     if (node_realigned (plug))
24556     {
24557         unused_arr_size += switch_alignment_size (already_padded_p);
24558     }
24559
24560     if (unused_arr_size != 0) 
24561     {
24562         make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
24563
24564         if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
24565         {
24566             dprintf (3, ("fix B for padding: %Id: %Ix->%Ix", 
24567                 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
24568             // The alignment padding is straddling one or more bricks;
24569             // it has to be the last "object" of its first brick.
24570             fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
24571         }
24572     }
24573 #endif // FEATURE_STRUCTALIGN
24574
24575 #ifdef SHORT_PLUGS
24576     if (is_plug_padded (plug))
24577     {
24578         make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
24579
24580         if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
24581         {
24582             // The alignment padding is straddling one or more bricks;
24583             // it has to be the last "object" of its first brick.
24584             fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
24585         }
24586     }
24587 #endif //SHORT_PLUGS
24588
24589     gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
24590
24591     if (args->check_gennum_p)
24592     {
24593         int src_gennum = args->src_gennum;
24594         if (src_gennum == -1)
24595         {
24596             src_gennum = object_gennum (plug);
24597         }
24598
24599         int dest_gennum = object_gennum_plan (reloc_plug);
24600
24601         if (src_gennum < dest_gennum)
24602         {
24603             generation_allocation_size (generation_of (dest_gennum)) += size;
24604         }
24605     }
24606
24607     size_t current_reloc_brick = args->current_compacted_brick;
24608
24609     if (brick_of (reloc_plug) != current_reloc_brick)
24610     {
24611         dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix", 
24612             current_reloc_brick, brick_of (reloc_plug)));
24613
24614         if (args->before_last_plug)
24615         {
24616             dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
24617                      current_reloc_brick,
24618                      args->before_last_plug, 
24619                      (args->before_last_plug - brick_address (current_reloc_brick))));
24620
24621             {
24622                 set_brick (current_reloc_brick,
24623                         args->before_last_plug - brick_address (current_reloc_brick));
24624             }
24625         }
24626         current_reloc_brick = brick_of (reloc_plug);
24627     }
24628     size_t end_brick = brick_of (reloc_plug + size-1);
24629     if (end_brick != current_reloc_brick)
24630     {
24631         // The plug is straddling one or more bricks
24632         // It has to be the last plug of its first brick
24633         dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
24634                  current_reloc_brick, (size_t)reloc_plug,
24635                  (reloc_plug - brick_address (current_reloc_brick))));
24636
24637         {
24638             set_brick (current_reloc_brick,
24639                     reloc_plug - brick_address (current_reloc_brick));
24640         }
24641         // update all intervening brick
24642         size_t brick = current_reloc_brick + 1;
24643         dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
24644             brick, (end_brick - 1)));
24645         while (brick < end_brick)
24646         {
24647             set_brick (brick, -1);
24648             brick++;
24649         }
24650         // code last brick offset as a plug address
24651         args->before_last_plug = brick_address (end_brick) -1;
24652         current_reloc_brick = end_brick;
24653         dprintf (3, ("setting before last to %Ix, last brick to %Ix",
24654             args->before_last_plug, current_reloc_brick));
24655     } 
24656     else
24657     {
24658         dprintf (3, ("still in the same brick: %Ix", end_brick));
24659         args->before_last_plug = reloc_plug;
24660     }
24661     args->current_compacted_brick = current_reloc_brick;
24662
24663     if (check_last_object_p)
24664     {
24665         mark* entry = args->pinned_plug_entry;
24666
24667         if (args->is_shortened)
24668         {
24669             entry->swap_post_plug_and_saved();
24670         }
24671         else
24672         {
24673             entry->swap_pre_plug_and_saved();
24674         }
24675     }
24676 }
24677
24678 void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args)
24679 {
24680     assert (tree != NULL);
24681     int   left_node = node_left_child (tree);
24682     int   right_node = node_right_child (tree);
24683     ptrdiff_t relocation = node_relocation_distance (tree);
24684
24685     args->print();
24686
24687     if (left_node)
24688     {
24689         dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
24690         compact_in_brick ((tree + left_node), args);
24691     }
24692
24693     uint8_t*  plug = tree;
24694     BOOL   has_pre_plug_info_p = FALSE;
24695     BOOL   has_post_plug_info_p = FALSE;
24696
24697     if (tree == oldest_pinned_plug)
24698     {
24699         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24700                                                            &has_post_plug_info_p);
24701         assert (tree == pinned_plug (args->pinned_plug_entry));
24702     }
24703
24704     if (args->last_plug != 0)
24705     {
24706         size_t gap_size = node_gap_size (tree);
24707         uint8_t*  gap = (plug - gap_size);
24708         uint8_t*  last_plug_end = gap;
24709         size_t last_plug_size = (last_plug_end - args->last_plug);
24710         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
24711             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24712         
24713         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24714         if (!check_last_object_p)
24715         {
24716             assert (last_plug_size >= Align (min_obj_size));
24717         }
24718
24719         compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24720     }
24721     else
24722     {
24723         assert (!has_pre_plug_info_p);
24724     }
24725
24726     dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
24727     args->last_plug = plug;
24728     args->last_plug_relocation = relocation;
24729     args->is_shortened = has_post_plug_info_p;
24730
24731     if (right_node)
24732     {
24733         dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
24734         compact_in_brick ((tree + right_node), args);
24735     }
24736 }
24737
24738 void gc_heap::recover_saved_pinned_info()
24739 {
24740     reset_pinned_queue_bos();
24741
24742     while (!(pinned_plug_que_empty_p()))
24743     {
24744         mark* oldest_entry = oldest_pin();
24745         oldest_entry->recover_plug_info();
24746 #ifdef GC_CONFIG_DRIVEN
24747         if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info())
24748             record_interesting_data_point (idp_pre_and_post_pin);
24749         else if (oldest_entry->has_pre_plug_info())
24750             record_interesting_data_point (idp_pre_pin);
24751         else if (oldest_entry->has_post_plug_info())
24752             record_interesting_data_point (idp_post_pin);
24753 #endif //GC_CONFIG_DRIVEN
24754
24755         deque_pinned_plug();
24756     }
24757 }
24758
24759 void gc_heap::compact_phase (int condemned_gen_number,
24760                              uint8_t*  first_condemned_address,
24761                              BOOL clear_cards)
24762 {
24763 //  %type%  category = quote (compact);
24764 #ifdef TIME_GC
24765         unsigned start;
24766         unsigned finish;
24767         start = GetCycleCount32();
24768 #endif //TIME_GC
24769     generation*   condemned_gen = generation_of (condemned_gen_number);
24770     uint8_t*  start_address = first_condemned_address;
24771     size_t   current_brick = brick_of (start_address);
24772     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24773
24774     PREFIX_ASSUME(current_heap_segment != NULL);
24775
24776     reset_pinned_queue_bos();
24777     update_oldest_pinned_plug();
24778
24779     BOOL reused_seg = expand_reused_seg_p();
24780     if (reused_seg)
24781     {
24782         for (int i = 1; i <= max_generation; i++)
24783         {
24784             generation_allocation_size (generation_of (i)) = 0;
24785         }
24786     }
24787
24788     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
24789
24790     size_t  end_brick = brick_of (end_address-1);
24791     compact_args args;
24792     args.last_plug = 0;
24793     args.before_last_plug = 0;
24794     args.current_compacted_brick = ~((size_t)1);
24795     args.is_shortened = FALSE;
24796     args.pinned_plug_entry = 0;
24797     args.copy_cards_p =  (condemned_gen_number >= 1) || !clear_cards;
24798     args.check_gennum_p = reused_seg;
24799     if (args.check_gennum_p)
24800     {
24801         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
24802     }
24803
24804     dprintf (2,("---- Compact Phase: %Ix(%Ix)----", 
24805         first_condemned_address, brick_of (first_condemned_address)));
24806
24807 #ifdef MULTIPLE_HEAPS
24808     //restart
24809     if (gc_t_join.joined())
24810     {
24811 #endif //MULTIPLE_HEAPS
24812
24813 #ifdef MULTIPLE_HEAPS
24814         dprintf(3, ("Restarting for compaction"));
24815         gc_t_join.restart();
24816     }
24817 #endif //MULTIPLE_HEAPS
24818
24819     reset_pinned_queue_bos();
24820
24821 #ifdef FEATURE_LOH_COMPACTION
24822     if (loh_compacted_p)
24823     {
24824         compact_loh();
24825     }
24826 #endif //FEATURE_LOH_COMPACTION
24827
24828     if ((start_address < end_address) ||
24829         (condemned_gen_number == max_generation))
24830     {
24831         while (1)
24832         {
24833             if (current_brick > end_brick)
24834             {
24835                 if (args.last_plug != 0)
24836                 {
24837                     dprintf (3, ("compacting last plug: %Ix", args.last_plug))
24838                     compact_plug (args.last_plug,
24839                                   (heap_segment_allocated (current_heap_segment) - args.last_plug),
24840                                   args.is_shortened,
24841                                   &args);
24842                 }
24843
24844                 if (heap_segment_next_rw (current_heap_segment))
24845                 {
24846                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
24847                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
24848                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24849                     args.last_plug = 0;
24850                     if (args.check_gennum_p)
24851                     {
24852                         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
24853                     }
24854                     continue;
24855                 }
24856                 else
24857                 {
24858                     if (args.before_last_plug !=0)
24859                     {
24860                         dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
24861                                     args.current_compacted_brick, (size_t)args.before_last_plug));
24862                         assert (args.current_compacted_brick != ~1u);
24863                         set_brick (args.current_compacted_brick,
24864                                    args.before_last_plug - brick_address (args.current_compacted_brick));
24865                     }
24866                     break;
24867                 }
24868             }
24869             {
24870                 int  brick_entry =  brick_table [ current_brick ];
24871                 dprintf (3, ("B: %Ix(%Ix)->%Ix", 
24872                     current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
24873
24874                 if (brick_entry >= 0)
24875                 {
24876                     compact_in_brick ((brick_address (current_brick) + brick_entry -1),
24877                                       &args);
24878
24879                 }
24880             }
24881             current_brick++;
24882         }
24883     }
24884
24885     recover_saved_pinned_info();
24886
24887 #ifdef TIME_GC
24888     finish = GetCycleCount32();
24889     compact_time = finish - start;
24890 #endif //TIME_GC
24891
24892     concurrent_print_time_delta ("compact end");
24893
24894     dprintf(2,("---- End of Compact phase ----"));
24895 }
24896
24897 #ifdef MULTIPLE_HEAPS
24898
24899 #ifdef _MSC_VER
24900 #pragma warning(push)
24901 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
24902 #endif //_MSC_VER
24903 void gc_heap::gc_thread_stub (void* arg)
24904 {
24905     gc_heap* heap = (gc_heap*)arg;
24906     if (!gc_thread_no_affinitize_p)
24907     {
24908         GCThreadAffinity affinity;
24909         affinity.Group = GCThreadAffinity::None;
24910         affinity.Processor = GCThreadAffinity::None;
24911
24912         // We are about to set affinity for GC threads. It is a good place to set up NUMA and
24913         // CPU groups because the process mask, processor number, and group number are all
24914         // readily available.
24915         if (GCToOSInterface::CanEnableGCCPUGroups())
24916             set_thread_group_affinity_for_heap(heap->heap_number, &affinity);
24917         else
24918             set_thread_affinity_mask_for_heap(heap->heap_number, &affinity);
24919
24920         if (!GCToOSInterface::SetThreadAffinity(&affinity))
24921         {
24922             dprintf(1, ("Failed to set thread affinity for server GC thread"));
24923         }
24924     }
24925
24926     // server GC threads run at a higher priority than normal.
24927     GCToOSInterface::BoostThreadPriority();
24928     _alloca (256*heap->heap_number);
24929     heap->gc_thread_function();
24930 }
24931 #ifdef _MSC_VER
24932 #pragma warning(pop)
24933 #endif //_MSC_VER
24934
24935 #endif //MULTIPLE_HEAPS
24936
24937 #ifdef BACKGROUND_GC
24938
24939 #ifdef _MSC_VER
24940 #pragma warning(push)
24941 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
24942 #endif //_MSC_VER
24943 void gc_heap::bgc_thread_stub (void* arg)
24944 {
24945     gc_heap* heap = (gc_heap*)arg;
24946     heap->bgc_thread = GCToEEInterface::GetThread();
24947     assert(heap->bgc_thread != nullptr);
24948     heap->bgc_thread_function();
24949 }
24950 #ifdef _MSC_VER
24951 #pragma warning(pop)
24952 #endif //_MSC_VER
24953
24954 #endif //BACKGROUND_GC
24955
24956 /*------------------ Background GC ----------------------------*/
24957
24958 #ifdef BACKGROUND_GC
24959
24960 void gc_heap::background_drain_mark_list (int thread)
24961 {
24962     UNREFERENCED_PARAMETER(thread);
24963
24964     size_t saved_c_mark_list_index = c_mark_list_index;
24965
24966     if (saved_c_mark_list_index)
24967     {
24968         concurrent_print_time_delta ("SML");
24969     }
24970     while (c_mark_list_index != 0)
24971     {
24972         size_t current_index = c_mark_list_index - 1;
24973         uint8_t* o = c_mark_list [current_index];
24974         background_mark_object (o THREAD_NUMBER_ARG);
24975         c_mark_list_index--;
24976     }
24977     if (saved_c_mark_list_index)
24978     {
24979
24980         concurrent_print_time_delta ("EML");
24981     }
24982
24983     fire_drain_mark_list_event (saved_c_mark_list_index);
24984 }
24985
24986
24987 // The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
24988 #ifdef MULTIPLE_HEAPS
24989 // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
24990 // them. So we can use the same static variables.
24991 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
24992 {
24993     // Whenever we call this method there may have been preceding object promotions. So set
24994     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
24995     // based on the how the scanning proceeded).
24996     s_fUnscannedPromotions = TRUE;
24997
24998     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
24999     // the state of this thread's portion of the dependent handle table. That's because promotions on other
25000     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
25001     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
25002     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
25003     // as all the others or they'll get out of step).
25004     while (true)
25005     {
25006         // The various worker threads are all currently racing in this code. We need to work out if at least
25007         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
25008         // dependent handle table when both of the following conditions apply:
25009         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
25010         //     object happens to correspond to a primary in one of our handles we might potentially have to
25011         //     promote the associated secondary).
25012         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
25013         //
25014         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
25015         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
25016         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
25017         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
25018         // follows below. Note that we can't read this outside of the join since on any iteration apart from
25019         // the first threads will be racing between reading this value and completing their previous
25020         // iteration's table scan.
25021         //
25022         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
25023         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
25024         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
25025         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
25026         // we're safely joined.
25027         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25028             s_fUnpromotedHandles = TRUE;
25029
25030         // Synchronize all the threads so we can read our state variables safely. The following shared
25031         // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
25032         // single thread inside the join.
25033         bgc_t_join.join(this, gc_join_scan_dependent_handles);
25034         if (bgc_t_join.joined())
25035         {
25036             // We're synchronized so it's safe to read our shared state variables. We update another shared
25037             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
25038             // the loop. We scan if there has been at least one object promotion since last time and at least
25039             // one thread has a dependent handle table with a potential handle promotion possible.
25040             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
25041
25042             // Reset our shared state variables (ready to be set again on this scan or with a good initial
25043             // value for the next call if we're terminating the loop).
25044             s_fUnscannedPromotions = FALSE;
25045             s_fUnpromotedHandles = FALSE;
25046
25047             if (!s_fScanRequired)
25048             {
25049                 uint8_t* all_heaps_max = 0;
25050                 uint8_t* all_heaps_min = MAX_PTR;
25051                 int i;
25052                 for (i = 0; i < n_heaps; i++)
25053                 {
25054                     if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25055                         all_heaps_max = g_heaps[i]->background_max_overflow_address;
25056                     if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25057                         all_heaps_min = g_heaps[i]->background_min_overflow_address;
25058                 }
25059                 for (i = 0; i < n_heaps; i++)
25060                 {
25061                     g_heaps[i]->background_max_overflow_address = all_heaps_max;
25062                     g_heaps[i]->background_min_overflow_address = all_heaps_min;
25063                 }
25064             }
25065
25066             // Restart all the workers.
25067             dprintf(2, ("Starting all gc thread mark stack overflow processing"));
25068             bgc_t_join.restart();
25069         }
25070
25071         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25072         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
25073         // global flag indicating that at least one object promotion may have occurred (the usual comment
25074         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
25075         // exit the method since we unconditionally set this variable on method entry anyway).
25076         if (background_process_mark_overflow (sc->concurrent))
25077             s_fUnscannedPromotions = TRUE;
25078
25079         // If we decided that no scan was required we can terminate the loop now.
25080         if (!s_fScanRequired)
25081             break;
25082
25083         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
25084         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
25085         // could miss noting the promotion of some primary objects).
25086         bgc_t_join.join(this, gc_join_rescan_dependent_handles);
25087         if (bgc_t_join.joined())
25088         {
25089             // Restart all the workers.
25090             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
25091             bgc_t_join.restart();
25092         }
25093
25094         // If the portion of the dependent handle table managed by this worker has handles that could still be
25095         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
25096         // could require a rescan of handles on this or other workers.
25097         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25098             if (GCScan::GcDhReScan(sc))
25099                 s_fUnscannedPromotions = TRUE;
25100     }
25101 }
25102 #else
25103 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25104 {
25105     // Whenever we call this method there may have been preceding object promotions. So set
25106     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25107     // based on the how the scanning proceeded).
25108     bool fUnscannedPromotions = true;
25109
25110     // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
25111     // scan without performing any new promotions.
25112     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
25113     {
25114         // On each iteration of the loop start with the assumption that no further objects have been promoted.
25115         fUnscannedPromotions = false;
25116
25117         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25118         // being visible. If there was an overflow (background_process_mark_overflow returned true) then
25119         // additional objects now appear to be promoted and we should set the flag.
25120         if (background_process_mark_overflow (sc->concurrent))
25121             fUnscannedPromotions = true;
25122
25123         // Perform the scan and set the flag if any promotions resulted.
25124         if (GCScan::GcDhReScan (sc))
25125             fUnscannedPromotions = true;
25126     }
25127
25128     // Perform a last processing of any overflowed mark stack.
25129     background_process_mark_overflow (sc->concurrent);
25130 }
25131 #endif //MULTIPLE_HEAPS
25132
25133 void gc_heap::recover_bgc_settings()
25134 {
25135     if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
25136     {
25137         dprintf (2, ("restoring bgc settings"));
25138         settings = saved_bgc_settings;
25139         GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
25140     }
25141 }
25142
25143 void gc_heap::allow_fgc()
25144 {
25145     assert (bgc_thread == GCToEEInterface::GetThread());
25146     bool bToggleGC = false;
25147
25148     if (g_fSuspensionPending > 0)
25149     {
25150         bToggleGC = GCToEEInterface::EnablePreemptiveGC();
25151         if (bToggleGC)
25152         {
25153             GCToEEInterface::DisablePreemptiveGC();
25154         }
25155     }
25156 }
25157
25158 BOOL gc_heap::should_commit_mark_array()
25159 {
25160     return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
25161 }
25162
25163 void gc_heap::clear_commit_flag()
25164 {
25165     generation* gen = generation_of (max_generation);
25166     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25167     while (1)
25168     {
25169         if (seg == 0)
25170         {
25171             if (gen != large_object_generation)
25172             {
25173                 gen = large_object_generation;
25174                 seg = heap_segment_in_range (generation_start_segment (gen));
25175             }
25176             else
25177             {
25178                 break;
25179             }
25180         }
25181
25182         if (seg->flags & heap_segment_flags_ma_committed)
25183         {
25184             seg->flags &= ~heap_segment_flags_ma_committed;
25185         }
25186
25187         if (seg->flags & heap_segment_flags_ma_pcommitted)
25188         {
25189             seg->flags &= ~heap_segment_flags_ma_pcommitted;
25190         }
25191
25192         seg = heap_segment_next (seg);
25193     }
25194 }
25195
25196 void gc_heap::clear_commit_flag_global()
25197 {
25198 #ifdef MULTIPLE_HEAPS
25199     for (int i = 0; i < n_heaps; i++)
25200     {
25201         g_heaps[i]->clear_commit_flag();
25202     }
25203 #else
25204     clear_commit_flag();
25205 #endif //MULTIPLE_HEAPS
25206 }
25207
25208 void gc_heap::verify_mark_array_cleared (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25209 {
25210 #ifdef _DEBUG
25211     size_t  markw = mark_word_of (begin);
25212     size_t  markw_end = mark_word_of (end);
25213
25214     while (markw < markw_end)
25215     {
25216         if (mark_array_addr[markw])
25217         {
25218             dprintf  (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
25219                             markw, mark_array_addr[markw], mark_word_address (markw)));
25220             FATAL_GC_ERROR();
25221         }
25222         markw++;
25223     }
25224 #else // _DEBUG
25225     UNREFERENCED_PARAMETER(begin);
25226     UNREFERENCED_PARAMETER(end);
25227     UNREFERENCED_PARAMETER(mark_array_addr);
25228 #endif //_DEBUG
25229 }
25230
25231 void gc_heap::verify_mark_array_cleared (heap_segment* seg, uint32_t* mark_array_addr)
25232 {
25233     verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
25234 }
25235
25236 BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, 
25237                                          heap_segment* seg,
25238                                          uint32_t* new_card_table,
25239                                          uint8_t* new_lowest_address)
25240 {
25241     UNREFERENCED_PARAMETER(hp); // compiler bug? -- this *is*, indeed, referenced
25242
25243     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25244     uint8_t* end = heap_segment_reserved (seg);
25245
25246     uint8_t* lowest = hp->background_saved_lowest_address;
25247     uint8_t* highest = hp->background_saved_highest_address;
25248
25249     uint8_t* commit_start = NULL;
25250     uint8_t* commit_end = NULL;
25251     size_t commit_flag = 0;
25252
25253     if ((highest >= start) &&
25254         (lowest <= end))
25255     {
25256         if ((start >= lowest) && (end <= highest))
25257         {
25258             dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25259                                     start, end, lowest, highest));
25260             commit_flag = heap_segment_flags_ma_committed;
25261         }
25262         else
25263         {
25264             dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25265                                     start, end, lowest, highest));
25266             commit_flag = heap_segment_flags_ma_pcommitted;
25267         }
25268
25269         commit_start = max (lowest, start);
25270         commit_end = min (highest, end);
25271
25272         if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
25273         {
25274             return FALSE;
25275         }
25276
25277         if (new_card_table == 0)
25278         {
25279             new_card_table = g_gc_card_table;
25280         }
25281
25282         if (hp->card_table != new_card_table)
25283         {
25284             if (new_lowest_address == 0)
25285             {
25286                 new_lowest_address = g_gc_lowest_address;
25287             }
25288
25289             uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))];
25290             uint32_t* ma = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
25291
25292             dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix", 
25293                                     hp->card_table, new_card_table,
25294                                     hp->mark_array, ma));
25295
25296             if (!commit_mark_array_by_range (commit_start, commit_end, ma))
25297             {
25298                 return FALSE;
25299             }
25300         }
25301
25302         seg->flags |= commit_flag;
25303     }
25304
25305     return TRUE;
25306 }
25307
25308 BOOL gc_heap::commit_mark_array_by_range (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25309 {
25310     size_t beg_word = mark_word_of (begin);
25311     size_t end_word = mark_word_of (align_on_mark_word (end));
25312     uint8_t* commit_start = align_lower_page ((uint8_t*)&mark_array_addr[beg_word]);
25313     uint8_t* commit_end = align_on_page ((uint8_t*)&mark_array_addr[end_word]);
25314     size_t size = (size_t)(commit_end - commit_start);
25315
25316 #ifdef SIMPLE_DPRINTF
25317     dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
25318                             begin, end,
25319                             beg_word, end_word,
25320                             (end_word - beg_word) * sizeof (uint32_t),
25321                             &mark_array_addr[beg_word],
25322                             &mark_array_addr[end_word],
25323                             (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
25324                             commit_start, commit_end,
25325                             size));
25326 #endif //SIMPLE_DPRINTF
25327
25328     if (GCToOSInterface::VirtualCommit (commit_start, size))
25329     {
25330         // We can only verify the mark array is cleared from begin to end, the first and the last
25331         // page aren't necessarily all cleared 'cause they could be used by other segments or 
25332         // card bundle.
25333         verify_mark_array_cleared (begin, end, mark_array_addr);
25334         return TRUE;
25335     }
25336     else
25337     {
25338         dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (uint32_t)));
25339         return FALSE;
25340     }
25341 }
25342
25343 BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, uint32_t* new_mark_array_addr)
25344 {
25345     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25346     uint8_t* end = heap_segment_reserved (seg);
25347
25348 #ifdef MULTIPLE_HEAPS
25349     uint8_t* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
25350     uint8_t* highest = heap_segment_heap (seg)->background_saved_highest_address;
25351 #else
25352     uint8_t* lowest = background_saved_lowest_address;
25353     uint8_t* highest = background_saved_highest_address;
25354 #endif //MULTIPLE_HEAPS
25355
25356     if ((highest >= start) &&
25357         (lowest <= end))
25358     {
25359         start = max (lowest, start);
25360         end = min (highest, end);
25361         if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
25362         {
25363             return FALSE;
25364         }
25365     }
25366
25367     return TRUE;
25368 }
25369
25370 BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, uint32_t* mark_array_addr)
25371 {
25372     dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix",
25373         seg,
25374         heap_segment_reserved (seg),
25375         mark_array_addr));
25376     uint8_t* start = (heap_segment_read_only_p (seg) ? heap_segment_mem (seg) : (uint8_t*)seg);
25377
25378     return commit_mark_array_by_range (start, heap_segment_reserved (seg), mark_array_addr);
25379 }
25380
25381 BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr)
25382 {
25383     UNREFERENCED_PARAMETER(mark_array_addr);
25384
25385     dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix", 
25386                             lowest_address, highest_address, mark_array));
25387
25388     generation* gen = generation_of (max_generation);
25389     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25390     while (1)
25391     {
25392         if (seg == 0)
25393         {
25394             if (gen != large_object_generation)
25395             {
25396                 gen = large_object_generation;
25397                 seg = heap_segment_in_range (generation_start_segment (gen));
25398             }
25399             else
25400             {
25401                 break;
25402             }
25403         }
25404
25405         dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
25406
25407         if (!(seg->flags & heap_segment_flags_ma_committed))
25408         {
25409             // For ro segments they could always be only partially in range so we'd
25410             // be calling this at the beginning of every BGC. We are not making this 
25411             // more efficient right now - ro segments are currently only used by redhawk.
25412             if (heap_segment_read_only_p (seg))
25413             {
25414                 if ((heap_segment_mem (seg) >= lowest_address) && 
25415                     (heap_segment_reserved (seg) <= highest_address))
25416                 {
25417                     if (commit_mark_array_by_seg (seg, mark_array))
25418                     {
25419                         seg->flags |= heap_segment_flags_ma_committed;
25420                     }
25421                     else
25422                     {
25423                         return FALSE;
25424                     }
25425                 }
25426                 else
25427                 {
25428                     uint8_t* start = max (lowest_address, heap_segment_mem (seg));
25429                     uint8_t* end = min (highest_address, heap_segment_reserved (seg));
25430                     if (commit_mark_array_by_range (start, end, mark_array))
25431                     {
25432                         seg->flags |= heap_segment_flags_ma_pcommitted;
25433                     }
25434                     else
25435                     {
25436                         return FALSE;
25437                     }
25438                 }
25439             }
25440             else
25441             {
25442                 // For normal segments they are by design completely in range so just 
25443                 // commit the whole mark array for each seg.
25444                 if (commit_mark_array_by_seg (seg, mark_array))
25445                 {
25446                     if (seg->flags & heap_segment_flags_ma_pcommitted)
25447                     {
25448                         seg->flags &= ~heap_segment_flags_ma_pcommitted;
25449                     }
25450                     seg->flags |= heap_segment_flags_ma_committed;
25451                 }
25452                 else
25453                 {
25454                     return FALSE;
25455                 }
25456             }
25457         }
25458
25459         seg = heap_segment_next (seg);
25460     }
25461
25462     return TRUE;
25463 }
25464
25465 // This function doesn't check the commit flag since it's for a new array -
25466 // the mark_array flag for these segments will remain the same.
25467 BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr)
25468 {
25469     dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
25470     generation* gen = generation_of (max_generation);
25471     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25472     while (1)
25473     {
25474         if (seg == 0)
25475         {
25476             if (gen != large_object_generation)
25477             {
25478                 gen = large_object_generation;
25479                 seg = heap_segment_in_range (generation_start_segment (gen));
25480             }
25481             else
25482             {
25483                 break;
25484             }
25485         }
25486
25487         if (!commit_mark_array_with_check (seg, new_mark_array_addr))
25488         {
25489             return FALSE;
25490         }
25491
25492         seg = heap_segment_next (seg);
25493     }
25494
25495 #ifdef MULTIPLE_HEAPS
25496     if (new_heap_segment)
25497     {
25498         if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
25499         {
25500             return FALSE;
25501         }        
25502     }
25503 #endif //MULTIPLE_HEAPS
25504
25505     return TRUE;
25506 }
25507
25508 BOOL gc_heap::commit_new_mark_array_global (uint32_t* new_mark_array)
25509 {
25510 #ifdef MULTIPLE_HEAPS
25511     for (int i = 0; i < n_heaps; i++)
25512     {
25513         if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
25514         {
25515             return FALSE;
25516         }
25517     }
25518 #else
25519     if (!commit_new_mark_array (new_mark_array))
25520     {
25521         return FALSE;
25522     }
25523 #endif //MULTIPLE_HEAPS
25524
25525     return TRUE;
25526 }
25527
25528 void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
25529 {
25530     // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
25531     // been set to NULL. 
25532     if (mark_array == NULL)
25533     {
25534         return;
25535     }
25536
25537     dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
25538
25539     size_t flags = seg->flags;
25540
25541     if ((flags & heap_segment_flags_ma_committed) ||
25542         (flags & heap_segment_flags_ma_pcommitted))
25543     {
25544         uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25545         uint8_t* end = heap_segment_reserved (seg);
25546
25547         if (flags & heap_segment_flags_ma_pcommitted)
25548         {
25549             start = max (lowest_address, start);
25550             end = min (highest_address, end);
25551         }
25552
25553         size_t beg_word = mark_word_of (start);
25554         size_t end_word = mark_word_of (align_on_mark_word (end));
25555         uint8_t* decommit_start = align_on_page ((uint8_t*)&mark_array[beg_word]);
25556         uint8_t* decommit_end = align_lower_page ((uint8_t*)&mark_array[end_word]);
25557         size_t size = (size_t)(decommit_end - decommit_start);
25558
25559 #ifdef SIMPLE_DPRINTF
25560         dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
25561                                 seg,
25562                                 beg_word, end_word,
25563                                 (end_word - beg_word) * sizeof (uint32_t),
25564                                 &mark_array[beg_word],
25565                                 &mark_array[end_word],
25566                                 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
25567                                 decommit_start, decommit_end,
25568                                 size));
25569 #endif //SIMPLE_DPRINTF
25570         
25571         if (decommit_start < decommit_end)
25572         {
25573             if (!GCToOSInterface::VirtualDecommit (decommit_start, size))
25574             {
25575                 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualDecommit on %Ix for %Id bytes failed", 
25576                                         decommit_start, size));
25577                 assert (!"decommit failed");
25578             }
25579         }
25580
25581         dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
25582     }
25583 }
25584
25585 void gc_heap::background_mark_phase ()
25586 {
25587     verify_mark_array_cleared();
25588
25589     ScanContext sc;
25590     sc.thread_number = heap_number;
25591     sc.promotion = TRUE;
25592     sc.concurrent = FALSE;
25593
25594     THREAD_FROM_HEAP;
25595     BOOL cooperative_mode = TRUE;
25596 #ifndef MULTIPLE_HEAPS
25597     const int thread = heap_number;
25598 #endif //!MULTIPLE_HEAPS
25599
25600     dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
25601
25602     assert (settings.concurrent);
25603
25604 #ifdef TIME_GC
25605     unsigned start;
25606     unsigned finish;
25607     start = GetCycleCount32();
25608 #endif //TIME_GC
25609
25610 #ifdef FFIND_OBJECT
25611     if (gen0_must_clear_bricks > 0)
25612         gen0_must_clear_bricks--;
25613 #endif //FFIND_OBJECT
25614
25615     background_soh_alloc_count = 0;
25616     background_loh_alloc_count = 0;
25617     bgc_overflow_count = 0;
25618
25619     bpromoted_bytes (heap_number) = 0;
25620     static uint32_t num_sizedrefs = 0;
25621
25622     background_min_overflow_address = MAX_PTR;
25623     background_max_overflow_address = 0;
25624     background_min_soh_overflow_address = MAX_PTR;
25625     background_max_soh_overflow_address = 0;
25626     processed_soh_overflow_p = FALSE;
25627
25628     {
25629         //set up the mark lists from g_mark_list
25630         assert (g_mark_list);
25631         mark_list = g_mark_list;
25632         //dont use the mark list for full gc
25633         //because multiple segments are more complex to handle and the list
25634         //is likely to overflow
25635         mark_list_end = &mark_list [0];
25636         mark_list_index = &mark_list [0];
25637
25638         c_mark_list_index = 0;
25639
25640         shigh = (uint8_t*) 0;
25641         slow  = MAX_PTR;
25642
25643         generation*   gen = generation_of (max_generation);
25644
25645         dprintf(3,("BGC: stack marking"));
25646         sc.concurrent = TRUE;
25647
25648         GCScan::GcScanRoots(background_promote_callback,
25649                                 max_generation, max_generation,
25650                                 &sc);
25651     }
25652
25653     {
25654         dprintf(3,("BGC: finalization marking"));
25655         finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
25656     }
25657
25658     size_t total_loh_size = generation_size (max_generation + 1);
25659     bgc_begin_loh_size = total_loh_size;
25660     bgc_alloc_spin_loh = 0;
25661     bgc_loh_size_increased = 0;
25662     bgc_loh_allocated_in_free = 0;
25663     size_t total_soh_size = generation_sizes (generation_of (max_generation));
25664
25665     dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25666
25667     {
25668         //concurrent_print_time_delta ("copying stack roots");
25669         concurrent_print_time_delta ("CS");
25670
25671         FIRE_EVENT(BGC1stNonConEnd);
25672
25673         expanded_in_fgc = FALSE;
25674         saved_overflow_ephemeral_seg = 0;
25675         current_bgc_state = bgc_reset_ww;
25676
25677         // we don't need a join here - just whichever thread that gets here
25678         // first can change the states and call restart_vm.
25679         // this is not true - we can't let the EE run when we are scanning stack.
25680         // since we now allow reset ww to run concurrently and have a join for it,
25681         // we can do restart ee on the 1st thread that got here. Make sure we handle the 
25682         // sizedref handles correctly.
25683 #ifdef MULTIPLE_HEAPS
25684         bgc_t_join.join(this, gc_join_restart_ee);
25685         if (bgc_t_join.joined())
25686 #endif //MULTIPLE_HEAPS
25687         {
25688 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25689             // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset
25690             // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while
25691             // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below.
25692 #ifdef WRITE_WATCH
25693             concurrent_print_time_delta ("CRWW begin");
25694
25695 #ifdef MULTIPLE_HEAPS
25696             for (int i = 0; i < n_heaps; i++)
25697             {
25698                 g_heaps[i]->reset_write_watch (FALSE);
25699             }
25700 #else
25701             reset_write_watch (FALSE);
25702 #endif //MULTIPLE_HEAPS
25703
25704             concurrent_print_time_delta ("CRWW");
25705 #endif //WRITE_WATCH
25706 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25707
25708             num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
25709
25710             // this c_write is not really necessary because restart_vm
25711             // has an instruction that will flush the cpu cache (interlocked
25712             // or whatever) but we don't want to rely on that.
25713             dprintf (BGC_LOG, ("setting cm_in_progress"));
25714             c_write (cm_in_progress, TRUE);
25715
25716             //restart all thread, doing the marking from the array
25717             assert (dont_restart_ee_p);
25718             dont_restart_ee_p = FALSE;
25719
25720             restart_vm();
25721             GCToOSInterface::YieldThread (0);
25722 #ifdef MULTIPLE_HEAPS
25723             dprintf(3, ("Starting all gc threads for gc"));
25724             bgc_t_join.restart();
25725 #endif //MULTIPLE_HEAPS
25726         }
25727
25728 #ifdef MULTIPLE_HEAPS
25729         bgc_t_join.join(this, gc_join_after_reset);
25730         if (bgc_t_join.joined())
25731 #endif //MULTIPLE_HEAPS
25732         {
25733             disable_preemptive (true);
25734
25735 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25736             // When software write watch is enabled, resetting write watch is done while the runtime is suspended above. The
25737             // post-reset call to revisit_written_pages is only necessary for concurrent reset_write_watch, to discard dirtied
25738             // pages during the concurrent reset.
25739
25740 #ifdef WRITE_WATCH
25741             concurrent_print_time_delta ("CRWW begin");
25742
25743 #ifdef MULTIPLE_HEAPS
25744             for (int i = 0; i < n_heaps; i++)
25745             {
25746                 g_heaps[i]->reset_write_watch (TRUE);
25747             }
25748 #else
25749             reset_write_watch (TRUE);
25750 #endif //MULTIPLE_HEAPS
25751
25752             concurrent_print_time_delta ("CRWW");
25753 #endif //WRITE_WATCH
25754
25755 #ifdef MULTIPLE_HEAPS
25756             for (int i = 0; i < n_heaps; i++)
25757             {
25758                 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
25759             }
25760 #else
25761             revisit_written_pages (TRUE, TRUE);
25762 #endif //MULTIPLE_HEAPS
25763
25764             concurrent_print_time_delta ("CRW");
25765 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25766
25767 #ifdef MULTIPLE_HEAPS
25768             for (int i = 0; i < n_heaps; i++)
25769             {
25770                 g_heaps[i]->current_bgc_state = bgc_mark_handles;
25771             }
25772 #else
25773             current_bgc_state = bgc_mark_handles;
25774 #endif //MULTIPLE_HEAPS
25775
25776             current_c_gc_state = c_gc_state_marking;
25777
25778             enable_preemptive ();
25779
25780 #ifdef MULTIPLE_HEAPS
25781             dprintf(3, ("Joining BGC threads after resetting writewatch"));
25782             bgc_t_join.restart();
25783 #endif //MULTIPLE_HEAPS
25784         }
25785
25786         disable_preemptive (true);
25787
25788         if (num_sizedrefs > 0)
25789         {
25790             GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
25791
25792             enable_preemptive ();
25793
25794 #ifdef MULTIPLE_HEAPS
25795             bgc_t_join.join(this, gc_join_scan_sizedref_done);
25796             if (bgc_t_join.joined())
25797             {
25798                 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
25799                 bgc_t_join.restart();
25800             }
25801 #endif //MULTIPLE_HEAPS
25802
25803             disable_preemptive (true);
25804         }
25805
25806         dprintf (3,("BGC: handle table marking"));
25807         GCScan::GcScanHandles(background_promote,
25808                                   max_generation, max_generation,
25809                                   &sc);
25810         //concurrent_print_time_delta ("concurrent marking handle table");
25811         concurrent_print_time_delta ("CRH");
25812
25813         current_bgc_state = bgc_mark_stack;
25814         dprintf (2,("concurrent draining mark list"));
25815         background_drain_mark_list (thread);
25816         //concurrent_print_time_delta ("concurrent marking stack roots");
25817         concurrent_print_time_delta ("CRS");
25818
25819         dprintf (2,("concurrent revisiting dirtied pages"));
25820         revisit_written_pages (TRUE);
25821         revisit_written_pages (TRUE);
25822         //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
25823         concurrent_print_time_delta ("CRre");
25824
25825         enable_preemptive ();
25826
25827 #ifdef MULTIPLE_HEAPS
25828         bgc_t_join.join(this, gc_join_concurrent_overflow);
25829         if (bgc_t_join.joined())
25830         {
25831             uint8_t* all_heaps_max = 0;
25832             uint8_t* all_heaps_min = MAX_PTR;
25833             int i;
25834             for (i = 0; i < n_heaps; i++)
25835             {
25836                 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix", 
25837                     i,
25838                     g_heaps[i]->background_max_overflow_address,
25839                     g_heaps[i]->background_min_overflow_address));
25840                 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25841                     all_heaps_max = g_heaps[i]->background_max_overflow_address;
25842                 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25843                     all_heaps_min = g_heaps[i]->background_min_overflow_address;
25844             }
25845             for (i = 0; i < n_heaps; i++)
25846             {
25847                 g_heaps[i]->background_max_overflow_address = all_heaps_max;
25848                 g_heaps[i]->background_min_overflow_address = all_heaps_min;
25849             }
25850             dprintf(3, ("Starting all bgc threads after updating the overflow info"));
25851             bgc_t_join.restart();
25852         }
25853 #endif //MULTIPLE_HEAPS
25854
25855         disable_preemptive (true);
25856
25857         dprintf (2, ("before CRov count: %d", bgc_overflow_count));
25858         bgc_overflow_count = 0;
25859         background_process_mark_overflow (TRUE);
25860         dprintf (2, ("after CRov count: %d", bgc_overflow_count));
25861         bgc_overflow_count = 0;
25862         //concurrent_print_time_delta ("concurrent processing mark overflow");
25863         concurrent_print_time_delta ("CRov");
25864
25865         // Stop all threads, crawl all stacks and revisit changed pages.
25866         FIRE_EVENT(BGC1stConEnd);
25867
25868         dprintf (2, ("Stopping the EE"));
25869
25870         enable_preemptive ();
25871
25872 #ifdef MULTIPLE_HEAPS
25873         bgc_t_join.join(this, gc_join_suspend_ee);
25874         if (bgc_t_join.joined())
25875         {
25876             bgc_threads_sync_event.Reset();
25877
25878             dprintf(3, ("Joining BGC threads for non concurrent final marking"));
25879             bgc_t_join.restart();
25880         }
25881 #endif //MULTIPLE_HEAPS
25882
25883         if (heap_number == 0)
25884         {
25885             enter_spin_lock (&gc_lock);
25886
25887             bgc_suspend_EE ();
25888             //suspend_EE ();
25889             bgc_threads_sync_event.Set();
25890         }
25891         else
25892         {
25893             bgc_threads_sync_event.Wait(INFINITE, FALSE);
25894             dprintf (2, ("bgc_threads_sync_event is signalled"));
25895         }
25896
25897         assert (settings.concurrent);
25898         assert (settings.condemned_generation == max_generation);
25899
25900         dprintf (2, ("clearing cm_in_progress"));
25901         c_write (cm_in_progress, FALSE);
25902
25903         bgc_alloc_lock->check();
25904
25905         current_bgc_state = bgc_final_marking;
25906
25907         //concurrent_print_time_delta ("concurrent marking ended");
25908         concurrent_print_time_delta ("CR");
25909
25910         FIRE_EVENT(BGC2ndNonConBegin);
25911
25912         mark_absorb_new_alloc();
25913
25914         // We need a join here 'cause find_object would complain if the gen0
25915         // bricks of another heap haven't been fixed up. So we need to make sure
25916         // that every heap's gen0 bricks are fixed up before we proceed.
25917 #ifdef MULTIPLE_HEAPS
25918         bgc_t_join.join(this, gc_join_after_absorb);
25919         if (bgc_t_join.joined())
25920         {
25921             dprintf(3, ("Joining BGC threads after absorb"));
25922             bgc_t_join.restart();
25923         }
25924 #endif //MULTIPLE_HEAPS
25925
25926         // give VM a chance to do work
25927         GCToEEInterface::GcBeforeBGCSweepWork();
25928
25929         //reset the flag, indicating that the EE no longer expect concurrent
25930         //marking
25931         sc.concurrent = FALSE;
25932
25933         total_loh_size = generation_size (max_generation + 1);
25934         total_soh_size = generation_sizes (generation_of (max_generation));
25935
25936         dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25937
25938         dprintf (2, ("nonconcurrent marking stack roots"));
25939         GCScan::GcScanRoots(background_promote,
25940                                 max_generation, max_generation,
25941                                 &sc);
25942         //concurrent_print_time_delta ("nonconcurrent marking stack roots");
25943         concurrent_print_time_delta ("NRS");
25944
25945 //        finalize_queue->EnterFinalizeLock();
25946         finalize_queue->GcScanRoots(background_promote, heap_number, 0);
25947 //        finalize_queue->LeaveFinalizeLock();
25948
25949         dprintf (2, ("nonconcurrent marking handle table"));
25950         GCScan::GcScanHandles(background_promote,
25951                                   max_generation, max_generation,
25952                                   &sc);
25953         //concurrent_print_time_delta ("nonconcurrent marking handle table");
25954         concurrent_print_time_delta ("NRH");
25955
25956         dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
25957         revisit_written_pages (FALSE);
25958         //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
25959         concurrent_print_time_delta ("NRre LOH");
25960
25961 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25962 #ifdef MULTIPLE_HEAPS
25963         bgc_t_join.join(this, gc_join_disable_software_write_watch);
25964         if (bgc_t_join.joined())
25965 #endif // MULTIPLE_HEAPS
25966         {
25967             // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to
25968             // avoid further perf penalty after the runtime is restarted
25969             SoftwareWriteWatch::DisableForGCHeap();
25970
25971 #ifdef MULTIPLE_HEAPS
25972             dprintf(3, ("Restarting BGC threads after disabling software write watch"));
25973             bgc_t_join.restart();
25974 #endif // MULTIPLE_HEAPS
25975         }
25976 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25977
25978         dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
25979         bgc_overflow_count = 0;
25980
25981         // Dependent handles need to be scanned with a special algorithm (see the header comment on
25982         // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
25983         // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
25984         // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
25985         // The call to background_scan_dependent_handles is what will cycle through more iterations if
25986         // required and will also perform processing of any mark stack overflow once the dependent handle
25987         // table has been fully promoted.
25988         dprintf (2, ("1st dependent handle scan and process mark overflow"));
25989         GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
25990         background_scan_dependent_handles (&sc);
25991         //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
25992         concurrent_print_time_delta ("NR 1st Hov");
25993
25994         dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
25995         bgc_overflow_count = 0;
25996
25997 #ifdef MULTIPLE_HEAPS
25998         bgc_t_join.join(this, gc_join_null_dead_short_weak);
25999         if (bgc_t_join.joined())
26000 #endif //MULTIPLE_HEAPS
26001         {
26002             GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
26003
26004 #ifdef MULTIPLE_HEAPS
26005             dprintf(3, ("Joining BGC threads for short weak handle scan"));
26006             bgc_t_join.restart();
26007 #endif //MULTIPLE_HEAPS
26008         }
26009
26010         // null out the target of short weakref that were not promoted.
26011         GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
26012
26013         //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
26014         concurrent_print_time_delta ("NR GcShortWeakPtrScan");
26015     }
26016
26017     {
26018 #ifdef MULTIPLE_HEAPS
26019         bgc_t_join.join(this, gc_join_scan_finalization);
26020         if (bgc_t_join.joined())
26021         {
26022             dprintf(3, ("Joining BGC threads for finalization"));
26023             bgc_t_join.restart();
26024         }
26025 #endif //MULTIPLE_HEAPS
26026
26027         //Handle finalization.
26028         dprintf(3,("Marking finalization data"));
26029         //concurrent_print_time_delta ("bgc joined to mark finalization");
26030         concurrent_print_time_delta ("NRj");
26031
26032 //        finalize_queue->EnterFinalizeLock();
26033         finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
26034 //        finalize_queue->LeaveFinalizeLock();
26035
26036         concurrent_print_time_delta ("NRF");
26037     }
26038
26039     dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
26040     bgc_overflow_count = 0;
26041
26042     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
26043     // for finalization. As before background_scan_dependent_handles will also process any mark stack
26044     // overflow.
26045     dprintf (2, ("2nd dependent handle scan and process mark overflow"));
26046     background_scan_dependent_handles (&sc);
26047     //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
26048     concurrent_print_time_delta ("NR 2nd Hov");
26049
26050 #ifdef MULTIPLE_HEAPS
26051     bgc_t_join.join(this, gc_join_null_dead_long_weak);
26052     if (bgc_t_join.joined())
26053     {
26054         dprintf(2, ("Joining BGC threads for weak pointer deletion"));
26055         bgc_t_join.restart();
26056     }
26057 #endif //MULTIPLE_HEAPS
26058
26059     // null out the target of long weakref that were not promoted.
26060     GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
26061     concurrent_print_time_delta ("NR GcWeakPtrScan");
26062
26063 #ifdef MULTIPLE_HEAPS
26064     bgc_t_join.join(this, gc_join_null_dead_syncblk);
26065     if (bgc_t_join.joined())
26066 #endif //MULTIPLE_HEAPS
26067     {
26068         dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
26069         // scan for deleted entries in the syncblk cache
26070         GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
26071         concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
26072 #ifdef MULTIPLE_HEAPS
26073         dprintf(2, ("Starting BGC threads for end of background mark phase"));
26074         bgc_t_join.restart();
26075 #endif //MULTIPLE_HEAPS
26076     }
26077
26078     gen0_bricks_cleared = FALSE;
26079
26080     dprintf (2, ("end of bgc mark: loh: %d, soh: %d", 
26081                  generation_size (max_generation + 1), 
26082                  generation_sizes (generation_of (max_generation))));
26083
26084     for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
26085     {
26086         generation* gen = generation_of (gen_idx);
26087         dynamic_data* dd = dynamic_data_of (gen_idx);
26088         dd_begin_data_size (dd) = generation_size (gen_idx) - 
26089                                    (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
26090                                    Align (size (generation_allocation_start (gen)));
26091         dd_survived_size (dd) = 0;
26092         dd_pinned_survived_size (dd) = 0;
26093         dd_artificial_pinned_survived_size (dd) = 0;
26094         dd_added_pinned_size (dd) = 0;
26095     }
26096
26097     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26098     PREFIX_ASSUME(seg != NULL);
26099
26100     while (seg)
26101     {
26102         seg->flags &= ~heap_segment_flags_swept;
26103
26104         if (heap_segment_allocated (seg) == heap_segment_mem (seg))
26105         {
26106             // This can't happen...
26107             FATAL_GC_ERROR();
26108         }
26109
26110         if (seg == ephemeral_heap_segment)
26111         {
26112             heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
26113         }
26114         else
26115         {
26116             heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
26117         }
26118
26119         dprintf (2, ("seg %Ix background allocated is %Ix", 
26120                       heap_segment_mem (seg), 
26121                       heap_segment_background_allocated (seg)));
26122         seg = heap_segment_next_rw (seg);
26123     }
26124
26125     // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
26126     // we can't let the user code consume the left over parts in these alloc contexts.
26127     repair_allocation_contexts (FALSE);
26128
26129 #ifdef TIME_GC
26130         finish = GetCycleCount32();
26131         mark_time = finish - start;
26132 #endif //TIME_GC
26133
26134     dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d", 
26135         generation_free_list_space (generation_of (max_generation)), 
26136         generation_free_obj_space (generation_of (max_generation))));
26137
26138     dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
26139 }
26140
26141 void
26142 gc_heap::suspend_EE ()
26143 {
26144     dprintf (2, ("suspend_EE"));
26145 #ifdef MULTIPLE_HEAPS
26146     gc_heap* hp = gc_heap::g_heaps[0];
26147     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26148 #else
26149     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26150 #endif //MULTIPLE_HEAPS
26151 }
26152
26153 #ifdef MULTIPLE_HEAPS
26154 void
26155 gc_heap::bgc_suspend_EE ()
26156 {
26157     for (int i = 0; i < n_heaps; i++)
26158     {
26159         gc_heap::g_heaps[i]->reset_gc_done();
26160     }
26161     gc_started = TRUE;
26162     dprintf (2, ("bgc_suspend_EE"));
26163     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26164
26165     gc_started = FALSE;
26166     for (int i = 0; i < n_heaps; i++)
26167     {
26168         gc_heap::g_heaps[i]->set_gc_done();
26169     }
26170 }
26171 #else
26172 void
26173 gc_heap::bgc_suspend_EE ()
26174 {
26175     reset_gc_done();
26176     gc_started = TRUE;
26177     dprintf (2, ("bgc_suspend_EE"));
26178     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26179     gc_started = FALSE;
26180     set_gc_done();
26181 }
26182 #endif //MULTIPLE_HEAPS
26183
26184 void
26185 gc_heap::restart_EE ()
26186 {
26187     dprintf (2, ("restart_EE"));
26188 #ifdef MULTIPLE_HEAPS
26189     GCToEEInterface::RestartEE(FALSE);
26190 #else
26191     GCToEEInterface::RestartEE(FALSE);
26192 #endif //MULTIPLE_HEAPS
26193 }
26194
26195 inline uint8_t* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
26196 {
26197     if (concurrent_p)
26198     {
26199         uint8_t* end = ((seg == ephemeral_heap_segment) ?
26200                      generation_allocation_start (generation_of (max_generation-1)) :
26201                      heap_segment_allocated (seg));
26202         return align_lower_page (end);
26203     }
26204     else 
26205     {
26206         return heap_segment_allocated (seg);
26207     }
26208 }
26209
26210 void gc_heap::revisit_written_page (uint8_t* page,
26211                                     uint8_t* end,
26212                                     BOOL concurrent_p,
26213                                     heap_segment* seg,
26214                                     uint8_t*& last_page,
26215                                     uint8_t*& last_object,
26216                                     BOOL large_objects_p,
26217                                     size_t& num_marked_objects)
26218 {
26219     UNREFERENCED_PARAMETER(seg);
26220
26221     uint8_t*   start_address = page;
26222     uint8_t*   o             = 0;
26223     int align_const = get_alignment_constant (!large_objects_p);
26224     uint8_t* high_address = end;
26225     uint8_t* current_lowest_address = background_saved_lowest_address;
26226     uint8_t* current_highest_address = background_saved_highest_address;
26227     BOOL no_more_loop_p = FALSE;
26228
26229     THREAD_FROM_HEAP;
26230 #ifndef MULTIPLE_HEAPS
26231     const int thread = heap_number;
26232 #endif //!MULTIPLE_HEAPS
26233
26234     if (large_objects_p)
26235     {
26236         o = last_object;
26237     }
26238     else
26239     {
26240         if (((last_page + WRITE_WATCH_UNIT_SIZE) == page)
26241             || (start_address <= last_object))
26242         {
26243             o = last_object;
26244         }
26245         else
26246         {
26247             o = find_first_object (start_address, last_object);
26248             // We can visit the same object again, but on a different page.
26249             assert (o >= last_object);
26250         }
26251     }
26252
26253     dprintf (3,("page %Ix start: %Ix, %Ix[ ",
26254                (size_t)page, (size_t)o,
26255                (size_t)(min (high_address, page + WRITE_WATCH_UNIT_SIZE))));
26256
26257     while (o < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26258     {
26259         size_t s;
26260
26261         if (concurrent_p && large_objects_p)
26262         {
26263             bgc_alloc_lock->bgc_mark_set (o);
26264
26265             if (((CObjectHeader*)o)->IsFree())
26266             {
26267                 s = unused_array_size (o);
26268             }
26269             else
26270             {
26271                 s = size (o);
26272             }
26273         }
26274         else
26275         {
26276             s = size (o);
26277         }
26278
26279         dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
26280
26281         assert (Align (s) >= Align (min_obj_size));
26282
26283         uint8_t* next_o =  o + Align (s, align_const);
26284
26285         if (next_o >= start_address) 
26286         {
26287 #ifdef MULTIPLE_HEAPS
26288             if (concurrent_p)
26289             {
26290                 // We set last_object here for SVR BGC here because SVR BGC has more than 
26291                 // one GC thread. When we have more than one GC thread we would run into this 
26292                 // situation if we skipped unmarked objects:
26293                 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it 
26294                 // for revisit. 
26295                 // bgc thread 2 marks X and all its current children.
26296                 // user thread comes along and dirties more (and later) pages in X.
26297                 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
26298                 // on them because it had already skipped X. We need to detect that this object is now
26299                 // marked and mark the children on the dirtied pages.
26300                 // In the future if we have less BGC threads than we have heaps we should add
26301                 // the check to the number of BGC threads.
26302                 last_object = o;
26303             }
26304 #endif //MULTIPLE_HEAPS
26305
26306             if (contain_pointers (o) &&
26307                 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
26308                 background_marked (o)))
26309             {
26310                 dprintf (3, ("going through %Ix", (size_t)o));
26311                 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
26312                                     if ((uint8_t*)poo >= min (high_address, page + WRITE_WATCH_UNIT_SIZE))
26313                                     {
26314                                         no_more_loop_p = TRUE;
26315                                         goto end_limit;
26316                                     }
26317                                     uint8_t* oo = *poo;
26318
26319                                     num_marked_objects++;
26320                                     background_mark_object (oo THREAD_NUMBER_ARG);
26321                                 );
26322             }
26323             else if (
26324                 concurrent_p &&
26325 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // see comment below
26326                 large_objects_p &&
26327 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26328                 ((CObjectHeader*)o)->IsFree() &&
26329                 (next_o > min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26330             {
26331                 // We need to not skip the object here because of this corner scenario:
26332                 // A large object was being allocated during BGC mark so we first made it 
26333                 // into a free object, then cleared its memory. In this loop we would detect
26334                 // that it's a free object which normally we would skip. But by the next time
26335                 // we call GetWriteWatch we could still be on this object and the object had
26336                 // been made into a valid object and some of its memory was changed. We need
26337                 // to be sure to process those written pages so we can't skip the object just
26338                 // yet.
26339                 //
26340                 // Similarly, when using software write watch, don't advance last_object when
26341                 // the current object is a free object that spans beyond the current page or
26342                 // high_address. Software write watch acquires gc_lock before the concurrent
26343                 // GetWriteWatch() call during revisit_written_pages(). A foreground GC may
26344                 // happen at that point and allocate from this free region, so when
26345                 // revisit_written_pages() continues, it cannot skip now-valid objects in this
26346                 // region.
26347                 no_more_loop_p = TRUE;
26348                 goto end_limit;                
26349             }
26350         }
26351 end_limit:
26352         if (concurrent_p && large_objects_p)
26353         {
26354             bgc_alloc_lock->bgc_mark_done ();
26355         }
26356         if (no_more_loop_p)
26357         {
26358             break;
26359         }
26360         o = next_o;
26361     }
26362
26363 #ifdef MULTIPLE_HEAPS
26364     if (concurrent_p)
26365     {
26366         assert (last_object < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)));
26367     }
26368     else
26369 #endif //MULTIPLE_HEAPS
26370     {
26371         last_object = o;
26372     }
26373
26374     dprintf (3,("Last object: %Ix", (size_t)last_object));
26375     last_page = align_write_watch_lower_page (o);
26376 }
26377
26378 // When reset_only_p is TRUE, we should only reset pages that are in range
26379 // because we need to consider the segments or part of segments that were
26380 // allocated out of range all live.
26381 void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
26382 {
26383 #ifdef WRITE_WATCH
26384     if (concurrent_p && !reset_only_p)
26385     {
26386         current_bgc_state = bgc_revisit_soh;
26387     }
26388
26389     size_t total_dirtied_pages = 0;
26390     size_t total_marked_objects = 0;
26391
26392     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26393
26394     PREFIX_ASSUME(seg != NULL);
26395
26396     bool reset_watch_state = !!concurrent_p;
26397     bool is_runtime_suspended = !concurrent_p;
26398     BOOL small_object_segments = TRUE;
26399     int align_const = get_alignment_constant (small_object_segments);
26400
26401     while (1)
26402     {
26403         if (seg == 0)
26404         {
26405             if (small_object_segments)
26406             {
26407                 //switch to large segment
26408                 if (concurrent_p && !reset_only_p)
26409                 {
26410                     current_bgc_state = bgc_revisit_loh;
26411                 }
26412
26413                 if (!reset_only_p)
26414                 {
26415                     dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26416                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26417                     concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
26418                     total_dirtied_pages = 0;
26419                     total_marked_objects = 0;
26420                 }
26421
26422                 small_object_segments = FALSE;
26423                 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
26424
26425                 dprintf (3, ("now revisiting large object segments"));
26426                 align_const = get_alignment_constant (small_object_segments);
26427                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
26428
26429                 PREFIX_ASSUME(seg != NULL);
26430
26431                 continue;
26432             }
26433             else
26434             {
26435                 if (reset_only_p)
26436                 {
26437                     dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
26438                 } 
26439                 else
26440                 {
26441                     dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26442                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26443                 }
26444                 break;
26445             }
26446         }
26447         uint8_t* base_address = (uint8_t*)heap_segment_mem (seg);
26448         //we need to truncate to the base of the page because
26449         //some newly allocated could exist beyond heap_segment_allocated
26450         //and if we reset the last page write watch status,
26451         // they wouldn't be guaranteed to be visited -> gc hole.
26452         uintptr_t bcount = array_size;
26453         uint8_t* last_page = 0;
26454         uint8_t* last_object = heap_segment_mem (seg);
26455         uint8_t* high_address = 0;
26456
26457         BOOL skip_seg_p = FALSE;
26458
26459         if (reset_only_p)
26460         {
26461             if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
26462                 (heap_segment_reserved (seg) <= background_saved_highest_address))
26463             {
26464                 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number, 
26465                     heap_segment_mem (seg), heap_segment_reserved (seg)));
26466                 skip_seg_p = TRUE;
26467             }
26468         }
26469
26470         if (!skip_seg_p)
26471         {
26472             dprintf (3, ("looking at seg %Ix", (size_t)last_object));
26473
26474             if (reset_only_p)
26475             {
26476                 base_address = max (base_address, background_saved_lowest_address);
26477                 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
26478             }
26479
26480             dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address, 
26481                 heap_segment_mem (seg), heap_segment_reserved (seg)));
26482
26483
26484             while (1)
26485             {
26486                 if (reset_only_p)
26487                 {
26488                     high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
26489                     high_address = min (high_address, background_saved_highest_address);
26490                 }
26491                 else
26492                 {
26493                     high_address = high_page (seg, concurrent_p);
26494                 }
26495
26496                 if ((base_address < high_address) &&
26497                     (bcount >= array_size))
26498                 {
26499                     ptrdiff_t region_size = high_address - base_address;
26500                     dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
26501
26502 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26503                     // When the runtime is not suspended, it's possible for the table to be resized concurrently with the scan
26504                     // for dirty pages below. Prevent that by synchronizing with grow_brick_card_tables(). When the runtime is
26505                     // suspended, it's ok to scan for dirty pages concurrently from multiple background GC threads for disjoint
26506                     // memory regions.
26507                     if (!is_runtime_suspended)
26508                     {
26509                         enter_spin_lock(&gc_lock);
26510                     }
26511 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26512
26513                     get_write_watch_for_gc_heap (reset_watch_state, base_address, region_size,
26514                                                  (void**)background_written_addresses,
26515                                                  &bcount, is_runtime_suspended);
26516
26517 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26518                     if (!is_runtime_suspended)
26519                     {
26520                         leave_spin_lock(&gc_lock);
26521                     }
26522 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26523
26524                     if (bcount != 0)
26525                     {
26526                         total_dirtied_pages += bcount;
26527
26528                         dprintf (3, ("Found %d pages [%Ix, %Ix[", 
26529                                         bcount, (size_t)base_address, (size_t)high_address));
26530                     }
26531
26532                     if (!reset_only_p)
26533                     {
26534                         for (unsigned i = 0; i < bcount; i++)
26535                         {
26536                             uint8_t* page = (uint8_t*)background_written_addresses[i];
26537                             dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i, 
26538                                 (size_t)page, (size_t)high_address));
26539                             if (page < high_address)
26540                             {
26541                                 //search for marked objects in the page
26542                                 revisit_written_page (page, high_address, concurrent_p,
26543                                                     seg, last_page, last_object,
26544                                                     !small_object_segments,
26545                                                     total_marked_objects);
26546                             }
26547                             else
26548                             {
26549                                 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
26550                                 assert (!"page shouldn't have exceeded limit");
26551                             }
26552                         }
26553                     }
26554
26555                     if (bcount >= array_size){
26556                         base_address = background_written_addresses [array_size-1] + WRITE_WATCH_UNIT_SIZE;
26557                         bcount = array_size;
26558                     }
26559                 }
26560                 else
26561                 {
26562                     break;
26563                 }
26564             }
26565         }
26566
26567         seg = heap_segment_next_rw (seg);
26568     }
26569
26570 #endif //WRITE_WATCH
26571 }
26572
26573 void gc_heap::background_grow_c_mark_list()
26574 {
26575     assert (c_mark_list_index >= c_mark_list_length);
26576     BOOL should_drain_p = FALSE;
26577     THREAD_FROM_HEAP;
26578 #ifndef MULTIPLE_HEAPS
26579     const int thread = heap_number;
26580 #endif //!MULTIPLE_HEAPS
26581
26582     dprintf (2, ("stack copy buffer overflow"));
26583     uint8_t** new_c_mark_list = 0;
26584     {
26585         FAULT_NOT_FATAL();
26586         if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (uint8_t*))))
26587         {
26588             should_drain_p = TRUE;
26589         }
26590         else
26591         {
26592             new_c_mark_list = new (nothrow) uint8_t*[c_mark_list_length*2];
26593             if (new_c_mark_list == 0)
26594             {
26595                 should_drain_p = TRUE;
26596             }
26597         }
26598     }
26599     if (should_drain_p)
26600
26601     {
26602         dprintf (2, ("No more memory for the stacks copy, draining.."));
26603         //drain the list by marking its elements
26604         background_drain_mark_list (thread);
26605     }
26606     else
26607     {
26608         assert (new_c_mark_list);
26609         memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(uint8_t*));
26610         c_mark_list_length = c_mark_list_length*2;
26611         delete c_mark_list;
26612         c_mark_list = new_c_mark_list;
26613     }
26614 }
26615
26616 void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
26617                                   uint32_t flags)
26618 {
26619     UNREFERENCED_PARAMETER(sc);
26620     //in order to save space on the array, mark the object,
26621     //knowing that it will be visited later
26622     assert (settings.concurrent);
26623
26624     THREAD_NUMBER_FROM_CONTEXT;
26625 #ifndef MULTIPLE_HEAPS
26626     const int thread = 0;
26627 #endif //!MULTIPLE_HEAPS
26628
26629     uint8_t* o = (uint8_t*)*ppObject;
26630
26631     if (o == 0)
26632         return;
26633
26634     HEAP_FROM_THREAD;
26635
26636     gc_heap* hp = gc_heap::heap_of (o);
26637
26638     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
26639     {
26640         return;
26641     }
26642
26643 #ifdef INTERIOR_POINTERS
26644     if (flags & GC_CALL_INTERIOR)
26645     {
26646         o = hp->find_object (o, hp->background_saved_lowest_address);
26647         if (o == 0)
26648             return;
26649     }
26650 #endif //INTERIOR_POINTERS
26651
26652 #ifdef FEATURE_CONSERVATIVE_GC
26653     // For conservative GC, a value on stack may point to middle of a free object.
26654     // In this case, we don't need to promote the pointer.
26655     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
26656     {
26657         return;
26658     }
26659 #endif //FEATURE_CONSERVATIVE_GC
26660
26661 #ifdef _DEBUG
26662     ((CObjectHeader*)o)->Validate();
26663 #endif //_DEBUG
26664
26665     dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
26666     if (o && (size (o) > LARGE_OBJECT_SIZE))
26667     {
26668         dprintf (3, ("Brc %Ix", (size_t)o));
26669     }
26670
26671     if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
26672     {
26673         hpt->background_grow_c_mark_list();
26674     }
26675     dprintf (3, ("pushing %08x into mark_list", (size_t)o));
26676     hpt->c_mark_list [hpt->c_mark_list_index++] = o;
26677
26678     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);
26679 }
26680
26681 void gc_heap::mark_absorb_new_alloc()
26682 {
26683     fix_allocation_contexts (FALSE);
26684     
26685     gen0_bricks_cleared = FALSE;
26686
26687     clear_gen0_bricks();
26688 }
26689
26690 BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
26691 {
26692     BOOL success = FALSE;
26693     BOOL thread_created = FALSE;
26694     dprintf (2, ("Preparing gc thread"));
26695     gh->bgc_threads_timeout_cs.Enter();
26696     if (!(gh->bgc_thread_running))
26697     {
26698         dprintf (2, ("GC thread not runnning"));
26699         if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
26700         {
26701             success = TRUE;
26702             thread_created = TRUE;
26703         }
26704     }
26705     else
26706     {
26707         dprintf (3, ("GC thread already running"));
26708         success = TRUE;
26709     }
26710     gh->bgc_threads_timeout_cs.Leave();
26711
26712     if(thread_created)
26713         FIRE_EVENT(GCCreateConcurrentThread_V1);
26714
26715     return success;
26716 }
26717
26718 BOOL gc_heap::create_bgc_thread(gc_heap* gh)
26719 {
26720     assert (background_gc_done_event.IsValid());
26721
26722     //dprintf (2, ("Creating BGC thread"));
26723
26724     gh->bgc_thread_running = GCToEEInterface::CreateThread(gh->bgc_thread_stub, gh, true, ".NET Background GC");
26725     return gh->bgc_thread_running;
26726 }
26727
26728 BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
26729 {
26730     BOOL ret = FALSE;
26731     dprintf (3, ("Creating concurrent GC thread for the first time"));
26732     if (!background_gc_done_event.CreateManualEventNoThrow(TRUE))
26733     {
26734         goto cleanup;
26735     }
26736     if (!bgc_threads_sync_event.CreateManualEventNoThrow(FALSE))
26737     {
26738         goto cleanup;
26739     }
26740     if (!ee_proceed_event.CreateAutoEventNoThrow(FALSE))
26741     {
26742         goto cleanup;
26743     }
26744     if (!bgc_start_event.CreateManualEventNoThrow(FALSE))
26745     {
26746         goto cleanup;
26747     }
26748
26749 #ifdef MULTIPLE_HEAPS
26750     bgc_t_join.init (number_of_heaps, join_flavor_bgc);
26751 #else
26752     UNREFERENCED_PARAMETER(number_of_heaps);
26753 #endif //MULTIPLE_HEAPS
26754
26755     ret = TRUE;
26756
26757 cleanup:
26758
26759     if (!ret)
26760     {
26761         if (background_gc_done_event.IsValid())
26762         {
26763             background_gc_done_event.CloseEvent();
26764         }
26765         if (bgc_threads_sync_event.IsValid())
26766         {
26767             bgc_threads_sync_event.CloseEvent();
26768         }
26769         if (ee_proceed_event.IsValid())
26770         {
26771             ee_proceed_event.CloseEvent();
26772         }
26773         if (bgc_start_event.IsValid())
26774         {
26775             bgc_start_event.CloseEvent();
26776         }
26777     }
26778
26779     return ret;
26780 }
26781
26782 BOOL gc_heap::create_bgc_thread_support()
26783 {
26784     BOOL ret = FALSE;
26785     uint8_t** parr;
26786     
26787     if (!gc_lh_block_event.CreateManualEventNoThrow(FALSE))
26788     {
26789         goto cleanup;
26790     }
26791
26792     //needs to have room for enough smallest objects fitting on a page
26793     parr = new (nothrow) uint8_t*[1 + OS_PAGE_SIZE / MIN_OBJECT_SIZE];
26794     if (!parr)
26795     {
26796         goto cleanup;
26797     }
26798
26799     make_c_mark_list (parr);
26800
26801     ret = TRUE;
26802
26803 cleanup:
26804
26805     if (!ret)
26806     {
26807         if (gc_lh_block_event.IsValid())
26808         {
26809             gc_lh_block_event.CloseEvent();
26810         }
26811     }
26812
26813     return ret;
26814 }
26815
26816 int gc_heap::check_for_ephemeral_alloc()
26817 {
26818     int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
26819
26820     if (gen == -1)
26821     {
26822 #ifdef MULTIPLE_HEAPS
26823         for (int heap_index = 0; heap_index < n_heaps; heap_index++)
26824 #endif //MULTIPLE_HEAPS
26825         {
26826             for (int i = 0; i <= (max_generation - 1); i++)
26827             {
26828 #ifdef MULTIPLE_HEAPS
26829                 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
26830 #else
26831                 if (get_new_allocation (i) <= 0)
26832 #endif //MULTIPLE_HEAPS
26833                 {
26834                     gen = max (gen, i);
26835                 }
26836                 else
26837                     break;
26838             }
26839         }
26840     }
26841
26842     return gen;
26843 }
26844
26845 // Wait for gc to finish sequential part
26846 void gc_heap::wait_to_proceed()
26847 {
26848     assert (background_gc_done_event.IsValid());
26849     assert (bgc_start_event.IsValid());
26850
26851     user_thread_wait(&ee_proceed_event, FALSE);
26852 }
26853
26854 // Start a new concurrent gc
26855 void gc_heap::start_c_gc()
26856 {
26857     assert (background_gc_done_event.IsValid());
26858     assert (bgc_start_event.IsValid());
26859
26860 //Need to make sure that the gc thread is in the right place.
26861     background_gc_done_event.Wait(INFINITE, FALSE);
26862     background_gc_done_event.Reset();
26863     bgc_start_event.Set();
26864 }
26865
26866 void gc_heap::do_background_gc()
26867 {
26868     dprintf (2, ("starting a BGC"));
26869 #ifdef MULTIPLE_HEAPS
26870     for (int i = 0; i < n_heaps; i++)
26871     {
26872         g_heaps[i]->init_background_gc();
26873     }
26874 #else
26875     init_background_gc();
26876 #endif //MULTIPLE_HEAPS
26877     //start the background gc
26878     start_c_gc ();
26879
26880     //wait until we get restarted by the BGC.
26881     wait_to_proceed();
26882 }
26883
26884 void gc_heap::kill_gc_thread()
26885 {
26886     //assert (settings.concurrent == FALSE);
26887
26888     // We are doing a two-stage shutdown now.
26889     // In the first stage, we do minimum work, and call ExitProcess at the end.
26890     // In the secodn stage, we have the Loader lock and only one thread is
26891     // alive.  Hence we do not need to kill gc thread.
26892     background_gc_done_event.CloseEvent();
26893     gc_lh_block_event.CloseEvent();
26894     bgc_start_event.CloseEvent();
26895     bgc_threads_timeout_cs.Destroy();
26896     bgc_thread = 0;
26897     recursive_gc_sync::shutdown();
26898 }
26899
26900 void gc_heap::bgc_thread_function()
26901 {
26902     assert (background_gc_done_event.IsValid());
26903     assert (bgc_start_event.IsValid());
26904
26905     dprintf (3, ("gc_thread thread starting..."));
26906
26907     BOOL do_exit = FALSE;
26908
26909     bool cooperative_mode = true;
26910     bgc_thread_id.SetToCurrentThread();
26911     dprintf (1, ("bgc_thread_id is set to %x", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()));
26912     while (1)
26913     {
26914         // Wait for work to do...
26915         dprintf (3, ("bgc thread: waiting..."));
26916
26917         cooperative_mode = enable_preemptive ();
26918         //current_thread->m_fPreemptiveGCDisabled = 0;
26919
26920         uint32_t result = bgc_start_event.Wait(
26921 #ifdef _DEBUG
26922 #ifdef MULTIPLE_HEAPS
26923                                              INFINITE,
26924 #else
26925                                              2000,
26926 #endif //MULTIPLE_HEAPS
26927 #else //_DEBUG
26928 #ifdef MULTIPLE_HEAPS
26929                                              INFINITE,
26930 #else
26931                                              20000,
26932 #endif //MULTIPLE_HEAPS
26933 #endif //_DEBUG
26934             FALSE);
26935         dprintf (2, ("gc thread: finished waiting"));
26936
26937         // not calling disable_preemptive here 'cause we 
26938         // can't wait for GC complete here - RestartEE will be called 
26939         // when we've done the init work.
26940
26941         if (result == WAIT_TIMEOUT)
26942         {
26943             // Should join the bgc threads and terminate all of them
26944             // at once.
26945             dprintf (1, ("GC thread timeout"));
26946             bgc_threads_timeout_cs.Enter();
26947             if (!keep_bgc_threads_p)
26948             {
26949                 dprintf (2, ("GC thread exiting"));
26950                 bgc_thread_running = FALSE;
26951                 bgc_thread = 0;
26952                 bgc_thread_id.Clear();
26953                 do_exit = TRUE;
26954             }
26955             bgc_threads_timeout_cs.Leave();
26956             if (do_exit)
26957                 break;
26958             else
26959             {
26960                 dprintf (3, ("GC thread needed, not exiting"));
26961                 continue;
26962             }
26963         }
26964         // if we signal the thread with no concurrent work to do -> exit
26965         if (!settings.concurrent)
26966         {
26967             dprintf (3, ("no concurrent GC needed, exiting"));
26968             break;
26969         }
26970 #ifdef TRACE_GC
26971         //trace_gc = TRUE;
26972 #endif //TRACE_GC
26973         recursive_gc_sync::begin_background();
26974         dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d", 
26975             generation_free_list_space (generation_of (max_generation)),
26976             generation_free_obj_space (generation_of (max_generation)),
26977             dd_fragmentation (dynamic_data_of (max_generation))));
26978
26979         gc1();
26980
26981         current_bgc_state = bgc_not_in_process;
26982
26983 #ifdef TRACE_GC
26984         //trace_gc = FALSE;
26985 #endif //TRACE_GC
26986
26987         enable_preemptive ();
26988 #ifdef MULTIPLE_HEAPS
26989         bgc_t_join.join(this, gc_join_done);
26990         if (bgc_t_join.joined())
26991 #endif //MULTIPLE_HEAPS
26992         {
26993             enter_spin_lock (&gc_lock);
26994             dprintf (SPINLOCK_LOG, ("bgc Egc"));
26995             
26996             bgc_start_event.Reset();
26997             do_post_gc();
26998 #ifdef MULTIPLE_HEAPS
26999             for (int gen = max_generation; gen <= (max_generation + 1); gen++)
27000             {
27001                 size_t desired_per_heap = 0;
27002                 size_t total_desired = 0;
27003                 gc_heap* hp = 0;
27004                 dynamic_data* dd;
27005                 for (int i = 0; i < n_heaps; i++)
27006                 {
27007                     hp = g_heaps[i];
27008                     dd = hp->dynamic_data_of (gen);
27009                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
27010                     if (temp_total_desired < total_desired)
27011                     {
27012                         // we overflowed.
27013                         total_desired = (size_t)MAX_PTR;
27014                         break;
27015                     }
27016                     total_desired = temp_total_desired;
27017                 }
27018
27019                 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
27020
27021                 for (int i = 0; i < n_heaps; i++)
27022                 {
27023                     hp = gc_heap::g_heaps[i];
27024                     dd = hp->dynamic_data_of (gen);
27025                     dd_desired_allocation (dd) = desired_per_heap;
27026                     dd_gc_new_allocation (dd) = desired_per_heap;
27027                     dd_new_allocation (dd) = desired_per_heap;
27028                 }
27029             }
27030 #endif //MULTIPLE_HEAPS
27031 #ifdef MULTIPLE_HEAPS
27032             fire_pevents();
27033 #endif //MULTIPLE_HEAPS
27034
27035             c_write (settings.concurrent, FALSE);
27036             recursive_gc_sync::end_background();
27037             keep_bgc_threads_p = FALSE;
27038             background_gc_done_event.Set();
27039
27040             dprintf (SPINLOCK_LOG, ("bgc Lgc"));
27041             leave_spin_lock (&gc_lock);
27042 #ifdef MULTIPLE_HEAPS
27043             dprintf(1, ("End of BGC - starting all BGC threads"));
27044             bgc_t_join.restart();
27045 #endif //MULTIPLE_HEAPS
27046         }
27047         // We can't disable preempt here because there might've been a GC already
27048         // started and decided to do a BGC and waiting for a BGC thread to restart 
27049         // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
27050         // to restart the VM so we deadlock.
27051         //gc_heap::disable_preemptive (current_thread, TRUE);
27052     }
27053
27054     FIRE_EVENT(GCTerminateConcurrentThread_V1);
27055
27056     dprintf (3, ("bgc_thread thread exiting"));
27057     return;
27058 }
27059
27060 #endif //BACKGROUND_GC
27061
27062 //Clear the cards [start_card, end_card[
27063 void gc_heap::clear_cards (size_t start_card, size_t end_card)
27064 {
27065     if (start_card < end_card)
27066     {
27067         size_t start_word = card_word (start_card);
27068         size_t end_word = card_word (end_card);
27069         if (start_word < end_word)
27070         {
27071             // Figure out the bit positions of the cards within their words
27072             unsigned bits = card_bit (start_card);
27073             card_table [start_word] &= lowbits (~0, bits);
27074             for (size_t i = start_word+1; i < end_word; i++)
27075                 card_table [i] = 0;
27076             bits = card_bit (end_card);
27077             // Don't write beyond end_card (and possibly uncommitted card table space).
27078             if (bits != 0)
27079             {
27080                 card_table [end_word] &= highbits (~0, bits);
27081             }
27082         }
27083         else
27084         {
27085             // If the start and end cards are in the same word, just clear the appropriate card
27086             // bits in that word.
27087             card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
27088                                         highbits (~0, card_bit (end_card)));
27089         }
27090 #ifdef VERYSLOWDEBUG
27091         size_t  card = start_card;
27092         while (card < end_card)
27093         {
27094             assert (! (card_set_p (card)));
27095             card++;
27096         }
27097 #endif //VERYSLOWDEBUG
27098         dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
27099                   start_card, (size_t)card_address (start_card),
27100                   end_card, (size_t)card_address (end_card)));
27101     }
27102 }
27103
27104 void gc_heap::clear_card_for_addresses (uint8_t* start_address, uint8_t* end_address)
27105 {
27106     size_t   start_card = card_of (align_on_card (start_address));
27107     size_t   end_card = card_of (align_lower_card (end_address));
27108     clear_cards (start_card, end_card);
27109 }
27110
27111 // copy [srccard, ...[ to [dst_card, end_card[
27112 // This will set the same bit twice. Can be optimized.
27113 inline
27114 void gc_heap::copy_cards (size_t dst_card,
27115                           size_t src_card,
27116                           size_t end_card, 
27117                           BOOL nextp)
27118 {
27119     // If the range is empty, this function is a no-op - with the subtlety that
27120     // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
27121     // outside the committed region.  To avoid the access, leave early.
27122     if (!(dst_card < end_card))
27123         return;
27124
27125     unsigned int srcbit = card_bit (src_card);
27126     unsigned int dstbit = card_bit (dst_card);
27127     size_t srcwrd = card_word (src_card);
27128     size_t dstwrd = card_word (dst_card);
27129     unsigned int srctmp = card_table[srcwrd];
27130     unsigned int dsttmp = card_table[dstwrd];
27131
27132     for (size_t card = dst_card; card < end_card; card++)
27133     {
27134         if (srctmp & (1 << srcbit))
27135             dsttmp |= 1 << dstbit;
27136         else
27137             dsttmp &= ~(1 << dstbit);
27138
27139         if (!(++srcbit % 32))
27140         {
27141             srctmp = card_table[++srcwrd];
27142             srcbit = 0;
27143         }
27144
27145         if (nextp)
27146         {
27147             if (srctmp & (1 << srcbit))
27148                 dsttmp |= 1 << dstbit;
27149         }
27150
27151         if (!(++dstbit % 32))
27152         {
27153             card_table[dstwrd] = dsttmp;
27154
27155 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27156             if (dsttmp != 0)
27157             {
27158                 card_bundle_set(cardw_card_bundle(dstwrd));
27159             }
27160 #endif
27161
27162             dstwrd++;
27163             dsttmp = card_table[dstwrd];
27164             dstbit = 0;
27165         }
27166     }
27167
27168     card_table[dstwrd] = dsttmp;
27169
27170 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27171     if (dsttmp != 0)
27172     {
27173         card_bundle_set(cardw_card_bundle(dstwrd));
27174     }
27175 #endif
27176 }
27177
27178 void gc_heap::copy_cards_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27179 {
27180     ptrdiff_t relocation_distance = src - dest;
27181     size_t start_dest_card = card_of (align_on_card (dest));
27182     size_t end_dest_card = card_of (dest + len - 1);
27183     size_t dest_card = start_dest_card;
27184     size_t src_card = card_of (card_address (dest_card)+relocation_distance);
27185     dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
27186                  src_card, (size_t)src, dest_card, (size_t)dest));
27187     dprintf (3,(" %Ix->%Ix:%Ix[",
27188               (size_t)src+len, end_dest_card, (size_t)dest+len));
27189
27190     dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
27191         dest, src, len, relocation_distance, (align_on_card (dest))));
27192
27193     dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
27194         start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
27195
27196     //First card has two boundaries
27197     if (start_dest_card != card_of (dest))
27198     {
27199         if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
27200             card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
27201         {
27202             dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
27203                     (card_address (start_dest_card) + relocation_distance),
27204                     card_of (card_address (start_dest_card) + relocation_distance),
27205                     (src + len - 1),
27206                     card_of (src + len - 1)));
27207
27208             dprintf (3, ("setting card: %Ix", card_of (dest)));
27209             set_card (card_of (dest));
27210         }
27211     }
27212
27213     if (card_set_p (card_of (src)))
27214         set_card (card_of (dest));
27215
27216
27217     copy_cards (dest_card, src_card, end_dest_card,
27218                 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
27219
27220     //Last card has two boundaries.
27221     if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
27222         card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
27223     {
27224         dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
27225                 (card_address (end_dest_card) + relocation_distance),
27226                 card_of (card_address (end_dest_card) + relocation_distance),
27227                 src,
27228                 card_of (src)));
27229
27230         dprintf (3, ("setting card: %Ix", end_dest_card));
27231         set_card (end_dest_card);
27232     }
27233
27234     if (card_set_p (card_of (src + len - 1)))
27235         set_card (end_dest_card);
27236
27237 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27238     card_bundles_set(cardw_card_bundle(card_word(card_of(dest))), cardw_card_bundle(align_cardw_on_bundle(card_word(end_dest_card))));
27239 #endif
27240 }
27241
27242 #ifdef BACKGROUND_GC
27243 // this does not need the Interlocked version of mark_array_set_marked.
27244 void gc_heap::copy_mark_bits_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27245 {
27246     dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
27247                  (size_t)src, (size_t)dest,
27248                  (size_t)src+len, (size_t)dest+len));
27249
27250     uint8_t* src_o = src;
27251     uint8_t* dest_o;
27252     uint8_t* src_end = src + len;
27253     int align_const = get_alignment_constant (TRUE);
27254     ptrdiff_t reloc = dest - src;
27255
27256     while (src_o < src_end)
27257     {
27258         uint8_t*  next_o = src_o + Align (size (src_o), align_const);
27259
27260         if (background_object_marked (src_o, TRUE))
27261         {
27262             dest_o = src_o + reloc;
27263
27264             //if (background_object_marked (dest_o, FALSE))
27265             //{
27266             //    dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
27267             //    FATAL_GC_ERROR();
27268             //}
27269
27270             background_mark (dest_o, 
27271                              background_saved_lowest_address, 
27272                              background_saved_highest_address);
27273             dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
27274         }
27275
27276         src_o = next_o;
27277     }
27278 }
27279 #endif //BACKGROUND_GC
27280
27281 void gc_heap::fix_brick_to_highest (uint8_t* o, uint8_t* next_o)
27282 {
27283     size_t new_current_brick = brick_of (o);
27284     set_brick (new_current_brick,
27285                (o - brick_address (new_current_brick)));
27286     size_t b = 1 + new_current_brick;
27287     size_t limit = brick_of (next_o);
27288     //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
27289     dprintf(3,("b:%Ix->%Ix-%Ix", 
27290                new_current_brick, (size_t)o, (size_t)next_o));
27291     while (b < limit)
27292     {
27293         set_brick (b,(new_current_brick - b));
27294         b++;
27295     }
27296 }
27297
27298 // start can not be >= heap_segment_allocated for the segment.
27299 uint8_t* gc_heap::find_first_object (uint8_t* start, uint8_t* first_object)
27300 {
27301     size_t brick = brick_of (start);
27302     uint8_t* o = 0;
27303     //last_object == null -> no search shortcut needed
27304     if ((brick == brick_of (first_object) || (start <= first_object)))
27305     {
27306         o = first_object;
27307     }
27308     else
27309     {
27310         ptrdiff_t  min_brick = (ptrdiff_t)brick_of (first_object);
27311         ptrdiff_t  prev_brick = (ptrdiff_t)brick - 1;
27312         int         brick_entry = 0;
27313         while (1)
27314         {
27315             if (prev_brick < min_brick)
27316             {
27317                 break;
27318             }
27319             if ((brick_entry = get_brick_entry(prev_brick)) >= 0)
27320             {
27321                 break;
27322             }
27323             assert (! ((brick_entry == 0)));
27324             prev_brick = (brick_entry + prev_brick);
27325
27326         }
27327         o = ((prev_brick < min_brick) ? first_object :
27328                       brick_address (prev_brick) + brick_entry - 1);
27329         assert (o <= start);
27330     }
27331
27332     assert (Align (size (o)) >= Align (min_obj_size));
27333     uint8_t*  next_o = o + Align (size (o));
27334     size_t curr_cl = (size_t)next_o / brick_size;
27335     size_t min_cl = (size_t)first_object / brick_size;
27336
27337     //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
27338 #ifdef TRACE_GC
27339     unsigned int n_o = 1;
27340 #endif //TRACE_GC
27341
27342     uint8_t* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27343
27344     while (next_o <= start)
27345     {
27346         do
27347         {
27348 #ifdef TRACE_GC
27349             n_o++;
27350 #endif //TRACE_GC
27351             o = next_o;
27352             assert (Align (size (o)) >= Align (min_obj_size));
27353             next_o = o + Align (size (o));
27354             Prefetch (next_o);
27355         }while (next_o < next_b);
27356
27357         if (((size_t)next_o / brick_size) != curr_cl)
27358         {
27359             if (curr_cl >= min_cl)
27360             {
27361                 fix_brick_to_highest (o, next_o);
27362             }
27363             curr_cl = (size_t) next_o / brick_size;
27364         }
27365         next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27366     }
27367
27368     size_t bo = brick_of (o);
27369     //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix", 
27370     dprintf (3, ("%Id o, [%Ix-[%Ix", 
27371         n_o, bo, brick));
27372     if (bo < brick)
27373     {
27374         set_brick (bo, (o - brick_address(bo)));
27375         size_t b = 1 + bo;
27376         int x = -1;
27377         while (b < brick)
27378         {
27379             set_brick (b,x--);
27380             b++;
27381         }
27382     }
27383
27384     return o;
27385 }
27386
27387 #ifdef CARD_BUNDLE
27388
27389 // Find the first non-zero card word between cardw and cardw_end.
27390 // The index of the word we find is returned in cardw.
27391 BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
27392 {
27393     dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
27394                  dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
27395
27396     if (card_bundles_enabled())
27397     {
27398         size_t cardb = cardw_card_bundle (cardw);
27399         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
27400         while (1)
27401         {
27402             // Find a non-zero bundle
27403             while ((cardb < end_cardb) && (card_bundle_set_p (cardb) == 0))
27404             {
27405                 cardb++;
27406             }
27407
27408             if (cardb == end_cardb)
27409                 return FALSE;
27410
27411             // We found a bundle, so go through its words and find a non-zero card word
27412             uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
27413             uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
27414             while ((card_word < card_word_end) && !(*card_word))
27415             {
27416                 card_word++;
27417             }
27418
27419             if (card_word != card_word_end)
27420             {
27421                 cardw = (card_word - &card_table[0]);
27422                 return TRUE;
27423             }
27424             else if ((cardw <= card_bundle_cardw (cardb)) &&
27425                      (card_word == &card_table [card_bundle_cardw (cardb+1)]))
27426             {
27427                 // a whole bundle was explored and is empty
27428                 dprintf  (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
27429                         dd_collection_count (dynamic_data_of (0)), 
27430                         cardb, card_bundle_cardw (cardb),
27431                         card_bundle_cardw (cardb+1)));
27432                 card_bundle_clear (cardb);
27433             }
27434
27435             cardb++;
27436         }
27437     }
27438     else
27439     {
27440         uint32_t* card_word = &card_table[cardw];
27441         uint32_t* card_word_end = &card_table [cardw_end];
27442
27443         while (card_word < card_word_end)
27444         {
27445             if (*card_word != 0)
27446             {
27447                 cardw = (card_word - &card_table [0]);
27448                 return TRUE;
27449             }
27450
27451             card_word++;
27452         }
27453
27454         return FALSE;
27455     }
27456 }
27457
27458 #endif //CARD_BUNDLE
27459
27460 // Find cards that are set between two points in a card table.
27461 // Parameters
27462 //     card_table    : The card table.
27463 //     card          : [in/out] As input, the card to start searching from.
27464 //                              As output, the first card that's set.
27465 //     card_word_end : The card word at which to stop looking.
27466 //     end_card      : [out] The last card which is set.
27467 BOOL gc_heap::find_card(uint32_t* card_table,
27468                         size_t&   card,
27469                         size_t    card_word_end,
27470                         size_t&   end_card)
27471 {
27472     uint32_t* last_card_word;
27473     uint32_t card_word_value;
27474     uint32_t bit_position;
27475     
27476     // Find the first card which is set
27477     last_card_word = &card_table [card_word (card)];
27478     bit_position = card_bit (card);
27479     card_word_value = (*last_card_word) >> bit_position;
27480     if (!card_word_value)
27481     {
27482         bit_position = 0;
27483 #ifdef CARD_BUNDLE
27484         // Using the card bundle, go through the remaining card words between here and 
27485         // card_word_end until we find one that is non-zero.
27486         size_t lcw = card_word(card) + 1;
27487         if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
27488         {
27489             return FALSE;
27490         }
27491         else
27492         {
27493             last_card_word = &card_table [lcw];
27494             card_word_value = *last_card_word;
27495         }
27496
27497 #else //CARD_BUNDLE
27498         // Go through the remaining card words between here and card_word_end until we find
27499         // one that is non-zero.
27500         do
27501         {
27502             ++last_card_word;
27503         }
27504         while ((last_card_word < &card_table [card_word_end]) && !(*last_card_word));
27505
27506         if (last_card_word < &card_table [card_word_end])
27507         {
27508             card_word_value = *last_card_word;
27509         }
27510         else
27511         {
27512             // We failed to find any non-zero card words before we got to card_word_end
27513             return FALSE;
27514         }
27515 #endif //CARD_BUNDLE
27516     }
27517
27518     // Look for the lowest bit set
27519     if (card_word_value)
27520     {
27521         while (!(card_word_value & 1))
27522         {
27523             bit_position++;
27524             card_word_value = card_word_value / 2;
27525         }
27526     }
27527     
27528     // card is the card word index * card size + the bit index within the card
27529     card = (last_card_word - &card_table[0]) * card_word_width + bit_position;
27530
27531     do
27532     {
27533         // Keep going until we get to an un-set card.
27534         bit_position++;
27535         card_word_value = card_word_value / 2;
27536
27537         // If we reach the end of the card word and haven't hit a 0 yet, start going
27538         // card word by card word until we get to one that's not fully set (0xFFFF...)
27539         // or we reach card_word_end.
27540         if ((bit_position == card_word_width) && (last_card_word < &card_table [card_word_end]))
27541         {
27542             do
27543             {
27544                 card_word_value = *(++last_card_word);
27545             } while ((last_card_word < &card_table [card_word_end]) &&
27546
27547 #ifdef _MSC_VER
27548                      (card_word_value == (1 << card_word_width)-1)
27549 #else
27550                      // if left shift count >= width of type,
27551                      // gcc reports error.
27552                      (card_word_value == ~0u)
27553 #endif // _MSC_VER
27554                 );
27555             bit_position = 0;
27556         }
27557     } while (card_word_value & 1);
27558
27559     end_card = (last_card_word - &card_table [0])* card_word_width + bit_position;
27560     
27561     //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
27562     dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
27563     return TRUE;
27564 }
27565
27566
27567     //because of heap expansion, computing end is complicated.
27568 uint8_t* compute_next_end (heap_segment* seg, uint8_t* low)
27569 {
27570     if ((low >=  heap_segment_mem (seg)) &&
27571         (low < heap_segment_allocated (seg)))
27572         return low;
27573     else
27574         return heap_segment_allocated (seg);
27575 }
27576
27577 uint8_t*
27578 gc_heap::compute_next_boundary (uint8_t* low, int gen_number,
27579                                 BOOL relocating)
27580 {
27581     UNREFERENCED_PARAMETER(low);
27582
27583     //when relocating, the fault line is the plan start of the younger
27584     //generation because the generation is promoted.
27585     if (relocating && (gen_number == (settings.condemned_generation + 1)))
27586     {
27587         generation* gen = generation_of (gen_number - 1);
27588         uint8_t* gen_alloc = generation_plan_allocation_start (gen);
27589         assert (gen_alloc);
27590         return gen_alloc;
27591     }
27592     else
27593     {
27594         assert (gen_number > settings.condemned_generation);
27595         return generation_allocation_start (generation_of (gen_number - 1 ));
27596     }
27597
27598 }
27599
27600 inline void
27601 gc_heap::keep_card_live (uint8_t* o, size_t& n_gen,
27602                          size_t& cg_pointers_found)
27603 {
27604     THREAD_FROM_HEAP;
27605     if ((gc_low <= o) && (gc_high > o))
27606     {
27607         n_gen++;
27608     }
27609 #ifdef MULTIPLE_HEAPS
27610     else if (o)
27611     {
27612         gc_heap* hp = heap_of (o);
27613         if (hp != this)
27614         {
27615             if ((hp->gc_low <= o) &&
27616                 (hp->gc_high > o))
27617             {
27618                 n_gen++;
27619             }
27620         }
27621     }
27622 #endif //MULTIPLE_HEAPS
27623     cg_pointers_found ++;
27624     dprintf (4, ("keep card live for %Ix", o));
27625 }
27626
27627 inline void
27628 gc_heap::mark_through_cards_helper (uint8_t** poo, size_t& n_gen,
27629                                     size_t& cg_pointers_found,
27630                                     card_fn fn, uint8_t* nhigh,
27631                                     uint8_t* next_boundary)
27632 {
27633     THREAD_FROM_HEAP;
27634     if ((gc_low <= *poo) && (gc_high > *poo))
27635     {
27636         n_gen++;
27637         call_fn(fn) (poo THREAD_NUMBER_ARG);
27638     }
27639 #ifdef MULTIPLE_HEAPS
27640     else if (*poo)
27641     {
27642         gc_heap* hp = heap_of_gc (*poo);
27643         if (hp != this)
27644         {
27645             if ((hp->gc_low <= *poo) &&
27646                 (hp->gc_high > *poo))
27647             {
27648                 n_gen++;
27649                 call_fn(fn) (poo THREAD_NUMBER_ARG);
27650             }
27651             if ((fn == &gc_heap::relocate_address) ||
27652                 ((hp->ephemeral_low <= *poo) &&
27653                  (hp->ephemeral_high > *poo)))
27654             {
27655                 cg_pointers_found++;
27656             }
27657         }
27658     }
27659 #endif //MULTIPLE_HEAPS
27660     if ((next_boundary <= *poo) && (nhigh > *poo))
27661     {
27662         cg_pointers_found ++;
27663         dprintf (4, ("cg pointer %Ix found, %Id so far",
27664                      (size_t)*poo, cg_pointers_found ));
27665
27666     }
27667 }
27668
27669 BOOL gc_heap::card_transition (uint8_t* po, uint8_t* end, size_t card_word_end,
27670                                size_t& cg_pointers_found, 
27671                                size_t& n_eph, size_t& n_card_set,
27672                                size_t& card, size_t& end_card,
27673                                BOOL& foundp, uint8_t*& start_address,
27674                                uint8_t*& limit, size_t& n_cards_cleared)
27675 {
27676     dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
27677     dprintf (3, ("ct: %Id cg", cg_pointers_found));
27678     BOOL passed_end_card_p = FALSE;
27679     foundp = FALSE;
27680
27681     if (cg_pointers_found == 0)
27682     {
27683         //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
27684         dprintf(3,(" CC [%Ix, %Ix[ ",
27685                 (size_t)card_address(card), (size_t)po));
27686         clear_cards (card, card_of(po));
27687         n_card_set -= (card_of (po) - card);
27688         n_cards_cleared += (card_of (po) - card);
27689
27690     }
27691     n_eph +=cg_pointers_found;
27692     cg_pointers_found = 0;
27693     card = card_of (po);
27694     if (card >= end_card)
27695     {
27696         passed_end_card_p = TRUE;
27697         dprintf (3, ("card %Ix exceeding end_card %Ix",
27698                     (size_t)card, (size_t)end_card));
27699         foundp = find_card (card_table, card, card_word_end, end_card);
27700         if (foundp)
27701         {
27702             n_card_set+= end_card - card;
27703             start_address = card_address (card);
27704             dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
27705                         (size_t)card, (size_t)start_address,
27706                         (size_t)card_address (end_card)));
27707         }
27708         limit = min (end, card_address (end_card));
27709
27710         assert (!((limit == card_address (end_card))&&
27711                 card_set_p (end_card)));
27712     }
27713
27714     return passed_end_card_p;
27715 }
27716
27717 void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
27718 {
27719 #ifdef BACKGROUND_GC
27720     dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
27721                  current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
27722
27723     heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
27724     PREFIX_ASSUME(soh_seg != NULL);
27725
27726     while (soh_seg)
27727     {
27728         dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix", 
27729             soh_seg, 
27730             heap_segment_background_allocated (soh_seg),
27731             heap_segment_allocated (soh_seg)));
27732
27733         soh_seg = heap_segment_next_rw (soh_seg);
27734     }
27735 #endif //BACKGROUND_GC
27736
27737     uint8_t* low = gc_low;
27738     uint8_t* high = gc_high;
27739     size_t end_card = 0;
27740
27741     generation*   oldest_gen        = generation_of (max_generation);
27742     int           curr_gen_number   = max_generation;
27743     uint8_t*      gen_boundary      = generation_allocation_start(generation_of(curr_gen_number - 1));
27744     uint8_t*      next_boundary     = compute_next_boundary(gc_low, curr_gen_number, relocating);
27745     
27746     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
27747     PREFIX_ASSUME(seg != NULL);
27748
27749     uint8_t*      beg               = generation_allocation_start (oldest_gen);
27750     uint8_t*      end               = compute_next_end (seg, low);
27751     uint8_t*      last_object       = beg;
27752
27753     size_t  cg_pointers_found = 0;
27754
27755     size_t  card_word_end = (card_of (align_on_card_word (end)) / card_word_width);
27756
27757     size_t        n_eph             = 0;
27758     size_t        n_gen             = 0;
27759     size_t        n_card_set        = 0;
27760     uint8_t*      nhigh             = (relocating ? heap_segment_plan_allocated (ephemeral_heap_segment) : high);
27761
27762     BOOL          foundp            = FALSE;
27763     uint8_t*      start_address     = 0;
27764     uint8_t*      limit             = 0;
27765     size_t        card              = card_of (beg);
27766 #ifdef BACKGROUND_GC
27767     BOOL consider_bgc_mark_p        = FALSE;
27768     BOOL check_current_sweep_p      = FALSE;
27769     BOOL check_saved_sweep_p        = FALSE;
27770     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
27771 #endif //BACKGROUND_GC
27772
27773     dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
27774     size_t total_cards_cleared = 0;
27775
27776     while (1)
27777     {
27778         if (card_of(last_object) > card)
27779         {
27780             // cg means cross-generational
27781             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
27782             if (cg_pointers_found == 0)
27783             {
27784                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
27785                 clear_cards (card, card_of(last_object));
27786                 n_card_set -= (card_of (last_object) - card);
27787                 total_cards_cleared += (card_of (last_object) - card);
27788             }
27789
27790             n_eph += cg_pointers_found;
27791             cg_pointers_found = 0;
27792             card = card_of (last_object);
27793         }
27794
27795         if (card >= end_card)
27796         {
27797             // Find the first card that's set (between card and card_word_end)
27798             foundp = find_card(card_table, card, card_word_end, end_card);
27799             if (foundp)
27800             {
27801                 // We found card(s) set. 
27802                 n_card_set += end_card - card;
27803                 start_address = max (beg, card_address (card));
27804             }
27805
27806             limit = min (end, card_address (end_card));
27807         }
27808
27809         if (!foundp || (last_object >= end) || (card_address (card) >= end))
27810         {
27811             if (foundp && (cg_pointers_found == 0))
27812             {
27813                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
27814                            (size_t)end));
27815                 clear_cards (card, card_of (end));
27816                 n_card_set -= (card_of (end) - card);
27817                 total_cards_cleared += (card_of (end) - card);
27818             }
27819
27820             n_eph += cg_pointers_found;
27821             cg_pointers_found = 0;
27822
27823             if ((seg = heap_segment_next_in_range (seg)) != 0)
27824             {
27825 #ifdef BACKGROUND_GC
27826                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
27827 #endif //BACKGROUND_GC
27828                 beg = heap_segment_mem (seg);
27829                 end = compute_next_end (seg, low);
27830                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
27831                 card = card_of (beg);
27832                 last_object = beg;
27833                 end_card = 0;
27834                 continue;
27835             }
27836             else
27837             {
27838                 break;
27839             }
27840         }
27841
27842         // We've found a card and will now go through the objects in it.
27843         assert (card_set_p (card));
27844         {
27845             uint8_t* o = last_object;
27846             o = find_first_object (start_address, last_object);
27847             // Never visit an object twice.
27848             assert (o >= last_object);
27849
27850             //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
27851             dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
27852                    card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
27853
27854             while (o < limit)
27855             {
27856                 assert (Align (size (o)) >= Align (min_obj_size));
27857                 size_t s = size (o);
27858
27859                 uint8_t* next_o =  o + Align (s);
27860                 Prefetch (next_o);
27861
27862                 if ((o >= gen_boundary) &&
27863                     (seg == ephemeral_heap_segment))
27864                 {
27865                     dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
27866                     curr_gen_number--;
27867                     assert ((curr_gen_number > 0));
27868                     gen_boundary = generation_allocation_start
27869                         (generation_of (curr_gen_number - 1));
27870                     next_boundary = (compute_next_boundary
27871                                      (low, curr_gen_number, relocating));
27872                 }
27873
27874                 dprintf (4, ("|%Ix|", (size_t)o));
27875
27876                 if (next_o < start_address)
27877                 {
27878                     goto end_object;
27879                 }
27880
27881 #ifdef BACKGROUND_GC
27882                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
27883                 {
27884                     goto end_object;
27885                 }
27886 #endif //BACKGROUND_GC
27887
27888 #ifdef COLLECTIBLE_CLASS
27889                 if (is_collectible(o))
27890                 {
27891                     BOOL passed_end_card_p = FALSE;
27892
27893                     if (card_of (o) > card)
27894                     {
27895                         passed_end_card_p = card_transition (o, end, card_word_end,
27896                             cg_pointers_found, 
27897                             n_eph, n_card_set,
27898                             card, end_card,
27899                             foundp, start_address,
27900                             limit, total_cards_cleared);
27901                     }
27902
27903                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
27904                     {
27905                         // card is valid and it covers the head of the object
27906                         if (fn == &gc_heap::relocate_address)
27907                         {
27908                             keep_card_live (o, n_gen, cg_pointers_found);
27909                         }
27910                         else
27911                         {
27912                             uint8_t* class_obj = get_class_object (o);
27913                             mark_through_cards_helper (&class_obj, n_gen,
27914                                                     cg_pointers_found, fn,
27915                                                     nhigh, next_boundary);
27916                         }
27917                     }
27918
27919                     if (passed_end_card_p)
27920                     {
27921                         if (foundp && (card_address (card) < next_o))
27922                         {
27923                             goto go_through_refs;
27924                         }
27925                         else if (foundp && (start_address < limit))
27926                         {
27927                             next_o = find_first_object (start_address, o);
27928                             goto end_object;
27929                         }
27930                         else
27931                             goto end_limit;                            
27932                     }
27933                 }
27934
27935 go_through_refs:
27936 #endif //COLLECTIBLE_CLASS
27937
27938                 if (contain_pointers (o))
27939                 {
27940                     dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
27941
27942                     {
27943                         dprintf (4, ("normal object path"));
27944                         go_through_object
27945                             (method_table(o), o, s, poo,
27946                              start_address, use_start, (o + s),
27947                              {
27948                                  dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
27949                                  if (card_of ((uint8_t*)poo) > card)
27950                                  {
27951                                     BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
27952                                             card_word_end,
27953                                             cg_pointers_found, 
27954                                             n_eph, n_card_set,
27955                                             card, end_card,
27956                                             foundp, start_address,
27957                                             limit, total_cards_cleared);
27958
27959                                      if (passed_end_card_p)
27960                                      {
27961                                         if (foundp && (card_address (card) < next_o))
27962                                         {
27963                                              //new_start();
27964                                              {
27965                                                  if (ppstop <= (uint8_t**)start_address)
27966                                                      {break;}
27967                                                  else if (poo < (uint8_t**)start_address)
27968                                                      {poo = (uint8_t**)start_address;}
27969                                              }
27970                                         }
27971                                         else if (foundp && (start_address < limit))
27972                                         {
27973                                             next_o = find_first_object (start_address, o);
27974                                             goto end_object;
27975                                         }
27976                                          else
27977                                             goto end_limit;
27978                                      }
27979                                  }
27980
27981                                  mark_through_cards_helper (poo, n_gen,
27982                                                             cg_pointers_found, fn,
27983                                                             nhigh, next_boundary);
27984                              }
27985                             );
27986                     }
27987                 }
27988
27989             end_object:
27990                 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
27991                 {
27992                     if (brick_table [brick_of (o)] <0)
27993                         fix_brick_to_highest (o, next_o);
27994                 }
27995                 o = next_o;
27996             }
27997         end_limit:
27998             last_object = o;
27999         }
28000     }
28001     // compute the efficiency ratio of the card table
28002     if (!relocating)
28003     {
28004         generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
28005         dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
28006             n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
28007     }
28008     else
28009     {
28010         dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
28011             n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
28012     }
28013 }
28014
28015 #ifdef SEG_REUSE_STATS
28016 size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
28017 {
28018     size_t total_items = 0;
28019     *total_size = 0;
28020     for (int i = 0; i < count; i++)
28021     {
28022         total_items += ordered_indices[i];
28023         *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
28024         dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
28025     } 
28026     dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
28027     return total_items;
28028 }
28029 #endif // SEG_REUSE_STATS
28030
28031 void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug)
28032 {
28033     // detect pinned plugs
28034     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
28035     {
28036         deque_pinned_plug();
28037         update_oldest_pinned_plug();
28038         dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
28039     }
28040     else
28041     {
28042         size_t plug_size = last_plug_size + Align(min_obj_size);
28043         BOOL is_padded = FALSE;
28044
28045 #ifdef SHORT_PLUGS
28046         plug_size += Align (min_obj_size);
28047         is_padded = TRUE;
28048 #endif //SHORT_PLUGS
28049
28050 #ifdef RESPECT_LARGE_ALIGNMENT
28051         plug_size += switch_alignment_size (is_padded);
28052 #endif //RESPECT_LARGE_ALIGNMENT
28053
28054         total_ephemeral_plugs += plug_size;
28055         size_t plug_size_power2 = round_up_power2 (plug_size);
28056         ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
28057         dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array", 
28058             heap_number, 
28059             last_plug, 
28060             plug_size, 
28061             (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
28062     }
28063 }
28064
28065 void gc_heap::count_plugs_in_brick (uint8_t* tree, uint8_t*& last_plug)
28066 {
28067     assert ((tree != NULL));
28068     if (node_left_child (tree))
28069     {
28070         count_plugs_in_brick (tree + node_left_child (tree), last_plug);
28071     }
28072
28073     if (last_plug != 0)
28074     {
28075         uint8_t*  plug = tree;
28076         size_t gap_size = node_gap_size (plug);
28077         uint8_t*   gap = (plug - gap_size);
28078         uint8_t*  last_plug_end = gap;
28079         size_t  last_plug_size = (last_plug_end - last_plug);
28080         dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
28081             tree, last_plug, gap_size, gap, last_plug_size));
28082
28083         if (tree == oldest_pinned_plug)
28084         {
28085             dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
28086                 tree, last_plug, last_plug_size));
28087             mark* m = oldest_pin();
28088             if (m->has_pre_plug_info())
28089             {
28090                 last_plug_size += sizeof (gap_reloc_pair);
28091                 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
28092             }
28093         }
28094         // Can't assert here - if it's a pinned plug it can be less.
28095         //assert (last_plug_size >= Align (min_obj_size));
28096
28097         count_plug (last_plug_size, last_plug);
28098     }
28099
28100     last_plug = tree;
28101
28102     if (node_right_child (tree))
28103     {
28104         count_plugs_in_brick (tree + node_right_child (tree), last_plug);
28105     }
28106 }
28107
28108 void gc_heap::build_ordered_plug_indices ()
28109 {
28110     memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
28111     memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
28112
28113     uint8_t*  start_address = generation_limit (max_generation);
28114     uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment);
28115     size_t  current_brick = brick_of (start_address);
28116     size_t  end_brick = brick_of (end_address - 1);
28117     uint8_t* last_plug = 0;
28118
28119     //Look for the right pinned plug to start from.
28120     reset_pinned_queue_bos();
28121     while (!pinned_plug_que_empty_p())
28122     {
28123         mark* m = oldest_pin();
28124         if ((m->first >= start_address) && (m->first < end_address))
28125         {
28126             dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
28127
28128             break;
28129         }
28130         else
28131             deque_pinned_plug();
28132     }
28133     
28134     update_oldest_pinned_plug();
28135
28136     while (current_brick <= end_brick)
28137     {
28138         int brick_entry =  brick_table [ current_brick ];
28139         if (brick_entry >= 0)
28140         {
28141             count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
28142         }
28143
28144         current_brick++;
28145     }
28146
28147     if (last_plug !=0)
28148     {
28149         count_plug (end_address - last_plug, last_plug);
28150     }
28151
28152     // we need to make sure that after fitting all the existing plugs, we
28153     // have big enough free space left to guarantee that the next allocation
28154     // will succeed.
28155     size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
28156     total_ephemeral_plugs += extra_size;
28157     dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
28158     ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
28159     
28160     memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
28161
28162 #ifdef SEG_REUSE_STATS
28163     dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
28164     size_t total_plug_power2 = 0;
28165     dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
28166     dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))", 
28167                 total_ephemeral_plugs, 
28168                 total_plug_power2, 
28169                 (total_ephemeral_plugs ? 
28170                     (total_plug_power2 * 100 / total_ephemeral_plugs) :
28171                     0)));
28172     dprintf (SEG_REUSE_LOG_0, ("-------------------"));
28173 #endif // SEG_REUSE_STATS
28174 }
28175
28176 void gc_heap::init_ordered_free_space_indices ()
28177 {
28178     memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
28179     memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
28180 }
28181
28182 void gc_heap::trim_free_spaces_indices ()
28183 {
28184     trimmed_free_space_index = -1;
28185     size_t max_count = max_free_space_items - 1;
28186     size_t count = 0;
28187     int i = 0;
28188     for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
28189     {
28190         count += ordered_free_space_indices[i];
28191
28192         if (count >= max_count)
28193         {
28194             break;
28195         }
28196     }
28197
28198     ptrdiff_t extra_free_space_items = count - max_count;
28199
28200     if (extra_free_space_items > 0)
28201     {
28202         ordered_free_space_indices[i] -= extra_free_space_items;
28203         free_space_items = max_count;
28204         trimmed_free_space_index = i;
28205     }
28206     else
28207     {
28208         free_space_items = count;
28209     }
28210
28211     if (i == -1)
28212     {
28213         i = 0;
28214     }
28215
28216     free_space_buckets = MAX_NUM_BUCKETS - i;
28217
28218     for (--i; i >= 0; i--)
28219     {
28220         ordered_free_space_indices[i] = 0;
28221     }
28222
28223     memcpy (saved_ordered_free_space_indices, 
28224             ordered_free_space_indices,
28225             sizeof(ordered_free_space_indices));
28226 }
28227
28228 // We fit as many plugs as we can and update the number of plugs left and the number
28229 // of free spaces left.
28230 BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
28231 {
28232     assert (small_index <= big_index);
28233     assert (big_index < MAX_NUM_BUCKETS);
28234
28235     size_t small_blocks = ordered_blocks[small_index];
28236
28237     if (small_blocks == 0)
28238     {
28239         return TRUE;
28240     }
28241
28242     size_t big_spaces = ordered_spaces[big_index];
28243
28244     if (big_spaces == 0)
28245     {
28246         return FALSE;
28247     }
28248
28249     dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces", 
28250         heap_number,
28251         small_blocks, (small_index + MIN_INDEX_POWER2),
28252         big_spaces, (big_index + MIN_INDEX_POWER2)));
28253
28254     size_t big_to_small = big_spaces << (big_index - small_index);
28255
28256     ptrdiff_t extra_small_spaces = big_to_small - small_blocks;
28257     dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks", 
28258         heap_number,
28259         big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
28260     BOOL can_fit = (extra_small_spaces >= 0);
28261
28262     if (can_fit) 
28263     {
28264         dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks", 
28265             heap_number,
28266             extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
28267     }
28268
28269     int i = 0;
28270
28271     dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
28272     ordered_spaces[big_index] = 0;
28273     if (extra_small_spaces > 0)
28274     {
28275         dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
28276         ordered_blocks[small_index] = 0;
28277         for (i = small_index; i < big_index; i++)
28278         {
28279             if (extra_small_spaces & 1)
28280             {
28281                 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d", 
28282                     heap_number,
28283                     (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
28284                 ordered_spaces[i] += 1;
28285             }
28286             extra_small_spaces >>= 1;
28287         }
28288
28289         dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d", 
28290             heap_number,
28291             (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
28292         ordered_spaces[i] += extra_small_spaces;
28293     }
28294     else
28295     {
28296         dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d", 
28297             heap_number,
28298             (small_index + MIN_INDEX_POWER2), 
28299             ordered_blocks[small_index], 
28300             (ordered_blocks[small_index] - big_to_small)));
28301         ordered_blocks[small_index] -= big_to_small;
28302     }
28303
28304 #ifdef SEG_REUSE_STATS
28305     size_t temp;
28306     dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
28307     dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
28308
28309     dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
28310     dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
28311 #endif //SEG_REUSE_STATS
28312
28313     return can_fit;
28314 }
28315
28316 // space_index gets updated to the biggest available space index.
28317 BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
28318 {
28319     assert (*space_index >= block_index);
28320
28321     while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
28322     {
28323         (*space_index)--;
28324         if (*space_index < block_index)
28325         {
28326             return FALSE;
28327         }
28328     }
28329
28330     return TRUE;
28331 }
28332
28333 BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
28334 {
28335 #ifdef FEATURE_STRUCTALIGN
28336     // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
28337     return FALSE;
28338 #endif // FEATURE_STRUCTALIGN
28339     int space_index = count - 1;
28340     for (int block_index = (count - 1); block_index >= 0; block_index--)
28341     {
28342         if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
28343         {
28344             return FALSE;
28345         }
28346     }
28347
28348     return TRUE;
28349 }
28350
28351 void gc_heap::build_ordered_free_spaces (heap_segment* seg)
28352 {
28353     assert (bestfit_seg);
28354
28355     //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2, 
28356     //                    ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets), 
28357     //                    free_space_buckets, 
28358     //                    free_space_items);
28359
28360     bestfit_seg->add_buckets (MIN_INDEX_POWER2, 
28361                         ordered_free_space_indices, 
28362                         MAX_NUM_BUCKETS, 
28363                         free_space_items);
28364
28365     assert (settings.condemned_generation == max_generation);
28366
28367     uint8_t* first_address = heap_segment_mem (seg);
28368     uint8_t* end_address   = heap_segment_reserved (seg);
28369     //look through the pinned plugs for relevant ones.
28370     //Look for the right pinned plug to start from.
28371     reset_pinned_queue_bos();
28372     mark* m = 0;
28373     // See comment in can_expand_into_p why we need (max_generation + 1).
28374     size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
28375     BOOL has_fit_gen_starts = FALSE;
28376
28377     while (!pinned_plug_que_empty_p())
28378     {
28379         m = oldest_pin();
28380         if ((pinned_plug (m) >= first_address) && 
28381             (pinned_plug (m) < end_address) &&
28382             (pinned_len (m) >= eph_gen_starts))
28383         {
28384
28385             assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
28386             break;
28387         }
28388         else
28389         {
28390             deque_pinned_plug();
28391         }
28392     }
28393
28394     if (!pinned_plug_que_empty_p())
28395     {
28396         bestfit_seg->add ((void*)m, TRUE, TRUE);
28397         deque_pinned_plug();
28398         m = oldest_pin();
28399         has_fit_gen_starts = TRUE;
28400     }
28401
28402     while (!pinned_plug_que_empty_p() &&
28403             ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28404     {
28405         bestfit_seg->add ((void*)m, TRUE, FALSE);
28406         deque_pinned_plug();
28407         m = oldest_pin();
28408     }
28409
28410     if (commit_end_of_seg)
28411     {
28412         if (!has_fit_gen_starts)
28413         {
28414             assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
28415         }
28416         bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
28417     }
28418
28419 #ifdef _DEBUG
28420     bestfit_seg->check();
28421 #endif //_DEBUG
28422 }
28423
28424 BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
28425 {
28426     if (!end_of_segment_p)
28427     {
28428         trim_free_spaces_indices ();
28429     }
28430
28431     BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices, 
28432                                              ordered_free_space_indices, 
28433                                              MAX_NUM_BUCKETS);
28434
28435     return can_bestfit;
28436 }
28437
28438 BOOL gc_heap::best_fit (size_t free_space, 
28439                         size_t largest_free_space, 
28440                         size_t additional_space, 
28441                         BOOL* use_additional_space)
28442 {
28443     dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
28444
28445     assert (!additional_space || (additional_space && use_additional_space));
28446     if (use_additional_space)
28447     {
28448         *use_additional_space = FALSE;
28449     }
28450
28451     if (ordered_plug_indices_init == FALSE)
28452     {
28453         total_ephemeral_plugs = 0;
28454         build_ordered_plug_indices();
28455         ordered_plug_indices_init = TRUE;
28456     }
28457     else
28458     {
28459         memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
28460     }
28461
28462     if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
28463     {
28464         dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
28465         size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
28466         BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
28467         if (!can_fit_empty_eph)
28468         {
28469             can_fit_empty_eph = (additional_space >= empty_eph);
28470
28471             if (can_fit_empty_eph)
28472             {
28473                 *use_additional_space = TRUE;
28474             }
28475         }
28476
28477         return can_fit_empty_eph;
28478     }
28479
28480     if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
28481     {
28482         dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
28483         return FALSE;
28484     }
28485
28486     if ((free_space + additional_space) == 0)
28487     {
28488         dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
28489         return FALSE;
28490     }
28491
28492 #ifdef SEG_REUSE_STATS
28493     dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
28494     size_t total_free_space_power2 = 0;
28495     size_t total_free_space_items = 
28496         dump_buckets (ordered_free_space_indices, 
28497                       MAX_NUM_BUCKETS,
28498                       &total_free_space_power2);
28499     dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
28500
28501     dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
28502                 total_ephemeral_plugs, 
28503                 free_space, 
28504                 total_free_space_power2, 
28505                 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
28506                 additional_space));
28507
28508     size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
28509     memcpy (saved_all_free_space_indices, 
28510             ordered_free_space_indices, 
28511             sizeof(saved_all_free_space_indices));
28512
28513 #endif // SEG_REUSE_STATS
28514
28515     if (total_ephemeral_plugs > (free_space + additional_space))
28516     {
28517         return FALSE;
28518     }
28519
28520     use_bestfit = try_best_fit(FALSE);
28521
28522     if (!use_bestfit && additional_space)
28523     {
28524         int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
28525
28526         if (relative_free_space_index != -1)
28527         {
28528             int relative_plug_index = 0;
28529             size_t plugs_to_fit = 0;
28530
28531             for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
28532             {
28533                 plugs_to_fit = ordered_plug_indices[relative_plug_index];
28534                 if (plugs_to_fit != 0)
28535                 {
28536                     break;
28537                 }
28538             }
28539
28540             if ((relative_plug_index > relative_free_space_index) ||
28541                 ((relative_plug_index == relative_free_space_index) &&
28542                 (plugs_to_fit > 1)))
28543             {
28544 #ifdef SEG_REUSE_STATS
28545                 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
28546                             (relative_free_space_index + MIN_INDEX_POWER2),
28547                             plugs_to_fit,
28548                             (relative_plug_index + MIN_INDEX_POWER2)));
28549 #endif // SEG_REUSE_STATS
28550                 goto adjust;
28551             }
28552             
28553             dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
28554             ordered_free_space_indices[relative_free_space_index]++;
28555             use_bestfit = try_best_fit(TRUE);
28556             if (use_bestfit)
28557             {
28558                 free_space_items++;
28559                 // Since we might've trimmed away some of the free spaces we had, we should see
28560                 // if we really need to use end of seg space - if it's the same or smaller than
28561                 // the largest space we trimmed we can just add that one back instead of 
28562                 // using end of seg.
28563                 if (relative_free_space_index > trimmed_free_space_index)
28564                 {
28565                     *use_additional_space = TRUE;
28566                 }
28567                 else 
28568                 {
28569                     // If the addition space is <= than the last trimmed space, we
28570                     // should just use that last trimmed space instead.
28571                     saved_ordered_free_space_indices[trimmed_free_space_index]++;
28572                 }
28573             }
28574         }
28575     }
28576
28577 adjust:
28578
28579     if (!use_bestfit)
28580     {
28581         dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
28582
28583 #ifdef SEG_REUSE_STATS
28584         size_t saved_max = max_free_space_items;
28585         BOOL temp_bestfit = FALSE;
28586
28587         dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
28588         dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
28589
28590         // TODO: need to take the end of segment into consideration.
28591         while (max_free_space_items <= total_free_space_items)
28592         {
28593             max_free_space_items += max_free_space_items / 2;
28594             dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
28595             memcpy (ordered_free_space_indices, 
28596                     saved_all_free_space_indices,
28597                     sizeof(ordered_free_space_indices));
28598             if (try_best_fit(FALSE))
28599             {
28600                 temp_bestfit = TRUE;
28601                 break;
28602             }
28603         }
28604
28605         if (temp_bestfit)
28606         {
28607             dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
28608         }
28609         else
28610         {
28611             dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
28612         }
28613
28614         dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
28615         max_free_space_items = saved_max;
28616 #endif // SEG_REUSE_STATS
28617         if (free_space_items)
28618         {
28619             max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
28620             max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
28621         }
28622         else
28623         {
28624             max_free_space_items = MAX_NUM_FREE_SPACES;
28625         }
28626     }
28627
28628     dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
28629     dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
28630
28631     return use_bestfit;
28632 }
28633
28634 BOOL gc_heap::process_free_space (heap_segment* seg, 
28635                          size_t free_space,
28636                          size_t min_free_size, 
28637                          size_t min_cont_size,
28638                          size_t* total_free_space,
28639                          size_t* largest_free_space)
28640 {
28641     *total_free_space += free_space;
28642     *largest_free_space = max (*largest_free_space, free_space);
28643
28644 #ifdef SIMPLE_DPRINTF
28645     dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix", 
28646                 free_space, *total_free_space, *largest_free_space));
28647 #endif //SIMPLE_DPRINTF
28648
28649     if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
28650     {
28651 #ifdef SIMPLE_DPRINTF
28652         dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit", 
28653             settings.condemned_generation,
28654             *total_free_space, min_free_size, *largest_free_space, min_cont_size,
28655             (size_t)seg));
28656 #else
28657         UNREFERENCED_PARAMETER(seg);
28658 #endif //SIMPLE_DPRINTF
28659         return TRUE;
28660     }
28661
28662     int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
28663     if (free_space_index != -1)
28664     {
28665         ordered_free_space_indices[free_space_index]++;
28666     }
28667     return FALSE;
28668 }
28669
28670 BOOL gc_heap::expand_reused_seg_p()
28671 {
28672     BOOL reused_seg = FALSE;
28673     int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
28674     if ((heap_expand_mechanism == expand_reuse_bestfit) || 
28675         (heap_expand_mechanism == expand_reuse_normal))
28676     {
28677         reused_seg = TRUE;
28678     }
28679
28680     return reused_seg;
28681 }
28682
28683 BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
28684                                  allocator* gen_allocator)
28685 {
28686     min_cont_size += END_SPACE_AFTER_GC;
28687     use_bestfit = FALSE;
28688     commit_end_of_seg = FALSE;
28689     bestfit_first_pin = 0;
28690     uint8_t* first_address = heap_segment_mem (seg);
28691     uint8_t* end_address   = heap_segment_reserved (seg);
28692     size_t end_extra_space = end_space_after_gc();
28693
28694     if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
28695     {
28696         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
28697                                    first_address, end_address, end_extra_space));
28698         return FALSE;
28699     }
28700
28701     end_address -= end_extra_space;
28702
28703     dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix", 
28704         settings.condemned_generation, min_free_size, min_cont_size));
28705     size_t eph_gen_starts = eph_gen_starts_size;
28706
28707     if (settings.condemned_generation == max_generation)
28708     {
28709         size_t free_space = 0;
28710         size_t largest_free_space = free_space;
28711         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
28712         //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from. 
28713         //We are going to allocate the generation starts in the 1st free space,
28714         //so start from the first free space that's big enough for gen starts and a min object size.
28715         // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it - 
28716         // we could use it by allocating the last generation start a bit bigger but 
28717         // the complexity isn't worth the effort (those plugs are from gen2 
28718         // already anyway).
28719         reset_pinned_queue_bos();
28720         mark* m = 0;
28721         BOOL has_fit_gen_starts = FALSE;
28722
28723         init_ordered_free_space_indices ();
28724         while (!pinned_plug_que_empty_p())
28725         {
28726             m = oldest_pin();
28727             if ((pinned_plug (m) >= first_address) && 
28728                 (pinned_plug (m) < end_address) &&
28729                 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
28730             {
28731                 break;
28732             }
28733             else
28734             {
28735                 deque_pinned_plug();
28736             }
28737         }
28738
28739         if (!pinned_plug_que_empty_p())
28740         {
28741             bestfit_first_pin = pinned_plug (m) - pinned_len (m);
28742
28743             if (process_free_space (seg, 
28744                                     pinned_len (m) - eph_gen_starts, 
28745                                     min_free_size, min_cont_size, 
28746                                     &free_space, &largest_free_space))
28747             {
28748                 return TRUE;
28749             }
28750
28751             deque_pinned_plug();
28752             m = oldest_pin();
28753             has_fit_gen_starts = TRUE;
28754         }
28755
28756         dprintf (3, ("first pin is %Ix", pinned_plug (m)));
28757
28758         //tally up free space
28759         while (!pinned_plug_que_empty_p() &&
28760                ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28761         {
28762             dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
28763             if (process_free_space (seg, 
28764                                     pinned_len (m), 
28765                                     min_free_size, min_cont_size, 
28766                                     &free_space, &largest_free_space))
28767             {
28768                 return TRUE;
28769             }
28770
28771             deque_pinned_plug();
28772             m = oldest_pin();
28773         }
28774
28775         //try to find space at the end of the segment. 
28776         size_t end_space = (end_address - heap_segment_plan_allocated (seg)); 
28777         size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0); 
28778         dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
28779         if (end_space >= additional_space)
28780         {
28781             BOOL can_fit = TRUE;
28782             commit_end_of_seg = TRUE;
28783
28784             if (largest_free_space < min_cont_size)
28785             {
28786                 if (end_space >= min_cont_size)
28787                 {
28788                     additional_space = max (min_cont_size, additional_space);
28789                     dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph", 
28790                         seg));
28791                 }
28792                 else 
28793                 {
28794                     if (settings.concurrent)
28795                     {
28796                         can_fit = FALSE;
28797                         commit_end_of_seg = FALSE;
28798                     }
28799                     else
28800                     {
28801                         size_t additional_space_bestfit = additional_space;
28802                         if (!has_fit_gen_starts)
28803                         {
28804                             if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
28805                             {
28806                                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
28807                                         additional_space_bestfit));
28808                                 return FALSE;
28809                             }
28810
28811                             bestfit_first_pin = heap_segment_plan_allocated (seg);
28812                             additional_space_bestfit -= eph_gen_starts;
28813                         }
28814
28815                         can_fit = best_fit (free_space, 
28816                                             largest_free_space,
28817                                             additional_space_bestfit, 
28818                                             &commit_end_of_seg);
28819
28820                         if (can_fit)
28821                         {
28822                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg", 
28823                                 seg, (commit_end_of_seg ? "with" : "without")));
28824                         }
28825                         else
28826                         {
28827                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
28828                         }
28829                     }
28830                 }
28831             }
28832             else
28833             {
28834                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
28835             }
28836
28837             assert (additional_space <= end_space);
28838             if (commit_end_of_seg)
28839             {
28840                 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
28841                 {
28842                     dprintf (2, ("Couldn't commit end of segment?!"));
28843                     use_bestfit = FALSE;
28844  
28845                     return FALSE;
28846                 }
28847
28848                 if (use_bestfit)
28849                 {
28850                     // We increase the index here because growing heap segment could create a discrepency with 
28851                     // the additional space we used (could be bigger).
28852                     size_t free_space_end_of_seg = 
28853                         heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
28854                     int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
28855                     saved_ordered_free_space_indices[relative_free_space_index]++;
28856                 }
28857             }
28858         
28859             if (use_bestfit)
28860             {
28861                 memcpy (ordered_free_space_indices, 
28862                         saved_ordered_free_space_indices, 
28863                         sizeof(ordered_free_space_indices));
28864                 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
28865                 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
28866                 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
28867             }
28868
28869             return can_fit;
28870         }
28871
28872         dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
28873         return FALSE;
28874     }
28875     else
28876     {
28877         assert (settings.condemned_generation == (max_generation-1));
28878         size_t free_space = (end_address - heap_segment_plan_allocated (seg));
28879         size_t largest_free_space = free_space;
28880         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
28881         //find the first free list in range of the current segment
28882         size_t sz_list = gen_allocator->first_bucket_size();
28883         unsigned int a_l_idx = 0;
28884         uint8_t* free_list = 0;
28885         for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
28886         {
28887             if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
28888             {
28889                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
28890                 while (free_list)
28891                 {
28892                     if ((free_list >= first_address) && 
28893                         (free_list < end_address) && 
28894                         (unused_array_size (free_list) >= eph_gen_starts))
28895                     {
28896                         goto next;
28897                     }
28898                     else
28899                     {
28900                         free_list = free_list_slot (free_list);
28901                     }
28902                 }
28903             }
28904         }
28905 next:
28906         if (free_list)
28907         {
28908             init_ordered_free_space_indices ();
28909             if (process_free_space (seg, 
28910                                     unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size), 
28911                                     min_free_size, min_cont_size, 
28912                                     &free_space, &largest_free_space))
28913             {
28914                 return TRUE;
28915             }
28916
28917             free_list = free_list_slot (free_list);
28918         }
28919         else
28920         {
28921             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
28922             return FALSE;
28923         }
28924
28925        //tally up free space
28926
28927         while (1)
28928         {
28929             while (free_list)
28930             {
28931                 if ((free_list >= first_address) && (free_list < end_address) &&
28932                     process_free_space (seg, 
28933                                         unused_array_size (free_list), 
28934                                         min_free_size, min_cont_size, 
28935                                         &free_space, &largest_free_space))
28936                 {
28937                     return TRUE;
28938                 }
28939
28940                 free_list = free_list_slot (free_list);
28941             }
28942             a_l_idx++;
28943             if (a_l_idx < gen_allocator->number_of_buckets())
28944             {
28945                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
28946             }
28947             else
28948                 break;
28949         } 
28950
28951         dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
28952         return FALSE;
28953
28954         /*
28955         BOOL can_fit = best_fit (free_space, 0, NULL);
28956         if (can_fit)
28957         {
28958             dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
28959         }
28960         else
28961         {
28962             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
28963         }
28964
28965         return can_fit;
28966         */
28967     }
28968 }
28969
28970 void gc_heap::realloc_plug (size_t last_plug_size, uint8_t*& last_plug,
28971                             generation* gen, uint8_t* start_address,
28972                             unsigned int& active_new_gen_number,
28973                             uint8_t*& last_pinned_gap, BOOL& leftp,
28974                             BOOL shortened_p
28975 #ifdef SHORT_PLUGS
28976                             , mark* pinned_plug_entry
28977 #endif //SHORT_PLUGS
28978                             )
28979 {
28980     // detect generation boundaries
28981     // make sure that active_new_gen_number is not the youngest generation.
28982     // because the generation_limit wouldn't return the right thing in this case.
28983     if (!use_bestfit)
28984     {
28985         if ((active_new_gen_number > 1) &&
28986             (last_plug >= generation_limit (active_new_gen_number)))
28987         {
28988             assert (last_plug >= start_address);
28989             active_new_gen_number--;
28990             realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
28991             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
28992             leftp = FALSE;
28993         }
28994     }
28995
28996     // detect pinned plugs
28997     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
28998     {
28999         size_t  entry = deque_pinned_plug();
29000         mark*  m = pinned_plug_of (entry);
29001
29002         size_t saved_pinned_len = pinned_len(m);
29003         pinned_len(m) = last_plug - last_pinned_gap;
29004         //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
29005
29006         if (m->has_post_plug_info())
29007         {
29008             last_plug_size += sizeof (gap_reloc_pair);
29009             dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29010         }
29011
29012         last_pinned_gap = last_plug + last_plug_size;
29013         dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
29014             pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
29015         leftp = FALSE;
29016
29017         //we are creating a generation fault. set the cards.
29018         {
29019             size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
29020             size_t card = card_of (last_plug);
29021             while (card != end_card)
29022             {
29023                 set_card (card);
29024                 card++;
29025             }
29026         }
29027     }
29028     else if (last_plug >= start_address)
29029     {
29030 #ifdef FEATURE_STRUCTALIGN
29031         int requiredAlignment;
29032         ptrdiff_t pad;
29033         node_aligninfo (last_plug, requiredAlignment, pad);
29034
29035         // from how we previously aligned the plug's destination address,
29036         // compute the actual alignment offset.
29037         uint8_t* reloc_plug = last_plug + node_relocation_distance (last_plug);
29038         ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
29039         if (!alignmentOffset)
29040         {
29041             // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
29042             alignmentOffset = requiredAlignment;
29043         }
29044
29045         //clear the alignment info because we are reallocating
29046         clear_node_aligninfo (last_plug);
29047 #else // FEATURE_STRUCTALIGN
29048         //clear the realignment flag because we are reallocating
29049         clear_node_realigned (last_plug);
29050 #endif // FEATURE_STRUCTALIGN
29051         BOOL adjacentp = FALSE;
29052         BOOL set_padding_on_saved_p = FALSE;
29053
29054         if (shortened_p)
29055         {
29056             last_plug_size += sizeof (gap_reloc_pair);
29057
29058 #ifdef SHORT_PLUGS
29059             assert (pinned_plug_entry != NULL);
29060             if (last_plug_size <= sizeof (plug_and_gap))
29061             {
29062                 set_padding_on_saved_p = TRUE;
29063             }
29064 #endif //SHORT_PLUGS
29065
29066             dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29067         }
29068
29069 #ifdef SHORT_PLUGS
29070         clear_padding_in_expand (last_plug, set_padding_on_saved_p, pinned_plug_entry);
29071 #endif //SHORT_PLUGS
29072
29073         uint8_t* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug,
29074 #ifdef SHORT_PLUGS
29075                                      set_padding_on_saved_p,
29076                                      pinned_plug_entry,
29077 #endif //SHORT_PLUGS
29078                                      TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
29079
29080         dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
29081         assert (new_address);
29082         set_node_relocation_distance (last_plug, new_address - last_plug);
29083 #ifdef FEATURE_STRUCTALIGN
29084         if (leftp && node_alignpad (last_plug) == 0)
29085 #else // FEATURE_STRUCTALIGN
29086         if (leftp && !node_realigned (last_plug))
29087 #endif // FEATURE_STRUCTALIGN
29088         {
29089             // TODO - temporarily disable L optimization because of a bug in it.
29090             //set_node_left (last_plug);
29091         }
29092         dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
29093         leftp = adjacentp;
29094     }
29095 }
29096
29097 void gc_heap::realloc_in_brick (uint8_t* tree, uint8_t*& last_plug,
29098                                 uint8_t* start_address,
29099                                 generation* gen,
29100                                 unsigned int& active_new_gen_number,
29101                                 uint8_t*& last_pinned_gap, BOOL& leftp)
29102 {
29103     assert (tree != NULL);
29104     int   left_node = node_left_child (tree);
29105     int   right_node = node_right_child (tree);
29106
29107     dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d", 
29108         tree, last_pinned_gap, last_plug, left_node, right_node));
29109
29110     if (left_node)
29111     {
29112         dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
29113         realloc_in_brick ((tree + left_node), last_plug, start_address,
29114                           gen, active_new_gen_number, last_pinned_gap,
29115                           leftp);
29116     }
29117
29118     if (last_plug != 0)
29119     {
29120         uint8_t*  plug = tree;
29121
29122         BOOL has_pre_plug_info_p = FALSE;
29123         BOOL has_post_plug_info_p = FALSE;
29124         mark* pinned_plug_entry = get_next_pinned_entry (tree, 
29125                                                          &has_pre_plug_info_p,
29126                                                          &has_post_plug_info_p, 
29127                                                          FALSE);
29128
29129         // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
29130         // The pinned plugs are handled in realloc_plug.
29131         size_t gap_size = node_gap_size (plug);
29132         uint8_t*   gap = (plug - gap_size);
29133         uint8_t*  last_plug_end = gap;
29134         size_t  last_plug_size = (last_plug_end - last_plug);
29135         // Cannot assert this - a plug could be less than that due to the shortened ones.
29136         //assert (last_plug_size >= Align (min_obj_size));
29137         dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
29138             plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
29139         realloc_plug (last_plug_size, last_plug, gen, start_address,
29140                       active_new_gen_number, last_pinned_gap,
29141                       leftp, has_pre_plug_info_p
29142 #ifdef SHORT_PLUGS
29143                       , pinned_plug_entry
29144 #endif //SHORT_PLUGS
29145                       );
29146     }
29147
29148     last_plug = tree;
29149
29150     if (right_node)
29151     {
29152         dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
29153         realloc_in_brick ((tree + right_node), last_plug, start_address,
29154                           gen, active_new_gen_number, last_pinned_gap,
29155                           leftp);
29156     }
29157 }
29158
29159 void
29160 gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
29161                         uint8_t* start_address, uint8_t* end_address,
29162                         unsigned active_new_gen_number)
29163 {
29164     dprintf (3, ("--- Reallocing ---"));
29165
29166     if (use_bestfit)
29167     {
29168         //make sure that every generation has a planned allocation start
29169         int  gen_number = max_generation - 1;
29170         while (gen_number >= 0)
29171         {
29172             generation* gen = generation_of (gen_number);
29173             if (0 == generation_plan_allocation_start (gen))
29174             {
29175                 generation_plan_allocation_start (gen) = 
29176                     bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
29177                 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
29178                 assert (generation_plan_allocation_start (gen));
29179             }
29180             gen_number--;
29181         }
29182     }
29183
29184     uint8_t* first_address = start_address;
29185     //Look for the right pinned plug to start from.
29186     reset_pinned_queue_bos();
29187     uint8_t* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
29188     while (!pinned_plug_que_empty_p())
29189     {
29190         mark* m = oldest_pin();
29191         if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
29192         {
29193             if (pinned_plug (m) < first_address)
29194             {
29195                 first_address = pinned_plug (m);
29196             }
29197             break;
29198         }
29199         else
29200             deque_pinned_plug();
29201     }
29202
29203     size_t  current_brick = brick_of (first_address);
29204     size_t  end_brick = brick_of (end_address-1);
29205     uint8_t*  last_plug = 0;
29206
29207     uint8_t* last_pinned_gap = heap_segment_plan_allocated (seg);
29208     BOOL leftp = FALSE;
29209
29210     dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
29211         start_address, first_address, pinned_plug (oldest_pin())));
29212
29213     while (current_brick <= end_brick)
29214     {
29215         int   brick_entry =  brick_table [ current_brick ];
29216         if (brick_entry >= 0)
29217         {
29218             realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
29219                               last_plug, start_address, consing_gen,
29220                               active_new_gen_number, last_pinned_gap,
29221                               leftp);
29222         }
29223         current_brick++;
29224     }
29225
29226     if (last_plug != 0)
29227     {
29228         realloc_plug (end_address - last_plug, last_plug, consing_gen,
29229                       start_address,
29230                       active_new_gen_number, last_pinned_gap,
29231                       leftp, FALSE
29232 #ifdef SHORT_PLUGS
29233                       , NULL
29234 #endif //SHORT_PLUGS
29235                       );
29236     }
29237
29238     //Fix the old segment allocated size
29239     assert (last_pinned_gap >= heap_segment_mem (seg));
29240     assert (last_pinned_gap <= heap_segment_committed (seg));
29241     heap_segment_plan_allocated (seg) = last_pinned_gap;
29242 }
29243
29244 void gc_heap::verify_no_pins (uint8_t* start, uint8_t* end)
29245 {
29246 #ifdef VERIFY_HEAP
29247     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
29248     {
29249         BOOL contains_pinned_plugs = FALSE;
29250         size_t mi = 0;
29251         mark* m = 0;
29252         while (mi != mark_stack_tos)
29253         {
29254             m = pinned_plug_of (mi);
29255             if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
29256             {
29257                 contains_pinned_plugs = TRUE;
29258                 break;
29259             }
29260             else
29261                 mi++;
29262         }
29263
29264         if (contains_pinned_plugs)
29265         {
29266             FATAL_GC_ERROR();
29267         }
29268     }
29269 #endif //VERIFY_HEAP
29270 }
29271
29272 void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
29273 {
29274     if (!should_expand_in_full_gc)
29275     {
29276         if ((condemned_gen_number != max_generation) && 
29277             (settings.pause_mode != pause_low_latency) &&
29278             (settings.pause_mode != pause_sustained_low_latency))
29279         {
29280             should_expand_in_full_gc = TRUE;
29281         }
29282     }
29283 }
29284
29285 void gc_heap::save_ephemeral_generation_starts()
29286 {
29287     for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
29288     {
29289         saved_ephemeral_plan_start[ephemeral_generation] = 
29290             generation_plan_allocation_start (generation_of (ephemeral_generation));
29291         saved_ephemeral_plan_start_size[ephemeral_generation] = 
29292             generation_plan_allocation_start_size (generation_of (ephemeral_generation));
29293     }
29294 }
29295
29296 generation* gc_heap::expand_heap (int condemned_generation,
29297                                   generation* consing_gen,
29298                                   heap_segment* new_heap_segment)
29299 {
29300     UNREFERENCED_PARAMETER(condemned_generation);
29301     assert (condemned_generation >= (max_generation -1));
29302     unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
29303     uint8_t*  start_address = generation_limit (max_generation);
29304     uint8_t*  end_address = heap_segment_allocated (ephemeral_heap_segment);
29305     BOOL should_promote_ephemeral = FALSE;
29306     ptrdiff_t eph_size = total_ephemeral_size;
29307 #ifdef BACKGROUND_GC
29308     dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
29309 #endif //BACKGROUND_GC
29310     settings.heap_expansion = TRUE;
29311
29312 #ifdef BACKGROUND_GC
29313     if (cm_in_progress)
29314     {
29315         if (!expanded_in_fgc)
29316         {
29317             expanded_in_fgc = TRUE;
29318         }
29319     }
29320 #endif //BACKGROUND_GC
29321
29322     //reset the elevation state for next time.
29323     dprintf (2, ("Elevation: elevation = el_none"));
29324     if (settings.should_lock_elevation && !expand_reused_seg_p())
29325         settings.should_lock_elevation = FALSE;
29326
29327     heap_segment* new_seg = new_heap_segment;
29328
29329     if (!new_seg)
29330         return consing_gen;
29331
29332     //copy the card and brick tables
29333     if (g_gc_card_table!= card_table)
29334         copy_brick_card_table();
29335
29336     BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
29337     dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
29338
29339     assert (generation_plan_allocation_start (generation_of (max_generation-1)));
29340     assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
29341             heap_segment_mem (ephemeral_heap_segment));
29342     assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
29343             heap_segment_committed (ephemeral_heap_segment));
29344
29345     assert (generation_plan_allocation_start (youngest_generation));
29346     assert (generation_plan_allocation_start (youngest_generation) <
29347             heap_segment_plan_allocated (ephemeral_heap_segment));
29348
29349     if (settings.pause_mode == pause_no_gc)
29350     {
29351         // We don't reuse for no gc, so the size used on the new eph seg is eph_size.
29352         if ((size_t)(heap_segment_reserved (new_seg) - heap_segment_mem (new_seg)) < (eph_size + soh_allocation_no_gc))
29353             should_promote_ephemeral = TRUE;
29354     }
29355     else
29356     {
29357         if (!use_bestfit)
29358         {
29359             should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
29360         }
29361     }
29362
29363     if (should_promote_ephemeral)
29364     {
29365         ephemeral_promotion = TRUE;
29366         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_new_seg_ep);
29367         dprintf (2, ("promoting ephemeral"));
29368         save_ephemeral_generation_starts();
29369     }
29370     else
29371     {
29372         // commit the new ephemeral segment all at once if it is a new one.
29373         if ((eph_size > 0) && new_segment_p)
29374         {
29375 #ifdef FEATURE_STRUCTALIGN
29376             // The destination may require a larger alignment padding than the source.
29377             // Assume the worst possible alignment padding.
29378             eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
29379 #endif // FEATURE_STRUCTALIGN
29380 #ifdef RESPECT_LARGE_ALIGNMENT
29381             //Since the generation start can be larger than min_obj_size
29382             //The alignment could be switched. 
29383             eph_size += switch_alignment_size(FALSE);
29384 #endif //RESPECT_LARGE_ALIGNMENT
29385             //Since the generation start can be larger than min_obj_size
29386             //Compare the alignment of the first object in gen1 
29387             if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
29388             {
29389                 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
29390                 return consing_gen;
29391             }
29392             heap_segment_used (new_seg) = heap_segment_committed (new_seg);
29393         }
29394
29395         //Fix the end of the old ephemeral heap segment
29396         heap_segment_plan_allocated (ephemeral_heap_segment) =
29397             generation_plan_allocation_start (generation_of (max_generation-1));
29398
29399         dprintf (3, ("Old ephemeral allocated set to %Ix",
29400                     (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
29401     }
29402
29403     if (new_segment_p)
29404     {
29405         // TODO - Is this really necessary? We should think about it.
29406         //initialize the first brick
29407         size_t first_brick = brick_of (heap_segment_mem (new_seg));
29408         set_brick (first_brick,
29409                 heap_segment_mem (new_seg) - brick_address (first_brick));
29410     }
29411
29412     //From this point on, we cannot run out of memory
29413
29414     //reset the allocation of the consing generation back to the end of the
29415     //old ephemeral segment
29416     generation_allocation_limit (consing_gen) =
29417         heap_segment_plan_allocated (ephemeral_heap_segment);
29418     generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
29419     generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
29420
29421     //clear the generation gap for all of the ephemeral generations
29422     {
29423         int generation_num = max_generation-1;
29424         while (generation_num >= 0)
29425         {
29426             generation* gen = generation_of (generation_num);
29427             generation_plan_allocation_start (gen) = 0;
29428             generation_num--;
29429         }
29430     }
29431
29432     heap_segment* old_seg = ephemeral_heap_segment;
29433     ephemeral_heap_segment = new_seg;
29434
29435     //Note: the ephemeral segment shouldn't be threaded onto the segment chain
29436     //because the relocation and compact phases shouldn't see it
29437
29438     // set the generation members used by allocate_in_expanded_heap
29439     // and switch to ephemeral generation
29440     consing_gen = ensure_ephemeral_heap_segment (consing_gen);
29441
29442     if (!should_promote_ephemeral)
29443     {
29444         realloc_plugs (consing_gen, old_seg, start_address, end_address,
29445                     active_new_gen_number);
29446     }
29447
29448     if (!use_bestfit)
29449     {
29450         repair_allocation_in_expanded_heap (consing_gen);
29451     }
29452
29453     // assert that the generation gap for all of the ephemeral generations were allocated.
29454 #ifdef _DEBUG
29455     {
29456         int generation_num = max_generation-1;
29457         while (generation_num >= 0)
29458         {
29459             generation* gen = generation_of (generation_num);
29460             assert (generation_plan_allocation_start (gen));
29461             generation_num--;
29462         }
29463     }
29464 #endif // _DEBUG
29465
29466     if (!new_segment_p)
29467     {
29468         dprintf (2, ("Demoting ephemeral segment"));
29469         //demote the entire segment.
29470         settings.demotion = TRUE;
29471         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
29472         demotion_low = heap_segment_mem (ephemeral_heap_segment);
29473         demotion_high = heap_segment_reserved (ephemeral_heap_segment);
29474     }
29475     else
29476     {
29477         demotion_low = MAX_PTR;
29478         demotion_high = 0;
29479 #ifndef MULTIPLE_HEAPS
29480         settings.demotion = FALSE;
29481         get_gc_data_per_heap()->clear_mechanism_bit (gc_demotion_bit);
29482 #endif //!MULTIPLE_HEAPS
29483     }
29484     ptrdiff_t eph_size1 = total_ephemeral_size;
29485     MAYBE_UNUSED_VAR(eph_size1);
29486
29487     if (!should_promote_ephemeral && new_segment_p)
29488     {
29489         assert (eph_size1 <= eph_size);
29490     }
29491
29492     if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
29493     {
29494         // This is to catch when we accidently delete a segment that has pins.
29495         verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
29496     }
29497
29498     verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
29499
29500     dprintf(2,("---- End of Heap Expansion ----"));
29501     return consing_gen;
29502 }
29503
29504 void gc_heap::set_static_data()
29505 {
29506     static_data* pause_mode_sdata = static_data_table[latency_level];
29507     for (int i = 0; i < NUMBERGENERATIONS; i++)
29508     {
29509         dynamic_data* dd = dynamic_data_of (i);
29510         static_data* sdata = &pause_mode_sdata[i];
29511
29512         dd->sdata = sdata;
29513         dd->min_size = sdata->min_size;
29514
29515         dprintf (GTC_LOG, ("PM: %d - min: %Id, max: %Id, fr_l: %Id, fr_b: %d%%",
29516             settings.pause_mode,
29517             dd->min_size, dd_max_size, 
29518             dd->fragmentation_limit, (int)(dd->fragmentation_burden_limit * 100)));
29519     }
29520 }
29521
29522 // Initialize the values that are not const.
29523 void gc_heap::init_static_data()
29524 {
29525     size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
29526     size_t gen0_min_size = Align(gen0size / 8 * 5);
29527
29528     size_t gen0_max_size =
29529 #ifdef MULTIPLE_HEAPS
29530         max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024));
29531 #else //MULTIPLE_HEAPS
29532         (gc_can_use_concurrent ?
29533             6*1024*1024 :
29534             max (6*1024*1024,  min ( Align(soh_segment_size/2), 200*1024*1024)));
29535 #endif //MULTIPLE_HEAPS
29536
29537     // TODO: gen0_max_size has a 200mb cap; gen1_max_size should also have a cap.
29538     size_t gen1_max_size = 
29539 #ifdef MULTIPLE_HEAPS
29540         max (6*1024*1024, Align(soh_segment_size/2));
29541 #else //MULTIPLE_HEAPS
29542         (gc_can_use_concurrent ?
29543             6*1024*1024 :
29544             max (6*1024*1024, Align(soh_segment_size/2)));
29545 #endif //MULTIPLE_HEAPS
29546
29547     dprintf (GTC_LOG, ("gen0size: %Id, gen0 min: %Id, max: %Id, gen1 max: %Id",
29548         gen0size, gen0_min_size, gen0_max_size, gen1_max_size));
29549
29550     for (int i = latency_level_first; i <= latency_level_last; i++)
29551     {
29552         static_data_table[i][0].min_size = gen0_min_size;
29553         static_data_table[i][0].max_size = gen0_max_size;
29554         static_data_table[i][1].max_size = gen1_max_size;
29555     }
29556 }
29557
29558 bool gc_heap::init_dynamic_data()
29559 {
29560     qpf = GCToOSInterface::QueryPerformanceFrequency();
29561
29562     uint32_t now = (uint32_t)GetHighPrecisionTimeStamp();
29563
29564     set_static_data();
29565
29566     for (int i = 0; i <= max_generation+1; i++)
29567     {
29568         dynamic_data* dd = dynamic_data_of (i);
29569         dd->gc_clock = 0;
29570         dd->time_clock = now;
29571         dd->current_size = 0;
29572         dd->promoted_size = 0;
29573         dd->collection_count = 0;
29574         dd->new_allocation = dd->min_size;
29575         dd->gc_new_allocation = dd->new_allocation;
29576         dd->desired_allocation = dd->new_allocation;
29577         dd->fragmentation = 0;
29578     }
29579
29580 #ifdef GC_CONFIG_DRIVEN
29581     if (heap_number == 0)
29582         time_init = now;
29583 #endif //GC_CONFIG_DRIVEN
29584
29585     return true;
29586 }
29587
29588 float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
29589 {
29590     if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
29591         return ((limit - limit*cst) / (1.0f - (cst * limit)));
29592     else
29593         return max_limit;
29594 }
29595
29596
29597 //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may 
29598 //not be correct (collection happened too soon). Correct with a linear estimation based on the previous 
29599 //value of the budget 
29600 static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation, 
29601                                        size_t previous_desired_allocation, size_t collection_count)
29602 {
29603     if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
29604     {
29605         dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0)));
29606         new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
29607     }
29608 #if 0 
29609     size_t smoothing = 3; // exponential smoothing factor
29610     if (smoothing  > collection_count)
29611         smoothing  = collection_count;
29612     new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
29613 #else
29614     UNREFERENCED_PARAMETER(collection_count);
29615 #endif //0
29616     return new_allocation;
29617 }
29618
29619 size_t gc_heap::desired_new_allocation (dynamic_data* dd,
29620                                         size_t out, int gen_number,
29621                                         int pass)
29622 {
29623     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29624
29625     if (dd_begin_data_size (dd) == 0)
29626     {
29627         size_t new_allocation = dd_min_size (dd);
29628         current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;        
29629         return new_allocation;
29630     }
29631     else
29632     {
29633         float     cst;
29634         size_t    previous_desired_allocation = dd_desired_allocation (dd);
29635         size_t    current_size = dd_current_size (dd);
29636         float     max_limit = dd_max_limit (dd);
29637         float     limit = dd_limit (dd);
29638         size_t    min_gc_size = dd_min_size (dd);
29639         float     f = 0;
29640         size_t    max_size = dd_max_size (dd);
29641         size_t    new_allocation = 0;
29642         float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
29643         if (gen_number >= max_generation)
29644         {
29645             size_t    new_size = 0;
29646
29647             cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
29648
29649             f = surv_to_growth (cst, limit, max_limit);
29650             size_t max_growth_size = (size_t)(max_size / f);
29651             if (current_size >= max_growth_size)
29652             {
29653                 new_size = max_size;
29654             }
29655             else
29656             {
29657                 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
29658             }
29659
29660             assert ((new_size >= current_size) || (new_size == max_size));
29661
29662             if (gen_number == max_generation)
29663             {
29664                 new_allocation  =  max((new_size - current_size), min_gc_size);
29665
29666                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
29667                                                           dd_desired_allocation (dd), dd_collection_count (dd));
29668
29669                 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
29670                 {
29671                     //reducing allocation in case of fragmentation
29672                     size_t new_allocation1 = max (min_gc_size,
29673                                                   // CAN OVERFLOW
29674                                                   (size_t)((float)new_allocation * current_size /
29675                                                            ((float)current_size + 2*dd_fragmentation (dd))));
29676                     dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
29677                                  new_allocation, new_allocation1));
29678                     new_allocation = new_allocation1;
29679                 }
29680             }
29681             else //large object heap
29682             {
29683                 uint32_t memory_load = 0;
29684                 uint64_t available_physical = 0;
29685                 get_memory_info (&memory_load, &available_physical);
29686                 if (heap_number == 0)
29687                     settings.exit_memory_load = memory_load;
29688                 if (available_physical > 1024*1024)
29689                     available_physical -= 1024*1024;
29690
29691                 uint64_t available_free = available_physical + (uint64_t)generation_free_list_space (generation_of (gen_number));
29692                 if (available_free > (uint64_t)MAX_PTR)
29693                 {
29694                     available_free = (uint64_t)MAX_PTR;
29695                 }
29696
29697                 //try to avoid OOM during large object allocation
29698                 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))), 
29699                                           (size_t)available_free), 
29700                                       max ((current_size/4), min_gc_size));
29701
29702                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29703                                                           dd_desired_allocation (dd), dd_collection_count (dd));
29704
29705             }
29706         }
29707         else
29708         {
29709             size_t survivors = out;
29710             cst = float (survivors) / float (dd_begin_data_size (dd));
29711             f = surv_to_growth (cst, limit, max_limit);
29712             new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
29713
29714             new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
29715                                                       dd_desired_allocation (dd), dd_collection_count (dd));
29716
29717             if (gen_number == 0)
29718             {
29719                 if (pass == 0)
29720                 {
29721
29722                     //printf ("%f, %Id\n", cst, new_allocation);
29723                     size_t free_space = generation_free_list_space (generation_of (gen_number));
29724                     // DTREVIEW - is min_gc_size really a good choice? 
29725                     // on 64-bit this will almost always be true.
29726                     dprintf (GTC_LOG, ("frag: %Id, min: %Id", free_space, min_gc_size));
29727                     if (free_space > min_gc_size)
29728                     {
29729                         settings.gen0_reduction_count = 2;
29730                     }
29731                     else
29732                     {
29733                         if (settings.gen0_reduction_count > 0)
29734                             settings.gen0_reduction_count--;
29735                     }
29736                 }
29737                 if (settings.gen0_reduction_count > 0)
29738                 {
29739                     dprintf (2, ("Reducing new allocation based on fragmentation"));
29740                     new_allocation = min (new_allocation,
29741                                           max (min_gc_size, (max_size/3)));
29742                 }
29743             }
29744         }
29745
29746         size_t new_allocation_ret = 
29747             Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
29748         int gen_data_index = gen_number;
29749         gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
29750         gen_data->new_allocation = new_allocation_ret;
29751
29752         dd_surv (dd) = cst;
29753
29754 #ifdef SIMPLE_DPRINTF
29755         dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
29756                      heap_number, gen_number, out, current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)),
29757                      (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
29758 #else
29759         dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
29760         dprintf (1,("current: %Id alloc: %Id ", current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd))));
29761         dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
29762                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
29763 #endif //SIMPLE_DPRINTF
29764
29765         return new_allocation_ret;
29766     }
29767 }
29768
29769 //returns the planned size of a generation (including free list element)
29770 size_t gc_heap::generation_plan_size (int gen_number)
29771 {
29772     if (0 == gen_number)
29773         return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
29774                     generation_plan_allocation_start (generation_of (gen_number))),
29775                    (int)Align (min_obj_size));
29776     else
29777     {
29778         generation* gen = generation_of (gen_number);
29779         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
29780             return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
29781                     generation_plan_allocation_start (generation_of (gen_number)));
29782         else
29783         {
29784             size_t gensize = 0;
29785             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
29786
29787             PREFIX_ASSUME(seg != NULL);
29788
29789             while (seg && (seg != ephemeral_heap_segment))
29790             {
29791                 gensize += heap_segment_plan_allocated (seg) -
29792                            heap_segment_mem (seg);
29793                 seg = heap_segment_next_rw (seg);
29794             }
29795             if (seg)
29796             {
29797                 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
29798                             heap_segment_mem (ephemeral_heap_segment));
29799             }
29800             return gensize;
29801         }
29802     }
29803
29804 }
29805
29806 //returns the size of a generation (including free list element)
29807 size_t gc_heap::generation_size (int gen_number)
29808 {
29809     if (0 == gen_number)
29810         return max((heap_segment_allocated (ephemeral_heap_segment) -
29811                     generation_allocation_start (generation_of (gen_number))),
29812                    (int)Align (min_obj_size));
29813     else
29814     {
29815         generation* gen = generation_of (gen_number);
29816         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
29817             return (generation_allocation_start (generation_of (gen_number - 1)) -
29818                     generation_allocation_start (generation_of (gen_number)));
29819         else
29820         {
29821             size_t gensize = 0;
29822             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
29823
29824             PREFIX_ASSUME(seg != NULL);
29825
29826             while (seg && (seg != ephemeral_heap_segment))
29827             {
29828                 gensize += heap_segment_allocated (seg) -
29829                            heap_segment_mem (seg);
29830                 seg = heap_segment_next_rw (seg);
29831             }
29832             if (seg)
29833             {
29834                 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
29835                             heap_segment_mem (ephemeral_heap_segment));
29836             }
29837
29838             return gensize;
29839         }
29840     }
29841
29842 }
29843
29844 size_t  gc_heap::compute_in (int gen_number)
29845 {
29846     assert (gen_number != 0);
29847     dynamic_data* dd = dynamic_data_of (gen_number);
29848
29849     size_t in = generation_allocation_size (generation_of (gen_number));
29850
29851     if (gen_number == max_generation && ephemeral_promotion)
29852     {
29853         in = 0;
29854         for (int i = 0; i <= max_generation; i++)
29855         {
29856             dynamic_data* dd = dynamic_data_of (i);
29857             in += dd_survived_size (dd);
29858             if (i != max_generation)
29859             {
29860                 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
29861             }
29862         }
29863     }
29864
29865     dd_gc_new_allocation (dd) -= in;
29866     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
29867
29868     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29869     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
29870     gen_data->in = in;
29871
29872     generation_allocation_size (generation_of (gen_number)) = 0;
29873     return in;
29874 }
29875
29876 void  gc_heap::compute_promoted_allocation (int gen_number)
29877 {
29878     compute_in (gen_number);
29879 }
29880
29881 #ifdef BIT64
29882 inline
29883 size_t gc_heap::trim_youngest_desired (uint32_t memory_load,
29884                                        size_t total_new_allocation,
29885                                        size_t total_min_allocation)
29886 {
29887     if (memory_load < MAX_ALLOWED_MEM_LOAD)
29888     {
29889         // If the total of memory load and gen0 budget exceeds 
29890         // our max memory load limit, trim the gen0 budget so the total 
29891         // is the max memory load limit.
29892         size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
29893         return min (total_new_allocation, remain_memory_load);
29894     }
29895     else
29896     {
29897         return max (mem_one_percent, total_min_allocation);
29898     }
29899 }
29900
29901 size_t gc_heap::joined_youngest_desired (size_t new_allocation)
29902 {
29903     dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
29904
29905     size_t final_new_allocation = new_allocation;
29906     if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
29907     {
29908         uint32_t num_heaps = 1;
29909
29910 #ifdef MULTIPLE_HEAPS
29911         num_heaps = gc_heap::n_heaps;
29912 #endif //MULTIPLE_HEAPS
29913
29914         size_t total_new_allocation = new_allocation * num_heaps;
29915         size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
29916
29917         if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
29918             (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
29919         {
29920             uint32_t memory_load = 0;
29921             get_memory_info (&memory_load);
29922             settings.exit_memory_load = memory_load;
29923             dprintf (2, ("Current emory load: %d", memory_load));
29924
29925             size_t final_total = 
29926                 trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation);
29927             size_t max_new_allocation = 
29928 #ifdef MULTIPLE_HEAPS
29929                                          dd_max_size (g_heaps[0]->dynamic_data_of (0));
29930 #else //MULTIPLE_HEAPS
29931                                          dd_max_size (dynamic_data_of (0));
29932 #endif //MULTIPLE_HEAPS
29933
29934             final_new_allocation  = min (Align ((final_total / num_heaps), get_alignment_constant (TRUE)), max_new_allocation);
29935         }
29936     }
29937
29938     if (final_new_allocation < new_allocation)
29939     {
29940         settings.gen0_reduction_count = 2;
29941     }
29942
29943     return final_new_allocation;
29944 }
29945 #endif // BIT64 
29946
29947 inline
29948 gc_history_per_heap* gc_heap::get_gc_data_per_heap()
29949 {
29950 #ifdef BACKGROUND_GC
29951     return (settings.concurrent ? &bgc_data_per_heap : &gc_data_per_heap);
29952 #else
29953     return &gc_data_per_heap;
29954 #endif //BACKGROUND_GC
29955 }
29956
29957 void gc_heap::compute_new_dynamic_data (int gen_number)
29958 {
29959     PREFIX_ASSUME(gen_number >= 0);
29960     PREFIX_ASSUME(gen_number <= max_generation);
29961
29962     dynamic_data* dd = dynamic_data_of (gen_number);
29963     generation*   gen = generation_of (gen_number);
29964     size_t        in = (gen_number==0) ? 0 : compute_in (gen_number);
29965
29966     size_t total_gen_size = generation_size (gen_number);
29967     //keep track of fragmentation
29968     dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
29969     dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
29970
29971     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29972
29973     size_t out = dd_survived_size (dd);
29974
29975     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
29976     gen_data->size_after = total_gen_size;
29977     gen_data->free_list_space_after = generation_free_list_space (gen);
29978     gen_data->free_obj_space_after = generation_free_obj_space (gen);
29979
29980     if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
29981     {
29982         // When we are in the low latency mode, we can still be
29983         // condemning more than gen1's 'cause of induced GCs.
29984         dd_desired_allocation (dd) = low_latency_alloc;
29985     }
29986     else
29987     {
29988         if (gen_number == 0)
29989         {
29990             //compensate for dead finalizable objects promotion.
29991             //they shoudn't be counted for growth.
29992             size_t final_promoted = 0;
29993             final_promoted = min (promoted_bytes (heap_number), out);
29994             // Prefast: this is clear from above but prefast needs to be told explicitly
29995             PREFIX_ASSUME(final_promoted <= out);
29996
29997             dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
29998             dd_freach_previous_promotion (dd) = final_promoted;
29999             size_t lower_bound = desired_new_allocation  (dd, out-final_promoted, gen_number, 0);
30000
30001             if (settings.condemned_generation == 0)
30002             {
30003                 //there is no noise.
30004                 dd_desired_allocation (dd) = lower_bound;
30005             }
30006             else
30007             {
30008                 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
30009
30010                 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
30011                 //assert ( lower_bound <= higher_bound);
30012
30013                 //discount the noise. Change the desired allocation
30014                 //only if the previous value is outside of the range.
30015                 if (dd_desired_allocation (dd) < lower_bound)
30016                 {
30017                     dd_desired_allocation (dd) = lower_bound;
30018                 }
30019                 else if (dd_desired_allocation (dd) > higher_bound)
30020                 {
30021                     dd_desired_allocation (dd) = higher_bound;
30022                 }
30023 #if defined (BIT64) && !defined (MULTIPLE_HEAPS)
30024                 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
30025 #endif // BIT64 && !MULTIPLE_HEAPS
30026                 trim_youngest_desired_low_memory();
30027                 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
30028             }
30029         }
30030         else
30031         {
30032             dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
30033         }
30034     }
30035
30036     gen_data->pinned_surv = dd_pinned_survived_size (dd);
30037     gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd);
30038
30039     dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
30040     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30041
30042     //update counter
30043     dd_promoted_size (dd) = out;
30044     if (gen_number == max_generation)
30045     {
30046         dd = dynamic_data_of (max_generation+1);
30047         total_gen_size = generation_size (max_generation + 1);
30048         dd_fragmentation (dd) = generation_free_list_space (large_object_generation) + 
30049                                 generation_free_obj_space (large_object_generation);
30050         dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
30051         dd_survived_size (dd) = dd_current_size (dd);
30052         in = 0;
30053         out = dd_current_size (dd);
30054         dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
30055         dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
30056                                            get_alignment_constant (FALSE));
30057         dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30058
30059         gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
30060         gen_data->size_after = total_gen_size;
30061         gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
30062         gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
30063         gen_data->npinned_surv = out;
30064 #ifdef BACKGROUND_GC
30065         end_loh_size = total_gen_size;
30066 #endif //BACKGROUND_GC
30067         //update counter
30068         dd_promoted_size (dd) = out;
30069     }
30070 }
30071
30072 void gc_heap::trim_youngest_desired_low_memory()
30073 {
30074     if (g_low_memory_status)
30075     {
30076         size_t committed_mem = 0;
30077         heap_segment* seg = generation_start_segment (generation_of (max_generation));
30078         while (seg)
30079         {
30080             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30081             seg = heap_segment_next (seg);
30082         }
30083         seg = generation_start_segment (generation_of (max_generation + 1));
30084         while (seg)
30085         {
30086             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30087             seg = heap_segment_next (seg);
30088         }
30089
30090         dynamic_data* dd = dynamic_data_of (0);
30091         size_t current = dd_desired_allocation (dd);
30092         size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_size (dd));
30093
30094         dd_desired_allocation (dd) = min (current, candidate);
30095     }
30096 }
30097
30098 void gc_heap::decommit_ephemeral_segment_pages()
30099 {
30100     if (settings.concurrent)
30101     {
30102         return;
30103     }
30104
30105     size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30106     dynamic_data* dd = dynamic_data_of (0);
30107
30108 #ifndef MULTIPLE_HEAPS
30109     size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
30110     size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
30111     size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
30112
30113     if (dd_desired_allocation (dd) > gc_gen0_desired_high)
30114     {
30115         gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space;
30116     }
30117
30118     if (ephemeral_elapsed >= decommit_timeout)
30119     {
30120         slack_space = min (slack_space, gc_gen0_desired_high);
30121
30122         gc_last_ephemeral_decommit_time = dd_time_clock(dd);
30123         gc_gen0_desired_high = 0;
30124     }
30125 #endif //!MULTIPLE_HEAPS
30126
30127     if (settings.condemned_generation >= (max_generation-1))
30128     {
30129         size_t new_slack_space = 
30130 #ifdef BIT64
30131                     max(min(min(soh_segment_size/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
30132 #else
30133 #ifdef FEATURE_CORECLR
30134                     dd_desired_allocation (dd);
30135 #else
30136                     dd_max_size (dd);
30137 #endif //FEATURE_CORECLR                                    
30138 #endif // BIT64
30139
30140         slack_space = min (slack_space, new_slack_space);
30141     }
30142
30143     decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);    
30144
30145     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30146     current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30147 }
30148
30149 size_t gc_heap::new_allocation_limit (size_t size, size_t free_size, int gen_number)
30150 {
30151     dynamic_data* dd        = dynamic_data_of (gen_number);
30152     ptrdiff_t           new_alloc = dd_new_allocation (dd);
30153     assert (new_alloc == (ptrdiff_t)Align (new_alloc,
30154                                            get_alignment_constant (!(gen_number == (max_generation+1)))));
30155     size_t        limit     = min (max (new_alloc, (ptrdiff_t)size), (ptrdiff_t)free_size);
30156     assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
30157     dd_new_allocation (dd) = (new_alloc - limit );
30158     return limit;
30159 }
30160
30161 //This is meant to be called by decide_on_compacting.
30162
30163 size_t gc_heap::generation_fragmentation (generation* gen,
30164                                           generation* consing_gen,
30165                                           uint8_t* end)
30166 {
30167     size_t frag;
30168     uint8_t* alloc = generation_allocation_pointer (consing_gen);
30169     // If the allocation pointer has reached the ephemeral segment
30170     // fine, otherwise the whole ephemeral segment is considered
30171     // fragmentation
30172     if (in_range_for_segment (alloc, ephemeral_heap_segment))
30173         {
30174             if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
30175                 frag = end - alloc;
30176             else
30177             {
30178                 // case when no survivors, allocated set to beginning
30179                 frag = 0;
30180             }
30181             dprintf (3, ("ephemeral frag: %Id", frag));
30182         }
30183     else
30184         frag = (heap_segment_allocated (ephemeral_heap_segment) -
30185                 heap_segment_mem (ephemeral_heap_segment));
30186     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30187
30188     PREFIX_ASSUME(seg != NULL);
30189
30190     while (seg != ephemeral_heap_segment)
30191     {
30192         frag += (heap_segment_allocated (seg) -
30193                  heap_segment_plan_allocated (seg));
30194         dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
30195                      (heap_segment_allocated (seg) -
30196                       heap_segment_plan_allocated (seg))));
30197
30198         seg = heap_segment_next_rw (seg);
30199         assert (seg);
30200     }
30201     dprintf (3, ("frag: %Id discounting pinned plugs", frag));
30202     //add the length of the dequeued plug free space
30203     size_t bos = 0;
30204     while (bos < mark_stack_bos)
30205     {
30206         frag += (pinned_len (pinned_plug_of (bos)));
30207         bos++;
30208     }
30209
30210     return frag;
30211 }
30212
30213 // for SOH this returns the total sizes of the generation and its 
30214 // younger generation(s).
30215 // for LOH this returns just LOH size.
30216 size_t gc_heap::generation_sizes (generation* gen)
30217 {
30218     size_t result = 0;
30219     if (generation_start_segment (gen ) == ephemeral_heap_segment)
30220         result = (heap_segment_allocated (ephemeral_heap_segment) -
30221                   generation_allocation_start (gen));
30222     else
30223     {
30224         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
30225
30226         PREFIX_ASSUME(seg != NULL);
30227
30228         while (seg)
30229         {
30230             result += (heap_segment_allocated (seg) -
30231                        heap_segment_mem (seg));
30232             seg = heap_segment_next_in_range (seg);
30233         }
30234     }
30235
30236     return result;
30237 }
30238
30239 BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
30240                                     size_t fragmentation,
30241                                     BOOL& should_expand)
30242 {
30243     BOOL should_compact = FALSE;
30244     should_expand = FALSE;
30245     generation*   gen = generation_of (condemned_gen_number);
30246     dynamic_data* dd = dynamic_data_of (condemned_gen_number);
30247     size_t gen_sizes     = generation_sizes(gen);
30248     float  fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
30249                                     (float (fragmentation) / gen_sizes) );
30250
30251     dprintf (GTC_LOG, ("fragmentation: %Id (%d%%)", fragmentation, (int)(fragmentation_burden * 100.0)));
30252
30253 #ifdef STRESS_HEAP
30254     // for pure GC stress runs we need compaction, for GC stress "mix"
30255     // we need to ensure a better mix of compacting and sweeping collections
30256     if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
30257         && !g_pConfig->IsGCStressMix())
30258         should_compact = TRUE;
30259
30260 #ifdef GC_STATS
30261     // in GC stress "mix" mode, for stress induced collections make sure we 
30262     // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
30263     // against the GC's determination, as it may lead to premature OOMs.
30264     if (g_pConfig->IsGCStressMix() && settings.stress_induced)
30265     {
30266         int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
30267         int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
30268         if (compactions < sweeps / 10)
30269         {
30270             should_compact = TRUE;
30271         }
30272     }
30273 #endif // GC_STATS
30274 #endif //STRESS_HEAP
30275
30276     if (GCConfig::GetForceCompact())
30277         should_compact = TRUE;
30278
30279     if ((condemned_gen_number == max_generation) && last_gc_before_oom)
30280     {
30281         should_compact = TRUE;
30282         last_gc_before_oom = FALSE;
30283         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc);
30284     }
30285
30286     if (settings.reason == reason_induced_compacting)
30287     {
30288         dprintf (2, ("induced compacting GC"));
30289         should_compact = TRUE;
30290         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
30291     }
30292
30293     dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
30294                 fragmentation, (int) (100*fragmentation_burden)));
30295
30296     if (!should_compact)
30297     {
30298         if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
30299         {
30300             dprintf(GTC_LOG, ("compacting due to low ephemeral"));
30301             should_compact = TRUE;
30302             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral);
30303         }
30304     }
30305
30306     if (should_compact)
30307     {
30308         if ((condemned_gen_number >= (max_generation - 1)))
30309         {
30310             if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
30311             {
30312                 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
30313                 should_expand = TRUE;
30314             }
30315         }
30316     }
30317
30318 #ifdef BIT64
30319     BOOL high_memory = FALSE;
30320 #endif // BIT64
30321
30322     if (!should_compact)
30323     {
30324         // We are not putting this in dt_high_frag_p because it's not exactly
30325         // high fragmentation - it's just enough planned fragmentation for us to 
30326         // want to compact. Also the "fragmentation" we are talking about here
30327         // is different from anywhere else.
30328         BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
30329                                 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
30330
30331         if (frag_exceeded)
30332         {
30333 #ifdef BACKGROUND_GC
30334             // do not force compaction if this was a stress-induced GC
30335             IN_STRESS_HEAP(if (!settings.stress_induced))
30336             {
30337 #endif // BACKGROUND_GC
30338             assert (settings.concurrent == FALSE);
30339             should_compact = TRUE;
30340             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag);
30341 #ifdef BACKGROUND_GC
30342             }
30343 #endif // BACKGROUND_GC
30344         }
30345
30346 #ifdef BIT64
30347         // check for high memory situation
30348         if(!should_compact)
30349         {
30350             uint32_t num_heaps = 1;
30351 #ifdef MULTIPLE_HEAPS
30352             num_heaps = gc_heap::n_heaps;
30353 #endif // MULTIPLE_HEAPS
30354             
30355             ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
30356             if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th))
30357             {
30358                 if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps)))
30359                 {
30360                     dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
30361                     should_compact = TRUE;
30362                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag);
30363                 }
30364                 high_memory = TRUE;
30365             }
30366             else if(settings.entry_memory_load >= v_high_memory_load_th)
30367             {
30368                 if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps)))
30369                 {
30370                     dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
30371                     should_compact = TRUE;
30372                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag);
30373                 }
30374                 high_memory = TRUE;
30375             }
30376         }
30377 #endif // BIT64
30378     }
30379
30380     // The purpose of calling ensure_gap_allocation here is to make sure
30381     // that we actually are able to commit the memory to allocate generation
30382     // starts.
30383     if ((should_compact == FALSE) &&
30384         (ensure_gap_allocation (condemned_gen_number) == FALSE))
30385     {
30386         should_compact = TRUE;
30387         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps);
30388     }
30389
30390     if (settings.condemned_generation == max_generation)
30391     {
30392         //check the progress
30393         if (
30394 #ifdef BIT64
30395             (high_memory && !should_compact) ||
30396 #endif // BIT64
30397             (generation_plan_allocation_start (generation_of (max_generation - 1)) >= 
30398                 generation_allocation_start (generation_of (max_generation - 1))))
30399         {
30400             dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked",
30401                      generation_size (max_generation),
30402                      generation_plan_size (max_generation)));
30403             //no progress -> lock
30404             settings.should_lock_elevation = TRUE;
30405         }
30406     }
30407
30408     if (settings.pause_mode == pause_no_gc)
30409     {
30410         should_compact = TRUE;
30411         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment))
30412             < soh_allocation_no_gc)
30413         {
30414             should_expand = TRUE;
30415         }
30416     }
30417
30418     dprintf (2, ("will %s", (should_compact ? "compact" : "sweep")));
30419     return should_compact;
30420 }
30421
30422 size_t align_lower_good_size_allocation (size_t size)
30423 {
30424     return (size/64)*64;
30425 }
30426
30427 size_t gc_heap::approximate_new_allocation()
30428 {
30429     dynamic_data* dd0 = dynamic_data_of (0);
30430     return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
30431 }
30432
30433 // After we did a GC we expect to have at least this 
30434 // much space at the end of the segment to satisfy
30435 // a reasonable amount of allocation requests.
30436 size_t gc_heap::end_space_after_gc()
30437 {
30438     return max ((dd_min_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
30439 }
30440
30441 BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
30442 {
30443     uint8_t* start = 0;
30444     
30445     if ((tp == tuning_deciding_condemned_gen) ||
30446         (tp == tuning_deciding_compaction))
30447     {
30448         start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
30449         if (settings.concurrent)
30450         {
30451             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)", 
30452                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30453         }
30454         else
30455         {
30456             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)", 
30457                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
30458         }
30459     }
30460     else if (tp == tuning_deciding_expansion)
30461     {
30462         start = heap_segment_plan_allocated (ephemeral_heap_segment);
30463         dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan", 
30464             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
30465     }
30466     else
30467     {
30468         assert (tp == tuning_deciding_full_gc);
30469         dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)", 
30470             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30471         start = alloc_allocated;
30472     }
30473     
30474     if (start == 0) // empty ephemeral generations
30475     {
30476         assert (tp == tuning_deciding_expansion);
30477         // if there are no survivors in the ephemeral segment, 
30478         // this should be the beginning of ephemeral segment.
30479         start = generation_allocation_pointer (generation_of (max_generation));
30480         assert (start == heap_segment_mem (ephemeral_heap_segment));
30481     }
30482
30483     if (tp == tuning_deciding_expansion)
30484     {
30485         assert (settings.condemned_generation >= (max_generation-1));
30486         size_t gen0size = approximate_new_allocation();
30487         size_t eph_size = gen0size;
30488
30489         for (int j = 1; j <= max_generation-1; j++)
30490         {
30491             eph_size += 2*dd_min_size (dynamic_data_of(j));
30492         }
30493         
30494         // We must find room for one large object and enough room for gen0size
30495         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
30496         {
30497             dprintf (3, ("Enough room before end of segment"));
30498             return TRUE;
30499         }
30500         else
30501         {
30502             size_t room = align_lower_good_size_allocation
30503                 (heap_segment_reserved (ephemeral_heap_segment) - start);
30504             size_t end_seg = room;
30505
30506             //look at the plug free space
30507             size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
30508             bool large_chunk_found = FALSE;
30509             size_t bos = 0;
30510             uint8_t* gen0start = generation_plan_allocation_start (youngest_generation);
30511             dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
30512             if (gen0start == 0)
30513                 return FALSE;
30514             dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
30515                          room, gen0size));
30516             while ((bos < mark_stack_bos) &&
30517                    !((room >= gen0size) && large_chunk_found))
30518             {
30519                 uint8_t* plug = pinned_plug (pinned_plug_of (bos));
30520                 if (in_range_for_segment (plug, ephemeral_heap_segment))
30521                 {
30522                     if (plug >= gen0start)
30523                     {
30524                         size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
30525                         room += chunk;
30526                         if (!large_chunk_found)
30527                         {
30528                             large_chunk_found = (chunk >= largest_alloc);
30529                         }
30530                         dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
30531                                      room, large_chunk_found));
30532                     }
30533                 }
30534                 bos++;
30535             }
30536
30537             if (room >= gen0size)
30538             {
30539                 if (large_chunk_found)
30540                 {
30541                     dprintf (3, ("Enough room"));
30542                     return TRUE;
30543                 }
30544                 else
30545                 {
30546                     // now we need to find largest_alloc at the end of the segment.
30547                     if (end_seg >= end_space_after_gc())
30548                     {
30549                         dprintf (3, ("Enough room (may need end of seg)"));
30550                         return TRUE;
30551                     }
30552                 }
30553             }
30554
30555             dprintf (3, ("Not enough room"));
30556                 return FALSE;
30557         }
30558     }
30559     else
30560     {
30561         size_t end_space = 0;
30562         dynamic_data* dd = dynamic_data_of (0);
30563         if ((tp == tuning_deciding_condemned_gen) ||
30564             (tp == tuning_deciding_full_gc))
30565         {
30566             end_space = 2*dd_min_size (dd);
30567         }
30568         else
30569         {
30570             assert (tp == tuning_deciding_compaction);
30571             end_space = approximate_new_allocation();
30572         }
30573
30574         if (!((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space))
30575         {
30576             dprintf (GTC_LOG, ("ephemeral_gen_fit_p: does not fit without compaction"));
30577         }
30578         return ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space);
30579     }
30580 }
30581
30582 CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_bytes)
30583 {
30584     //create a new alloc context because gen3context is shared.
30585     alloc_context acontext;
30586     acontext.alloc_ptr = 0;
30587     acontext.alloc_limit = 0;
30588     acontext.alloc_bytes = 0;
30589 #ifdef MULTIPLE_HEAPS
30590     acontext.set_alloc_heap(vm_heap);
30591 #endif //MULTIPLE_HEAPS
30592
30593 #ifdef MARK_ARRAY
30594     uint8_t* current_lowest_address = lowest_address;
30595     uint8_t* current_highest_address = highest_address;
30596 #ifdef BACKGROUND_GC
30597     if (recursive_gc_sync::background_running_p())
30598     {
30599         current_lowest_address = background_saved_lowest_address;
30600         current_highest_address = background_saved_highest_address;
30601     }
30602 #endif //BACKGROUND_GC
30603 #endif // MARK_ARRAY
30604
30605     #if BIT64
30606     size_t maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
30607     #else
30608     size_t maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
30609     #endif
30610
30611     if (jsize >= maxObjectSize)
30612     {
30613         if (GCConfig::GetBreakOnOOM())
30614         {
30615             GCToOSInterface::DebugBreak();
30616         }
30617         return NULL;
30618     }
30619
30620     size_t size = AlignQword (jsize);
30621     int align_const = get_alignment_constant (FALSE);
30622 #ifdef FEATURE_LOH_COMPACTION
30623     size_t pad = Align (loh_padding_obj_size, align_const);
30624 #else
30625     size_t pad = 0;
30626 #endif //FEATURE_LOH_COMPACTION
30627
30628     assert (size >= Align (min_obj_size, align_const));
30629 #ifdef _MSC_VER
30630 #pragma inline_depth(0)
30631 #endif //_MSC_VER
30632     if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
30633     {
30634         return 0;
30635     }
30636
30637 #ifdef _MSC_VER
30638 #pragma inline_depth(20)
30639 #endif //_MSC_VER
30640
30641 #ifdef FEATURE_LOH_COMPACTION
30642     // The GC allocator made a free object already in this alloc context and
30643     // adjusted the alloc_ptr accordingly.
30644 #endif //FEATURE_LOH_COMPACTION
30645
30646     uint8_t*  result = acontext.alloc_ptr;
30647
30648     assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
30649     alloc_bytes += size;
30650
30651     CObjectHeader* obj = (CObjectHeader*)result;
30652
30653 #ifdef MARK_ARRAY
30654     if (recursive_gc_sync::background_running_p())
30655     {
30656         if ((result < current_highest_address) && (result >= current_lowest_address))
30657         {
30658             dprintf (3, ("Clearing mark bit at address %Ix",
30659                      (size_t)(&mark_array [mark_word_of (result)])));
30660
30661             mark_array_clear_marked (result);
30662         }
30663 #ifdef BACKGROUND_GC
30664         //the object has to cover one full mark uint32_t
30665         assert (size > mark_word_size);
30666         if (current_c_gc_state == c_gc_state_marking)
30667         {
30668             dprintf (3, ("Concurrent allocation of a large object %Ix",
30669                         (size_t)obj));
30670             //mark the new block specially so we know it is a new object
30671             if ((result < current_highest_address) && (result >= current_lowest_address))
30672             {
30673                 dprintf (3, ("Setting mark bit at address %Ix",
30674                             (size_t)(&mark_array [mark_word_of (result)])));
30675     
30676                 mark_array_set_marked (result);
30677             }
30678         }
30679 #endif //BACKGROUND_GC
30680     }
30681 #endif //MARK_ARRAY
30682
30683     assert (obj != 0);
30684     assert ((size_t)obj == Align ((size_t)obj, align_const));
30685
30686     return obj;
30687 }
30688
30689 void reset_memory (uint8_t* o, size_t sizeo)
30690 {
30691     if (sizeo > 128 * 1024)
30692     {
30693         // We cannot reset the memory for the useful part of a free object.
30694         size_t size_to_skip = min_free_list - plug_skew;
30695
30696         size_t page_start = align_on_page ((size_t)(o + size_to_skip));
30697         size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
30698         // Note we need to compensate for an OS bug here. This bug would cause the MEM_RESET to fail
30699         // on write watched memory.
30700         if (reset_mm_p)
30701         {
30702             reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, true /* unlock */);
30703         }
30704     }
30705 }
30706
30707 void gc_heap::reset_large_object (uint8_t* o)
30708 {
30709     // If it's a large object, allow the O/S to discard the backing store for these pages.
30710     reset_memory (o, size(o));
30711 }
30712
30713 BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp)
30714 {
30715     BOOL m = FALSE;
30716     // It shouldn't be necessary to do these comparisons because this is only used for blocking
30717     // GCs and LOH segments cannot be out of range.
30718     if ((o >= lowest_address) && (o < highest_address))
30719     {
30720         if (marked (o))
30721         {
30722             if (clearp)
30723             {
30724                 clear_marked (o);
30725                 if (pinned (o))
30726                     clear_pinned(o);
30727             }
30728             m = TRUE;
30729         }
30730         else
30731             m = FALSE;
30732     }
30733     else
30734         m = TRUE;
30735     return m;
30736 }
30737
30738 void gc_heap::walk_survivors_relocation (void* profiling_context, record_surv_fn fn)
30739 {
30740     // Now walk the portion of memory that is actually being relocated.
30741     walk_relocation (profiling_context, fn);
30742
30743 #ifdef FEATURE_LOH_COMPACTION
30744     if (loh_compacted_p)
30745     {
30746         walk_relocation_for_loh (profiling_context, fn);
30747     }
30748 #endif //FEATURE_LOH_COMPACTION
30749 }
30750
30751 void gc_heap::walk_survivors_for_loh (void* profiling_context, record_surv_fn fn)
30752 {
30753     generation* gen        = large_object_generation;
30754     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));;
30755
30756     PREFIX_ASSUME(seg != NULL);
30757
30758     uint8_t* o                = generation_allocation_start (gen);
30759     uint8_t* plug_end         = o;
30760     uint8_t* plug_start       = o;
30761
30762     while (1)
30763     {
30764         if (o >= heap_segment_allocated (seg))
30765         {
30766             seg = heap_segment_next (seg);
30767             if (seg == 0)
30768                 break;
30769             else
30770                 o = heap_segment_mem (seg);
30771         }
30772         if (large_object_marked(o, FALSE))
30773         {
30774             plug_start = o;
30775
30776             BOOL m = TRUE;
30777             while (m)
30778             {
30779                 o = o + AlignQword (size (o));
30780                 if (o >= heap_segment_allocated (seg))
30781                 {
30782                     break;
30783                 }
30784                 m = large_object_marked (o, FALSE);
30785             }
30786
30787             plug_end = o;
30788
30789             fn (plug_start, plug_end, 0, profiling_context, false, false);
30790         }
30791         else
30792         {
30793             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
30794             {
30795                 o = o + AlignQword (size (o));
30796             }
30797         }
30798     }
30799 }
30800
30801 #ifdef BACKGROUND_GC
30802
30803 BOOL gc_heap::background_object_marked (uint8_t* o, BOOL clearp)
30804 {
30805     BOOL m = FALSE;
30806     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
30807     {
30808         if (mark_array_marked (o))
30809         {
30810             if (clearp)
30811             {
30812                 mark_array_clear_marked (o);
30813                 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
30814                 dprintf (3, ("CM: %Ix", o));
30815             }
30816             m = TRUE;
30817         }
30818         else
30819             m = FALSE;
30820     }
30821     else
30822         m = TRUE;
30823
30824     dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
30825     return m;
30826 }
30827
30828 uint8_t* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
30829 {
30830     return
30831         (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
30832 }
30833
30834 void gc_heap::set_mem_verify (uint8_t* start, uint8_t* end, uint8_t b)
30835 {
30836 #ifdef VERIFY_HEAP
30837     if (end > start)
30838     {
30839         if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
30840            !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_MEM_FILL))
30841         {
30842             dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
30843             memset (start, b, (end - start));
30844         }
30845     }
30846 #endif //VERIFY_HEAP
30847 }
30848
30849 void gc_heap::generation_delete_heap_segment (generation* gen, 
30850                                               heap_segment* seg,
30851                                               heap_segment* prev_seg,
30852                                               heap_segment* next_seg)
30853 {
30854     dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
30855     if (gen == large_object_generation)
30856     {
30857         heap_segment_next (prev_seg) = next_seg;
30858
30859         dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
30860
30861         heap_segment_next (seg) = freeable_large_heap_segment;
30862         freeable_large_heap_segment = seg;
30863     }
30864     else
30865     {
30866         if (seg == ephemeral_heap_segment)
30867         {
30868             FATAL_GC_ERROR();
30869         }
30870
30871         heap_segment_next (next_seg) = prev_seg;
30872
30873         dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
30874         heap_segment_next (seg) = freeable_small_heap_segment;
30875         freeable_small_heap_segment = seg;
30876     }
30877
30878     decommit_heap_segment (seg);
30879     seg->flags |= heap_segment_flags_decommitted;
30880
30881     set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
30882 }
30883
30884 void gc_heap::process_background_segment_end (heap_segment* seg, 
30885                                           generation* gen,
30886                                           uint8_t* last_plug_end,
30887                                           heap_segment* start_seg,
30888                                           BOOL* delete_p)
30889 {
30890     *delete_p = FALSE;
30891     uint8_t* allocated = heap_segment_allocated (seg);
30892     uint8_t* background_allocated = heap_segment_background_allocated (seg);
30893
30894     dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)", 
30895                 (size_t)heap_segment_mem (seg), background_allocated, allocated));
30896
30897
30898     if (allocated != background_allocated)
30899     {
30900         if (gen == large_object_generation)
30901         {
30902             FATAL_GC_ERROR();
30903         }
30904
30905         dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[", 
30906                     (size_t)last_plug_end, background_allocated));
30907         thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
30908
30909         fix_brick_to_highest (last_plug_end, background_allocated);
30910
30911         // When we allowed fgc's during going through gaps, we could have erased the brick
30912         // that corresponds to bgc_allocated 'cause we had to update the brick there, 
30913         // recover it here.
30914         fix_brick_to_highest (background_allocated, background_allocated);
30915     }
30916     else
30917     {
30918         // by default, if allocated == background_allocated, it can't
30919         // be the ephemeral segment.
30920         if (seg == ephemeral_heap_segment)
30921         {
30922             FATAL_GC_ERROR();
30923         }
30924
30925         if (allocated == heap_segment_mem (seg))
30926         {
30927             // this can happen with LOH segments when multiple threads
30928             // allocate new segments and not all of them were needed to
30929             // satisfy allocation requests.
30930             assert (gen == large_object_generation);
30931         }
30932
30933         if (last_plug_end == heap_segment_mem (seg))
30934         {
30935             dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
30936                         (size_t)allocated, (*delete_p ? "should" : "should not")));
30937
30938             if (seg != start_seg)
30939             {
30940                 *delete_p = TRUE;
30941             }
30942         }
30943         else
30944         {
30945             dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
30946             heap_segment_allocated (seg) = last_plug_end;
30947             set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
30948
30949             decommit_heap_segment_pages (seg, 0);
30950         }
30951     }
30952
30953     dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
30954     bgc_verify_mark_array_cleared (seg);
30955 }
30956
30957 void gc_heap::process_n_background_segments (heap_segment* seg, 
30958                                              heap_segment* prev_seg,
30959                                              generation* gen)
30960 {
30961     assert (gen != large_object_generation);
30962
30963     while (seg)
30964     {
30965         dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
30966         heap_segment* next_seg = heap_segment_next (seg);
30967
30968         if (heap_segment_read_only_p (seg))
30969         {
30970             prev_seg = seg;
30971         }
30972         else
30973         {
30974             if (heap_segment_allocated (seg) == heap_segment_mem (seg))
30975             {
30976                 // This can happen - if we have a LOH segment where nothing survived
30977                 // or a SOH segment allocated by a gen1 GC when BGC was going where 
30978                 // nothing survived last time we did a gen1 GC.
30979                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
30980             }
30981             else
30982             {
30983                 prev_seg = seg;
30984             }
30985         }
30986
30987         verify_soh_segment_list();
30988         seg = next_seg;
30989     }
30990 }
30991
30992 inline
30993 BOOL gc_heap::fgc_should_consider_object (uint8_t* o,
30994                                           heap_segment* seg,
30995                                           BOOL consider_bgc_mark_p, 
30996                                           BOOL check_current_sweep_p, 
30997                                           BOOL check_saved_sweep_p)
30998 {
30999     // the logic for this function must be kept in sync with the analogous function
31000     // in ToolBox\SOS\Strike\gc.cpp
31001
31002     // TRUE means we don't need to check the bgc mark bit
31003     // FALSE means we do.
31004     BOOL no_bgc_mark_p = FALSE;
31005
31006     if (consider_bgc_mark_p)
31007     {
31008         if (check_current_sweep_p && (o < current_sweep_pos))
31009         {
31010             dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
31011             no_bgc_mark_p = TRUE;
31012         }
31013
31014         if (!no_bgc_mark_p)
31015         {
31016             if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
31017             {
31018                 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
31019                 no_bgc_mark_p = TRUE;
31020             }
31021
31022             if (!check_saved_sweep_p)
31023             {
31024                 uint8_t* background_allocated = heap_segment_background_allocated (seg);
31025                 // if this was the saved ephemeral segment, check_saved_sweep_p 
31026                 // would've been true.
31027                 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
31028                 // background_allocated could be 0 for the new segments acquired during bgc
31029                 // sweep and we still want no_bgc_mark_p to be true.
31030                 if (o >= background_allocated)
31031                 {
31032                     dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
31033                     no_bgc_mark_p = TRUE;
31034                 }
31035             }
31036         }
31037     }
31038     else
31039     {
31040         no_bgc_mark_p = TRUE;
31041     }
31042
31043     dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
31044     return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
31045 }
31046
31047 // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
31048 // if it's TRUE, check_current_sweep_p tells you if you should consider the
31049 // current sweep position or not.
31050 void gc_heap::should_check_bgc_mark (heap_segment* seg, 
31051                                      BOOL* consider_bgc_mark_p, 
31052                                      BOOL* check_current_sweep_p,
31053                                      BOOL* check_saved_sweep_p)
31054 {
31055     // the logic for this function must be kept in sync with the analogous function
31056     // in ToolBox\SOS\Strike\gc.cpp
31057     *consider_bgc_mark_p = FALSE;
31058     *check_current_sweep_p = FALSE;
31059     *check_saved_sweep_p = FALSE;
31060
31061     if (current_c_gc_state == c_gc_state_planning)
31062     {
31063         // We are doing the current_sweep_pos comparison here because we have yet to 
31064         // turn on the swept flag for the segment but in_range_for_segment will return
31065         // FALSE if the address is the same as reserved.
31066         if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
31067         {
31068             dprintf (3, ("seg %Ix is already swept by bgc", seg));
31069         }
31070         else
31071         {
31072             *consider_bgc_mark_p = TRUE;
31073
31074             dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
31075
31076             if (seg == saved_sweep_ephemeral_seg)
31077             {
31078                 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
31079                 *check_saved_sweep_p = TRUE;
31080             }
31081
31082             if (in_range_for_segment (current_sweep_pos, seg))
31083             {
31084                 dprintf (3, ("current sweep pos is %Ix and within seg %Ix", 
31085                               current_sweep_pos, seg));
31086                 *check_current_sweep_p = TRUE;
31087             }
31088         }
31089     }
31090 }
31091
31092 void gc_heap::background_ephemeral_sweep()
31093 {
31094     dprintf (3, ("bgc ephemeral sweep"));
31095
31096     int align_const = get_alignment_constant (TRUE);
31097
31098     saved_sweep_ephemeral_seg = ephemeral_heap_segment;
31099     saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
31100
31101     // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
31102     // we thread onto a list first then publish it when we are done.
31103     allocator youngest_free_list;
31104     size_t youngest_free_list_space = 0;
31105     size_t youngest_free_obj_space = 0;
31106
31107     youngest_free_list.clear();
31108
31109     for (int i = 0; i <= (max_generation - 1); i++)
31110     {
31111         generation* gen_to_reset = generation_of (i);
31112         assert (generation_free_list_space (gen_to_reset) == 0);
31113         // Can only assert free_list_space is 0, not free_obj_space as the allocator could have added 
31114         // something there.
31115     }
31116
31117     for (int i = (max_generation - 1); i >= 0; i--)
31118     {
31119         generation* current_gen = generation_of (i);
31120         uint8_t* o = generation_allocation_start (current_gen);
31121         //Skip the generation gap object
31122         o = o + Align(size (o), align_const);
31123         uint8_t* end = ((i > 0) ?
31124                      generation_allocation_start (generation_of (i - 1)) : 
31125                      heap_segment_allocated (ephemeral_heap_segment));
31126
31127         uint8_t* plug_end = o;
31128         uint8_t* plug_start = o;
31129         BOOL marked_p = FALSE;
31130
31131         while (o < end)
31132         {
31133             marked_p = background_object_marked (o, TRUE);
31134             if (marked_p)
31135             {
31136                 plug_start = o;
31137                 size_t plug_size = plug_start - plug_end;
31138
31139                 if (i >= 1)
31140                 {
31141                     thread_gap (plug_end, plug_size, current_gen);
31142                 }
31143                 else
31144                 {
31145                     if (plug_size > 0)
31146                     {
31147                         make_unused_array (plug_end, plug_size);
31148                         if (plug_size >= min_free_list)
31149                         {
31150                             youngest_free_list_space += plug_size;
31151                             youngest_free_list.thread_item (plug_end, plug_size);
31152                         }
31153                         else
31154                         {
31155                             youngest_free_obj_space += plug_size;
31156                         }
31157                     }
31158                 }
31159
31160                 fix_brick_to_highest (plug_end, plug_start);
31161                 fix_brick_to_highest (plug_start, plug_start);
31162
31163                 BOOL m = TRUE;
31164                 while (m)
31165                 {
31166                     o = o + Align (size (o), align_const);
31167                     if (o >= end)
31168                     {
31169                         break;
31170                     }
31171
31172                     m = background_object_marked (o, TRUE);
31173                 }
31174                 plug_end = o;
31175                 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31176             }
31177             else
31178             {
31179                 while ((o < end) && !background_object_marked (o, FALSE))
31180                 {
31181                     o = o + Align (size (o), align_const);
31182                 }
31183             }
31184         }
31185
31186         if (plug_end != end)
31187         {
31188             if (i >= 1)
31189             {
31190                 thread_gap (plug_end, end - plug_end, current_gen);
31191                 fix_brick_to_highest (plug_end, end);
31192             }
31193             else
31194             {
31195                 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
31196                 // the following line is temporary.
31197                 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
31198 #ifdef VERIFY_HEAP
31199                 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
31200                 {
31201                     make_unused_array (plug_end, (end - plug_end));
31202                 }
31203 #endif //VERIFY_HEAP
31204             }
31205         }
31206
31207         dd_fragmentation (dynamic_data_of (i)) = 
31208             generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
31209     }
31210
31211     generation* youngest_gen = generation_of (0);
31212     generation_free_list_space (youngest_gen) = youngest_free_list_space;
31213     generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
31214     dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
31215     generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
31216 }
31217
31218 void gc_heap::background_sweep()
31219 {
31220     generation* gen         = generation_of (max_generation);
31221     dynamic_data* dd        = dynamic_data_of (max_generation);
31222     // For SOH segments we go backwards.
31223     heap_segment* start_seg = ephemeral_heap_segment;
31224     PREFIX_ASSUME(start_seg != NULL);
31225     heap_segment* fseg      = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31226     heap_segment* seg       = start_seg;
31227     uint8_t* o                 = heap_segment_mem (seg);
31228
31229     heap_segment* prev_seg = heap_segment_next (seg);
31230     int align_const        = get_alignment_constant (TRUE);
31231     if (seg == fseg)
31232     {
31233         assert (o == generation_allocation_start (generation_of (max_generation)));
31234         o = o + Align(size (o), align_const);
31235     }
31236
31237     uint8_t* plug_end      = o;
31238     uint8_t* plug_start    = o;
31239     next_sweep_obj         = o;
31240     current_sweep_pos      = o;
31241
31242     //uint8_t* end              = background_next_end (seg, (gen == large_object_generation));
31243     uint8_t* end              = heap_segment_background_allocated (seg);
31244     BOOL delete_p          = FALSE;
31245
31246     //concurrent_print_time_delta ("finished with mark and start with sweep");
31247     concurrent_print_time_delta ("Sw");
31248     dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31249
31250     //block concurrent allocation for large objects
31251     dprintf (3, ("lh state: planning"));
31252     if (gc_lh_block_event.IsValid())
31253     {
31254         gc_lh_block_event.Reset();
31255     }
31256
31257     for (int i = 0; i <= (max_generation + 1); i++)
31258     {
31259         generation* gen_to_reset = generation_of (i);
31260         generation_allocator (gen_to_reset)->clear();
31261         generation_free_list_space (gen_to_reset) = 0;
31262         generation_free_obj_space (gen_to_reset) = 0;
31263         generation_free_list_allocated (gen_to_reset) = 0;
31264         generation_end_seg_allocated (gen_to_reset) = 0;
31265         generation_condemned_allocated (gen_to_reset) = 0; 
31266         //reset the allocation so foreground gc can allocate into older generation
31267         generation_allocation_pointer (gen_to_reset)= 0;
31268         generation_allocation_limit (gen_to_reset) = 0;
31269         generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
31270     }
31271
31272     FIRE_EVENT(BGC2ndNonConEnd);
31273
31274     current_bgc_state = bgc_sweep_soh;
31275     verify_soh_segment_list();
31276
31277 #ifdef FEATURE_BASICFREEZE
31278     if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
31279         ro_segments_in_range)
31280     {
31281         sweep_ro_segments (generation_start_segment (gen));
31282     }
31283 #endif // FEATURE_BASICFREEZE
31284
31285     //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
31286     if (current_c_gc_state != c_gc_state_planning)
31287     {
31288         current_c_gc_state = c_gc_state_planning;
31289     }
31290
31291     concurrent_print_time_delta ("Swe");
31292
31293     heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
31294     PREFIX_ASSUME(loh_seg  != NULL);
31295     while (loh_seg )
31296     {
31297         loh_seg->flags &= ~heap_segment_flags_swept;
31298         heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
31299         loh_seg = heap_segment_next_rw (loh_seg);
31300     }
31301
31302 #ifdef MULTIPLE_HEAPS
31303     bgc_t_join.join(this, gc_join_restart_ee);
31304     if (bgc_t_join.joined())
31305 #endif //MULTIPLE_HEAPS 
31306     {
31307 #ifdef MULTIPLE_HEAPS
31308         dprintf(2, ("Starting BGC threads for resuming EE"));
31309         bgc_t_join.restart();
31310 #endif //MULTIPLE_HEAPS
31311     }
31312
31313     if (heap_number == 0)
31314     {
31315         restart_EE ();
31316     }
31317
31318     FIRE_EVENT(BGC2ndConBegin);
31319
31320     background_ephemeral_sweep();
31321
31322 #ifdef MULTIPLE_HEAPS
31323     bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
31324     if (bgc_t_join.joined())
31325 #endif //MULTIPLE_HEAPS
31326     {
31327 #ifdef FEATURE_EVENT_TRACE
31328         bgc_heap_walk_for_etw_p = GCEventStatus::IsEnabled(GCEventProvider_Default, 
31329                                                            GCEventKeyword_GCHeapSurvivalAndMovement, 
31330                                                            GCEventLevel_Information);
31331 #endif //FEATURE_EVENT_TRACE
31332
31333         leave_spin_lock (&gc_lock);
31334
31335 #ifdef MULTIPLE_HEAPS
31336         dprintf(2, ("Starting BGC threads for BGC sweeping"));
31337         bgc_t_join.restart();
31338 #endif //MULTIPLE_HEAPS
31339     }
31340
31341     disable_preemptive (true);
31342
31343     dprintf (2, ("bgs: sweeping gen2 objects"));
31344     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31345                     (size_t)heap_segment_mem (seg),
31346                     (size_t)heap_segment_allocated (seg),
31347                     (size_t)heap_segment_background_allocated (seg)));
31348
31349     int num_objs = 256;
31350     int current_num_objs = 0;
31351     heap_segment* next_seg = 0;
31352
31353     while (1)
31354     {
31355         if (o >= end)
31356         {
31357             if (gen == large_object_generation)
31358             {
31359                 next_seg = heap_segment_next (seg);
31360             }
31361             else
31362             {
31363                 next_seg = heap_segment_prev (fseg, seg);
31364             }
31365
31366             delete_p = FALSE;
31367
31368             if (!heap_segment_read_only_p (seg))
31369             {
31370                 if (gen == large_object_generation)
31371                 {
31372                     // we can treat all LOH segments as in the bgc domain
31373                     // regardless of whether we saw in bgc mark or not
31374                     // because we don't allow LOH allocations during bgc
31375                     // sweep anyway - the LOH segments can't change.
31376                     process_background_segment_end (seg, gen, plug_end, 
31377                                                     start_seg, &delete_p);
31378                 }
31379                 else
31380                 {
31381                     assert (heap_segment_background_allocated (seg) != 0);
31382                     process_background_segment_end (seg, gen, plug_end, 
31383                                                     start_seg, &delete_p);
31384
31385                     assert (next_seg || !delete_p);
31386                 }
31387             }
31388
31389             if (delete_p)
31390             {
31391                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
31392             }
31393             else
31394             {
31395                 prev_seg = seg;
31396                 dprintf (2, ("seg %Ix has been swept", seg));
31397                 seg->flags |= heap_segment_flags_swept;
31398             }
31399
31400             verify_soh_segment_list();
31401
31402             seg = next_seg;
31403
31404             dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
31405             
31406             if (seg == 0)
31407             {
31408                 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31409
31410                 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31411
31412                 if (gen != large_object_generation)
31413                 {
31414                     dprintf (2, ("bgs: sweeping gen3 objects"));
31415                     current_bgc_state = bgc_sweep_loh;
31416                     gen = generation_of (max_generation+1);
31417                     start_seg = heap_segment_rw (generation_start_segment (gen));
31418
31419                     PREFIX_ASSUME(start_seg != NULL);
31420
31421                     seg = start_seg;
31422                     prev_seg = 0;
31423                     o = generation_allocation_start (gen);
31424                     assert (method_table (o) == g_gc_pFreeObjectMethodTable);
31425                     align_const = get_alignment_constant (FALSE);
31426                     o = o + Align(size (o), align_const);
31427                     plug_end = o;
31428                     end = heap_segment_allocated (seg);
31429                     dprintf (2, ("sweeping gen3 objects"));
31430                     generation_free_obj_space (gen) = 0;
31431                     generation_allocator (gen)->clear();
31432                     generation_free_list_space (gen) = 0;
31433
31434                     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31435                                     (size_t)heap_segment_mem (seg),
31436                                     (size_t)heap_segment_allocated (seg),
31437                                     (size_t)heap_segment_background_allocated (seg)));
31438                 }
31439                 else
31440                     break;
31441             }
31442             else
31443             {
31444                 o = heap_segment_mem (seg);
31445                 if (seg == fseg)
31446                 {
31447                     assert (gen != large_object_generation);
31448                     assert (o == generation_allocation_start (generation_of (max_generation)));
31449                     align_const = get_alignment_constant (TRUE);
31450                     o = o + Align(size (o), align_const);
31451                 }
31452
31453                 plug_end = o;
31454                 current_sweep_pos = o;
31455                 next_sweep_obj = o;
31456                 
31457                 allow_fgc();
31458                 end = background_next_end (seg, (gen == large_object_generation));
31459                 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31460                                 (size_t)heap_segment_mem (seg),
31461                                 (size_t)heap_segment_allocated (seg),
31462                                 (size_t)heap_segment_background_allocated (seg)));
31463             }
31464         }
31465
31466         if ((o < end) && background_object_marked (o, TRUE))
31467         {
31468             plug_start = o;
31469             if (gen == large_object_generation)
31470             {
31471                 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
31472             }
31473
31474             thread_gap (plug_end, plug_start-plug_end, gen);
31475             if (gen != large_object_generation)
31476             {
31477                 add_gen_free (max_generation, plug_start-plug_end);
31478                 fix_brick_to_highest (plug_end, plug_start);
31479                 // we need to fix the brick for the next plug here 'cause an FGC can
31480                 // happen and can't read a stale brick.
31481                 fix_brick_to_highest (plug_start, plug_start);
31482             }
31483
31484             BOOL m = TRUE;
31485
31486             while (m)
31487             {
31488                 next_sweep_obj = o + Align(size (o), align_const);
31489                 current_num_objs++;
31490                 if (current_num_objs >= num_objs)
31491                 {
31492                     current_sweep_pos = next_sweep_obj;
31493
31494                     allow_fgc();
31495                     current_num_objs = 0;
31496                 }
31497
31498                 o = next_sweep_obj;
31499                 if (o >= end)
31500                 {
31501                     break;
31502                 }
31503
31504                 m = background_object_marked (o, TRUE);
31505             }
31506             plug_end = o;
31507             if (gen != large_object_generation)
31508             {
31509                 add_gen_plug (max_generation, plug_end-plug_start);
31510                 dd_survived_size (dd) += (plug_end - plug_start);
31511             }
31512             dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31513         }
31514         else
31515         {
31516             while ((o < end) && !background_object_marked (o, FALSE))
31517             {
31518                 next_sweep_obj = o + Align(size (o), align_const);;
31519                 current_num_objs++;
31520                 if (current_num_objs >= num_objs)
31521                 {
31522                     current_sweep_pos = plug_end;
31523                     dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
31524                     allow_fgc();
31525                     current_num_objs = 0;
31526                 }
31527
31528                 o = next_sweep_obj;
31529             }
31530         }
31531     }
31532
31533     size_t total_loh_size = generation_size (max_generation + 1);
31534     size_t total_soh_size = generation_sizes (generation_of (max_generation));
31535
31536     dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
31537
31538     dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id", 
31539         generation_free_list_space (generation_of (max_generation)),
31540         generation_free_obj_space (generation_of (max_generation))));
31541     dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id", 
31542         heap_number,
31543         generation_free_list_space (generation_of (max_generation + 1)),
31544         generation_free_obj_space (generation_of (max_generation + 1))));
31545
31546     FIRE_EVENT(BGC2ndConEnd);
31547     concurrent_print_time_delta ("background sweep");
31548     
31549     heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31550     PREFIX_ASSUME(reset_seg != NULL);
31551
31552     while (reset_seg)
31553     {
31554         heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
31555         heap_segment_background_allocated (reset_seg) = 0;
31556         reset_seg = heap_segment_next_rw (reset_seg);
31557     }
31558
31559     // We calculate dynamic data here because if we wait till we signal the lh event, 
31560     // the allocation thread can change the fragmentation and we may read an intermediate
31561     // value (which can be greater than the generation size). Plus by that time it won't 
31562     // be accurate.
31563     compute_new_dynamic_data (max_generation);
31564
31565     enable_preemptive ();
31566
31567 #ifdef MULTIPLE_HEAPS
31568     bgc_t_join.join(this, gc_join_set_state_free);
31569     if (bgc_t_join.joined())
31570 #endif //MULTIPLE_HEAPS
31571     {
31572         // TODO: We are using this join just to set the state. Should
31573         // look into eliminating it - check to make sure things that use 
31574         // this state can live with per heap state like should_check_bgc_mark.
31575         current_c_gc_state = c_gc_state_free;
31576
31577 #ifdef MULTIPLE_HEAPS
31578         dprintf(2, ("Starting BGC threads after background sweep phase"));
31579         bgc_t_join.restart();
31580 #endif //MULTIPLE_HEAPS
31581     }
31582
31583     disable_preemptive (true);
31584
31585     if (gc_lh_block_event.IsValid())
31586     {
31587         gc_lh_block_event.Set();
31588     }
31589
31590     //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31591     dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
31592 }
31593 #endif //BACKGROUND_GC
31594
31595 void gc_heap::sweep_large_objects ()
31596 {
31597     //this min value is for the sake of the dynamic tuning.
31598     //so we know that we are not starting even if we have no
31599     //survivors.
31600     generation* gen        = large_object_generation;
31601     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
31602
31603     PREFIX_ASSUME(start_seg != NULL);
31604
31605     heap_segment* seg      = start_seg;
31606     heap_segment* prev_seg = 0;
31607     uint8_t* o             = generation_allocation_start (gen);
31608     int align_const        = get_alignment_constant (FALSE);
31609
31610     //Skip the generation gap object
31611     o = o + Align(size (o), align_const);
31612
31613     uint8_t* plug_end         = o;
31614     uint8_t* plug_start       = o;
31615
31616     generation_allocator (gen)->clear();
31617     generation_free_list_space (gen) = 0;
31618     generation_free_obj_space (gen) = 0;
31619
31620
31621     dprintf (3, ("sweeping large objects"));
31622     dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix", 
31623                  (size_t)seg,
31624                  (size_t)heap_segment_mem (seg),
31625                  (size_t)heap_segment_allocated (seg),
31626                  o));
31627
31628     while (1)
31629     {
31630         if (o >= heap_segment_allocated (seg))
31631         {
31632             heap_segment* next_seg = heap_segment_next (seg);
31633             //delete the empty segment if not the only one
31634             if ((plug_end == heap_segment_mem (seg)) &&
31635                 (seg != start_seg) && !heap_segment_read_only_p (seg))
31636             {
31637                 //prepare for deletion
31638                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
31639                 assert (prev_seg);
31640                 heap_segment_next (prev_seg) = next_seg;
31641                 heap_segment_next (seg) = freeable_large_heap_segment;
31642                 freeable_large_heap_segment = seg;
31643             }
31644             else
31645             {
31646                 if (!heap_segment_read_only_p (seg))
31647                 {
31648                     dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
31649                     heap_segment_allocated (seg) = plug_end;
31650                     decommit_heap_segment_pages (seg, 0);
31651                 }
31652                 prev_seg = seg;
31653             }
31654             seg = next_seg;
31655             if (seg == 0)
31656                 break;
31657             else
31658             {
31659                 o = heap_segment_mem (seg);
31660                 plug_end = o;
31661                 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
31662                              (size_t)heap_segment_mem (seg),
31663                              (size_t)heap_segment_allocated (seg)));
31664             }
31665         }
31666         if (large_object_marked(o, TRUE))
31667         {
31668             plug_start = o;
31669             //everything between plug_end and plug_start is free
31670             thread_gap (plug_end, plug_start-plug_end, gen);
31671
31672             BOOL m = TRUE;
31673             while (m)
31674             {
31675                 o = o + AlignQword (size (o));
31676                 if (o >= heap_segment_allocated (seg))
31677                 {
31678                     break;
31679                 }
31680                 m = large_object_marked (o, TRUE);
31681             }
31682             plug_end = o;
31683             dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31684         }
31685         else
31686         {
31687             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
31688             {
31689                 o = o + AlignQword (size (o));
31690             }
31691         }
31692     }
31693
31694     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31695
31696     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31697 }
31698
31699 void gc_heap::relocate_in_large_objects ()
31700 {
31701     relocate_args args;
31702     args.low = gc_low;
31703     args.high = gc_high;
31704     args.last_plug = 0;
31705
31706     generation* gen = large_object_generation;
31707
31708     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31709
31710     PREFIX_ASSUME(seg != NULL);
31711
31712     uint8_t* o = generation_allocation_start (gen);
31713
31714     while (1)
31715     {
31716         if (o >= heap_segment_allocated (seg))
31717         {
31718             seg = heap_segment_next_rw (seg);
31719             if (seg == 0)
31720                 break;
31721             else
31722             {
31723                 o = heap_segment_mem (seg);
31724             }
31725         }
31726         while (o < heap_segment_allocated (seg))
31727         {
31728             check_class_object_demotion (o);
31729             if (contain_pointers (o))
31730             {
31731                 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
31732                 go_through_object_nostart (method_table (o), o, size(o), pval,
31733                         {
31734                             reloc_survivor_helper (pval);
31735                         });
31736             }
31737             o = o + AlignQword (size (o));
31738         }
31739     }
31740 }
31741
31742 void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
31743                                                     BOOL relocating)
31744 {
31745     uint8_t*      low               = gc_low;
31746     size_t        end_card          = 0;
31747     generation*   oldest_gen        = generation_of (max_generation+1);
31748     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
31749
31750     PREFIX_ASSUME(seg != NULL);
31751
31752     uint8_t*      beg               = generation_allocation_start (oldest_gen);
31753     uint8_t*      end               = heap_segment_allocated (seg);
31754
31755     size_t  cg_pointers_found = 0;
31756
31757     size_t  card_word_end = (card_of (align_on_card_word (end)) /
31758                              card_word_width);
31759
31760     size_t      n_eph             = 0;
31761     size_t      n_gen             = 0;
31762     size_t      n_card_set        = 0;
31763     uint8_t*    next_boundary = (relocating ?
31764                               generation_plan_allocation_start (generation_of (max_generation -1)) :
31765                               ephemeral_low);
31766
31767     uint8_t*    nhigh         = (relocating ?
31768                               heap_segment_plan_allocated (ephemeral_heap_segment) :
31769                               ephemeral_high);
31770
31771     BOOL          foundp            = FALSE;
31772     uint8_t*      start_address     = 0;
31773     uint8_t*      limit             = 0;
31774     size_t        card              = card_of (beg);
31775     uint8_t*      o                 = beg;
31776 #ifdef BACKGROUND_GC
31777     BOOL consider_bgc_mark_p        = FALSE;
31778     BOOL check_current_sweep_p      = FALSE;
31779     BOOL check_saved_sweep_p        = FALSE;
31780     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31781 #endif //BACKGROUND_GC
31782
31783     size_t total_cards_cleared = 0;
31784
31785     //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
31786     dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
31787     while (1)
31788     {
31789         if ((o < end) && (card_of(o) > card))
31790         {
31791             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
31792             if (cg_pointers_found == 0)
31793             {
31794                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
31795                 clear_cards (card, card_of((uint8_t*)o));
31796                 total_cards_cleared += (card_of((uint8_t*)o) - card);
31797             }
31798             n_eph +=cg_pointers_found;
31799             cg_pointers_found = 0;
31800             card = card_of ((uint8_t*)o);
31801         }
31802         if ((o < end) &&(card >= end_card))
31803         {
31804             foundp = find_card (card_table, card, card_word_end, end_card);
31805             if (foundp)
31806             {
31807                 n_card_set+= end_card - card;
31808                 start_address = max (beg, card_address (card));
31809             }
31810             limit = min (end, card_address (end_card));
31811         }
31812         if ((!foundp) || (o >= end) || (card_address (card) >= end))
31813         {
31814             if ((foundp) && (cg_pointers_found == 0))
31815             {
31816                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
31817                            (size_t)card_address(card+1)));
31818                 clear_cards (card, card+1);
31819                 total_cards_cleared += 1;
31820             }
31821             n_eph +=cg_pointers_found;
31822             cg_pointers_found = 0;
31823             if ((seg = heap_segment_next_rw (seg)) != 0)
31824             {
31825 #ifdef BACKGROUND_GC
31826                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31827 #endif //BACKGROUND_GC
31828                 beg = heap_segment_mem (seg);
31829                 end = compute_next_end (seg, low);
31830                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
31831                 card = card_of (beg);
31832                 o  = beg;
31833                 end_card = 0;
31834                 continue;
31835             }
31836             else
31837             {
31838                 break;
31839             }
31840         }
31841
31842         assert (card_set_p (card));
31843         {
31844             dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
31845                        card, (size_t)o, (size_t)limit));
31846
31847             assert (Align (size (o)) >= Align (min_obj_size));
31848             size_t s = size (o);
31849             uint8_t* next_o =  o + AlignQword (s);
31850             Prefetch (next_o);
31851
31852             while (o < limit)
31853             {
31854                 s = size (o);
31855                 assert (Align (s) >= Align (min_obj_size));
31856                 next_o =  o + AlignQword (s);
31857                 Prefetch (next_o);
31858
31859                 dprintf (4, ("|%Ix|", (size_t)o));
31860                 if (next_o < start_address)
31861                 {
31862                     goto end_object;
31863                 }
31864
31865 #ifdef BACKGROUND_GC
31866                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
31867                 {
31868                     goto end_object;
31869                 }
31870 #endif //BACKGROUND_GC
31871
31872 #ifdef COLLECTIBLE_CLASS
31873                 if (is_collectible(o))
31874                 {
31875                     BOOL passed_end_card_p = FALSE;
31876
31877                     if (card_of (o) > card)
31878                     {
31879                         passed_end_card_p = card_transition (o, end, 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
31887                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
31888                     {
31889                         // card is valid and it covers the head of the object
31890                         if (fn == &gc_heap::relocate_address)
31891                         {
31892                             keep_card_live (o, n_gen, cg_pointers_found);
31893                         }
31894                         else
31895                         {
31896                             uint8_t* class_obj = get_class_object (o);
31897                             mark_through_cards_helper (&class_obj, n_gen,
31898                                                     cg_pointers_found, fn,
31899                                                     nhigh, next_boundary);
31900                         }
31901                     }
31902
31903                     if (passed_end_card_p)
31904                     {
31905                         if (foundp && (card_address (card) < next_o))
31906                         {
31907                             goto go_through_refs;
31908                         }
31909                         else 
31910                         {
31911                             goto end_object;
31912                         }
31913                     }
31914                 }
31915
31916 go_through_refs:
31917 #endif //COLLECTIBLE_CLASS
31918
31919                 if (contain_pointers (o))
31920                 {
31921                     dprintf(3,("Going through %Ix", (size_t)o));
31922
31923                     go_through_object (method_table(o), o, s, poo,
31924                                        start_address, use_start, (o + s),
31925                        {
31926                            if (card_of ((uint8_t*)poo) > card)
31927                            {
31928                                 BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
31929                                         card_word_end,
31930                                         cg_pointers_found, 
31931                                         n_eph, n_card_set,
31932                                         card, end_card,
31933                                         foundp, start_address,
31934                                         limit, total_cards_cleared);
31935
31936                                 if (passed_end_card_p)
31937                                 {
31938                                     if (foundp && (card_address (card) < next_o))
31939                                     {
31940                                         //new_start();
31941                                         {
31942                                             if (ppstop <= (uint8_t**)start_address)
31943                                             {break;}
31944                                             else if (poo < (uint8_t**)start_address)
31945                                             {poo = (uint8_t**)start_address;}
31946                                         }
31947                                     }
31948                                     else
31949                                     {
31950                                         goto end_object;
31951                                     }
31952                                 }
31953                             }
31954
31955                            mark_through_cards_helper (poo, n_gen,
31956                                                       cg_pointers_found, fn,
31957                                                       nhigh, next_boundary);
31958                        }
31959                         );
31960                 }
31961
31962             end_object:
31963                 o = next_o;
31964             }
31965
31966         }
31967     }
31968
31969     // compute the efficiency ratio of the card table
31970     if (!relocating)
31971     {
31972         generation_skip_ratio = min (((n_eph > 800) ?
31973                                       (int)(((float)n_gen / (float)n_eph) * 100) : 100),
31974                                      generation_skip_ratio);
31975
31976         dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d", 
31977              n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
31978     }
31979     else
31980     {
31981         dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d", 
31982              n_eph, n_gen, n_card_set, generation_skip_ratio));
31983     }
31984 }
31985
31986 void gc_heap::descr_segment (heap_segment* seg )
31987 {
31988 #ifdef TRACE_GC
31989     uint8_t*  x = heap_segment_mem (seg);
31990     while (x < heap_segment_allocated (seg))
31991     {
31992         dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
31993         x = x + Align(size (x));
31994     }
31995 #else // TRACE_GC
31996     UNREFERENCED_PARAMETER(seg);
31997 #endif // TRACE_GC
31998 }
31999
32000 void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
32001 {
32002 #ifdef MULTIPLE_HEAPS
32003     int n_heaps = g_theGCHeap->GetNumberOfHeaps ();
32004     for (int i = 0; i < n_heaps; i++)
32005     {
32006         gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
32007 #else //MULTIPLE_HEAPS
32008     {
32009         gc_heap* hp = NULL;
32010 #ifdef _PREFAST_
32011         // prefix complains about us dereferencing hp in wks build even though we only access static members
32012         // this way. not sure how to shut it up except for this ugly workaround:
32013         PREFIX_ASSUME(hp != NULL);
32014 #endif // _PREFAST_
32015 #endif //MULTIPLE_HEAPS
32016
32017         int curr_gen_number0 = max_generation+1;
32018         while (curr_gen_number0 >= 0)
32019         {
32020             generation* gen = hp->generation_of (curr_gen_number0);
32021             heap_segment* seg = generation_start_segment (gen);
32022             while (seg && (seg != hp->ephemeral_heap_segment))
32023             {
32024                 assert (curr_gen_number0 > 0);
32025
32026                 // report bounds from heap_segment_mem (seg) to
32027                 // heap_segment_allocated (seg);
32028                 // for generation # curr_gen_number0
32029                 // for heap # heap_no
32030
32031                 fn(context, curr_gen_number0, heap_segment_mem (seg),
32032                                               heap_segment_allocated (seg),
32033                                               curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
32034
32035                 seg = heap_segment_next (seg);
32036             }
32037             if (seg)
32038             {
32039                 assert (seg == hp->ephemeral_heap_segment);
32040                 assert (curr_gen_number0 <= max_generation);
32041                 //
32042                 if (curr_gen_number0 == max_generation)
32043                 {
32044                     if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
32045                     {
32046                         // report bounds from heap_segment_mem (seg) to
32047                         // generation_allocation_start (generation_of (max_generation-1))
32048                         // for heap # heap_number
32049
32050                         fn(context, curr_gen_number0, heap_segment_mem (seg),
32051                                                       generation_allocation_start (hp->generation_of (max_generation-1)),
32052                                                       generation_allocation_start (hp->generation_of (max_generation-1)) );
32053                     }
32054                 }
32055                 else if (curr_gen_number0 != 0)
32056                 {
32057                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32058                     // to generation_allocation_start (generation_of (curr_gen_number0-1))
32059                     // for heap # heap_number
32060
32061                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32062                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
32063                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
32064                 }
32065                 else
32066                 {
32067                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32068                     // to heap_segment_allocated (ephemeral_heap_segment);
32069                     // for heap # heap_number
32070
32071                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32072                                                   heap_segment_allocated (hp->ephemeral_heap_segment),
32073                                                   heap_segment_reserved (hp->ephemeral_heap_segment) );
32074                 }
32075             }
32076             curr_gen_number0--;
32077         }
32078     }
32079 }
32080
32081 #ifdef TRACE_GC
32082 // Note that when logging is on it can take a long time to go through the free items.
32083 void gc_heap::print_free_list (int gen, heap_segment* seg)
32084 {
32085     UNREFERENCED_PARAMETER(gen);
32086     UNREFERENCED_PARAMETER(seg);
32087 /*
32088     if (settings.concurrent == FALSE)
32089     {
32090         uint8_t* seg_start = heap_segment_mem (seg);
32091         uint8_t* seg_end = heap_segment_allocated (seg);
32092
32093         dprintf (3, ("Free list in seg %Ix:", seg_start));
32094
32095         size_t total_free_item = 0;
32096
32097         allocator* gen_allocator = generation_allocator (generation_of (gen));
32098         for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
32099         {
32100             uint8_t* fo = gen_allocator->alloc_list_head_of (b);
32101             while (fo)
32102             {
32103                 if (fo >= seg_start && fo < seg_end)
32104                 {
32105                     total_free_item++;
32106
32107                     size_t free_item_len = size(fo);
32108
32109                     dprintf (3, ("[%Ix, %Ix[:%Id",
32110                                  (size_t)fo,
32111                                  (size_t)(fo + free_item_len),
32112                                  free_item_len));
32113                 }
32114
32115                 fo = free_list_slot (fo);
32116             }
32117         }
32118
32119         dprintf (3, ("total %Id free items", total_free_item));
32120     }
32121 */
32122 }
32123 #endif //TRACE_GC
32124
32125 void gc_heap::descr_generations (BOOL begin_gc_p)
32126 {
32127     UNREFERENCED_PARAMETER(begin_gc_p);
32128 #ifdef STRESS_LOG
32129     if (StressLog::StressLogOn(LF_GC, LL_INFO10))
32130     {
32131         gc_heap* hp = 0;
32132 #ifdef MULTIPLE_HEAPS
32133         hp= this;
32134 #endif //MULTIPLE_HEAPS
32135
32136         STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
32137         for (int n = max_generation; n >= 0; --n)
32138         {
32139             STRESS_LOG4(LF_GC, LL_INFO10, "    Generation %d [%p, %p] cur = %p\n",
32140                     n,
32141                     generation_allocation_start(generation_of(n)),
32142                     generation_allocation_limit(generation_of(n)),
32143                     generation_allocation_pointer(generation_of(n)));
32144
32145             heap_segment* seg = generation_start_segment(generation_of(n));
32146             while (seg)
32147             {
32148                 STRESS_LOG4(LF_GC, LL_INFO10, "        Segment mem %p alloc = %p used %p committed %p\n",
32149                         heap_segment_mem(seg),
32150                         heap_segment_allocated(seg),
32151                         heap_segment_used(seg),
32152                         heap_segment_committed(seg));
32153                 seg = heap_segment_next(seg);
32154             }
32155         }
32156     }
32157 #endif  // STRESS_LOG
32158
32159 #ifdef TRACE_GC
32160     dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
32161              (size_t) lowest_address, (size_t) highest_address));
32162 #ifdef BACKGROUND_GC
32163     dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
32164              (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
32165 #endif //BACKGROUND_GC
32166
32167     if (heap_number == 0)
32168     {
32169         dprintf (1, ("total heap size: %Id, commit size: %Id", get_total_heap_size(), get_total_committed_size()));
32170     }
32171
32172     int curr_gen_number = max_generation+1;
32173     while (curr_gen_number >= 0)
32174     {
32175         size_t total_gen_size = generation_size (curr_gen_number);
32176 #ifdef SIMPLE_DPRINTF
32177         dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
32178                       (begin_gc_p ? "BEG" : "END"),
32179                       settings.condemned_generation,
32180                       curr_gen_number,
32181                       total_gen_size,
32182                       dd_fragmentation (dynamic_data_of (curr_gen_number)),
32183                       generation_free_list_space (generation_of (curr_gen_number)),
32184                       generation_free_obj_space (generation_of (curr_gen_number)),
32185                       (total_gen_size ? 
32186                         (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
32187                         0),
32188                       (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
32189                       (settings.heap_expansion ? "(EX)" : " "),
32190                       (settings.promotion ? "Promotion" : "NoPromotion")));
32191 #else
32192         dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
32193                       curr_gen_number,
32194                       size (generation_allocation_start (generation_of (curr_gen_number))),
32195                       total_gen_size,
32196                       dd_fragmentation (dynamic_data_of (curr_gen_number))));
32197 #endif //SIMPLE_DPRINTF
32198
32199         generation* gen = generation_of (curr_gen_number);
32200         heap_segment* seg = generation_start_segment (gen);
32201         while (seg && (seg != ephemeral_heap_segment))
32202         {
32203             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
32204                         curr_gen_number,
32205                         (size_t)heap_segment_mem (seg),
32206                         (size_t)heap_segment_allocated (seg),
32207                         (size_t)heap_segment_committed (seg),
32208                         (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
32209                         (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
32210             print_free_list (curr_gen_number, seg);
32211             seg = heap_segment_next (seg);
32212         }
32213         if (seg && (seg != generation_start_segment (gen)))
32214         {
32215             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32216                          curr_gen_number,
32217                          (size_t)heap_segment_mem (seg),
32218                          (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
32219             print_free_list (curr_gen_number, seg);
32220
32221         }
32222         else if (seg)
32223         {
32224             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32225                          curr_gen_number,
32226                          (size_t)generation_allocation_start (generation_of (curr_gen_number)),
32227                          (size_t)(((curr_gen_number == 0)) ?
32228                                   (heap_segment_allocated
32229                                    (generation_start_segment
32230                                     (generation_of (curr_gen_number)))) :
32231                                   (generation_allocation_start
32232                                    (generation_of (curr_gen_number - 1))))
32233                          ));
32234             print_free_list (curr_gen_number, seg);
32235         }
32236         curr_gen_number--;
32237     }
32238
32239 #endif //TRACE_GC
32240 }
32241
32242 #undef TRACE_GC
32243
32244 //#define TRACE_GC
32245
32246 //-----------------------------------------------------------------------------
32247 //
32248 //                                  VM Specific support
32249 //
32250 //-----------------------------------------------------------------------------
32251
32252
32253 #ifdef TRACE_GC
32254
32255  unsigned int PromotedObjectCount  = 0;
32256  unsigned int CreatedObjectCount       = 0;
32257  unsigned int AllocDuration            = 0;
32258  unsigned int AllocCount               = 0;
32259  unsigned int AllocBigCount            = 0;
32260  unsigned int AllocSmallCount      = 0;
32261  unsigned int AllocStart             = 0;
32262 #endif //TRACE_GC
32263
32264 //Static member variables.
32265 VOLATILE(BOOL)    GCHeap::GcInProgress            = FALSE;
32266 //GCTODO
32267 //CMCSafeLock*      GCHeap::fGcLock;
32268 GCEvent            *GCHeap::WaitForGCEvent         = NULL;
32269 //GCTODO
32270 #ifdef TRACE_GC
32271 unsigned int       GCHeap::GcDuration;
32272 #endif //TRACE_GC
32273 unsigned            GCHeap::GcCondemnedGeneration   = 0;
32274 size_t              GCHeap::totalSurvivedSize       = 0;
32275 #ifdef FEATURE_PREMORTEM_FINALIZATION
32276 CFinalize*          GCHeap::m_Finalize              = 0;
32277 BOOL                GCHeap::GcCollectClasses        = FALSE;
32278 VOLATILE(int32_t)      GCHeap::m_GCFLock               = 0;
32279
32280 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32281 #ifdef STRESS_HEAP
32282 #ifdef BACKGROUND_GC
32283 int                 GCHeap::gc_stress_fgcs_in_bgc   = 0;
32284 #endif // BACKGROUND_GC
32285 #ifndef MULTIPLE_HEAPS
32286 OBJECTHANDLE        GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
32287 int                 GCHeap::m_CurStressObj          = 0;
32288 #endif // !MULTIPLE_HEAPS
32289 #endif // STRESS_HEAP
32290 #endif // FEATURE_REDHAWK
32291
32292 #endif //FEATURE_PREMORTEM_FINALIZATION
32293
32294 class NoGCRegionLockHolder
32295 {
32296 public:
32297     NoGCRegionLockHolder()
32298     {
32299         enter_spin_lock_noinstru(&g_no_gc_lock);
32300     }
32301
32302     ~NoGCRegionLockHolder()
32303     {
32304         leave_spin_lock_noinstru(&g_no_gc_lock);
32305     }
32306 };
32307
32308 // An explanation of locking for finalization:
32309 //
32310 // Multiple threads allocate objects.  During the allocation, they are serialized by
32311 // the AllocLock above.  But they release that lock before they register the object
32312 // for finalization.  That's because there is much contention for the alloc lock, but
32313 // finalization is presumed to be a rare case.
32314 //
32315 // So registering an object for finalization must be protected by the FinalizeLock.
32316 //
32317 // There is another logical queue that involves finalization.  When objects registered
32318 // for finalization become unreachable, they are moved from the "registered" queue to
32319 // the "unreachable" queue.  Note that this only happens inside a GC, so no other
32320 // threads can be manipulating either queue at that time.  Once the GC is over and
32321 // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
32322 // queue and call their finalizers.  This dequeue operation is also protected with
32323 // the finalize lock.
32324 //
32325 // At first, this seems unnecessary.  Only one thread is ever enqueuing or dequeuing
32326 // on the unreachable queue (either the GC thread during a GC or the finalizer thread
32327 // when a GC is not in progress).  The reason we share a lock with threads enqueuing
32328 // on the "registered" queue is that the "registered" and "unreachable" queues are
32329 // interrelated.
32330 //
32331 // They are actually two regions of a longer list, which can only grow at one end.
32332 // So to enqueue an object to the "registered" list, you actually rotate an unreachable
32333 // object at the boundary between the logical queues, out to the other end of the
32334 // unreachable queue -- where all growing takes place.  Then you move the boundary
32335 // pointer so that the gap we created at the boundary is now on the "registered"
32336 // side rather than the "unreachable" side.  Now the object can be placed into the
32337 // "registered" side at that point.  This is much more efficient than doing moves
32338 // of arbitrarily long regions, but it causes the two queues to require a shared lock.
32339 //
32340 // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock.  Instead, it relies
32341 // on the fact that the lock will only be taken for a brief period and that it will
32342 // never provoke or allow a GC while the lock is held.  This is critical.  If the
32343 // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
32344 // allow a GC), then the Alloc client would have to GC protect a finalizable object
32345 // to protect against that eventuality.  That is too slow!
32346
32347
32348
32349 BOOL IsValidObject99(uint8_t *pObject)
32350 {
32351 #ifdef VERIFY_HEAP
32352     if (!((CObjectHeader*)pObject)->IsFree())
32353         ((CObjectHeader *) pObject)->Validate();
32354 #endif //VERIFY_HEAP
32355     return(TRUE);
32356 }
32357
32358 #ifdef BACKGROUND_GC 
32359 BOOL gc_heap::bgc_mark_array_range (heap_segment* seg, 
32360                                     BOOL whole_seg_p,
32361                                     uint8_t** range_beg,
32362                                     uint8_t** range_end)
32363 {
32364     uint8_t* seg_start = heap_segment_mem (seg);
32365     uint8_t* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
32366
32367     if ((seg_start < background_saved_highest_address) &&
32368         (seg_end > background_saved_lowest_address))
32369     {
32370         *range_beg = max (seg_start, background_saved_lowest_address);
32371         *range_end = min (seg_end, background_saved_highest_address);
32372         return TRUE;
32373     }
32374     else
32375     {
32376         return FALSE;
32377     }
32378 }
32379
32380 void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
32381 {
32382 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32383     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32384     {
32385         uint8_t* range_beg = 0;
32386         uint8_t* range_end = 0;
32387
32388         if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
32389         {
32390             size_t  markw = mark_word_of (range_beg);
32391             size_t  markw_end = mark_word_of (range_end);
32392             while (markw < markw_end)
32393             {
32394                 if (mark_array [markw])
32395                 {
32396                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32397                                     markw, mark_array [markw], mark_word_address (markw)));
32398                     FATAL_GC_ERROR();
32399                 }
32400                 markw++;
32401             }
32402             uint8_t* p = mark_word_address (markw_end);
32403             while (p < range_end)
32404             {
32405                 assert (!(mark_array_marked (p)));
32406                 p++;
32407             }
32408         }
32409     }
32410 #endif //VERIFY_HEAP && MARK_ARRAY
32411 }
32412
32413 void gc_heap::verify_mark_bits_cleared (uint8_t* obj, size_t s)
32414 {
32415 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32416     size_t start_mark_bit = mark_bit_of (obj) + 1;
32417     size_t end_mark_bit = mark_bit_of (obj + s);
32418     unsigned int startbit = mark_bit_bit (start_mark_bit);
32419     unsigned int endbit = mark_bit_bit (end_mark_bit);
32420     size_t startwrd = mark_bit_word (start_mark_bit);
32421     size_t endwrd = mark_bit_word (end_mark_bit);
32422     unsigned int result = 0;
32423
32424     unsigned int firstwrd = ~(lowbits (~0, startbit));
32425     unsigned int lastwrd = ~(highbits (~0, endbit));
32426
32427     if (startwrd == endwrd)
32428     {
32429         unsigned int wrd = firstwrd & lastwrd;
32430         result = mark_array[startwrd] & wrd;
32431         if (result)
32432         {
32433             FATAL_GC_ERROR();
32434         }
32435         return;
32436     }
32437
32438     // verify the first mark word is cleared.
32439     if (startbit)
32440     {
32441         result = mark_array[startwrd] & firstwrd;
32442         if (result)
32443         {
32444             FATAL_GC_ERROR();
32445         }
32446         startwrd++;
32447     }
32448
32449     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
32450     {
32451         result = mark_array[wrdtmp];
32452         if (result)
32453         {
32454             FATAL_GC_ERROR();
32455         }
32456     }
32457
32458     // set the last mark word.
32459     if (endbit)
32460     {
32461         result = mark_array[endwrd] & lastwrd;
32462         if (result)
32463         {
32464             FATAL_GC_ERROR();
32465         }
32466     }
32467 #endif //VERIFY_HEAP && MARK_ARRAY
32468 }
32469
32470 void gc_heap::clear_all_mark_array()
32471 {
32472 #ifdef MARK_ARRAY
32473     //size_t num_dwords_written = 0;
32474     //size_t begin_time = GetHighPrecisionTimeStamp();
32475
32476     generation* gen = generation_of (max_generation);
32477     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32478     
32479     while (1)
32480     {
32481         if (seg == 0)
32482         {
32483             if (gen != large_object_generation)
32484             {
32485                 gen = generation_of (max_generation+1);
32486                 seg = heap_segment_rw (generation_start_segment (gen));
32487             }
32488             else
32489             {
32490                 break;
32491             }
32492         }
32493
32494         uint8_t* range_beg = 0;
32495         uint8_t* range_end = 0;
32496
32497         if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
32498         { 
32499             size_t markw = mark_word_of (range_beg);
32500             size_t markw_end = mark_word_of (range_end);
32501             size_t size_total = (markw_end - markw) * sizeof (uint32_t);
32502             //num_dwords_written = markw_end - markw;
32503             size_t size = 0;
32504             size_t size_left = 0;
32505
32506             assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
32507
32508             if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
32509             {
32510                 size = (size_total & ~(sizeof(PTR_PTR) - 1));
32511                 size_left = size_total - size;
32512                 assert ((size_left & (sizeof (uint32_t) - 1)) == 0);
32513             }
32514             else
32515             {
32516                 size = size_total;
32517             }
32518
32519             memclr ((uint8_t*)&mark_array[markw], size);
32520
32521             if (size_left != 0)
32522             {
32523                 uint32_t* markw_to_clear = &mark_array[markw + size / sizeof (uint32_t)];
32524                 for (size_t i = 0; i < (size_left / sizeof (uint32_t)); i++)
32525                 {
32526                     *markw_to_clear = 0;
32527                     markw_to_clear++;
32528                 }
32529             }
32530         }
32531
32532         seg = heap_segment_next_rw (seg);
32533     }
32534
32535     //size_t end_time = GetHighPrecisionTimeStamp() - begin_time; 
32536
32537     //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(uint32_t));
32538
32539 #endif //MARK_ARRAY
32540 }
32541
32542 #endif //BACKGROUND_GC 
32543
32544 void gc_heap::verify_mark_array_cleared (heap_segment* seg)
32545 {
32546 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32547     assert (card_table == g_gc_card_table);
32548     size_t  markw = mark_word_of (heap_segment_mem (seg));
32549     size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
32550
32551     while (markw < markw_end)
32552     {
32553         if (mark_array [markw])
32554         {
32555             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32556                             markw, mark_array [markw], mark_word_address (markw)));
32557             FATAL_GC_ERROR();
32558         }
32559         markw++;
32560     }
32561 #endif //VERIFY_HEAP && MARK_ARRAY
32562 }
32563
32564 void gc_heap::verify_mark_array_cleared ()
32565 {
32566 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32567     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32568     {
32569         generation* gen = generation_of (max_generation);
32570         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32571         
32572         while (1)
32573         {
32574             if (seg == 0)
32575             {
32576                 if (gen != large_object_generation)
32577                 {
32578                     gen = generation_of (max_generation+1);
32579                     seg = heap_segment_rw (generation_start_segment (gen));
32580                 }
32581                 else
32582                 {
32583                     break;
32584                 }
32585             }
32586
32587             bgc_verify_mark_array_cleared (seg);
32588             seg = heap_segment_next_rw (seg);
32589         }
32590     }
32591 #endif //VERIFY_HEAP && MARK_ARRAY
32592 }
32593
32594 void gc_heap::verify_seg_end_mark_array_cleared()
32595 {
32596 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32597     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32598     {
32599         generation* gen = generation_of (max_generation);
32600         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32601         
32602         while (1)
32603         {
32604             if (seg == 0)
32605             {
32606                 if (gen != large_object_generation)
32607                 {
32608                     gen = generation_of (max_generation+1);
32609                     seg = heap_segment_rw (generation_start_segment (gen));
32610                 }
32611                 else
32612                 {
32613                     break;
32614                 }
32615             }
32616
32617             // We already cleared all mark array bits for ephemeral generations
32618             // at the beginning of bgc sweep
32619             uint8_t* from = ((seg == ephemeral_heap_segment) ?
32620                           generation_allocation_start (generation_of (max_generation - 1)) :
32621                           heap_segment_allocated (seg));
32622             size_t  markw = mark_word_of (align_on_mark_word (from));
32623             size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
32624
32625             while (from < mark_word_address (markw))
32626             {
32627                 if (is_mark_bit_set (from))
32628                 {
32629                     dprintf (3, ("mark bit for %Ix was not cleared", from));
32630                     FATAL_GC_ERROR();
32631                 }
32632
32633                 from += mark_bit_pitch;
32634             }
32635
32636             while (markw < markw_end)
32637             {
32638                 if (mark_array [markw])
32639                 {
32640                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
32641                                     markw, mark_array [markw], mark_word_address (markw)));
32642                     FATAL_GC_ERROR();
32643                 }
32644                 markw++;
32645             }
32646             seg = heap_segment_next_rw (seg);
32647         }
32648     }
32649 #endif //VERIFY_HEAP && MARK_ARRAY
32650 }
32651
32652 // This function is called to make sure we don't mess up the segment list
32653 // in SOH. It's called by:
32654 // 1) begin and end of ephemeral GCs
32655 // 2) during bgc sweep when we switch segments.
32656 void gc_heap::verify_soh_segment_list()
32657 {
32658 #ifdef VERIFY_HEAP
32659     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32660     {
32661         generation* gen = generation_of (max_generation);
32662         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32663         heap_segment* last_seg = 0;
32664         while (seg)
32665         {
32666             last_seg = seg;
32667             seg = heap_segment_next_rw (seg);
32668         }
32669         if (last_seg != ephemeral_heap_segment)
32670         {
32671             FATAL_GC_ERROR();
32672         }
32673     }
32674 #endif //VERIFY_HEAP
32675 }
32676
32677 // This function can be called at any foreground GCs or blocking GCs. For background GCs,
32678 // it can be called at the end of the final marking; and at any point during background
32679 // sweep.
32680 // NOTE - to be able to call this function during background sweep, we need to temporarily 
32681 // NOT clear the mark array bits as we go.
32682 void gc_heap::verify_partial ()
32683 {
32684 #ifdef BACKGROUND_GC
32685     //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
32686     //generation* gen = large_object_generation;
32687     generation* gen = generation_of (max_generation);
32688     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32689     int align_const = get_alignment_constant (gen != large_object_generation);
32690
32691     uint8_t* o = 0;
32692     uint8_t* end = 0;
32693     size_t s = 0;
32694
32695     // Different ways to fail.
32696     BOOL mark_missed_p = FALSE;
32697     BOOL bad_ref_p = FALSE;
32698     BOOL free_ref_p = FALSE;
32699
32700     while (1)
32701     {
32702         if (seg == 0)
32703         {
32704             if (gen != large_object_generation)
32705             {
32706                 //switch to LOH
32707                 gen = large_object_generation;
32708                 align_const = get_alignment_constant (gen != large_object_generation);
32709                 seg = heap_segment_rw (generation_start_segment (gen));
32710                 continue;
32711             }
32712             else
32713             {
32714                 break;
32715             }
32716         }
32717
32718         o = heap_segment_mem (seg);
32719         end  = heap_segment_allocated (seg);
32720         //printf ("validating [%Ix-[%Ix\n", o, end);
32721         while (o < end)
32722         {
32723             s = size (o);
32724
32725             BOOL marked_p = background_object_marked (o, FALSE);
32726
32727             if (marked_p)
32728             {
32729                 go_through_object_cl (method_table (o), o, s, oo,
32730                     {
32731                         if (*oo)
32732                         {
32733                             //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
32734                             MethodTable *pMT = method_table (*oo);
32735
32736                             if (pMT == g_gc_pFreeObjectMethodTable)
32737                             {
32738                                 free_ref_p = TRUE;
32739                                 FATAL_GC_ERROR();
32740                             }
32741
32742                             if (!pMT->SanityCheck()) 
32743                             {
32744                                 bad_ref_p = TRUE;
32745                                 dprintf (3, ("Bad member of %Ix %Ix",
32746                                             (size_t)oo, (size_t)*oo));
32747                                 FATAL_GC_ERROR();
32748                             }
32749
32750                             if (current_bgc_state == bgc_final_marking)
32751                             {
32752                                 if (marked_p && !background_object_marked (*oo, FALSE))
32753                                 {
32754                                     mark_missed_p = TRUE;
32755                                     FATAL_GC_ERROR();
32756                                 }
32757                             }
32758                         }
32759                     }
32760                                     );
32761             }
32762
32763             o = o + Align(s, align_const);
32764         }
32765         seg = heap_segment_next_rw (seg);
32766     }
32767
32768     //printf ("didn't find any large object large enough...\n");
32769     //printf ("finished verifying loh\n");
32770 #endif //BACKGROUND_GC 
32771 }
32772
32773 #ifdef VERIFY_HEAP
32774
32775 void 
32776 gc_heap::verify_free_lists ()
32777 {
32778     for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
32779     {
32780         dprintf (3, ("Verifying free list for gen:%d", gen_num));
32781         allocator* gen_alloc = generation_allocator (generation_of (gen_num));
32782         size_t sz = gen_alloc->first_bucket_size();
32783         bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
32784
32785         for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
32786         {
32787             uint8_t* free_list = gen_alloc->alloc_list_head_of (a_l_number);
32788             uint8_t* prev = 0;
32789             while (free_list)
32790             {
32791                 if (!((CObjectHeader*)free_list)->IsFree())
32792                 {
32793                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
32794                                  (size_t)free_list));
32795                     FATAL_GC_ERROR();
32796                 }
32797                 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
32798                     || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
32799                 {
32800                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
32801                                  (size_t)free_list));
32802                     FATAL_GC_ERROR();
32803                 }
32804                 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
32805                 {
32806                     dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
32807                                  (size_t)free_list));
32808                     FATAL_GC_ERROR();
32809                 }
32810                 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
32811                 {
32812                     dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
32813                                  (size_t)free_list));
32814                     FATAL_GC_ERROR();
32815                 }
32816                     
32817                 prev = free_list;
32818                 free_list = free_list_slot (free_list);
32819             }
32820             //verify the sanity of the tail 
32821             uint8_t* tail = gen_alloc->alloc_list_tail_of (a_l_number);
32822             if (!((tail == 0) || (tail == prev)))
32823             {
32824                 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
32825                 FATAL_GC_ERROR();
32826             }
32827             if (tail == 0)
32828             {
32829                 uint8_t* head = gen_alloc->alloc_list_head_of (a_l_number);
32830                 if ((head != 0) && (free_list_slot (head) != 0))
32831                 {
32832                     dprintf (3, ("Verifying Heap: tail of free list is not correct"));
32833                     FATAL_GC_ERROR();
32834                 }
32835             }
32836
32837             sz *=2;
32838         }
32839     }
32840 }
32841
32842 void
32843 gc_heap::verify_heap (BOOL begin_gc_p)
32844 {
32845     int             heap_verify_level = static_cast<int>(GCConfig::GetHeapVerifyLevel());
32846     size_t          last_valid_brick = 0;
32847     BOOL            bCurrentBrickInvalid = FALSE;
32848     BOOL            large_brick_p = TRUE;
32849     size_t          curr_brick = 0;
32850     size_t          prev_brick = (size_t)-1;
32851     int             curr_gen_num = max_generation+1;    
32852     heap_segment*   seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
32853
32854     PREFIX_ASSUME(seg != NULL);
32855
32856     uint8_t*        curr_object = heap_segment_mem (seg);
32857     uint8_t*        prev_object = 0;
32858     uint8_t*        begin_youngest = generation_allocation_start(generation_of(0));
32859     uint8_t*        end_youngest = heap_segment_allocated (ephemeral_heap_segment);
32860     uint8_t*        next_boundary = generation_allocation_start (generation_of (max_generation - 1));
32861     int             align_const = get_alignment_constant (FALSE);
32862     size_t          total_objects_verified = 0;
32863     size_t          total_objects_verified_deep = 0;
32864
32865 #ifdef BACKGROUND_GC
32866     BOOL consider_bgc_mark_p    = FALSE;
32867     BOOL check_current_sweep_p  = FALSE;
32868     BOOL check_saved_sweep_p    = FALSE;
32869     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32870 #endif //BACKGROUND_GC
32871
32872 #ifdef MULTIPLE_HEAPS
32873     t_join* current_join = &gc_t_join;
32874 #ifdef BACKGROUND_GC
32875     if (settings.concurrent && (bgc_thread_id.IsCurrentThread()))
32876     {
32877         // We always call verify_heap on entry of GC on the SVR GC threads.
32878         current_join = &bgc_t_join;
32879     }
32880 #endif //BACKGROUND_GC
32881 #endif //MULTIPLE_HEAPS
32882
32883     UNREFERENCED_PARAMETER(begin_gc_p);
32884 #ifdef BACKGROUND_GC 
32885     dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin", 
32886         (begin_gc_p ? "BEG" : "END"),
32887         VolatileLoad(&settings.gc_index), 
32888         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
32889 #else
32890     dprintf (2,("[%s]GC#%d: Verifying heap - begin", 
32891                 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
32892 #endif //BACKGROUND_GC 
32893
32894 #ifndef MULTIPLE_HEAPS
32895     if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
32896         (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
32897     {
32898         FATAL_GC_ERROR();
32899     }
32900 #endif //MULTIPLE_HEAPS
32901
32902 #ifdef BACKGROUND_GC
32903     //don't touch the memory because the program is allocating from it.
32904     if (!settings.concurrent)
32905 #endif //BACKGROUND_GC
32906     {
32907         if (!(heap_verify_level & GCConfig::HEAPVERIFY_NO_MEM_FILL))
32908         {
32909             //uninit the unused portions of segments.
32910             generation* gen1 = large_object_generation;
32911             heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
32912             PREFIX_ASSUME(seg1 != NULL);
32913
32914             while (1)
32915             {
32916                 if (seg1)
32917                 {
32918                     uint8_t* clear_start = heap_segment_allocated (seg1) - plug_skew;
32919                     if (heap_segment_used (seg1) > clear_start)
32920                     {
32921                         dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa", 
32922                                     heap_segment_mem (seg1),
32923                                     clear_start ,
32924                                     heap_segment_used (seg1)));
32925                         memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
32926                             (heap_segment_used (seg1) - clear_start));
32927                     }
32928                     seg1 = heap_segment_next_rw (seg1);
32929                 }
32930                 else
32931                 {
32932                     if (gen1 == large_object_generation)
32933                     {
32934                         gen1 = generation_of (max_generation);
32935                         seg1 = heap_segment_rw (generation_start_segment (gen1));
32936                         PREFIX_ASSUME(seg1 != NULL);
32937                     }
32938                     else
32939                     {
32940                         break;
32941                     }
32942                 }
32943             }
32944         }
32945     }
32946
32947 #ifdef MULTIPLE_HEAPS
32948     current_join->join(this, gc_join_verify_copy_table);
32949     if (current_join->joined())
32950     {
32951         // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
32952         for (int i = 0; i < n_heaps; i++)
32953         {
32954             //copy the card and brick tables
32955             if (g_gc_card_table != g_heaps[i]->card_table)
32956             {
32957                 g_heaps[i]->copy_brick_card_table();
32958             }
32959         }
32960
32961         current_join->restart();
32962     }
32963 #else
32964         if (g_gc_card_table != card_table)
32965             copy_brick_card_table();
32966 #endif //MULTIPLE_HEAPS
32967
32968     //verify that the generation structures makes sense
32969     {
32970         generation* gen = generation_of (max_generation);
32971
32972         assert (generation_allocation_start (gen) ==
32973                 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
32974         int gen_num = max_generation-1;
32975         generation* prev_gen = gen;
32976         while (gen_num >= 0)
32977         {
32978             gen = generation_of (gen_num);
32979             assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
32980             assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
32981             assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
32982
32983             if (generation_start_segment (prev_gen ) ==
32984                 generation_start_segment (gen))
32985             {
32986                 assert (generation_allocation_start (prev_gen) <
32987                         generation_allocation_start (gen));
32988             }
32989             prev_gen = gen;
32990             gen_num--;
32991         }
32992     }
32993
32994     while (1)
32995     {
32996         // Handle segment transitions
32997         if (curr_object >= heap_segment_allocated (seg))
32998         {
32999             if (curr_object > heap_segment_allocated(seg))
33000             {
33001                 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
33002                         (size_t)curr_object, (size_t)seg));
33003                 FATAL_GC_ERROR();
33004             }
33005             seg = heap_segment_next_in_range (seg);
33006             if (seg)
33007             {
33008 #ifdef BACKGROUND_GC
33009                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33010 #endif //BACKGROUND_GC
33011                 curr_object = heap_segment_mem(seg);
33012                 prev_object = 0;
33013                 continue;
33014             }
33015             else
33016             {
33017                 if (curr_gen_num == (max_generation+1))
33018                 {
33019                     curr_gen_num--;
33020                     seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
33021
33022                     PREFIX_ASSUME(seg != NULL);
33023
33024 #ifdef BACKGROUND_GC
33025                     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33026 #endif //BACKGROUND_GC
33027                     curr_object = heap_segment_mem (seg);
33028                     prev_object = 0;
33029                     large_brick_p = FALSE;
33030                     align_const = get_alignment_constant (TRUE);
33031                 }
33032                 else
33033                     break;  // Done Verifying Heap -- no more segments
33034             }
33035         }
33036
33037         // Are we at the end of the youngest_generation?
33038         if (seg == ephemeral_heap_segment)
33039         {
33040             if (curr_object >= end_youngest)
33041             {
33042                 // prev_object length is too long if we hit this int3
33043                 if (curr_object > end_youngest)
33044                 {
33045                     dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
33046                             (size_t)curr_object, (size_t)end_youngest));
33047                     FATAL_GC_ERROR();
33048                 }
33049                 break;
33050             }
33051             
33052             if ((curr_object >= next_boundary) && (curr_gen_num > 0))
33053             {
33054                 curr_gen_num--;
33055                 if (curr_gen_num > 0)
33056                 {
33057                     next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
33058                 }
33059             }
33060         }
33061
33062          //if (is_mark_set (curr_object))
33063          //{
33064          //        printf ("curr_object: %Ix is marked!",(size_t)curr_object);
33065          //        FATAL_GC_ERROR();
33066          //}
33067
33068         size_t s = size (curr_object);
33069         dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
33070         if (s == 0)
33071         {
33072             dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
33073             FATAL_GC_ERROR();
33074         }
33075
33076         // If object is not in the youngest generation, then lets
33077         // verify that the brick table is correct....
33078         if (((seg != ephemeral_heap_segment) ||
33079              (brick_of(curr_object) < brick_of(begin_youngest))))
33080         {
33081             curr_brick = brick_of(curr_object);
33082
33083             // Brick Table Verification...
33084             //
33085             // On brick transition
33086             //     if brick is negative
33087             //          verify that brick indirects to previous valid brick
33088             //     else
33089             //          set current brick invalid flag to be flipped if we
33090             //          encounter an object at the correct place
33091             //
33092             if (curr_brick != prev_brick)
33093             {
33094                 // If the last brick we were examining had positive
33095                 // entry but we never found the matching object, then
33096                 // we have a problem
33097                 // If prev_brick was the last one of the segment
33098                 // it's ok for it to be invalid because it is never looked at
33099                 if (bCurrentBrickInvalid &&
33100                     (curr_brick != brick_of (heap_segment_mem (seg))) &&
33101                     !heap_segment_read_only_p (seg))
33102                 {
33103                     dprintf (3, ("curr brick %Ix invalid", curr_brick));
33104                     FATAL_GC_ERROR();
33105                 }
33106
33107                 if (large_brick_p)
33108                 {
33109                     //large objects verify the table only if they are in
33110                     //range.
33111                     if ((heap_segment_reserved (seg) <= highest_address) &&
33112                         (heap_segment_mem (seg) >= lowest_address) &&
33113                         brick_table [curr_brick] != 0)
33114                     {
33115                         dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
33116                                 curr_brick, (size_t)curr_object));
33117                         FATAL_GC_ERROR();
33118                     }
33119                     else
33120                     {
33121                         bCurrentBrickInvalid = FALSE;
33122                     }
33123                 }
33124                 else
33125                 {
33126                     // If the current brick contains a negative value make sure
33127                     // that the indirection terminates at the last  valid brick
33128                     if (brick_table [curr_brick] <= 0)
33129                     {
33130                         if (brick_table [curr_brick] == 0)
33131                         {
33132                             dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
33133                                     curr_brick, (size_t)curr_object));
33134                             FATAL_GC_ERROR();
33135                         }
33136                         ptrdiff_t i = curr_brick;
33137                         while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
33138                                (brick_table[i] < 0))
33139                         {
33140                             i = i + brick_table[i];
33141                         }
33142                         if (i <  ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
33143                         {
33144                             dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
33145                                     i, brick_of (heap_segment_mem (seg)),
33146                                     curr_brick));
33147                             FATAL_GC_ERROR();
33148                         }
33149                         // if (i != last_valid_brick)
33150                         //  FATAL_GC_ERROR();
33151                         bCurrentBrickInvalid = FALSE;
33152                     }
33153                     else if (!heap_segment_read_only_p (seg))
33154                     {
33155                         bCurrentBrickInvalid = TRUE;
33156                     }
33157                 }
33158             }
33159
33160             if (bCurrentBrickInvalid)
33161             {
33162                 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
33163                 {
33164                     bCurrentBrickInvalid = FALSE;
33165                     last_valid_brick = curr_brick;
33166                 }
33167             }
33168         }
33169
33170         if (*((uint8_t**)curr_object) != (uint8_t *) g_gc_pFreeObjectMethodTable)
33171         {
33172 #ifdef FEATURE_LOH_COMPACTION
33173             if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
33174             {
33175                 assert (method_table (prev_object) == g_gc_pFreeObjectMethodTable);
33176             }
33177 #endif //FEATURE_LOH_COMPACTION
33178
33179             total_objects_verified++;
33180
33181             BOOL can_verify_deep = TRUE;
33182 #ifdef BACKGROUND_GC
33183             can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
33184 #endif //BACKGROUND_GC
33185
33186             BOOL deep_verify_obj = can_verify_deep;
33187             if ((heap_verify_level & GCConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
33188                 deep_verify_obj = FALSE;
33189
33190             ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
33191
33192             if (can_verify_deep)
33193             {
33194                 if (curr_gen_num > 0)
33195                 {
33196                     BOOL need_card_p = FALSE;
33197                     if (contain_pointers_or_collectible (curr_object))
33198                     {
33199                         dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
33200                         size_t crd = card_of (curr_object);
33201                         BOOL found_card_p = card_set_p (crd);
33202
33203 #ifdef COLLECTIBLE_CLASS
33204                         if (is_collectible(curr_object))
33205                         {
33206                             uint8_t* class_obj = get_class_object (curr_object);
33207                             if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
33208                             {
33209                                 if (!found_card_p)
33210                                 {
33211                                     dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
33212                                                 card_of (curr_object), (size_t)curr_object, class_obj));
33213
33214                                     FATAL_GC_ERROR();
33215                                 }
33216                             }
33217                         }
33218 #endif //COLLECTIBLE_CLASS
33219
33220                         if (contain_pointers(curr_object))
33221                         {
33222                             go_through_object_nostart
33223                                 (method_table(curr_object), curr_object, s, oo,
33224                                 {
33225                                     if ((crd != card_of ((uint8_t*)oo)) && !found_card_p)
33226                                     {
33227                                         crd = card_of ((uint8_t*)oo);
33228                                         found_card_p = card_set_p (crd);
33229                                         need_card_p = FALSE;
33230                                     }
33231                                     if ((*oo < ephemeral_high) && (*oo >= next_boundary))
33232                                     {
33233                                         need_card_p = TRUE;
33234                                     }
33235
33236                                 if (need_card_p && !found_card_p)
33237                                 {
33238
33239                                         dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33240                                                     card_of (curr_object), (size_t)curr_object,
33241                                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33242                                         FATAL_GC_ERROR();
33243                                     }
33244                                 }
33245                                     );
33246                         }
33247                         if (need_card_p && !found_card_p)
33248                         {
33249                             dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33250                                     card_of (curr_object), (size_t)curr_object,
33251                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33252                             FATAL_GC_ERROR();
33253                         }
33254                     }
33255                 }
33256                 total_objects_verified_deep++;
33257             }
33258         }
33259
33260         prev_object = curr_object;
33261         prev_brick = curr_brick;
33262         curr_object = curr_object + Align(s, align_const);
33263         if (curr_object < prev_object)
33264         {
33265             dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
33266             FATAL_GC_ERROR();
33267         }
33268     }
33269
33270 #ifdef BACKGROUND_GC
33271     dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id", 
33272                  (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
33273                  (begin_gc_p ? "BEG" : "END"),
33274                  ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
33275                  total_objects_verified, total_objects_verified_deep));
33276     if (current_c_gc_state != c_gc_state_planning)
33277     {
33278         assert (total_objects_verified == total_objects_verified_deep);
33279     }
33280 #endif //BACKGROUND_GC
33281     
33282     verify_free_lists();
33283
33284 #ifdef FEATURE_PREMORTEM_FINALIZATION
33285     finalize_queue->CheckFinalizerObjects();
33286 #endif // FEATURE_PREMORTEM_FINALIZATION
33287
33288     {
33289         // to be consistent with handle table APIs pass a ScanContext*
33290         // to provide the heap number.  the SC isn't complete though so
33291         // limit its scope to handle table verification.
33292         ScanContext sc;
33293         sc.thread_number = heap_number;
33294         GCScan::VerifyHandleTable(max_generation, max_generation, &sc);
33295     }
33296
33297 #ifdef MULTIPLE_HEAPS
33298     current_join->join(this, gc_join_verify_objects_done);
33299     if (current_join->joined())
33300 #endif //MULTIPLE_HEAPS
33301     {
33302         SyncBlockCache::GetSyncBlockCache()->VerifySyncTableEntry();
33303 #ifdef MULTIPLE_HEAPS
33304         current_join->restart();
33305 #endif //MULTIPLE_HEAPS
33306     }
33307
33308 #ifdef BACKGROUND_GC 
33309     if (!settings.concurrent)
33310     {
33311         if (current_c_gc_state == c_gc_state_planning)
33312         {
33313             // temporarily commenting this out 'cause an FGC
33314             // could be triggered before we sweep ephemeral.
33315             //verify_seg_end_mark_array_cleared();
33316         }
33317     }
33318
33319     if (settings.concurrent)
33320     {
33321         verify_mark_array_cleared();
33322     }
33323     dprintf (2,("GC%d(%s): Verifying heap - end", 
33324         VolatileLoad(&settings.gc_index), 
33325         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
33326 #else
33327     dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
33328 #endif //BACKGROUND_GC 
33329 }
33330
33331 #endif  //VERIFY_HEAP
33332
33333
33334 void GCHeap::ValidateObjectMember (Object* obj)
33335 {
33336 #ifdef VERIFY_HEAP
33337     size_t s = size (obj);
33338     uint8_t* o = (uint8_t*)obj;
33339
33340     go_through_object_cl (method_table (obj), o, s, oo,
33341                                 {
33342                                     uint8_t* child_o = *oo;
33343                                     if (child_o)
33344                                     {
33345                                         dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
33346                                         MethodTable *pMT = method_table (child_o);
33347                                         assert(pMT);
33348                                         if (!pMT->SanityCheck()) {
33349                                             dprintf (3, ("Bad member of %Ix %Ix",
33350                                                         (size_t)oo, (size_t)child_o));
33351                                             FATAL_GC_ERROR();
33352                                         }
33353                                     }
33354                                 } );
33355 #endif // VERIFY_HEAP
33356 }
33357
33358 void DestructObject (CObjectHeader* hdr)
33359 {
33360     UNREFERENCED_PARAMETER(hdr); // compiler bug? -- this *is*, indeed, referenced
33361     hdr->~CObjectHeader();
33362 }
33363
33364 HRESULT GCHeap::Shutdown ()
33365 {
33366     deleteGCShadow();
33367
33368     GCScan::GcRuntimeStructuresValid (FALSE);
33369
33370     // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
33371     // threads except the one performing the shutdown.
33372     // ASSERT( !GcInProgress );
33373
33374     // Guard against any more GC occurring and against any threads blocking
33375     // for GC to complete when the GC heap is gone.  This fixes a race condition
33376     // where a thread in GC is destroyed as part of process destruction and
33377     // the remaining threads block for GC complete.
33378
33379     //GCTODO
33380     //EnterAllocLock();
33381     //Enter();
33382     //EnterFinalizeLock();
33383     //SetGCDone();
33384
33385     // during shutdown lot of threads are suspended
33386     // on this even, we don't want to wake them up just yet
33387     //CloseHandle (WaitForGCEvent);
33388
33389     //find out if the global card table hasn't been used yet
33390     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
33391     if (card_table_refcount (ct) == 0)
33392     {
33393         destroy_card_table (ct);
33394         g_gc_card_table = nullptr;
33395
33396 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
33397         g_gc_card_bundle_table = nullptr;
33398 #endif
33399 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33400         SoftwareWriteWatch::StaticClose();
33401 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33402     }
33403
33404     //destroy all segments on the standby list
33405     while(gc_heap::segment_standby_list != 0)
33406     {
33407         heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
33408 #ifdef MULTIPLE_HEAPS
33409         (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33410 #else //MULTIPLE_HEAPS
33411         pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33412 #endif //MULTIPLE_HEAPS
33413         gc_heap::segment_standby_list = next_seg;
33414     }
33415
33416
33417 #ifdef MULTIPLE_HEAPS
33418
33419     for (int i = 0; i < gc_heap::n_heaps; i ++)
33420     {
33421         delete gc_heap::g_heaps[i]->vm_heap;
33422         //destroy pure GC stuff
33423         gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
33424     }
33425 #else
33426     gc_heap::destroy_gc_heap (pGenGCHeap);
33427
33428 #endif //MULTIPLE_HEAPS
33429     gc_heap::shutdown_gc();
33430
33431     return S_OK;
33432 }
33433
33434 // Wait until a garbage collection is complete
33435 // returns NOERROR if wait was OK, other error code if failure.
33436 // WARNING: This will not undo the must complete state. If you are
33437 // in a must complete when you call this, you'd better know what you're
33438 // doing.
33439
33440 #ifdef FEATURE_PREMORTEM_FINALIZATION
33441 static
33442 HRESULT AllocateCFinalize(CFinalize **pCFinalize)
33443 {
33444     *pCFinalize = new (nothrow) CFinalize();
33445     if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
33446         return E_OUTOFMEMORY;
33447
33448     return S_OK;
33449 }
33450 #endif // FEATURE_PREMORTEM_FINALIZATION
33451
33452 // init the instance heap
33453 HRESULT GCHeap::Init(size_t hn)
33454 {
33455     HRESULT hres = S_OK;
33456
33457 #ifdef MULTIPLE_HEAPS
33458     if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
33459         hres = E_OUTOFMEMORY;
33460 #else
33461     UNREFERENCED_PARAMETER(hn);
33462     if (!gc_heap::make_gc_heap())
33463         hres = E_OUTOFMEMORY;
33464 #endif //MULTIPLE_HEAPS
33465
33466     // Failed.
33467     return hres;
33468 }
33469
33470 //System wide initialization
33471 HRESULT GCHeap::Initialize ()
33472 {
33473     HRESULT hr = S_OK;
33474
33475     g_gc_pFreeObjectMethodTable = GCToEEInterface::GetFreeObjectMethodTable();
33476     g_num_processors = GCToOSInterface::GetTotalProcessorCount();
33477     assert(g_num_processors != 0);
33478
33479 //Initialize the static members.
33480 #ifdef TRACE_GC
33481     GcDuration = 0;
33482     CreatedObjectCount = 0;
33483 #endif //TRACE_GC
33484
33485     size_t seg_size = get_valid_segment_size();
33486     gc_heap::soh_segment_size = seg_size;
33487     size_t large_seg_size = get_valid_segment_size(TRUE);
33488     gc_heap::min_loh_segment_size = large_seg_size;
33489     gc_heap::min_segment_size = min (seg_size, large_seg_size);
33490 #ifdef SEG_MAPPING_TABLE
33491     gc_heap::min_segment_size_shr = index_of_set_bit (gc_heap::min_segment_size);
33492 #endif //SEG_MAPPING_TABLE
33493
33494 #ifdef MULTIPLE_HEAPS
33495     if (GCConfig::GetNoAffinitize())
33496         gc_heap::gc_thread_no_affinitize_p = true;
33497
33498     uint32_t nhp_from_config = static_cast<uint32_t>(GCConfig::GetHeapCount());
33499     
33500     uint32_t nhp_from_process = GCToOSInterface::GetCurrentProcessCpuCount();
33501
33502     uint32_t nhp = ((nhp_from_config == 0) ? nhp_from_process :
33503                                              (min (nhp_from_config, nhp_from_process)));
33504
33505     nhp = min (nhp, MAX_SUPPORTED_CPUS);
33506
33507     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
33508 #else
33509     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
33510 #endif //MULTIPLE_HEAPS
33511
33512     if (hr != S_OK)
33513         return hr;
33514
33515     gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit();
33516
33517     gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
33518 #ifndef MULTIPLE_HEAPS
33519     gc_heap::mem_one_percent /= g_num_processors;
33520 #endif //!MULTIPLE_HEAPS
33521
33522     // We should only use this if we are in the "many process" mode which really is only applicable
33523     // to very powerful machines - before that's implemented, temporarily I am only enabling this for 80GB+ memory. 
33524     // For now I am using an estimate to calculate these numbers but this should really be obtained 
33525     // programmatically going forward.
33526     // I am assuming 47 processes using WKS GC and 3 using SVR GC.
33527     // I am assuming 3 in part due to the "very high memory load" is 97%.
33528     int available_mem_th = 10;
33529     if (gc_heap::total_physical_mem >= ((uint64_t)80 * 1024 * 1024 * 1024))
33530     {
33531         int adjusted_available_mem_th = 3 + (int)((float)47 / (float)(g_num_processors));
33532         available_mem_th = min (available_mem_th, adjusted_available_mem_th);
33533     }
33534
33535     gc_heap::high_memory_load_th = 100 - available_mem_th;
33536
33537 #if defined(BIT64) 
33538     gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
33539 #endif // BIT64
33540
33541     WaitForGCEvent = new (nothrow) GCEvent;
33542
33543     if (!WaitForGCEvent)
33544     {
33545         return E_OUTOFMEMORY;
33546     }
33547
33548     if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE))
33549     {
33550         return E_FAIL;
33551     }
33552
33553 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33554 #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
33555     if (GCStress<cfg_any>::IsEnabled())  {
33556         for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
33557             m_StressObjs[i] = CreateGlobalHandle(0);
33558         m_CurStressObj = 0;
33559     }
33560 #endif //STRESS_HEAP && !MULTIPLE_HEAPS
33561 #endif // FEATURE_REDHAWK
33562
33563     initGCShadow();         // If we are debugging write barriers, initialize heap shadow
33564
33565 #ifdef MULTIPLE_HEAPS
33566
33567     for (unsigned i = 0; i < nhp; i++)
33568     {
33569         GCHeap* Hp = new (nothrow) GCHeap();
33570         if (!Hp)
33571             return E_OUTOFMEMORY;
33572
33573         if ((hr = Hp->Init (i))!= S_OK)
33574         {
33575             return hr;
33576         }
33577     }
33578     // initialize numa node to heap map
33579     heap_select::init_numa_node_to_heap_map(nhp);
33580 #else
33581     hr = Init (0);
33582 #endif //MULTIPLE_HEAPS
33583
33584     if (hr == S_OK)
33585     {
33586         GCScan::GcRuntimeStructuresValid (TRUE);
33587
33588         GCToEEInterface::DiagUpdateGenerationBounds();
33589     }
33590
33591     return hr;
33592 };
33593
33594 ////
33595 // GC callback functions
33596 bool GCHeap::IsPromoted(Object* object)
33597 {
33598 #ifdef _DEBUG
33599     ((CObjectHeader*)object)->Validate();
33600 #endif //_DEBUG
33601
33602     uint8_t* o = (uint8_t*)object;
33603
33604     if (gc_heap::settings.condemned_generation == max_generation)
33605     {
33606 #ifdef MULTIPLE_HEAPS
33607         gc_heap* hp = gc_heap::g_heaps[0];
33608 #else
33609         gc_heap* hp = pGenGCHeap;
33610 #endif //MULTIPLE_HEAPS
33611
33612 #ifdef BACKGROUND_GC
33613         if (gc_heap::settings.concurrent)
33614         {
33615             bool is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
33616                             hp->background_marked (o));
33617             return is_marked;
33618         }
33619         else
33620 #endif //BACKGROUND_GC
33621         {
33622             return (!((o < hp->highest_address) && (o >= hp->lowest_address))
33623                     || hp->is_mark_set (o));
33624         }
33625     }
33626     else
33627     {
33628         gc_heap* hp = gc_heap::heap_of (o);
33629         return (!((o < hp->gc_high) && (o >= hp->gc_low))
33630                 || hp->is_mark_set (o));
33631     }
33632 }
33633
33634 size_t GCHeap::GetPromotedBytes(int heap_index)
33635 {
33636 #ifdef BACKGROUND_GC
33637     if (gc_heap::settings.concurrent)
33638     {
33639         return gc_heap::bpromoted_bytes (heap_index);
33640     }
33641     else
33642 #endif //BACKGROUND_GC
33643     {
33644         return gc_heap::promoted_bytes (heap_index);
33645     }
33646 }
33647
33648 unsigned int GCHeap::WhichGeneration (Object* object)
33649 {
33650     gc_heap* hp = gc_heap::heap_of ((uint8_t*)object);
33651     unsigned int g = hp->object_gennum ((uint8_t*)object);
33652     dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
33653     return g;
33654 }
33655
33656 bool GCHeap::IsEphemeral (Object* object)
33657 {
33658     uint8_t* o = (uint8_t*)object;
33659     gc_heap* hp = gc_heap::heap_of (o);
33660     return !!hp->ephemeral_pointer_p (o);
33661 }
33662
33663 // Return NULL if can't find next object. When EE is not suspended,
33664 // the result is not accurate: if the input arg is in gen0, the function could 
33665 // return zeroed out memory as next object
33666 Object * GCHeap::NextObj (Object * object)
33667 {
33668 #ifdef VERIFY_HEAP
33669     uint8_t* o = (uint8_t*)object;
33670
33671 #ifndef FEATURE_BASICFREEZE
33672     if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)))
33673     {
33674         return NULL;
33675     }
33676 #endif //!FEATURE_BASICFREEZE
33677
33678     heap_segment * hs = gc_heap::find_segment (o, FALSE);
33679     if (!hs)
33680     {
33681         return NULL;
33682     }
33683
33684     BOOL large_object_p = heap_segment_loh_p (hs);
33685     if (large_object_p)
33686         return NULL; //could be racing with another core allocating. 
33687 #ifdef MULTIPLE_HEAPS
33688     gc_heap* hp = heap_segment_heap (hs);
33689 #else //MULTIPLE_HEAPS
33690     gc_heap* hp = 0;
33691 #endif //MULTIPLE_HEAPS
33692     unsigned int g = hp->object_gennum ((uint8_t*)object);
33693     if ((g == 0) && hp->settings.demotion)
33694         return NULL;//could be racing with another core allocating. 
33695     int align_const = get_alignment_constant (!large_object_p);
33696     uint8_t* nextobj = o + Align (size (o), align_const);
33697     if (nextobj <= o) // either overflow or 0 sized object.
33698     {
33699         return NULL;
33700     }
33701
33702     if ((nextobj < heap_segment_mem(hs)) || 
33703         (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) || 
33704         (nextobj >= hp->alloc_allocated))
33705     {
33706         return NULL;
33707     }
33708
33709     return (Object *)nextobj;
33710 #else
33711     return nullptr;
33712 #endif // VERIFY_HEAP
33713 }
33714
33715 #ifdef VERIFY_HEAP
33716
33717 #ifdef FEATURE_BASICFREEZE
33718 BOOL GCHeap::IsInFrozenSegment (Object * object)
33719 {
33720     uint8_t* o = (uint8_t*)object;
33721     heap_segment * hs = gc_heap::find_segment (o, FALSE);
33722     //We create a frozen object for each frozen segment before the segment is inserted
33723     //to segment list; during ngen, we could also create frozen objects in segments which
33724     //don't belong to current GC heap.
33725     //So we return true if hs is NULL. It might create a hole about detecting invalidate 
33726     //object. But given all other checks present, the hole should be very small
33727     return !hs || heap_segment_read_only_p (hs);
33728 }
33729 #endif //FEATURE_BASICFREEZE
33730
33731 #endif //VERIFY_HEAP
33732
33733 // returns TRUE if the pointer is in one of the GC heaps.
33734 bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only)
33735 {
33736     STATIC_CONTRACT_SO_TOLERANT;
33737
33738     // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment 
33739     // no longer calls GCEvent::Wait which eventually takes a lock.
33740
33741     uint8_t* object = (uint8_t*) vpObject;
33742 #ifndef FEATURE_BASICFREEZE
33743     if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address)))
33744         return FALSE;
33745 #endif //!FEATURE_BASICFREEZE
33746
33747     heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
33748     return !!hs;
33749 }
33750
33751 #ifdef STRESS_PINNING
33752 static n_promote = 0;
33753 #endif //STRESS_PINNING
33754 // promote an object
33755 void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
33756 {
33757     THREAD_NUMBER_FROM_CONTEXT;
33758 #ifndef MULTIPLE_HEAPS
33759     const int thread = 0;
33760 #endif //!MULTIPLE_HEAPS
33761
33762     uint8_t* o = (uint8_t*)*ppObject;
33763
33764     if (o == 0)
33765         return;
33766
33767 #ifdef DEBUG_DestroyedHandleValue
33768     // we can race with destroy handle during concurrent scan
33769     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
33770         return;
33771 #endif //DEBUG_DestroyedHandleValue
33772
33773     HEAP_FROM_THREAD;
33774
33775     gc_heap* hp = gc_heap::heap_of (o);
33776
33777     dprintf (3, ("Promote %Ix", (size_t)o));
33778
33779 #ifdef INTERIOR_POINTERS
33780     if (flags & GC_CALL_INTERIOR)
33781     {
33782         if ((o < hp->gc_low) || (o >= hp->gc_high))
33783         {
33784             return;
33785         }
33786         if ( (o = hp->find_object (o, hp->gc_low)) == 0)
33787         {
33788             return;
33789         }
33790
33791     }
33792 #endif //INTERIOR_POINTERS
33793
33794 #ifdef FEATURE_CONSERVATIVE_GC
33795     // For conservative GC, a value on stack may point to middle of a free object.
33796     // In this case, we don't need to promote the pointer.
33797     if (GCConfig::GetConservativeGC()
33798         && ((CObjectHeader*)o)->IsFree())
33799     {
33800         return;
33801     }
33802 #endif
33803
33804 #ifdef _DEBUG
33805     ((CObjectHeader*)o)->ValidatePromote(sc, flags);
33806 #else 
33807     UNREFERENCED_PARAMETER(sc);
33808 #endif //_DEBUG
33809
33810     if (flags & GC_CALL_PINNED)
33811         hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
33812
33813 #ifdef STRESS_PINNING
33814     if ((++n_promote % 20) == 1)
33815             hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
33816 #endif //STRESS_PINNING
33817
33818 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33819     size_t promoted_size_begin = hp->promoted_bytes (thread);
33820 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33821
33822     if ((o >= hp->gc_low) && (o < hp->gc_high))
33823     {
33824         hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
33825     }
33826
33827 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33828     size_t promoted_size_end = hp->promoted_bytes (thread);
33829     if (g_fEnableARM)
33830     {
33831         if (sc->pCurrentDomain)
33832         {
33833             sc->pCurrentDomain->RecordSurvivedBytes ((promoted_size_end - promoted_size_begin), thread);
33834         }
33835     }
33836 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33837
33838     STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
33839 }
33840
33841 void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
33842                        uint32_t flags)
33843 {
33844     UNREFERENCED_PARAMETER(sc);
33845
33846     uint8_t* object = (uint8_t*)(Object*)(*ppObject);
33847     
33848     THREAD_NUMBER_FROM_CONTEXT;
33849
33850     //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
33851     dprintf (3, ("R: %Ix", (size_t)ppObject));
33852     
33853     if (object == 0)
33854         return;
33855
33856     gc_heap* hp = gc_heap::heap_of (object);
33857
33858 #ifdef _DEBUG
33859     if (!(flags & GC_CALL_INTERIOR))
33860     {
33861         // We cannot validate this object if it's in the condemned gen because it could 
33862         // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
33863         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
33864         {
33865             ((CObjectHeader*)object)->Validate(FALSE);
33866         }
33867     }
33868 #endif //_DEBUG
33869
33870     dprintf (3, ("Relocate %Ix\n", (size_t)object));
33871
33872     uint8_t* pheader;
33873
33874     if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
33875     {
33876         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
33877         {
33878             return;
33879         }
33880
33881         if (gc_heap::loh_object_p (object))
33882         {
33883             pheader = hp->find_object (object, 0);
33884             if (pheader == 0)
33885             {
33886                 return;
33887             }
33888
33889             ptrdiff_t ref_offset = object - pheader;
33890             hp->relocate_address(&pheader THREAD_NUMBER_ARG);
33891             *ppObject = (Object*)(pheader + ref_offset);
33892             return;
33893         }
33894     }
33895
33896     {
33897         pheader = object;
33898         hp->relocate_address(&pheader THREAD_NUMBER_ARG);
33899         *ppObject = (Object*)pheader;
33900     }
33901
33902     STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
33903 }
33904
33905 /*static*/ bool GCHeap::IsObjectInFixedHeap(Object *pObj)
33906 {
33907     // For now we simply look at the size of the object to determine if it in the
33908     // fixed heap or not. If the bit indicating this gets set at some point
33909     // we should key off that instead.
33910     return size( pObj ) >= LARGE_OBJECT_SIZE;
33911 }
33912
33913 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33914 #ifdef STRESS_HEAP
33915
33916 void StressHeapDummy ();
33917
33918 static int32_t GCStressStartCount = -1;
33919 static int32_t GCStressCurCount = 0;
33920 static int32_t GCStressStartAtJit = -1;
33921
33922 // the maximum number of foreground GCs we'll induce during one BGC
33923 // (this number does not include "naturally" occuring GCs).
33924 static int32_t GCStressMaxFGCsPerBGC = -1;
33925
33926 // CLRRandom implementation can produce FPU exceptions if 
33927 // the test/application run by CLR is enabling any FPU exceptions. 
33928 // We want to avoid any unexpected exception coming from stress 
33929 // infrastructure, so CLRRandom is not an option.
33930 // The code below is a replicate of CRT rand() implementation.
33931 // Using CRT rand() is not an option because we will interfere with the user application
33932 // that may also use it. 
33933 int StressRNG(int iMaxValue)
33934 {
33935     static BOOL bisRandInit = FALSE;
33936     static int lHoldrand = 1L;
33937
33938     if (!bisRandInit)
33939     {
33940         lHoldrand = (int)time(NULL);
33941         bisRandInit = TRUE;
33942     }
33943     int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
33944     return randValue % iMaxValue;
33945 }
33946 #endif // STRESS_HEAP
33947 #endif // !FEATURE_REDHAWK
33948
33949 // free up object so that things will move and then do a GC
33950 //return TRUE if GC actually happens, otherwise FALSE
33951 bool GCHeap::StressHeap(gc_alloc_context * context)
33952 {
33953 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
33954     alloc_context* acontext = static_cast<alloc_context*>(context);
33955     assert(context != nullptr);
33956
33957     // if GC stress was dynamically disabled during this run we return FALSE
33958     if (!GCStressPolicy::IsEnabled())
33959         return FALSE;
33960
33961 #ifdef _DEBUG
33962     if (g_pConfig->FastGCStressLevel() && !GCToEEInterface::GetThread()->StressHeapIsEnabled()) {
33963         return FALSE;
33964     }
33965
33966 #endif //_DEBUG
33967
33968     if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
33969 #ifdef _DEBUG
33970         || g_pConfig->FastGCStressLevel() > 1
33971 #endif //_DEBUG
33972         ) {
33973         if (!Thread::UniqueStack(&acontext)) {
33974             return FALSE;
33975         }
33976     }
33977
33978 #ifdef BACKGROUND_GC
33979         // don't trigger a GC from the GC threads but still trigger GCs from user threads.
33980         if (GCToEEInterface::WasCurrentThreadCreatedByGC())
33981         {
33982             return FALSE;
33983         }
33984 #endif //BACKGROUND_GC
33985
33986         if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
33987         {
33988             GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
33989             GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
33990         }
33991
33992         if (GCStressMaxFGCsPerBGC == -1)
33993         {
33994             GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
33995             if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
33996                 GCStressMaxFGCsPerBGC = 6;
33997         }
33998
33999 #ifdef _DEBUG
34000         if (g_JitCount < GCStressStartAtJit)
34001             return FALSE;
34002 #endif //_DEBUG
34003
34004         // Allow programmer to skip the first N Stress GCs so that you can
34005         // get to the interesting ones faster.
34006         Interlocked::Increment(&GCStressCurCount);
34007         if (GCStressCurCount < GCStressStartCount)
34008             return FALSE;
34009
34010         // throttle the number of stress-induced GCs by a factor given by GCStressStep
34011         if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
34012         {
34013             return FALSE;
34014         }
34015
34016 #ifdef BACKGROUND_GC
34017         if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
34018         {
34019             // allow a maximum number of stress induced FGCs during one BGC
34020             if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
34021                 return FALSE;
34022             ++gc_stress_fgcs_in_bgc;
34023         }
34024 #endif // BACKGROUND_GC
34025
34026     if (g_pStringClass == 0)
34027     {
34028         // If the String class has not been loaded, dont do any stressing. This should
34029         // be kept to a minimum to get as complete coverage as possible.
34030         _ASSERTE(g_fEEInit);
34031         return FALSE;
34032     }
34033
34034 #ifndef MULTIPLE_HEAPS
34035     static int32_t OneAtATime = -1;
34036
34037     // Only bother with this if the stress level is big enough and if nobody else is
34038     // doing it right now.  Note that some callers are inside the AllocLock and are
34039     // guaranteed synchronized.  But others are using AllocationContexts and have no
34040     // particular synchronization.
34041     //
34042     // For this latter case, we want a very high-speed way of limiting this to one
34043     // at a time.  A secondary advantage is that we release part of our StressObjs
34044     // buffer sparingly but just as effectively.
34045
34046     if (Interlocked::Increment(&OneAtATime) == 0 &&
34047         !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
34048     {
34049         StringObject* str;
34050
34051         // If the current string is used up
34052         if (HndFetchHandle(m_StressObjs[m_CurStressObj]) == 0)
34053         {
34054             // Populate handles with strings
34055             int i = m_CurStressObj;
34056             while(HndFetchHandle(m_StressObjs[i]) == 0)
34057             {
34058                 _ASSERTE(m_StressObjs[i] != 0);
34059                 unsigned strLen = (LARGE_OBJECT_SIZE - 32) / sizeof(WCHAR);
34060                 unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
34061                 
34062                 // update the cached type handle before allocating
34063                 SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
34064                 str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
34065                 if (str)
34066                 {
34067                     str->SetMethodTable (g_pStringClass);
34068                     str->SetStringLength (strLen);
34069
34070                     HndAssignHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
34071                 }
34072                 i = (i + 1) % NUM_HEAP_STRESS_OBJS;
34073                 if (i == m_CurStressObj) break;
34074             }
34075
34076             // advance the current handle to the next string
34077             m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
34078         }
34079
34080         // Get the current string
34081         str = (StringObject*) OBJECTREFToObject(HndFetchHandle(m_StressObjs[m_CurStressObj]));
34082         if (str)
34083         {
34084             // Chop off the end of the string and form a new object out of it.
34085             // This will 'free' an object at the begining of the heap, which will
34086             // force data movement.  Note that we can only do this so many times.
34087             // before we have to move on to the next string.
34088             unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
34089             if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
34090             {
34091                 unsigned sizeToNextObj = (unsigned)Align(size(str));
34092                 uint8_t* freeObj = ((uint8_t*) str) + sizeToNextObj - sizeOfNewObj;
34093                 pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);                    
34094                 str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
34095             }
34096             else
34097             {
34098                 // Let the string itself become garbage.
34099                 // will be realloced next time around
34100                 HndAssignHandle(m_StressObjs[m_CurStressObj], 0);
34101             }
34102         }
34103     }
34104     Interlocked::Decrement(&OneAtATime);
34105 #endif // !MULTIPLE_HEAPS
34106     if (IsConcurrentGCEnabled())
34107     {
34108         int rgen = StressRNG(10);
34109
34110         // gen0:gen1:gen2 distribution: 40:40:20
34111         if (rgen >= 8)
34112             rgen = 2;
34113         else if (rgen >= 4)
34114             rgen = 1;
34115     else
34116             rgen = 0;
34117
34118         GarbageCollectTry (rgen, FALSE, collection_gcstress);
34119     }
34120     else
34121     {
34122         GarbageCollect(max_generation, FALSE, collection_gcstress);
34123     }
34124
34125     return TRUE;
34126 #else
34127     UNREFERENCED_PARAMETER(context);
34128     return FALSE;
34129 #endif // defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34130 }
34131
34132
34133 #ifdef FEATURE_PREMORTEM_FINALIZATION
34134 #define REGISTER_FOR_FINALIZATION(_object, _size) \
34135     hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
34136 #else // FEATURE_PREMORTEM_FINALIZATION
34137 #define REGISTER_FOR_FINALIZATION(_object, _size) true
34138 #endif // FEATURE_PREMORTEM_FINALIZATION
34139
34140 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {  \
34141     if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size)))   \
34142     {                                                                                       \
34143         STRESS_LOG_OOM_STACK(_size);                                                        \
34144         return NULL;                                                                        \
34145     }                                                                                       \
34146 } while (false)
34147
34148 //
34149 // Small Object Allocator
34150 //
34151 //
34152 // Allocate small object with an alignment requirement of 8-bytes.
34153 Object*
34154 GCHeap::AllocAlign8(gc_alloc_context* ctx, size_t size, uint32_t flags )
34155 {
34156 #ifdef FEATURE_64BIT_ALIGNMENT
34157     CONTRACTL {
34158         NOTHROW;
34159         GC_TRIGGERS;
34160     } CONTRACTL_END;
34161
34162     alloc_context* acontext = static_cast<alloc_context*>(ctx);
34163
34164 #ifdef MULTIPLE_HEAPS
34165     if (acontext->get_alloc_heap() == 0)
34166     {
34167         AssignHeap (acontext);
34168         assert (acontext->get_alloc_heap());
34169     }
34170
34171     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34172 #else
34173     gc_heap* hp = pGenGCHeap;
34174 #endif //MULTIPLE_HEAPS
34175
34176     return AllocAlign8Common(hp, acontext, size, flags);
34177 #else
34178     UNREFERENCED_PARAMETER(ctx);
34179     UNREFERENCED_PARAMETER(size);
34180     UNREFERENCED_PARAMETER(flags);
34181     assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!");
34182     return nullptr;
34183 #endif  //FEATURE_64BIT_ALIGNMENT
34184 }
34185
34186 // Common code used by both variants of AllocAlign8 above.
34187 Object*
34188 GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, uint32_t flags)
34189 {
34190 #ifdef FEATURE_64BIT_ALIGNMENT
34191     CONTRACTL {
34192         NOTHROW;
34193         GC_TRIGGERS;
34194     } CONTRACTL_END;
34195
34196     gc_heap* hp = (gc_heap*)_hp;
34197
34198     TRIGGERSGC();
34199
34200     Object* newAlloc = NULL;
34201
34202 #ifdef TRACE_GC
34203 #ifdef COUNT_CYCLES
34204     AllocStart = GetCycleCount32();
34205     unsigned finish;
34206 #elif defined(ENABLE_INSTRUMENTATION)
34207     unsigned AllocStart = GetInstLogTime();
34208     unsigned finish;
34209 #endif //COUNT_CYCLES
34210 #endif //TRACE_GC
34211
34212     if (size < LARGE_OBJECT_SIZE)
34213     {
34214 #ifdef TRACE_GC
34215         AllocSmallCount++;
34216 #endif //TRACE_GC
34217
34218         // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
34219         // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
34220         // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
34221         size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
34222
34223         // Retrieve the address of the next allocation from the context (note that we're inside the alloc
34224         // lock at this point).
34225         uint8_t*  result = acontext->alloc_ptr;
34226
34227         // Will an allocation at this point yield the correct alignment and fit into the remainder of the
34228         // context?
34229         if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
34230         {
34231             // Yes, we can just go ahead and make the allocation.
34232             newAlloc = (Object*) hp->allocate (size, acontext);
34233             ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34234         }
34235         else
34236         {
34237             // No, either the next available address is not aligned in the way we require it or there's
34238             // not enough space to allocate an object of the required size. In both cases we allocate a
34239             // padding object (marked as a free object). This object's size is such that it will reverse
34240             // the alignment of the next header (asserted below).
34241             //
34242             // We allocate both together then decide based on the result whether we'll format the space as
34243             // free object + real object or real object + free object.
34244             ASSERT((Align(min_obj_size) & 7) == 4);
34245             CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
34246             if (freeobj)
34247             {
34248                 if (((size_t)freeobj & 7) == desiredAlignment)
34249                 {
34250                     // New allocation has desired alignment, return this one and place the free object at the
34251                     // end of the allocated space.
34252                     newAlloc = (Object*)freeobj;
34253                     freeobj = (CObjectHeader*)((uint8_t*)freeobj + Align(size));
34254                 }
34255                 else
34256                 {
34257                     // New allocation is still mis-aligned, format the initial space as a free object and the
34258                     // rest of the space should be correctly aligned for the real object.
34259                     newAlloc = (Object*)((uint8_t*)freeobj + Align(min_obj_size));
34260                     ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34261                 }
34262                 freeobj->SetFree(min_obj_size);
34263             }
34264         }
34265     }
34266     else
34267     {
34268         // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
34269         // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
34270         // we've managed to arrange things so the only case where we see a bias is for boxed value types and
34271         // these can never get large enough to be allocated on the LOH.
34272         ASSERT(65536 < LARGE_OBJECT_SIZE);
34273         ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
34274
34275         alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34276
34277         newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
34278         ASSERT(((size_t)newAlloc & 7) == 0);
34279     }
34280
34281     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34282
34283 #ifdef TRACE_GC
34284 #ifdef COUNT_CYCLES
34285     finish = GetCycleCount32();
34286 #elif defined(ENABLE_INSTRUMENTATION)
34287     finish = GetInstLogTime();
34288 #endif //COUNT_CYCLES
34289     AllocDuration += finish - AllocStart;
34290     AllocCount++;
34291 #endif //TRACE_GC
34292     return newAlloc;
34293 #else
34294     UNREFERENCED_PARAMETER(_hp);
34295     UNREFERENCED_PARAMETER(acontext);
34296     UNREFERENCED_PARAMETER(size);
34297     UNREFERENCED_PARAMETER(flags);
34298     assert(!"Should not call GCHeap::AllocAlign8Common without FEATURE_64BIT_ALIGNMENT defined!");
34299     return nullptr;
34300 #endif // FEATURE_64BIT_ALIGNMENT
34301 }
34302
34303 Object *
34304 GCHeap::AllocLHeap( size_t size, uint32_t flags REQD_ALIGN_DCL)
34305 {
34306     CONTRACTL {
34307         NOTHROW;
34308         GC_TRIGGERS;
34309     } CONTRACTL_END;
34310
34311     TRIGGERSGC();
34312
34313     Object* newAlloc = NULL;
34314
34315 #ifdef TRACE_GC
34316 #ifdef COUNT_CYCLES
34317     AllocStart = GetCycleCount32();
34318     unsigned finish;
34319 #elif defined(ENABLE_INSTRUMENTATION)
34320     unsigned AllocStart = GetInstLogTime();
34321     unsigned finish;
34322 #endif //COUNT_CYCLES
34323 #endif //TRACE_GC
34324
34325 #ifdef MULTIPLE_HEAPS
34326     //take the first heap....
34327     gc_heap* hp = gc_heap::g_heaps[0];
34328 #else
34329     gc_heap* hp = pGenGCHeap;
34330 #ifdef _PREFAST_
34331     // prefix complains about us dereferencing hp in wks build even though we only access static members
34332     // this way. not sure how to shut it up except for this ugly workaround:
34333     PREFIX_ASSUME(hp != NULL);
34334 #endif //_PREFAST_
34335 #endif //MULTIPLE_HEAPS
34336
34337     alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34338
34339     newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34340 #ifdef FEATURE_STRUCTALIGN
34341     newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34342 #endif // FEATURE_STRUCTALIGN
34343     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34344
34345 #ifdef TRACE_GC
34346 #ifdef COUNT_CYCLES
34347     finish = GetCycleCount32();
34348 #elif defined(ENABLE_INSTRUMENTATION)
34349     finish = GetInstLogTime();
34350 #endif //COUNT_CYCLES
34351     AllocDuration += finish - AllocStart;
34352     AllocCount++;
34353 #endif //TRACE_GC
34354     return newAlloc;
34355 }
34356
34357 Object*
34358 GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_DCL)
34359 {
34360     CONTRACTL {
34361         NOTHROW;
34362         GC_TRIGGERS;
34363     } CONTRACTL_END;
34364
34365     TRIGGERSGC();
34366
34367     Object* newAlloc = NULL;
34368     alloc_context* acontext = static_cast<alloc_context*>(context);
34369
34370 #ifdef TRACE_GC
34371 #ifdef COUNT_CYCLES
34372     AllocStart = GetCycleCount32();
34373     unsigned finish;
34374 #elif defined(ENABLE_INSTRUMENTATION)
34375     unsigned AllocStart = GetInstLogTime();
34376     unsigned finish;
34377 #endif //COUNT_CYCLES
34378 #endif //TRACE_GC
34379
34380 #ifdef MULTIPLE_HEAPS
34381     if (acontext->get_alloc_heap() == 0)
34382     {
34383         AssignHeap (acontext);
34384         assert (acontext->get_alloc_heap());
34385     }
34386 #endif //MULTIPLE_HEAPS
34387
34388 #ifdef MULTIPLE_HEAPS
34389     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34390 #else
34391     gc_heap* hp = pGenGCHeap;
34392 #ifdef _PREFAST_
34393     // prefix complains about us dereferencing hp in wks build even though we only access static members
34394     // this way. not sure how to shut it up except for this ugly workaround:
34395     PREFIX_ASSUME(hp != NULL);
34396 #endif //_PREFAST_
34397 #endif //MULTIPLE_HEAPS
34398
34399     if (size < LARGE_OBJECT_SIZE)
34400     {
34401
34402 #ifdef TRACE_GC
34403         AllocSmallCount++;
34404 #endif //TRACE_GC
34405         newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
34406 #ifdef FEATURE_STRUCTALIGN
34407         newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
34408 #endif // FEATURE_STRUCTALIGN
34409 //        ASSERT (newAlloc);
34410     }
34411     else 
34412     {
34413         newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34414 #ifdef FEATURE_STRUCTALIGN
34415         newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34416 #endif // FEATURE_STRUCTALIGN
34417     }
34418
34419     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34420
34421 #ifdef TRACE_GC
34422 #ifdef COUNT_CYCLES
34423     finish = GetCycleCount32();
34424 #elif defined(ENABLE_INSTRUMENTATION)
34425     finish = GetInstLogTime();
34426 #endif //COUNT_CYCLES
34427     AllocDuration += finish - AllocStart;
34428     AllocCount++;
34429 #endif //TRACE_GC
34430     return newAlloc;
34431 }
34432
34433 void
34434 GCHeap::FixAllocContext (gc_alloc_context* context, bool lockp, void* arg, void *heap)
34435 {
34436     alloc_context* acontext = static_cast<alloc_context*>(context);
34437 #ifdef MULTIPLE_HEAPS
34438
34439     if (arg != 0)
34440         acontext->alloc_count = 0;
34441
34442     uint8_t * alloc_ptr = acontext->alloc_ptr;
34443
34444     if (!alloc_ptr)
34445         return;
34446
34447     // The acontext->alloc_heap can be out of sync with the ptrs because
34448     // of heap re-assignment in allocate
34449     gc_heap* hp = gc_heap::heap_of (alloc_ptr);
34450 #else
34451     gc_heap* hp = pGenGCHeap;
34452 #endif //MULTIPLE_HEAPS
34453
34454     if (heap == NULL || heap == hp)
34455     {
34456         if (lockp)
34457         {
34458             enter_spin_lock (&hp->more_space_lock);
34459         }
34460         hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
34461                                 get_alignment_constant(TRUE));
34462         if (lockp)
34463         {
34464             leave_spin_lock (&hp->more_space_lock);
34465         }
34466     }
34467 }
34468
34469 Object*
34470 GCHeap::GetContainingObject (void *pInteriorPtr, bool fCollectedGenOnly)
34471 {
34472     uint8_t *o = (uint8_t*)pInteriorPtr;
34473
34474     gc_heap* hp = gc_heap::heap_of (o);
34475
34476     uint8_t* lowest = (fCollectedGenOnly ? hp->gc_low : hp->lowest_address);
34477     uint8_t* highest = (fCollectedGenOnly ? hp->gc_high : hp->highest_address);
34478
34479     if (o >= lowest && o < highest)
34480     {
34481         o = hp->find_object (o, lowest);
34482     }
34483     else
34484     {
34485         o = NULL;
34486     }
34487     
34488     return (Object *)o;
34489 }
34490
34491 BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
34492 {
34493     if (dd_new_allocation (dd) < 0)
34494     {
34495         return TRUE;
34496     }
34497
34498     if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
34499     {
34500         return TRUE;
34501     }
34502
34503     return FALSE;
34504 }
34505
34506 //----------------------------------------------------------------------------
34507 // #GarbageCollector
34508 //
34509 //  API to ensure that a complete new garbage collection takes place
34510 //
34511 HRESULT
34512 GCHeap::GarbageCollect (int generation, bool low_memory_p, int mode)
34513 {
34514 #if defined(BIT64) 
34515     if (low_memory_p)
34516     {
34517         size_t total_allocated = 0;
34518         size_t total_desired = 0;
34519 #ifdef MULTIPLE_HEAPS
34520         int hn = 0;
34521         for (hn = 0; hn < gc_heap::n_heaps; hn++)
34522         {
34523             gc_heap* hp = gc_heap::g_heaps [hn];
34524             total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
34525             total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
34526                 dd_new_allocation (hp->dynamic_data_of (0));
34527         }
34528 #else
34529         gc_heap* hp = pGenGCHeap;
34530         total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
34531         total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
34532             dd_new_allocation (hp->dynamic_data_of (0));
34533 #endif //MULTIPLE_HEAPS
34534
34535         if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
34536         {
34537             dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
34538                          total_allocated, total_desired));
34539
34540             return S_OK;
34541         }
34542     }
34543 #endif // BIT64 
34544
34545 #ifdef MULTIPLE_HEAPS
34546     gc_heap* hpt = gc_heap::g_heaps[0];
34547 #else
34548     gc_heap* hpt = 0;
34549 #endif //MULTIPLE_HEAPS
34550
34551     generation = (generation < 0) ? max_generation : min (generation, max_generation);
34552     dynamic_data* dd = hpt->dynamic_data_of (generation);
34553
34554 #ifdef BACKGROUND_GC
34555     if (recursive_gc_sync::background_running_p())
34556     {
34557         if ((mode == collection_optimized) || (mode & collection_non_blocking))
34558         {
34559             return S_OK;
34560         }
34561         if (mode & collection_blocking)
34562         {
34563             pGenGCHeap->background_gc_wait();
34564             if (mode & collection_optimized)
34565             {
34566                 return S_OK;
34567             }
34568         }
34569     }
34570 #endif //BACKGROUND_GC
34571
34572     if (mode & collection_optimized)
34573     {
34574         if (pGenGCHeap->gc_started)
34575         {
34576             return S_OK;
34577         }
34578         else 
34579         {
34580             BOOL should_collect = FALSE;
34581             BOOL should_check_loh = (generation == max_generation);
34582 #ifdef MULTIPLE_HEAPS
34583             for (int i = 0; i < gc_heap::n_heaps; i++)
34584             {
34585                 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
34586                 dynamic_data* dd2 = (should_check_loh ? 
34587                                      (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
34588                                      0);
34589
34590                 if (should_collect_optimized (dd1, low_memory_p))
34591                 {
34592                     should_collect = TRUE;
34593                     break;
34594                 }
34595                 if (dd2 && should_collect_optimized (dd2, low_memory_p))
34596                 {
34597                     should_collect = TRUE;
34598                     break;
34599                 }
34600             }
34601 #else
34602             should_collect = should_collect_optimized (dd, low_memory_p);
34603             if (!should_collect && should_check_loh)
34604             {
34605                 should_collect = 
34606                     should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
34607             }
34608 #endif //MULTIPLE_HEAPS
34609             if (!should_collect)
34610             {
34611                 return S_OK;
34612             }
34613         }
34614     }
34615
34616     size_t CollectionCountAtEntry = dd_collection_count (dd);
34617     size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
34618     size_t CurrentCollectionCount = 0;
34619
34620 retry:
34621
34622     CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
34623     
34624     if ((mode & collection_blocking) && 
34625         (generation == max_generation) && 
34626         (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
34627     {
34628 #ifdef BACKGROUND_GC
34629         if (recursive_gc_sync::background_running_p())
34630         {
34631             pGenGCHeap->background_gc_wait();
34632         }
34633 #endif //BACKGROUND_GC
34634
34635         goto retry;
34636     }
34637
34638     if (CollectionCountAtEntry == CurrentCollectionCount)
34639     {
34640         goto retry;
34641     }
34642
34643     return S_OK;
34644 }
34645
34646 size_t
34647 GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
34648 {
34649     int gen = (generation < 0) ? 
34650                max_generation : min (generation, max_generation);
34651
34652     gc_reason reason = reason_empty;
34653     
34654     if (low_memory_p) 
34655     {
34656         if (mode & collection_blocking)
34657             reason = reason_lowmemory_blocking;
34658         else
34659             reason = reason_lowmemory;
34660     }
34661     else
34662         reason = reason_induced;
34663
34664     if (reason == reason_induced)
34665     {
34666         if (mode & collection_compacting)
34667         {
34668             reason = reason_induced_compacting;
34669         }
34670         else if (mode & collection_non_blocking)
34671         {
34672             reason = reason_induced_noforce;
34673         }
34674 #ifdef STRESS_HEAP
34675         else if (mode & collection_gcstress)
34676         {
34677             reason = reason_gcstress;
34678         }
34679 #endif
34680     }
34681
34682     return GarbageCollectGeneration (gen, reason);
34683 }
34684
34685 void gc_heap::do_pre_gc()
34686 {
34687     STRESS_LOG_GC_STACK;
34688
34689 #ifdef STRESS_LOG
34690     STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
34691                         (uint32_t)settings.condemned_generation,
34692                         (uint32_t)settings.reason);
34693 #endif // STRESS_LOG
34694
34695 #ifdef MULTIPLE_HEAPS
34696     gc_heap* hp = g_heaps[0];
34697 #else
34698     gc_heap* hp = 0;
34699 #endif //MULTIPLE_HEAPS
34700
34701 #ifdef BACKGROUND_GC
34702     settings.b_state = hp->current_bgc_state;
34703 #endif //BACKGROUND_GC
34704
34705 #ifdef BACKGROUND_GC
34706     dprintf (1, ("*GC* %d(gen0:%d)(%d)(%s)(%d)", 
34707         VolatileLoad(&settings.gc_index), 
34708         dd_collection_count (hp->dynamic_data_of (0)),
34709         settings.condemned_generation,
34710         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
34711         settings.b_state));
34712 #else
34713     dprintf (1, ("*GC* %d(gen0:%d)(%d)", 
34714         VolatileLoad(&settings.gc_index), 
34715         dd_collection_count(hp->dynamic_data_of(0)),
34716         settings.condemned_generation));
34717 #endif //BACKGROUND_GC
34718
34719     // TODO: this can happen...it's because of the way we are calling
34720     // do_pre_gc, will fix later.
34721     //if (last_gc_index > VolatileLoad(&settings.gc_index))
34722     //{
34723     //    FATAL_GC_ERROR();
34724     //}
34725
34726     last_gc_index = VolatileLoad(&settings.gc_index);
34727     GCHeap::UpdatePreGCCounters();
34728
34729     if (settings.concurrent)
34730     {
34731 #ifdef BACKGROUND_GC
34732         full_gc_counts[gc_type_background]++;
34733 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34734         GCHeap::gc_stress_fgcs_in_bgc = 0;
34735 #endif // STRESS_HEAP && !FEATURE_REDHAWK
34736 #endif // BACKGROUND_GC
34737     }
34738     else
34739     {
34740         if (settings.condemned_generation == max_generation)
34741         {
34742             full_gc_counts[gc_type_blocking]++;
34743         }
34744         else
34745         {
34746 #ifdef BACKGROUND_GC
34747             if (settings.background_p)
34748             {
34749                 ephemeral_fgc_counts[settings.condemned_generation]++;
34750             }
34751 #endif //BACKGROUND_GC
34752         }
34753     }
34754
34755 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34756     if (g_fEnableARM)
34757     {
34758         SystemDomain::ResetADSurvivedBytes();
34759     }
34760 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34761 }
34762
34763 #ifdef GC_CONFIG_DRIVEN
34764 void gc_heap::record_interesting_info_per_heap()
34765 {
34766     // datapoints are always from the last blocking GC so don't record again
34767     // for BGCs.
34768     if (!(settings.concurrent))
34769     {
34770         for (int i = 0; i < max_idp_count; i++)
34771         {
34772             interesting_data_per_heap[i] += interesting_data_per_gc[i];
34773         }
34774     }
34775
34776     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
34777     if (compact_reason >= 0)
34778         (compact_reasons_per_heap[compact_reason])++;
34779     int expand_mechanism = get_gc_data_per_heap()->get_mechanism (gc_heap_expand);
34780     if (expand_mechanism >= 0)
34781         (expand_mechanisms_per_heap[expand_mechanism])++;
34782
34783     for (int i = 0; i < max_gc_mechanism_bits_count; i++)
34784     {
34785         if (get_gc_data_per_heap()->is_mechanism_bit_set ((gc_mechanism_bit_per_heap)i))
34786             (interesting_mechanism_bits_per_heap[i])++;
34787     }
34788
34789     //         h#  | GC  | gen | C   | EX  | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
34790     cprintf (("%2d | %6d | %1d | %1s | %2s | %2s | %2s | %2s | %2s || %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id |",
34791             heap_number,
34792             (size_t)settings.gc_index,
34793             settings.condemned_generation,
34794             // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs.
34795             (settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W") : ""), // compaction
34796             ((expand_mechanism >= 0)? "X" : ""), // EX
34797             ((expand_mechanism == expand_reuse_normal) ? "X" : ""), // NF
34798             ((expand_mechanism == expand_reuse_bestfit) ? "X" : ""), // BF
34799             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_mark_list_bit) ? "X" : ""), // ML
34800             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_demotion_bit) ? "X" : ""), // DM
34801             interesting_data_per_gc[idp_pre_short],
34802             interesting_data_per_gc[idp_post_short],
34803             interesting_data_per_gc[idp_merged_pin],
34804             interesting_data_per_gc[idp_converted_pin],
34805             interesting_data_per_gc[idp_pre_pin],
34806             interesting_data_per_gc[idp_post_pin],
34807             interesting_data_per_gc[idp_pre_and_post_pin],
34808             interesting_data_per_gc[idp_pre_short_padded],
34809             interesting_data_per_gc[idp_post_short_padded]));
34810 }
34811
34812 void gc_heap::record_global_mechanisms()
34813 {
34814     for (int i = 0; i < max_global_mechanisms_count; i++)
34815     {
34816         if (gc_data_global.get_mechanism_p ((gc_global_mechanism_p)i))
34817         {
34818             ::record_global_mechanism (i);
34819         }
34820     }
34821 }
34822
34823 BOOL gc_heap::should_do_sweeping_gc (BOOL compact_p)
34824 {
34825     if (!compact_ratio)
34826         return (!compact_p);
34827
34828     size_t compact_count = compact_or_sweep_gcs[0];
34829     size_t sweep_count = compact_or_sweep_gcs[1];
34830
34831     size_t total_count = compact_count + sweep_count;
34832     BOOL should_compact = compact_p;
34833     if (total_count > 3)
34834     {
34835         if (compact_p)
34836         {
34837             int temp_ratio = (int)((compact_count + 1) * 100 / (total_count + 1));
34838             if (temp_ratio > compact_ratio)
34839             {
34840                 // cprintf (("compact would be: %d, total_count: %d, ratio would be %d%% > target\n",
34841                 //     (compact_count + 1), (total_count + 1), temp_ratio));
34842                 should_compact = FALSE;
34843             }
34844         }
34845         else
34846         {
34847             int temp_ratio = (int)((sweep_count + 1) * 100 / (total_count + 1));
34848             if (temp_ratio > (100 - compact_ratio))
34849             {
34850                 // cprintf (("sweep would be: %d, total_count: %d, ratio would be %d%% > target\n",
34851                 //     (sweep_count + 1), (total_count + 1), temp_ratio));
34852                 should_compact = TRUE;
34853             }
34854         }
34855     }
34856
34857     return !should_compact;
34858 }
34859 #endif //GC_CONFIG_DRIVEN
34860
34861 void gc_heap::do_post_gc()
34862 {
34863     if (!settings.concurrent)
34864     {
34865         initGCShadow();
34866     }
34867
34868 #ifdef TRACE_GC
34869 #ifdef COUNT_CYCLES
34870     AllocStart = GetCycleCount32();
34871 #else
34872     AllocStart = clock();
34873 #endif //COUNT_CYCLES
34874 #endif //TRACE_GC
34875
34876 #ifdef MULTIPLE_HEAPS
34877     gc_heap* hp = g_heaps[0];
34878 #else
34879     gc_heap* hp = 0;
34880 #endif //MULTIPLE_HEAPS
34881     
34882     GCToEEInterface::GcDone(settings.condemned_generation);
34883
34884     GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index),
34885                          (uint32_t)settings.condemned_generation,
34886                          (uint32_t)settings.reason,
34887                          !!settings.concurrent);
34888
34889     //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)", 
34890     dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)", 
34891         VolatileLoad(&settings.gc_index), 
34892         dd_collection_count(hp->dynamic_data_of(0)),
34893         settings.condemned_generation,
34894         (settings.concurrent ? "BGC" : "GC")));
34895
34896     if (settings.exit_memory_load != 0)
34897         last_gc_memory_load = settings.exit_memory_load;
34898     else if (settings.entry_memory_load != 0)
34899         last_gc_memory_load = settings.entry_memory_load;
34900
34901     last_gc_heap_size = get_total_heap_size();
34902     last_gc_fragmentation = get_total_fragmentation();
34903
34904     GCHeap::UpdatePostGCCounters();
34905 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34906     //if (g_fEnableARM)
34907     //{
34908     //    SystemDomain::GetADSurvivedBytes();
34909     //}
34910 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34911
34912 #ifdef STRESS_LOG
34913     STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
34914                       (uint32_t)settings.condemned_generation,
34915                       (uint32_t)settings.reason);
34916 #endif // STRESS_LOG
34917
34918 #ifdef GC_CONFIG_DRIVEN
34919     if (!settings.concurrent)
34920     {
34921         if (settings.compaction)
34922             (compact_or_sweep_gcs[0])++;
34923         else
34924             (compact_or_sweep_gcs[1])++;
34925     }
34926
34927 #ifdef MULTIPLE_HEAPS
34928     for (int i = 0; i < n_heaps; i++)
34929         g_heaps[i]->record_interesting_info_per_heap();
34930 #else
34931     record_interesting_info_per_heap();
34932 #endif //MULTIPLE_HEAPS
34933     record_global_mechanisms();
34934 #endif //GC_CONFIG_DRIVEN
34935 }
34936
34937 unsigned GCHeap::GetGcCount()
34938 {
34939     return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
34940 }
34941
34942 size_t
34943 GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
34944 {
34945     dprintf (2, ("triggered a GC!"));
34946
34947 #ifdef MULTIPLE_HEAPS
34948     gc_heap* hpt = gc_heap::g_heaps[0];
34949 #else
34950     gc_heap* hpt = 0;
34951 #endif //MULTIPLE_HEAPS
34952     bool cooperative_mode = true;
34953     dynamic_data* dd = hpt->dynamic_data_of (gen);
34954     size_t localCount = dd_collection_count (dd);
34955
34956     enter_spin_lock (&gc_heap::gc_lock);
34957     dprintf (SPINLOCK_LOG, ("GC Egc"));
34958     ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
34959
34960     //don't trigger another GC if one was already in progress
34961     //while waiting for the lock
34962     {
34963         size_t col_count = dd_collection_count (dd);
34964
34965         if (localCount != col_count)
34966         {
34967 #ifdef SYNCHRONIZATION_STATS
34968             gc_lock_contended++;
34969 #endif //SYNCHRONIZATION_STATS
34970             dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
34971             leave_spin_lock (&gc_heap::gc_lock);
34972
34973             // We don't need to release msl here 'cause this means a GC
34974             // has happened and would have release all msl's.
34975             return col_count;
34976          }
34977     }
34978
34979 #ifdef COUNT_CYCLES
34980     int gc_start = GetCycleCount32();
34981 #endif //COUNT_CYCLES
34982
34983 #ifdef TRACE_GC
34984 #ifdef COUNT_CYCLES
34985     AllocDuration += GetCycleCount32() - AllocStart;
34986 #else
34987     AllocDuration += clock() - AllocStart;
34988 #endif //COUNT_CYCLES
34989 #endif //TRACE_GC
34990
34991     gc_heap::g_low_memory_status = (reason == reason_lowmemory) || 
34992                                    (reason == reason_lowmemory_blocking) ||
34993                                    (gc_heap::latency_level == latency_level_memory_footprint);
34994
34995     gc_trigger_reason = reason;
34996
34997 #ifdef MULTIPLE_HEAPS
34998     for (int i = 0; i < gc_heap::n_heaps; i++)
34999     {
35000         gc_heap::g_heaps[i]->reset_gc_done();
35001     }
35002 #else
35003     gc_heap::reset_gc_done();
35004 #endif //MULTIPLE_HEAPS
35005
35006     gc_heap::gc_started = TRUE;
35007
35008     {
35009         init_sync_log_stats();
35010
35011 #ifndef MULTIPLE_HEAPS
35012         cooperative_mode = gc_heap::enable_preemptive ();
35013
35014         dprintf (2, ("Suspending EE"));
35015         BEGIN_TIMING(suspend_ee_during_log);
35016         GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
35017         END_TIMING(suspend_ee_during_log);
35018         gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();
35019         gc_heap::disable_preemptive (cooperative_mode);
35020         if (gc_heap::proceed_with_gc_p)
35021             pGenGCHeap->settings.init_mechanisms();
35022         else
35023             gc_heap::update_collection_counts_for_no_gc();
35024
35025 #endif //!MULTIPLE_HEAPS
35026     }
35027
35028 // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
35029
35030 #ifdef TRACE_GC
35031 #ifdef COUNT_CYCLES
35032     unsigned start;
35033     unsigned finish;
35034     start = GetCycleCount32();
35035 #else
35036     clock_t start;
35037     clock_t finish;
35038     start = clock();
35039 #endif //COUNT_CYCLES
35040     PromotedObjectCount = 0;
35041 #endif //TRACE_GC
35042
35043     unsigned int condemned_generation_number = gen;
35044
35045     // We want to get a stack from the user thread that triggered the GC
35046     // instead of on the GC thread which is the case for Server GC.
35047     // But we are doing it for Workstation GC as well to be uniform.
35048     FIRE_EVENT(GCTriggered, static_cast<uint32_t>(reason));
35049
35050 #ifdef MULTIPLE_HEAPS
35051     GcCondemnedGeneration = condemned_generation_number;
35052
35053     cooperative_mode = gc_heap::enable_preemptive ();
35054
35055     BEGIN_TIMING(gc_during_log);
35056     gc_heap::ee_suspend_event.Set();
35057     gc_heap::wait_for_gc_done();
35058     END_TIMING(gc_during_log);
35059
35060     gc_heap::disable_preemptive (cooperative_mode);
35061
35062     condemned_generation_number = GcCondemnedGeneration;
35063 #else
35064     if (gc_heap::proceed_with_gc_p)
35065     {
35066         BEGIN_TIMING(gc_during_log);
35067         pGenGCHeap->garbage_collect (condemned_generation_number);
35068         END_TIMING(gc_during_log);
35069     }
35070 #endif //MULTIPLE_HEAPS
35071
35072 #ifdef TRACE_GC
35073 #ifdef COUNT_CYCLES
35074     finish = GetCycleCount32();
35075 #else
35076     finish = clock();
35077 #endif //COUNT_CYCLES
35078     GcDuration += finish - start;
35079     dprintf (3,
35080              ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
35081               VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
35082               finish - start, GcDuration,
35083               AllocCount ? (AllocDuration / AllocCount) : 0,
35084               AllocSmallCount, AllocBigCount));
35085     AllocCount = 0;
35086     AllocDuration = 0;
35087 #endif // TRACE_GC
35088
35089 #ifdef BACKGROUND_GC
35090     // We are deciding whether we should fire the alloc wait end event here
35091     // because in begin_foreground we could be calling end_foreground 
35092     // if we need to retry.
35093     if (gc_heap::alloc_wait_event_p)
35094     {
35095         hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
35096         gc_heap::alloc_wait_event_p = FALSE;
35097     }
35098 #endif //BACKGROUND_GC
35099
35100 #ifndef MULTIPLE_HEAPS
35101 #ifdef BACKGROUND_GC
35102     if (!gc_heap::dont_restart_ee_p)
35103     {
35104 #endif //BACKGROUND_GC
35105         BEGIN_TIMING(restart_ee_during_log);
35106         GCToEEInterface::RestartEE(TRUE);
35107         END_TIMING(restart_ee_during_log);
35108 #ifdef BACKGROUND_GC
35109     }
35110 #endif //BACKGROUND_GC
35111 #endif //!MULTIPLE_HEAPS
35112
35113 #ifdef COUNT_CYCLES
35114     printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
35115             GetCycleCount32() - gc_start);
35116 #endif //COUNT_CYCLES
35117
35118 #ifndef MULTIPLE_HEAPS
35119     process_sync_log_stats();
35120     gc_heap::gc_started = FALSE;
35121     gc_heap::set_gc_done();
35122     dprintf (SPINLOCK_LOG, ("GC Lgc"));
35123     leave_spin_lock (&gc_heap::gc_lock);    
35124 #endif //!MULTIPLE_HEAPS
35125
35126 #ifdef FEATURE_PREMORTEM_FINALIZATION
35127     GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers);
35128 #endif // FEATURE_PREMORTEM_FINALIZATION
35129
35130     return dd_collection_count (dd);
35131 }
35132
35133 size_t      GCHeap::GetTotalBytesInUse ()
35134 {
35135 #ifdef MULTIPLE_HEAPS
35136     //enumarate all the heaps and get their size.
35137     size_t tot_size = 0;
35138     for (int i = 0; i < gc_heap::n_heaps; i++)
35139     {
35140         GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
35141         tot_size += Hp->ApproxTotalBytesInUse (FALSE);
35142     }
35143     return tot_size;
35144 #else
35145     return ApproxTotalBytesInUse ();
35146 #endif //MULTIPLE_HEAPS
35147 }
35148
35149 int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
35150 {
35151     if (get_bgc_fgc_count != 0)
35152     {
35153 #ifdef BACKGROUND_GC
35154         if (generation == max_generation)
35155         {
35156             return (int)(gc_heap::full_gc_counts[gc_type_background]);
35157         }
35158         else
35159         {
35160             return (int)(gc_heap::ephemeral_fgc_counts[generation]);
35161         }
35162 #else
35163         return 0;
35164 #endif //BACKGROUND_GC
35165     }
35166
35167 #ifdef MULTIPLE_HEAPS
35168     gc_heap* hp = gc_heap::g_heaps [0];
35169 #else  //MULTIPLE_HEAPS
35170     gc_heap* hp = pGenGCHeap;
35171 #endif //MULTIPLE_HEAPS
35172     if (generation > max_generation)
35173         return 0;
35174     else
35175         return (int)dd_collection_count (hp->dynamic_data_of (generation));
35176 }
35177
35178 size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
35179 {
35180     size_t totsize = 0;
35181     //GCTODO
35182     //ASSERT(InMustComplete());
35183     enter_spin_lock (&pGenGCHeap->gc_lock);
35184
35185     heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
35186     // Get small block heap size info
35187     totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
35188     heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
35189     while (seg1 != eph_seg)
35190     {
35191         totsize += heap_segment_allocated (seg1) -
35192             heap_segment_mem (seg1);
35193         seg1 = heap_segment_next (seg1);
35194     }
35195
35196     //discount the fragmentation
35197     for (int i = 0; i <= max_generation; i++)
35198     {
35199         generation* gen = pGenGCHeap->generation_of (i);
35200         totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
35201     }
35202
35203     if (!small_heap_only)
35204     {
35205         heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
35206
35207         while (seg2 != 0)
35208         {
35209             totsize += heap_segment_allocated (seg2) -
35210                 heap_segment_mem (seg2);
35211             seg2 = heap_segment_next (seg2);
35212         }
35213
35214         //discount the fragmentation
35215         generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
35216         size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
35217         totsize -= frag;
35218     }
35219     leave_spin_lock (&pGenGCHeap->gc_lock);
35220     return totsize;
35221 }
35222
35223 #ifdef MULTIPLE_HEAPS
35224 void GCHeap::AssignHeap (alloc_context* acontext)
35225 {
35226     // Assign heap based on processor
35227     acontext->set_alloc_heap(GetHeap(heap_select::select_heap(acontext, 0)));
35228     acontext->set_home_heap(acontext->get_alloc_heap());
35229 }
35230 GCHeap* GCHeap::GetHeap (int n)
35231 {
35232     assert (n < gc_heap::n_heaps);
35233     return gc_heap::g_heaps [n]->vm_heap;
35234 }
35235 #endif //MULTIPLE_HEAPS
35236
35237 bool GCHeap::IsThreadUsingAllocationContextHeap(gc_alloc_context* context, int thread_number)
35238 {
35239     alloc_context* acontext = static_cast<alloc_context*>(context);
35240 #ifdef MULTIPLE_HEAPS
35241     return ((acontext->get_home_heap() == GetHeap(thread_number)) ||
35242             ((acontext->get_home_heap() == 0) && (thread_number == 0)));
35243 #else
35244     UNREFERENCED_PARAMETER(acontext);
35245     UNREFERENCED_PARAMETER(thread_number);
35246     return true;
35247 #endif //MULTIPLE_HEAPS
35248 }
35249
35250 // Returns the number of processors required to trigger the use of thread based allocation contexts
35251 int GCHeap::GetNumberOfHeaps ()
35252 {
35253 #ifdef MULTIPLE_HEAPS
35254     return gc_heap::n_heaps;
35255 #else
35256     return 1;
35257 #endif //MULTIPLE_HEAPS
35258 }
35259
35260 /*
35261   in this way we spend extra time cycling through all the heaps while create the handle
35262   it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
35263 */
35264 int GCHeap::GetHomeHeapNumber ()
35265 {
35266 #ifdef MULTIPLE_HEAPS
35267     Thread *pThread = GCToEEInterface::GetThread();
35268     for (int i = 0; i < gc_heap::n_heaps; i++)
35269     {
35270         if (pThread)
35271         {
35272             gc_alloc_context* ctx = GCToEEInterface::GetAllocContext();
35273             GCHeap *hp = static_cast<alloc_context*>(ctx)->get_home_heap();
35274             if (hp == gc_heap::g_heaps[i]->vm_heap) return i;
35275         }
35276     }
35277     return 0;
35278 #else
35279     return 0;
35280 #endif //MULTIPLE_HEAPS
35281 }
35282
35283 unsigned int GCHeap::GetCondemnedGeneration()
35284
35285     return gc_heap::settings.condemned_generation;
35286 }
35287
35288 void GCHeap::GetMemoryInfo(uint32_t* highMemLoadThreshold, 
35289                            uint64_t* totalPhysicalMem, 
35290                            uint32_t* lastRecordedMemLoad,
35291                            size_t* lastRecordedHeapSize,
35292                            size_t* lastRecordedFragmentation)
35293 {
35294     *highMemLoadThreshold = gc_heap::high_memory_load_th;
35295     *totalPhysicalMem = gc_heap::total_physical_mem;
35296     *lastRecordedMemLoad = gc_heap::last_gc_memory_load;
35297     *lastRecordedHeapSize = gc_heap::last_gc_heap_size;
35298     *lastRecordedFragmentation = gc_heap::last_gc_fragmentation;
35299 }
35300
35301 int GCHeap::GetGcLatencyMode()
35302 {
35303     return (int)(pGenGCHeap->settings.pause_mode);
35304 }
35305
35306 int GCHeap::SetGcLatencyMode (int newLatencyMode)
35307 {
35308     if (gc_heap::settings.pause_mode == pause_no_gc)
35309         return (int)set_pause_mode_no_gc;
35310
35311     gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
35312
35313     if (new_mode == pause_low_latency)
35314     {
35315 #ifndef MULTIPLE_HEAPS
35316         pGenGCHeap->settings.pause_mode = new_mode;
35317 #endif //!MULTIPLE_HEAPS
35318     }
35319     else if (new_mode == pause_sustained_low_latency)
35320     {
35321 #ifdef BACKGROUND_GC
35322         if (gc_heap::gc_can_use_concurrent)
35323         {
35324             pGenGCHeap->settings.pause_mode = new_mode;
35325         }
35326 #endif //BACKGROUND_GC
35327     }
35328     else
35329     {
35330         pGenGCHeap->settings.pause_mode = new_mode;
35331     }
35332
35333 #ifdef BACKGROUND_GC
35334     if (recursive_gc_sync::background_running_p())
35335     {
35336         // If we get here, it means we are doing an FGC. If the pause
35337         // mode was altered we will need to save it in the BGC settings.
35338         if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
35339         {
35340             gc_heap::saved_bgc_settings.pause_mode = new_mode;
35341         }
35342     }
35343 #endif //BACKGROUND_GC
35344
35345     return (int)set_pause_mode_success;
35346 }
35347
35348 int GCHeap::GetLOHCompactionMode()
35349 {
35350     return pGenGCHeap->loh_compaction_mode;
35351 }
35352
35353 void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
35354 {
35355 #ifdef FEATURE_LOH_COMPACTION
35356     pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
35357 #endif //FEATURE_LOH_COMPACTION
35358 }
35359
35360 bool GCHeap::RegisterForFullGCNotification(uint32_t gen2Percentage,
35361                                            uint32_t lohPercentage)
35362 {
35363 #ifdef MULTIPLE_HEAPS
35364     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35365     {
35366         gc_heap* hp = gc_heap::g_heaps [hn];
35367         hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
35368     }
35369 #else //MULTIPLE_HEAPS
35370     pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
35371 #endif //MULTIPLE_HEAPS
35372
35373     pGenGCHeap->full_gc_approach_event.Reset();
35374     pGenGCHeap->full_gc_end_event.Reset();
35375     pGenGCHeap->full_gc_approach_event_set = false;
35376
35377     pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
35378     pGenGCHeap->fgn_loh_percent = lohPercentage;
35379
35380     return TRUE;
35381 }
35382
35383 bool GCHeap::CancelFullGCNotification()
35384 {
35385     pGenGCHeap->fgn_maxgen_percent = 0;
35386     pGenGCHeap->fgn_loh_percent = 0;
35387
35388     pGenGCHeap->full_gc_approach_event.Set();
35389     pGenGCHeap->full_gc_end_event.Set();
35390     
35391     return TRUE;
35392 }
35393
35394 int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
35395 {
35396     dprintf (2, ("WFGA: Begin wait"));
35397     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
35398     dprintf (2, ("WFGA: End wait"));
35399     return result;
35400 }
35401
35402 int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
35403 {
35404     dprintf (2, ("WFGE: Begin wait"));
35405     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
35406     dprintf (2, ("WFGE: End wait"));
35407     return result;
35408 }
35409
35410 int GCHeap::StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC)
35411 {
35412     NoGCRegionLockHolder lh;
35413
35414     dprintf (1, ("begin no gc called"));
35415     start_no_gc_region_status status = gc_heap::prepare_for_no_gc_region (totalSize, lohSizeKnown, lohSize, disallowFullBlockingGC);
35416     if (status == start_no_gc_success)
35417     {
35418         GarbageCollect (max_generation);
35419         status = gc_heap::get_start_no_gc_region_status();
35420     }
35421
35422     if (status != start_no_gc_success)
35423         gc_heap::handle_failure_for_no_gc();
35424
35425     return (int)status;
35426 }
35427
35428 int GCHeap::EndNoGCRegion()
35429 {
35430     NoGCRegionLockHolder lh;
35431     return (int)gc_heap::end_no_gc_region();
35432 }
35433
35434 void GCHeap::PublishObject (uint8_t* Obj)
35435 {
35436 #ifdef BACKGROUND_GC
35437     gc_heap* hp = gc_heap::heap_of (Obj);
35438     hp->bgc_alloc_lock->loh_alloc_done (Obj);
35439 #endif //BACKGROUND_GC
35440 }
35441
35442 // The spec for this one isn't clear. This function
35443 // returns the size that can be allocated without
35444 // triggering a GC of any kind.
35445 size_t GCHeap::ApproxFreeBytes()
35446 {
35447     //GCTODO
35448     //ASSERT(InMustComplete());
35449     enter_spin_lock (&pGenGCHeap->gc_lock);
35450
35451     generation* gen = pGenGCHeap->generation_of (0);
35452     size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
35453
35454     leave_spin_lock (&pGenGCHeap->gc_lock);
35455
35456     return res;
35457 }
35458
35459 HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
35460 {
35461     if ((gen < 0) || (gen > max_generation))
35462         return E_FAIL;
35463 #ifdef MULTIPLE_HEAPS
35464     counters->current_size = 0;
35465     counters->promoted_size = 0;
35466     counters->collection_count = 0;
35467
35468     //enumarate all the heaps and get their counters.
35469     for (int i = 0; i < gc_heap::n_heaps; i++)
35470     {
35471         dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
35472
35473         counters->current_size += dd_current_size (dd);
35474         counters->promoted_size += dd_promoted_size (dd);
35475         if (i == 0)
35476         counters->collection_count += dd_collection_count (dd);
35477     }
35478 #else
35479     dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
35480     counters->current_size = dd_current_size (dd);
35481     counters->promoted_size = dd_promoted_size (dd);
35482     counters->collection_count = dd_collection_count (dd);
35483 #endif //MULTIPLE_HEAPS
35484     return S_OK;
35485 }
35486
35487 // Get the segment size to use, making sure it conforms.
35488 size_t GCHeap::GetValidSegmentSize(bool large_seg)
35489 {
35490     return get_valid_segment_size (large_seg);
35491 }
35492
35493 // Get the max gen0 heap size, making sure it conforms.
35494 size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
35495 {
35496     size_t gen0size = static_cast<size_t>(GCConfig::GetGen0Size());
35497
35498     if ((gen0size == 0) || !g_theGCHeap->IsValidGen0MaxSize(gen0size))
35499     {
35500 #ifdef SERVER_GC
35501         // performance data seems to indicate halving the size results
35502         // in optimal perf.  Ask for adjusted gen0 size.
35503         gen0size = max(GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),(256*1024));
35504
35505         // if gen0 size is too large given the available memory, reduce it.
35506         // Get true cache size, as we don't want to reduce below this.
35507         size_t trueSize = max(GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE),(256*1024));
35508         dprintf (2, ("cache: %Id-%Id, cpu: %Id", 
35509             GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),
35510             GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)));
35511
35512         int n_heaps = gc_heap::n_heaps;
35513 #else //SERVER_GC
35514         size_t trueSize = GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE);
35515         gen0size = max((4*trueSize/5),(256*1024));
35516         trueSize = max(trueSize, (256*1024));
35517         int n_heaps = 1;
35518 #endif //SERVER_GC
35519
35520         // if the total min GC across heaps will exceed 1/6th of available memory,
35521         // then reduce the min GC size until it either fits or has been reduced to cache size.
35522         while ((gen0size * n_heaps) > GCToOSInterface::GetPhysicalMemoryLimit() / 6)
35523         {
35524             gen0size = gen0size / 2;
35525             if (gen0size <= trueSize)
35526             {
35527                 gen0size = trueSize;
35528                 break;
35529             }
35530         }
35531     }
35532
35533     // Generation 0 must never be more than 1/2 the segment size.
35534     if (gen0size >= (seg_size / 2))
35535         gen0size = seg_size / 2;
35536
35537     return (gen0size);
35538 }
35539
35540 void GCHeap::SetReservedVMLimit (size_t vmlimit)
35541 {
35542     gc_heap::reserved_memory_limit = vmlimit;
35543 }
35544
35545
35546 //versions of same method on each heap
35547
35548 #ifdef FEATURE_PREMORTEM_FINALIZATION
35549
35550 Object* GCHeap::GetNextFinalizableObject()
35551 {
35552
35553 #ifdef MULTIPLE_HEAPS
35554
35555     //return the first non critical one in the first queue.
35556     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35557     {
35558         gc_heap* hp = gc_heap::g_heaps [hn];
35559         Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
35560         if (O)
35561             return O;
35562     }
35563     //return the first non crtitical/critical one in the first queue.
35564     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35565     {
35566         gc_heap* hp = gc_heap::g_heaps [hn];
35567         Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
35568         if (O)
35569             return O;
35570     }
35571     return 0;
35572
35573
35574 #else //MULTIPLE_HEAPS
35575     return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
35576 #endif //MULTIPLE_HEAPS
35577
35578 }
35579
35580 size_t GCHeap::GetNumberFinalizableObjects()
35581 {
35582 #ifdef MULTIPLE_HEAPS
35583     size_t cnt = 0;
35584     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35585     {
35586         gc_heap* hp = gc_heap::g_heaps [hn];
35587         cnt += hp->finalize_queue->GetNumberFinalizableObjects();
35588     }
35589     return cnt;
35590
35591
35592 #else //MULTIPLE_HEAPS
35593     return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
35594 #endif //MULTIPLE_HEAPS
35595 }
35596
35597 size_t GCHeap::GetFinalizablePromotedCount()
35598 {
35599 #ifdef MULTIPLE_HEAPS
35600     size_t cnt = 0;
35601
35602     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35603     {
35604         gc_heap* hp = gc_heap::g_heaps [hn];
35605         cnt += hp->finalize_queue->GetPromotedCount();
35606     }
35607     return cnt;
35608
35609 #else //MULTIPLE_HEAPS
35610     return pGenGCHeap->finalize_queue->GetPromotedCount();
35611 #endif //MULTIPLE_HEAPS
35612 }
35613
35614 bool GCHeap::FinalizeAppDomain(void *pDomain, bool fRunFinalizers)
35615 {
35616 #ifdef MULTIPLE_HEAPS
35617     bool foundp = false;
35618     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35619     {
35620         gc_heap* hp = gc_heap::g_heaps [hn];
35621         if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
35622             foundp = true;
35623     }
35624     return foundp;
35625
35626 #else //MULTIPLE_HEAPS
35627     return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
35628 #endif //MULTIPLE_HEAPS
35629 }
35630
35631 bool GCHeap::ShouldRestartFinalizerWatchDog()
35632 {
35633     // This condition was historically used as part of the condition to detect finalizer thread timeouts
35634     return gc_heap::gc_lock.lock != -1;
35635 }
35636
35637 void GCHeap::SetFinalizeQueueForShutdown(bool fHasLock)
35638 {
35639 #ifdef MULTIPLE_HEAPS
35640     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35641     {
35642         gc_heap* hp = gc_heap::g_heaps [hn];
35643         hp->finalize_queue->SetSegForShutDown(fHasLock);
35644     }
35645
35646 #else //MULTIPLE_HEAPS
35647     pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
35648 #endif //MULTIPLE_HEAPS
35649 }
35650
35651 //---------------------------------------------------------------------------
35652 // Finalized class tracking
35653 //---------------------------------------------------------------------------
35654
35655 bool GCHeap::RegisterForFinalization (int gen, Object* obj)
35656 {
35657     if (gen == -1)
35658         gen = 0;
35659     if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
35660     {
35661         //just reset the bit
35662         ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
35663         return true;
35664     }
35665     else
35666     {
35667         gc_heap* hp = gc_heap::heap_of ((uint8_t*)obj);
35668         return hp->finalize_queue->RegisterForFinalization (gen, obj);
35669     }
35670 }
35671
35672 void GCHeap::SetFinalizationRun (Object* obj)
35673 {
35674     ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
35675 }
35676
35677
35678 //--------------------------------------------------------------------
35679 //
35680 //          Support for finalization
35681 //
35682 //--------------------------------------------------------------------
35683
35684 inline
35685 unsigned int gen_segment (int gen)
35686 {
35687     assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
35688     return (NUMBERGENERATIONS - gen - 1);
35689 }
35690
35691 bool CFinalize::Initialize()
35692 {
35693     CONTRACTL {
35694         NOTHROW;
35695         GC_NOTRIGGER;
35696     } CONTRACTL_END;
35697
35698     m_Array = new (nothrow)(Object*[100]);
35699
35700     if (!m_Array)
35701     {
35702         ASSERT (m_Array);
35703         STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
35704         if (GCConfig::GetBreakOnOOM())
35705         {
35706             GCToOSInterface::DebugBreak();
35707         }
35708         return false;
35709     }
35710     m_EndArray = &m_Array[100];
35711
35712     for (int i =0; i < FreeList; i++)
35713     {
35714         SegQueueLimit (i) = m_Array;
35715     }
35716     m_PromotedCount = 0;
35717     lock = -1;
35718 #ifdef _DEBUG
35719     lockowner_threadid.Clear();
35720 #endif // _DEBUG
35721
35722     return true;
35723 }
35724
35725 CFinalize::~CFinalize()
35726 {
35727     delete m_Array;
35728 }
35729
35730 size_t CFinalize::GetPromotedCount ()
35731 {
35732     return m_PromotedCount;
35733 }
35734
35735 inline
35736 void CFinalize::EnterFinalizeLock()
35737 {
35738     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
35739              GCToEEInterface::GetThread() == 0 ||
35740              GCToEEInterface::IsPreemptiveGCDisabled());
35741
35742 retry:
35743     if (Interlocked::CompareExchange(&lock, 0, -1) >= 0)
35744     {
35745         unsigned int i = 0;
35746         while (lock >= 0)
35747         {
35748             YieldProcessor();           // indicate to the processor that we are spining
35749             if (++i & 7)
35750                 GCToOSInterface::YieldThread (0);
35751             else
35752                 GCToOSInterface::Sleep (5);
35753         }
35754         goto retry;
35755     }
35756
35757 #ifdef _DEBUG
35758     lockowner_threadid.SetToCurrentThread();
35759 #endif // _DEBUG
35760 }
35761
35762 inline
35763 void CFinalize::LeaveFinalizeLock()
35764 {
35765     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
35766              GCToEEInterface::GetThread() == 0 ||
35767              GCToEEInterface::IsPreemptiveGCDisabled());
35768
35769 #ifdef _DEBUG
35770     lockowner_threadid.Clear();
35771 #endif // _DEBUG
35772     lock = -1;
35773 }
35774
35775 bool
35776 CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
35777 {
35778     CONTRACTL {
35779         NOTHROW;
35780         GC_NOTRIGGER;
35781     } CONTRACTL_END;
35782
35783     EnterFinalizeLock();
35784     // Adjust gen
35785     unsigned int dest = 0;
35786
35787     if (g_fFinalizerRunOnShutDown)
35788     {
35789         //no method table available yet,
35790         //put it in the finalizer queue and sort out when
35791         //dequeueing
35792         dest = FinalizerListSeg;
35793     }
35794
35795     else
35796         dest = gen_segment (gen);
35797
35798     // Adjust boundary for segments so that GC will keep objects alive.
35799     Object*** s_i = &SegQueue (FreeList);
35800     if ((*s_i) == m_EndArray)
35801     {
35802         if (!GrowArray())
35803         {
35804             LeaveFinalizeLock();
35805             if (method_table(obj) == NULL)
35806             {
35807                 // If the object is uninitialized, a valid size should have been passed.
35808                 assert (size >= Align (min_obj_size));
35809                 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
35810                 ((CObjectHeader*)obj)->SetFree(size);
35811             }
35812             STRESS_LOG_OOM_STACK(0);
35813             if (GCConfig::GetBreakOnOOM())
35814             {
35815                 GCToOSInterface::DebugBreak();
35816             }
35817             return false;
35818         }
35819     }
35820     Object*** end_si = &SegQueueLimit (dest);
35821     do
35822     {
35823         //is the segment empty?
35824         if (!(*s_i == *(s_i-1)))
35825         {
35826             //no, swap the end elements.
35827             *(*s_i) = *(*(s_i-1));
35828         }
35829         //increment the fill pointer
35830         (*s_i)++;
35831         //go to the next segment.
35832         s_i--;
35833     } while (s_i > end_si);
35834
35835     // We have reached the destination segment
35836     // store the object
35837     **s_i = obj;
35838     // increment the fill pointer
35839     (*s_i)++;
35840
35841     LeaveFinalizeLock();
35842
35843     return true;
35844 }
35845
35846 Object*
35847 CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
35848 {
35849     Object* obj = 0;
35850     //serialize
35851     EnterFinalizeLock();
35852
35853 retry:
35854     if (!IsSegEmpty(FinalizerListSeg))
35855     {
35856         if (g_fFinalizerRunOnShutDown)
35857         {
35858             obj = *(SegQueueLimit (FinalizerListSeg)-1);
35859             if (method_table(obj)->HasCriticalFinalizer())
35860             {
35861                 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
35862                           FinalizerListSeg, CriticalFinalizerListSeg);
35863                 goto retry;
35864             }
35865             else
35866                 --SegQueueLimit (FinalizerListSeg);
35867         }
35868         else
35869             obj =  *(--SegQueueLimit (FinalizerListSeg));
35870
35871     }
35872     else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
35873     {
35874         //the FinalizerList is empty, we can adjust both
35875         // limit instead of moving the object to the free list
35876         obj =  *(--SegQueueLimit (CriticalFinalizerListSeg));
35877         --SegQueueLimit (FinalizerListSeg);
35878     }
35879     if (obj)
35880     {
35881         dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
35882     }
35883     LeaveFinalizeLock();
35884     return obj;
35885 }
35886
35887 void
35888 CFinalize::SetSegForShutDown(BOOL fHasLock)
35889 {
35890     int i;
35891
35892     if (!fHasLock)
35893         EnterFinalizeLock();
35894     for (i = 0; i <= max_generation; i++)
35895     {
35896         unsigned int seg = gen_segment (i);
35897         Object** startIndex = SegQueueLimit (seg)-1;
35898         Object** stopIndex  = SegQueue (seg);
35899         for (Object** po = startIndex; po >= stopIndex; po--)
35900         {
35901             Object* obj = *po;
35902             if (method_table(obj)->HasCriticalFinalizer())
35903             {
35904                 MoveItem (po, seg, CriticalFinalizerListSeg);
35905             }
35906             else
35907             {
35908                 MoveItem (po, seg, FinalizerListSeg);
35909             }
35910         }
35911     }
35912     if (!fHasLock)
35913         LeaveFinalizeLock();
35914 }
35915
35916 void
35917 CFinalize::DiscardNonCriticalObjects()
35918 {
35919     //empty the finalization queue
35920     Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
35921     Object** stopIndex  = SegQueue (FinalizerListSeg);
35922     for (Object** po = startIndex; po >= stopIndex; po--)
35923     {
35924         MoveItem (po, FinalizerListSeg, FreeList);
35925     }
35926 }
35927
35928 size_t
35929 CFinalize::GetNumberFinalizableObjects()
35930 {
35931     return SegQueueLimit (FinalizerListSeg) -
35932         (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
35933 }
35934
35935 BOOL
35936 CFinalize::FinalizeSegForAppDomain (void *pDomain, 
35937                                     BOOL fRunFinalizers, 
35938                                     unsigned int Seg)
35939 {
35940     BOOL finalizedFound = FALSE;
35941     Object** endIndex = SegQueue (Seg);
35942     for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
35943     {
35944         CObjectHeader* obj = (CObjectHeader*)*i;
35945
35946         // Objects are put into the finalization queue before they are complete (ie their methodtable
35947         // may be null) so we must check that the object we found has a method table before checking
35948         // if it has the index we are looking for. If the methodtable is null, it can't be from the
35949         // unloading domain, so skip it.
35950         if (method_table(obj) == NULL)
35951         {
35952             continue;
35953         }
35954
35955         // does the EE actually want us to finalize this object?
35956         if (!GCToEEInterface::ShouldFinalizeObjectForUnload(pDomain, obj))
35957         {
35958             continue;
35959         }
35960
35961         if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
35962         {
35963             //remove the object because we don't want to
35964             //run the finalizer
35965             MoveItem (i, Seg, FreeList);
35966             //Reset the bit so it will be put back on the queue
35967             //if resurrected and re-registered.
35968             obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
35969         }
35970         else
35971         {
35972             if (method_table(obj)->HasCriticalFinalizer())
35973             {
35974                 finalizedFound = TRUE;
35975                 MoveItem (i, Seg, CriticalFinalizerListSeg);
35976             }
35977             else
35978             {
35979                 if (GCToEEInterface::AppDomainIsRudeUnload(pDomain))
35980                 {
35981                     MoveItem (i, Seg, FreeList);
35982                 }
35983                 else
35984                 {
35985                     finalizedFound = TRUE;
35986                     MoveItem (i, Seg, FinalizerListSeg);
35987                 }
35988             }
35989         }
35990     }
35991
35992     return finalizedFound;
35993 }
35994
35995 bool
35996 CFinalize::FinalizeAppDomain (void *pDomain, bool fRunFinalizers)
35997 {
35998     bool finalizedFound = false;
35999
36000     unsigned int startSeg = gen_segment (max_generation);
36001
36002     EnterFinalizeLock();
36003
36004     for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
36005     {
36006         if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
36007         {
36008             finalizedFound = true;
36009         }
36010     }
36011
36012     LeaveFinalizeLock();
36013
36014     return finalizedFound;
36015 }
36016
36017 void
36018 CFinalize::MoveItem (Object** fromIndex,
36019                      unsigned int fromSeg,
36020                      unsigned int toSeg)
36021 {
36022
36023     int step;
36024     ASSERT (fromSeg != toSeg);
36025     if (fromSeg > toSeg)
36026         step = -1;
36027     else
36028         step = +1;
36029     // Place the element at the boundary closest to dest
36030     Object** srcIndex = fromIndex;
36031     for (unsigned int i = fromSeg; i != toSeg; i+= step)
36032     {
36033         Object**& destFill = m_FillPointers[i+(step - 1 )/2];
36034         Object** destIndex = destFill - (step + 1)/2;
36035         if (srcIndex != destIndex)
36036         {
36037             Object* tmp = *srcIndex;
36038             *srcIndex = *destIndex;
36039             *destIndex = tmp;
36040         }
36041         destFill -= step;
36042         srcIndex = destIndex;
36043     }
36044 }
36045
36046 void
36047 CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
36048 {
36049     ScanContext sc;
36050     if (pSC == 0)
36051         pSC = &sc;
36052
36053     pSC->thread_number = hn;
36054
36055     //scan the finalization queue
36056     Object** startIndex  = SegQueue (CriticalFinalizerListSeg);
36057     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
36058
36059     for (Object** po = startIndex; po < stopIndex; po++)
36060     {
36061         Object* o = *po;
36062         //dprintf (3, ("scan freacheable %Ix", (size_t)o));
36063         dprintf (3, ("scan f %Ix", (size_t)o));
36064 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
36065         if (g_fEnableARM)
36066         {
36067             pSC->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(o->GetAppDomainIndex());
36068         }
36069 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
36070
36071         (*fn)(po, pSC, 0);
36072     }
36073 }
36074
36075 void CFinalize::WalkFReachableObjects (fq_walk_fn fn)
36076 {
36077     Object** startIndex = SegQueue (CriticalFinalizerListSeg);
36078     Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
36079     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
36080     for (Object** po = startIndex; po < stopIndex; po++)
36081     {
36082         //report *po
36083         fn(po < stopCriticalIndex, *po);
36084     }
36085 }
36086
36087 BOOL
36088 CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
36089                                 gc_heap* hp)
36090 {
36091     ScanContext sc;
36092     sc.promotion = TRUE;
36093 #ifdef MULTIPLE_HEAPS
36094     sc.thread_number = hp->heap_number;
36095 #else
36096     UNREFERENCED_PARAMETER(hp);
36097 #endif //MULTIPLE_HEAPS
36098
36099     BOOL finalizedFound = FALSE;
36100
36101     //start with gen and explore all the younger generations.
36102     unsigned int startSeg = gen_segment (gen);
36103     {
36104         m_PromotedCount = 0;
36105         for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
36106         {
36107             Object** endIndex = SegQueue (Seg);
36108             for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36109             {
36110                 CObjectHeader* obj = (CObjectHeader*)*i;
36111                 dprintf (3, ("scanning: %Ix", (size_t)obj));
36112                 if (!g_theGCHeap->IsPromoted (obj))
36113                 {
36114                     dprintf (3, ("freacheable: %Ix", (size_t)obj));
36115
36116                     assert (method_table(obj)->HasFinalizer());
36117
36118                     if (GCToEEInterface::EagerFinalized(obj))
36119                     {
36120                         MoveItem (i, Seg, FreeList);
36121                     }
36122                     else if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36123                     {
36124                         //remove the object because we don't want to
36125                         //run the finalizer
36126
36127                         MoveItem (i, Seg, FreeList);
36128
36129                         //Reset the bit so it will be put back on the queue
36130                         //if resurrected and re-registered.
36131                         obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36132
36133                     }
36134                     else
36135                     {
36136                         m_PromotedCount++;
36137
36138                         if (method_table(obj)->HasCriticalFinalizer())
36139                         {
36140                             MoveItem (i, Seg, CriticalFinalizerListSeg);
36141                         }
36142                         else
36143                         {
36144                             MoveItem (i, Seg, FinalizerListSeg);
36145                         }
36146                     }
36147                 }
36148 #ifdef BACKGROUND_GC
36149                 else
36150                 {
36151                     if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
36152                     {
36153                         // TODO - fix the following line.
36154                         //assert (gc_heap::background_object_marked ((uint8_t*)obj, FALSE));
36155                         dprintf (3, ("%Ix is marked", (size_t)obj));
36156                     }
36157                 }
36158 #endif //BACKGROUND_GC
36159             }
36160         }
36161     }
36162     finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
36163                      !IsSegEmpty(CriticalFinalizerListSeg);
36164                     
36165     if (finalizedFound)
36166     {
36167         //Promote the f-reachable objects
36168         GcScanRoots (pfn,
36169 #ifdef MULTIPLE_HEAPS
36170                      hp->heap_number
36171 #else
36172                      0
36173 #endif //MULTIPLE_HEAPS
36174                      , 0);
36175
36176         hp->settings.found_finalizers = TRUE;
36177
36178 #ifdef BACKGROUND_GC
36179         if (hp->settings.concurrent)
36180         {
36181             hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
36182         }
36183 #endif //BACKGROUND_GC
36184         if (hp->settings.concurrent && hp->settings.found_finalizers)
36185         {
36186             if (!mark_only_p)
36187                 GCToEEInterface::EnableFinalization(true);
36188         }
36189     }
36190
36191     return finalizedFound;
36192 }
36193
36194 //Relocates all of the objects in the finalization array
36195 void
36196 CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
36197 {
36198     ScanContext sc;
36199     sc.promotion = FALSE;
36200 #ifdef MULTIPLE_HEAPS
36201     sc.thread_number = hp->heap_number;
36202 #else
36203     UNREFERENCED_PARAMETER(hp);
36204 #endif //MULTIPLE_HEAPS
36205
36206     unsigned int Seg = gen_segment (gen);
36207
36208     Object** startIndex = SegQueue (Seg);
36209     for (Object** po = startIndex; po < SegQueue (FreeList);po++)
36210     {
36211         GCHeap::Relocate (po, &sc);
36212     }
36213 }
36214
36215 void
36216 CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
36217 {
36218     // update the generation fill pointers.
36219     // if gen_0_empty is FALSE, test each object to find out if
36220     // it was promoted or not
36221     if (gen_0_empty_p)
36222     {
36223         for (int i = min (gen+1, max_generation); i > 0; i--)
36224         {
36225             m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
36226         }
36227     }
36228     else
36229     {
36230         //Look for demoted or promoted plugs
36231
36232         for (int i = gen; i >= 0; i--)
36233         {
36234             unsigned int Seg = gen_segment (i);
36235             Object** startIndex = SegQueue (Seg);
36236
36237             for (Object** po = startIndex;
36238                  po < SegQueueLimit (gen_segment(i)); po++)
36239             {
36240                 int new_gen = g_theGCHeap->WhichGeneration (*po);
36241                 if (new_gen != i)
36242                 {
36243                     if (new_gen > i)
36244                     {
36245                         //promotion
36246                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
36247                     }
36248                     else
36249                     {
36250                         //demotion
36251                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
36252                         //back down in order to see all objects.
36253                         po--;
36254                     }
36255                 }
36256
36257             }
36258         }
36259     }
36260 }
36261
36262 BOOL
36263 CFinalize::GrowArray()
36264 {
36265     size_t oldArraySize = (m_EndArray - m_Array);
36266     size_t newArraySize =  (size_t)(((float)oldArraySize / 10) * 12);
36267
36268     Object** newArray = new (nothrow) Object*[newArraySize];
36269     if (!newArray)
36270     {
36271         // It's not safe to throw here, because of the FinalizeLock.  Tell our caller
36272         // to throw for us.
36273 //        ASSERT (newArray);
36274         return FALSE;
36275     }
36276     memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
36277
36278     //adjust the fill pointers
36279     for (int i = 0; i < FreeList; i++)
36280     {
36281         m_FillPointers [i] += (newArray - m_Array);
36282     }
36283     delete m_Array;
36284     m_Array = newArray;
36285     m_EndArray = &m_Array [newArraySize];
36286
36287     return TRUE;
36288 }
36289
36290 #ifdef VERIFY_HEAP
36291 void CFinalize::CheckFinalizerObjects()
36292 {
36293     for (int i = 0; i <= max_generation; i++)
36294     {
36295         Object **startIndex = SegQueue (gen_segment (i));
36296         Object **stopIndex  = SegQueueLimit (gen_segment (i));
36297
36298         for (Object **po = startIndex; po < stopIndex; po++)
36299         {
36300             if ((int)g_theGCHeap->WhichGeneration (*po) < i)
36301                 FATAL_GC_ERROR ();
36302             ((CObjectHeader*)*po)->Validate();
36303         }
36304     }
36305 }
36306 #endif //VERIFY_HEAP
36307
36308 #endif // FEATURE_PREMORTEM_FINALIZATION
36309
36310
36311 //------------------------------------------------------------------------------
36312 //
36313 //                      End of VM specific support
36314 //
36315 //------------------------------------------------------------------------------
36316 void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36317 {
36318     generation* gen = gc_heap::generation_of (gen_number);
36319     heap_segment*    seg = generation_start_segment (gen);
36320     uint8_t*       x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
36321                      generation_allocation_start (gen));
36322
36323     uint8_t*       end = heap_segment_allocated (seg);
36324     BOOL small_object_segments = TRUE;
36325     int align_const = get_alignment_constant (small_object_segments);
36326
36327     while (1)
36328
36329     {
36330         if (x >= end)
36331         {
36332             if ((seg = heap_segment_next (seg)) != 0)
36333             {
36334                 x = heap_segment_mem (seg);
36335                 end = heap_segment_allocated (seg);
36336                 continue;
36337             }
36338             else
36339             {
36340                 if (small_object_segments && walk_large_object_heap_p)
36341
36342                 {
36343                     small_object_segments = FALSE;
36344                     align_const = get_alignment_constant (small_object_segments);
36345                     seg = generation_start_segment (large_object_generation);
36346                     x = heap_segment_mem (seg);
36347                     end = heap_segment_allocated (seg);
36348                     continue;
36349                 }
36350                 else
36351                 {
36352                     break;
36353                 }
36354             }
36355         }
36356
36357         size_t s = size (x);
36358         CObjectHeader* o = (CObjectHeader*)x;
36359
36360         if (!o->IsFree())
36361
36362         {
36363             _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
36364
36365             if (!fn (o->GetObjectBase(), context))
36366                 return;
36367         }
36368         x = x + Align (s, align_const);
36369     }
36370 }
36371
36372 void gc_heap::walk_finalize_queue (fq_walk_fn fn)
36373 {
36374 #ifdef FEATURE_PREMORTEM_FINALIZATION
36375     finalize_queue->WalkFReachableObjects (fn);
36376 #endif //FEATURE_PREMORTEM_FINALIZATION
36377 }
36378
36379 void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36380 {
36381 #ifdef MULTIPLE_HEAPS
36382     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36383     {
36384         gc_heap* hp = gc_heap::g_heaps [hn];
36385
36386         hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p);
36387     }
36388 #else
36389     walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p);
36390 #endif //MULTIPLE_HEAPS
36391 }
36392
36393 void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context)
36394 {
36395     uint8_t* o = (uint8_t*)obj;
36396     if (o)
36397     {
36398         go_through_object_cl (method_table (o), o, size(o), oo,
36399                                     {
36400                                         if (*oo)
36401                                         {
36402                                             Object *oh = (Object*)*oo;
36403                                             if (!fn (oh, context))
36404                                                 return;
36405                                         }
36406                                     }
36407             );
36408     }
36409 }
36410
36411 void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type)
36412 {
36413     gc_heap* hp = (gc_heap*)gc_context;
36414     hp->walk_survivors (fn, diag_context, type);
36415 }
36416
36417 void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p)
36418 {
36419     gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
36420 }
36421
36422 void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
36423 {
36424     gc_heap* hp = (gc_heap*)gc_context;
36425     hp->walk_finalize_queue (fn);
36426 }
36427
36428 void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc)
36429 {
36430 #ifdef MULTIPLE_HEAPS
36431     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36432     {
36433         gc_heap* hp = gc_heap::g_heaps [hn];
36434         hp->finalize_queue->GcScanRoots(fn, hn, sc);
36435     }
36436 #else
36437         pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc);
36438 #endif //MULTIPLE_HEAPS
36439 }
36440
36441 void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36442 {
36443     UNREFERENCED_PARAMETER(gen_number);
36444     GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn);
36445 }
36446
36447 void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36448 {
36449     UNREFERENCED_PARAMETER(gen_number);
36450     GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn);
36451 }
36452
36453 // Go through and touch (read) each page straddled by a memory block.
36454 void TouchPages(void * pStart, size_t cb)
36455 {
36456     const uint32_t pagesize = OS_PAGE_SIZE;
36457     _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
36458     if (cb)
36459     {
36460         VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
36461         VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) -  (((size_t)pStart) & (pagesize-1)));
36462         while (p < pEnd)
36463         {
36464             char a;
36465             a = VolatileLoad(p);
36466             //printf("Touching page %lxh\n", (uint32_t)p);
36467             p += pagesize;
36468         }
36469     }
36470 }
36471
36472 #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
36473     // This code is designed to catch the failure to update the write barrier
36474     // The way it works is to copy the whole heap right after every GC.  The write
36475     // barrier code has been modified so that it updates the shadow as well as the
36476     // real GC heap.  Before doing the next GC, we walk the heap, looking for pointers
36477     // that were updated in the real heap, but not the shadow.  A mismatch indicates
36478     // an error.  The offending code can be found by breaking after the correct GC,
36479     // and then placing a data breakpoint on the Heap location that was updated without
36480     // going through the write barrier.
36481
36482     // Called at process shutdown
36483 void deleteGCShadow()
36484 {
36485     if (g_GCShadow != 0)
36486         GCToOSInterface::VirtualRelease (g_GCShadow, g_GCShadowEnd - g_GCShadow);
36487     g_GCShadow = 0;
36488     g_GCShadowEnd = 0;
36489 }
36490
36491     // Called at startup and right after a GC, get a snapshot of the GC Heap
36492 void initGCShadow()
36493 {
36494     if (!(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK))
36495         return;
36496
36497     size_t len = g_gc_highest_address - g_gc_lowest_address;
36498     if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) 
36499     {
36500         deleteGCShadow();
36501         g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None);
36502         if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len))
36503         {
36504             _ASSERTE(!"Not enough memory to run HeapVerify level 2");
36505             // If after the assert we decide to allow the program to continue 
36506             // running we need to be in a state that will not trigger any 
36507             // additional AVs while we fail to allocate a shadow segment, i.e. 
36508             // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
36509             deleteGCShadow();
36510             return;
36511         }
36512
36513         g_GCShadowEnd += len;
36514     }
36515
36516     // save the value of g_gc_lowest_address at this time.  If this value changes before
36517     // the next call to checkGCWriteBarrier() it means we extended the heap (with a
36518     // large object segment most probably), and the whole shadow segment is inconsistent.
36519     g_shadow_lowest_address = g_gc_lowest_address;
36520
36521         //****** Copy the whole GC heap ******
36522     //
36523     // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
36524     // can produce a NULL result.  This is because the initialization has not completed.
36525     //
36526     generation* gen = gc_heap::generation_of (max_generation);
36527     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36528
36529     ptrdiff_t delta = g_GCShadow - g_gc_lowest_address;
36530     BOOL small_object_segments = TRUE;
36531     while(1)
36532     {
36533         if (!seg)
36534         {
36535             if (small_object_segments)
36536             {
36537                 small_object_segments = FALSE;
36538                 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
36539                 continue;
36540             }
36541             else
36542                 break;
36543         }
36544             // Copy the segment
36545         uint8_t* start = heap_segment_mem(seg);
36546         uint8_t* end = heap_segment_allocated (seg);
36547         memcpy(start + delta, start, end - start);
36548         seg = heap_segment_next_rw (seg);
36549     }
36550 }
36551
36552 #define INVALIDGCVALUE (void*)((size_t)0xcccccccd)
36553
36554     // test to see if 'ptr' was only updated via the write barrier.
36555 inline void testGCShadow(Object** ptr)
36556 {
36557     Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)];
36558     if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow)
36559     {
36560
36561         // If you get this assertion, someone updated a GC pointer in the heap without
36562         // using the write barrier.  To find out who, check the value of 
36563         // dd_collection_count (dynamic_data_of (0)). Also
36564         // note the value of 'ptr'.  Rerun the App that the previous GC just occurred.
36565         // Then put a data breakpoint for the value of 'ptr'  Then check every write
36566         // to pointer between the two GCs.  The last one is not using the write barrier.
36567
36568         // If the memory of interest does not exist at system startup,
36569         // you need to set the data breakpoint right after the memory gets committed
36570         // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
36571         // in the memory window.  run until the memory gets mapped. Then you can set
36572         // your breakpoint
36573
36574         // Note a recent change, we've identified race conditions when updating the gc shadow.
36575         // Throughout the runtime, code will update an address in the gc heap, then erect the
36576         // write barrier, which calls updateGCShadow. With an app that pounds one heap location
36577         // from multiple threads, you can hit this assert even though all involved are using the
36578         // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
36579         // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
36580         // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
36581         // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
36582         // TODO: erroneous asserts in here.
36583
36584         if(*shadow!=INVALIDGCVALUE)
36585         {
36586 #ifdef FEATURE_BASICFREEZE
36587             // Write barriers for stores of references to frozen objects may be optimized away.
36588             if (!gc_heap::frozen_object_p(*ptr))
36589 #endif // FEATURE_BASICFREEZE
36590             {
36591                 _ASSERTE(!"Pointer updated without using write barrier");
36592             }
36593         }
36594         /*
36595         else
36596         {
36597              printf("saw a INVALIDGCVALUE. (just to let you know)\n");
36598         }
36599         */
36600     }
36601 }
36602
36603 void testGCShadowHelper (uint8_t* x)
36604 {
36605     size_t s = size (x);
36606     if (contain_pointers (x))
36607     {
36608         go_through_object_nostart (method_table(x), x, s, oo,
36609                            { testGCShadow((Object**) oo); });
36610     }
36611 }
36612
36613     // Walk the whole heap, looking for pointers that were not updated with the write barrier.
36614 void checkGCWriteBarrier()
36615 {
36616     // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment
36617     // and the GC shadow segment did not track that change!
36618     if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address)
36619     {
36620         // No shadow stack, nothing to check.
36621         return;
36622     }
36623
36624     {
36625         generation* gen = gc_heap::generation_of (max_generation);
36626         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36627
36628         PREFIX_ASSUME(seg != NULL);
36629
36630         while(seg)
36631         {
36632             uint8_t* x = heap_segment_mem(seg);
36633             while (x < heap_segment_allocated (seg))
36634             {
36635                 size_t s = size (x);
36636                 testGCShadowHelper (x);
36637                 x = x + Align (s);
36638             }
36639             seg = heap_segment_next_rw (seg);
36640         }
36641     }
36642
36643     {
36644         // go through large object heap
36645         int alignment = get_alignment_constant(FALSE);
36646         generation* gen = gc_heap::generation_of (max_generation+1);
36647         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36648
36649         PREFIX_ASSUME(seg != NULL);
36650
36651         while(seg)
36652         {
36653             uint8_t* x = heap_segment_mem(seg);
36654             while (x < heap_segment_allocated (seg))
36655             {
36656                 size_t s = size (x);
36657                 testGCShadowHelper (x);
36658                 x = x + Align (s, alignment);
36659             }
36660             seg = heap_segment_next_rw (seg);
36661         }
36662     }
36663 }
36664 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
36665
36666 #endif // !DACCESS_COMPILE
36667
36668 #ifdef FEATURE_BASICFREEZE
36669 void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
36670 {
36671 #ifdef DACCESS_COMPILE
36672     UNREFERENCED_PARAMETER(seg);
36673     UNREFERENCED_PARAMETER(pvContext);
36674     UNREFERENCED_PARAMETER(pfnMethodTable);
36675     UNREFERENCED_PARAMETER(pfnObjRef);
36676 #else
36677     uint8_t *o = heap_segment_mem(seg);
36678
36679     // small heap alignment constant
36680     int alignment = get_alignment_constant(TRUE);
36681
36682     while (o < heap_segment_allocated(seg))
36683     {
36684         pfnMethodTable(pvContext, o);
36685
36686         if (contain_pointers (o))
36687         {
36688             go_through_object_nostart (method_table (o), o, size(o), oo,
36689                    {
36690                        if (*oo)
36691                            pfnObjRef(pvContext, oo);
36692                    }
36693             );
36694         }
36695
36696         o += Align(size(o), alignment);
36697     }
36698 #endif //!DACCESS_COMPILE
36699 }
36700 #endif // FEATURE_BASICFREEZE
36701
36702 #ifndef DACCESS_COMPILE
36703 HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
36704 {
36705 #ifdef BACKGROUND_GC
36706     if (recursive_gc_sync::background_running_p())
36707     {
36708         uint32_t dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
36709         if (dwRet == WAIT_OBJECT_0)
36710             return S_OK;
36711         else if (dwRet == WAIT_TIMEOUT)
36712             return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
36713         else
36714             return E_FAIL;      // It is not clear if what the last error would be if the wait failed,
36715                                 // as there are too many layers in between. The best we can do is to return E_FAIL;
36716     }
36717 #endif
36718
36719     return S_OK;
36720 }
36721 #endif // !DACCESS_COMPILE
36722
36723 void GCHeap::TemporaryEnableConcurrentGC()
36724 {
36725 #ifdef BACKGROUND_GC
36726     gc_heap::temp_disable_concurrent_p = false;
36727 #endif //BACKGROUND_GC
36728 }
36729
36730 void GCHeap::TemporaryDisableConcurrentGC()
36731 {
36732 #ifdef BACKGROUND_GC
36733     gc_heap::temp_disable_concurrent_p = true;
36734 #endif //BACKGROUND_GC
36735 }
36736
36737 bool GCHeap::IsConcurrentGCEnabled()
36738 {
36739 #ifdef BACKGROUND_GC
36740     return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
36741 #else
36742     return FALSE;
36743 #endif //BACKGROUND_GC
36744 }
36745
36746 void GCHeap::SetFinalizeRunOnShutdown(bool value)
36747 {
36748     g_fFinalizerRunOnShutDown = value;
36749 }
36750
36751 void PopulateDacVars(GcDacVars *gcDacVars)
36752 {
36753 #ifndef DACCESS_COMPILE
36754     assert(gcDacVars != nullptr);
36755     *gcDacVars = {};
36756     gcDacVars->major_version_number = 1;
36757     gcDacVars->minor_version_number = 0;
36758     gcDacVars->built_with_svr = &g_built_with_svr_gc;
36759     gcDacVars->build_variant = &g_build_variant;
36760     gcDacVars->gc_structures_invalid_cnt = const_cast<int32_t*>(&GCScan::m_GcStructuresInvalidCnt);
36761     gcDacVars->generation_size = sizeof(generation);
36762     gcDacVars->max_gen = &g_max_generation;
36763 #ifndef MULTIPLE_HEAPS
36764     gcDacVars->mark_array = &gc_heap::mark_array;
36765     gcDacVars->ephemeral_heap_segment = reinterpret_cast<dac_heap_segment**>(&gc_heap::ephemeral_heap_segment);
36766     gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state);
36767     gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast<dac_heap_segment**>(&gc_heap::saved_sweep_ephemeral_seg);
36768     gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start;
36769     gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address;
36770     gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address;
36771     gcDacVars->alloc_allocated = &gc_heap::alloc_allocated;
36772     gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj;
36773     gcDacVars->oom_info = &gc_heap::oom_info;
36774     gcDacVars->finalize_queue = reinterpret_cast<dac_finalize_queue**>(&gc_heap::finalize_queue);
36775     gcDacVars->generation_table = reinterpret_cast<dac_generation**>(&gc_heap::generation_table);
36776 #ifdef GC_CONFIG_DRIVEN
36777     gcDacVars->gc_global_mechanisms = reinterpret_cast<size_t**>(&gc_global_mechanisms);
36778     gcDacVars->interesting_data_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_data_per_heap);
36779     gcDacVars->compact_reasons_per_heap = reinterpret_cast<size_t**>(&gc_heap::compact_reasons_per_heap);
36780     gcDacVars->expand_mechanisms_per_heap = reinterpret_cast<size_t**>(&gc_heap::expand_mechanisms_per_heap);
36781     gcDacVars->interesting_mechanism_bits_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_mechanism_bits_per_heap);
36782 #endif // GC_CONFIG_DRIVEN
36783 #ifdef HEAP_ANALYZE
36784     gcDacVars->internal_root_array = &gc_heap::internal_root_array;
36785     gcDacVars->internal_root_array_index = &gc_heap::internal_root_array_index;
36786     gcDacVars->heap_analyze_success = &gc_heap::heap_analyze_success;
36787 #endif // HEAP_ANALYZE
36788 #else
36789     gcDacVars->n_heaps = &gc_heap::n_heaps;
36790     gcDacVars->g_heaps = reinterpret_cast<dac_gc_heap***>(&gc_heap::g_heaps);
36791 #endif // MULTIPLE_HEAPS
36792 #endif // DACCESS_COMPILE
36793 }