9b9d234e083e5587d3048d4002df18ad9c200639
[platform/upstream/coreclr.git] / src / gc / gc.cpp
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4 //
5
6
7 // 
8 // #Overview
9 // 
10 // GC automatically manages memory allocated by managed code. 
11 // The design doc for GC can be found at 
12 // file:../../doc/BookOfTheRuntime/GC/GCDesign.doc
13 // 
14 // This file includes both the code for GC and the allocator. The most common
15 // case for a GC to be triggered is from the allocator code. See
16 // code:#try_allocate_more_space where it calls GarbageCollectGeneration.
17 // 
18 // Entry points for the allocate is GCHeap::Alloc* which are called by the
19 // allocation helpers in gcscan.cpp
20 // 
21
22 #include "gcpriv.h"
23
24 #define USE_INTROSORT
25
26 // defines for ETW events.
27 #define ETW_TYPE_GC_MARK_1              21   // after marking stack roots
28 #define ETW_TYPE_GC_MARK_2              22   // after marking finalize queue roots
29 #define ETW_TYPE_GC_MARK_3              23   // after marking handles
30 #define ETW_TYPE_GC_MARK_4              24   // after marking cards
31
32 #define ETW_TYPE_BGC_BEGIN              25
33 #define ETW_TYPE_BGC_1ST_NONCON_END     26
34 #define ETW_TYPE_BGC_1ST_CON_END        27
35 #define ETW_TYPE_BGC_2ND_NONCON_BEGIN   28
36 #define ETW_TYPE_BGC_2ND_NONCON_END     29
37 #define ETW_TYPE_BGC_2ND_CON_BEGIN      30
38 #define ETW_TYPE_BGC_2ND_CON_END        31
39 #define ETW_TYPE_BGC_PLAN_END           32
40 #define ETW_TYPE_BGC_SWEEP_END          33
41
42 #define ETW_TYPE_BGC_DRAIN_MARK_LIST    34
43 #define ETW_TYPE_BGC_REVISIT            35
44 #define ETW_TYPE_BGC_OVERFLOW           36
45
46 #define ETW_TYPE_ALLOC_WAIT_BEGIN       37
47 #define ETW_TYPE_ALLOC_WAIT_END         38
48
49 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
50 inline BOOL ShouldTrackMovementForProfilerOrEtw()
51 {
52 #ifdef GC_PROFILING
53     if (CORProfilerTrackGC())
54         return true;
55 #endif
56
57 #ifdef FEATURE_EVENT_TRACE
58     if (ETW::GCLog::ShouldTrackMovementForEtw())
59         return true;
60 #endif
61
62     return false;
63 }
64 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
65
66 #if defined(FEATURE_REDHAWK)
67 #define MAYBE_UNUSED_VAR(v) v = v
68 #else
69 #define MAYBE_UNUSED_VAR(v)
70 #endif // FEATURE_REDHAWK
71
72 #define MAX_PTR ((BYTE*)(~(SSIZE_T)0))
73
74 #ifdef SERVER_GC
75 #define partial_size_th 100
76 #define num_partial_refs 64
77 #else //SERVER_GC
78 #define partial_size_th 100
79 #define num_partial_refs 32
80 #endif //SERVER_GC
81
82 #define demotion_plug_len_th (6*1024*1024)
83
84 #ifdef _WIN64
85 #define MARK_STACK_INITIAL_LENGTH 1024
86 #else
87 #define MARK_STACK_INITIAL_LENGTH 128
88 #endif //_WIN64
89
90 #define LOH_PIN_QUEUE_LENGTH 100
91 #define LOH_PIN_DECAY 10
92
93 // Right now we support maximum 256 procs - meaning that we will create at most
94 // 256 GC threads and 256 GC heaps. 
95 #define MAX_SUPPORTED_CPUS 256
96
97 #if defined (TRACE_GC) && !defined (DACCESS_COMPILE)
98 const char * const allocation_state_str[] = {
99     "start",
100     "can_allocate",
101     "cant_allocate",
102     "try_fit",
103     "try_fit_new_seg",
104     "try_fit_new_seg_after_cg",
105     "try_fit_no_seg",
106     "try_fit_after_cg",
107     "try_fit_after_bgc",
108     "try_free_full_seg_in_bgc", 
109     "try_free_after_bgc",
110     "try_seg_end",
111     "acquire_seg",
112     "acquire_seg_after_cg",
113     "acquire_seg_after_bgc",
114     "check_and_wait_for_bgc",
115     "trigger_full_compact_gc",
116     "trigger_ephemeral_gc",
117     "trigger_2nd_ephemeral_gc",
118     "check_retry_seg"
119 };
120 #endif //TRACE_GC && !DACCESS_COMPILE
121
122
123 // Keep this in sync with the definition of gc_reaon
124 #if (defined(DT_LOG) || defined(TRACE_GC)) && !defined (DACCESS_COMPILE)
125 static const char* const str_gc_reasons[] = 
126 {
127     "alloc_soh",
128     "induced",
129     "lowmem",
130     "empty",
131     "alloc_loh",
132     "oos_soh",
133     "oos_loh",
134     "induced_noforce",
135     "gcstress",
136     "induced_lowmem"
137 };
138 #endif // defined(DT_LOG) || defined(TRACE_GC)
139
140 inline
141 BOOL is_induced (gc_reason reason)
142 {
143     return ((reason == reason_induced) ||
144             (reason == reason_induced_noforce) ||
145             (reason == reason_lowmemory) ||
146             (reason == reason_lowmemory_blocking) || 
147             (reason == reason_induced_compacting));
148 }
149
150 inline
151 BOOL is_induced_blocking (gc_reason reason)
152 {
153     return ((reason == reason_induced) ||
154             (reason == reason_lowmemory_blocking) || 
155             (reason == reason_induced_compacting));
156 }
157
158 #ifdef GC_STATS
159 // There is a current and a prior copy of the statistics.  This allows us to display deltas per reporting
160 // interval, as well as running totals.  The 'min' and 'max' values require special treatment.  They are
161 // Reset (zeroed) in the current statistics when we begin a new interval and they are updated via a
162 // comparison with the global min/max.
163 GCStatistics g_GCStatistics;
164 GCStatistics g_LastGCStatistics;
165
166 WCHAR* GCStatistics::logFileName = NULL;
167 FILE*  GCStatistics::logFile = NULL;
168
169 void GCStatistics::AddGCStats(const gc_mechanisms& settings, size_t timeInMSec)
170 {
171 #ifdef BACKGROUND_GC
172     if (settings.concurrent)
173     {
174         bgc.Accumulate((DWORD)timeInMSec*1000);
175         cntBGC++;
176     }
177     else if (settings.background_p)
178     {
179         fgc.Accumulate((DWORD)timeInMSec*1000);
180         cntFGC++;
181         if (settings.compaction)
182             cntCompactFGC++;
183         assert(settings.condemned_generation < max_generation);
184         cntFGCGen[settings.condemned_generation]++;
185     }
186     else
187 #endif // BACKGROUND_GC
188     {
189         ngc.Accumulate((DWORD)timeInMSec*1000);
190         cntNGC++;
191         if (settings.compaction)
192             cntCompactNGC++;
193         cntNGCGen[settings.condemned_generation]++;
194     }
195
196     if (is_induced (settings.reason))
197         cntReasons[(int)reason_induced]++;
198     else if (settings.stress_induced)
199         cntReasons[(int)reason_gcstress]++;
200     else
201         cntReasons[(int)settings.reason]++;
202
203 #ifdef BACKGROUND_GC
204     if (settings.concurrent || !settings.background_p)
205     {
206 #endif // BACKGROUND_GC
207         RollOverIfNeeded();
208 #ifdef BACKGROUND_GC
209     }
210 #endif // BACKGROUND_GC
211 }
212
213 void GCStatistics::Initialize()
214 {
215     LIMITED_METHOD_CONTRACT;
216     // for efficiency sake we're taking a dependency on the layout of a C++ object
217     // with a vtable. protect against violations of our premise:
218     static_assert(offsetof(GCStatistics, cntDisplay) == sizeof(void*),
219             "The first field of GCStatistics follows the pointer sized vtable");
220
221     int podOffs = offsetof(GCStatistics, cntDisplay);       // offset of the first POD field
222     memset((BYTE*)(&g_GCStatistics)+podOffs, 0, sizeof(g_GCStatistics)-podOffs);
223     memset((BYTE*)(&g_LastGCStatistics)+podOffs, 0, sizeof(g_LastGCStatistics)-podOffs);
224 }
225
226 void GCStatistics::DisplayAndUpdate()
227 {
228     LIMITED_METHOD_CONTRACT;
229
230     if (logFileName == NULL || logFile == NULL)
231         return;
232
233     {
234         if (cntDisplay == 0)
235             fprintf(logFile, "\nGCMix **** Initialize *****\n\n");
236             
237         fprintf(logFile, "GCMix **** Summary ***** %d\n", cntDisplay);
238
239         // NGC summary (total, timing info)
240         ngc.DisplayAndUpdate(logFile, "NGC ", &g_LastGCStatistics.ngc, cntNGC, g_LastGCStatistics.cntNGC, msec);
241
242         // FGC summary (total, timing info)
243         fgc.DisplayAndUpdate(logFile, "FGC ", &g_LastGCStatistics.fgc, cntFGC, g_LastGCStatistics.cntFGC, msec);
244
245         // BGC summary
246         bgc.DisplayAndUpdate(logFile, "BGC ", &g_LastGCStatistics.bgc, cntBGC, g_LastGCStatistics.cntBGC, msec);
247
248         // NGC/FGC break out by generation & compacting vs. sweeping
249         fprintf(logFile, "NGC   ");
250         for (int i = max_generation; i >= 0; --i)
251             fprintf(logFile, "gen%d %d (%d). ", i, cntNGCGen[i]-g_LastGCStatistics.cntNGCGen[i], cntNGCGen[i]);
252         fprintf(logFile, "\n");
253
254         fprintf(logFile, "FGC   ");
255         for (int i = max_generation-1; i >= 0; --i)
256             fprintf(logFile, "gen%d %d (%d). ", i, cntFGCGen[i]-g_LastGCStatistics.cntFGCGen[i], cntFGCGen[i]);
257         fprintf(logFile, "\n");
258
259         // Compacting vs. Sweeping break out
260         int _cntSweep = cntNGC-cntCompactNGC;
261         int _cntLastSweep = g_LastGCStatistics.cntNGC-g_LastGCStatistics.cntCompactNGC;
262         fprintf(logFile, "NGC   Sweeping %d (%d) Compacting %d (%d)\n",
263                _cntSweep - _cntLastSweep, _cntSweep,
264                cntCompactNGC - g_LastGCStatistics.cntCompactNGC, cntCompactNGC);
265
266         _cntSweep = cntFGC-cntCompactFGC;
267         _cntLastSweep = g_LastGCStatistics.cntFGC-g_LastGCStatistics.cntCompactFGC;
268         fprintf(logFile, "FGC   Sweeping %d (%d) Compacting %d (%d)\n",
269                _cntSweep - _cntLastSweep, _cntSweep,
270                cntCompactFGC - g_LastGCStatistics.cntCompactFGC, cntCompactFGC);
271
272 #ifdef TRACE_GC
273         // GC reasons...
274         for (int reason=(int)reason_alloc_soh; reason <= (int)reason_gcstress; ++reason)
275         {
276             if (cntReasons[reason] != 0)
277                 fprintf(logFile, "%s %d (%d). ", str_gc_reasons[reason], 
278                     cntReasons[reason]-g_LastGCStatistics.cntReasons[reason], cntReasons[reason]);
279         }
280 #endif // TRACE_GC
281         fprintf(logFile, "\n\n");
282
283         // flush the log file...
284         fflush(logFile);
285     }
286
287     memcpy(&g_LastGCStatistics, this, sizeof(g_LastGCStatistics));
288
289     ngc.Reset();
290     fgc.Reset();
291     bgc.Reset();
292 }
293
294 #endif // GC_STATS
295
296 #ifdef BACKGROUND_GC
297 DWORD bgc_alloc_spin_count = 140;
298 DWORD bgc_alloc_spin_count_loh = 16;
299 DWORD bgc_alloc_spin = 2;
300
301
302 inline
303 void c_write (DWORD& place, DWORD value)
304 {
305     FastInterlockExchange (&(LONG&)place, value);
306     //place = value;
307 }
308
309 // TODO - can't make it work with the syntax for Volatile<T>
310 inline
311 void c_write_volatile (BOOL* place, DWORD value)
312 {
313     FastInterlockExchange ((LONG*)place, value);
314     //place = value;
315 }
316
317 #ifndef DACCESS_COMPILE
318 // If every heap's gen2 or gen3 size is less than this threshold we will do a blocking GC.
319 const size_t bgc_min_per_heap = 4*1024*1024;
320
321 int gc_heap::gchist_index = 0;
322 gc_mechanisms_store gc_heap::gchist[max_history_count];
323
324 #ifndef MULTIPLE_HEAPS
325 size_t gc_heap::total_promoted_bytes = 0;
326 VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process;
327 int gc_heap::gchist_index_per_heap = 0;
328 gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count];
329 #endif //MULTIPLE_HEAPS
330
331 void gc_heap::add_to_history_per_heap()
332 {
333 #ifdef GC_HISTORY
334     gc_history* current_hist = &gchist_per_heap[gchist_index_per_heap];
335     current_hist->gc_index = settings.gc_index;
336     current_hist->current_bgc_state = current_bgc_state;
337     size_t elapsed = dd_gc_elapsed_time (dynamic_data_of (0));
338     current_hist->gc_time_ms = (DWORD)elapsed;
339     current_hist->gc_efficiency = (elapsed ? (total_promoted_bytes / elapsed) : total_promoted_bytes);
340     current_hist->eph_low = generation_allocation_start (generation_of (max_generation-1));
341     current_hist->gen0_start = generation_allocation_start (generation_of (0));
342     current_hist->eph_high = heap_segment_allocated (ephemeral_heap_segment);
343 #ifdef BACKGROUND_GC
344     current_hist->bgc_lowest = background_saved_lowest_address;
345     current_hist->bgc_highest = background_saved_highest_address;
346 #endif //BACKGROUND_GC
347     current_hist->fgc_lowest = lowest_address;
348     current_hist->fgc_highest = highest_address;
349     current_hist->g_lowest = g_lowest_address;
350     current_hist->g_highest = g_highest_address;
351
352     gchist_index_per_heap++;
353     if (gchist_index_per_heap == max_history_count)
354     {
355         gchist_index_per_heap = 0;
356     }
357 #endif //GC_HISTORY
358 }
359
360 void gc_heap::add_to_history()
361 {
362 #ifdef GC_HISTORY
363     gc_mechanisms_store* current_settings = &gchist[gchist_index];
364     current_settings->store (&settings);
365
366     gchist_index++;
367     if (gchist_index == max_history_count)
368     {
369         gchist_index = 0;
370     }
371 #endif //GC_HISTORY
372 }
373
374 #endif //DACCESS_COMPILE
375 #endif //BACKGROUND_GC
376
377 #ifdef TRACE_GC
378
379 BOOL   gc_log_on = TRUE;
380 HANDLE gc_log = INVALID_HANDLE_VALUE;
381 size_t gc_log_file_size = 0;
382
383 size_t gc_buffer_index = 0;
384 size_t max_gc_buffers = 0;
385
386 static MUTEX_COOKIE   gc_log_lock = 0;
387
388 // we keep this much in a buffer and only flush when the buffer is full
389 #define gc_log_buffer_size (1024*1024)
390 BYTE* gc_log_buffer = 0;
391 size_t gc_log_buffer_offset = 0;
392
393 void log_va_msg(const char *fmt, va_list args)
394 {
395     DWORD status = ClrWaitForMutex(gc_log_lock, INFINITE, FALSE);
396     assert (WAIT_OBJECT_0 == status);
397
398     const int BUFFERSIZE = 512;
399     static char rgchBuffer[BUFFERSIZE];
400     char *  pBuffer  = &rgchBuffer[0];
401
402     pBuffer[0] = '\r';
403     pBuffer[1] = '\n';
404     int buffer_start = 2;
405     int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", GetCurrentThreadId());
406     buffer_start += pid_len;
407     memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start);
408     int msg_len = _vsnprintf(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, fmt, args );
409     if (msg_len == -1)
410     {
411         msg_len = BUFFERSIZE - buffer_start;
412     }
413
414     msg_len += buffer_start;
415
416     if ((gc_log_buffer_offset + msg_len) > (gc_log_buffer_size - 12))
417     {
418         char index_str[8];
419         memset (index_str, '-', 8);
420         sprintf_s (index_str, _countof(index_str), "%d", (int)gc_buffer_index);
421         gc_log_buffer[gc_log_buffer_offset] = '\r';
422         gc_log_buffer[gc_log_buffer_offset + 1] = '\n';
423         memcpy (gc_log_buffer + (gc_log_buffer_offset + 2), index_str, 8);
424
425         gc_buffer_index++;
426         if (gc_buffer_index > max_gc_buffers)
427         {
428             SetFilePointer (gc_log, 0, NULL, FILE_BEGIN);
429             gc_buffer_index = 0;
430         }
431         DWORD written_to_log = 0;
432         WriteFile (gc_log, gc_log_buffer, (DWORD)gc_log_buffer_size, &written_to_log, NULL);
433         FlushFileBuffers (gc_log);
434         memset (gc_log_buffer, '*', gc_log_buffer_size);
435         gc_log_buffer_offset = 0;
436     }
437
438     memcpy (gc_log_buffer + gc_log_buffer_offset, pBuffer, msg_len);
439     gc_log_buffer_offset += msg_len;
440
441     status = ClrReleaseMutex(gc_log_lock);
442     assert (status);
443 }
444
445 void GCLog (const char *fmt, ... )
446 {
447     if (gc_log_on && (gc_log != INVALID_HANDLE_VALUE))
448     {
449         va_list     args;
450         va_start( args, fmt );
451         log_va_msg (fmt, args);
452     }
453 }
454
455 #endif //TRACE_GC
456
457 #ifdef SYNCHRONIZATION_STATS
458
459 // Number of GCs have we done since we last logged.
460 static unsigned int         gc_count_during_log;
461  // In ms. This is how often we print out stats.
462 static const unsigned int   log_interval = 5000;
463 // Time (in ms) when we start a new log interval.
464 static unsigned int         log_start_tick;
465 static unsigned int         gc_lock_contended;
466 // Cycles accumulated in SuspendEE during log_interval.
467 static ULONGLONG            suspend_ee_during_log;
468 // Cycles accumulated in RestartEE during log_interval.
469 static ULONGLONG            restart_ee_during_log;
470 static ULONGLONG            gc_during_log;
471
472 #endif //SYNCHRONIZATION_STATS
473
474 void
475 init_sync_log_stats()
476 {
477 #ifdef SYNCHRONIZATION_STATS
478     if (gc_count_during_log == 0)
479     {
480         gc_heap::init_sync_stats();
481         suspend_ee_during_log = 0;
482         restart_ee_during_log = 0;
483         gc_during_log = 0;
484         gc_lock_contended = 0;
485
486         log_start_tick = GetTickCount();
487     }
488     gc_count_during_log++;
489 #endif //SYNCHRONIZATION_STATS
490 }
491
492 void
493 process_sync_log_stats()
494 {
495 #ifdef SYNCHRONIZATION_STATS
496
497     unsigned int log_elapsed = GetTickCount() - log_start_tick;
498
499     if (log_elapsed > log_interval)
500     {
501         // Print out the cycles we spent on average in each suspend and restart.
502         printf("\n_________________________________________________________________________________\n"
503             "Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n"
504             "SuspendEE: %8u; RestartEE: %8u\n",
505             log_interval / 1000,
506             gc_count_during_log,
507             gc_lock_contended,
508             (unsigned int)(gc_during_log / gc_count_during_log),
509             (unsigned int)(suspend_ee_during_log / gc_count_during_log),
510             (unsigned int)(restart_ee_during_log / gc_count_during_log));
511         gc_heap::print_sync_stats(gc_count_during_log);
512
513         gc_count_during_log = 0;
514     }
515 #endif //SYNCHRONIZATION_STATS
516 }
517
518 #ifdef MULTIPLE_HEAPS
519
520 enum gc_join_stage
521 {
522     gc_join_init_cpu_mapping = 0,
523     gc_join_done = 1,
524     gc_join_generation_determined = 2,
525     gc_join_begin_mark_phase = 3,
526     gc_join_scan_dependent_handles = 4,
527     gc_join_rescan_dependent_handles = 5,
528     gc_join_scan_sizedref_done = 6,
529     gc_join_null_dead_short_weak = 7,
530     gc_join_scan_finalization = 8,
531     gc_join_null_dead_long_weak = 9, 
532     gc_join_null_dead_syncblk = 10, 
533     gc_join_decide_on_compaction = 11, 
534     gc_join_rearrange_segs_compaction = 12, 
535     gc_join_adjust_handle_age_compact = 13,
536     gc_join_adjust_handle_age_sweep = 14,
537     gc_join_begin_relocate_phase = 15,
538     gc_join_relocate_phase_done = 16,
539     gc_join_verify_objects_done = 17,
540     gc_join_start_bgc = 18,
541     gc_join_restart_ee = 19,
542     gc_join_concurrent_overflow = 20,
543     gc_join_suspend_ee = 21,
544     gc_join_bgc_after_ephemeral = 22,
545     gc_join_allow_fgc = 23,
546     gc_join_bgc_sweep = 24,
547     gc_join_suspend_ee_verify = 25,
548     gc_join_restart_ee_verify = 26,
549     gc_join_set_state_free = 27,
550     gc_r_join_update_card_bundle = 28,
551     gc_join_after_absorb = 29, 
552     gc_join_verify_copy_table = 30,
553     gc_join_after_reset = 31,
554     gc_join_after_ephemeral_sweep = 32,
555     gc_join_after_profiler_heap_walk = 33,
556     gc_join_minimal_gc = 34
557 };
558
559 enum gc_join_flavor
560 {
561     join_flavor_server_gc = 0,
562     join_flavor_bgc = 1
563 };
564
565 #define first_thread_arrived 2
566 struct join_structure
567 {
568     CLREvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
569     VOLATILE(LONG) join_lock;
570     VOLATILE(LONG) r_join_lock;
571     VOLATILE(LONG) join_restart;
572     VOLATILE(LONG) r_join_restart; // only used by get_here_first and friends.
573     int n_threads;
574     VOLATILE(BOOL) joined_p;
575     // avoid lock_color and join_lock being on same cache line
576     // make sure to modify this if adding/removing variables to layout
577     char cache_line_separator[HS_CACHE_LINE_SIZE - (3*sizeof(int) + sizeof(int) + sizeof(BOOL))];
578     VOLATILE(int) lock_color;
579     VOLATILE(BOOL) wait_done;
580 };
581
582 typedef enum _join_type {
583     type_last_join, type_join, type_restart
584 } join_type;
585
586 typedef enum _join_time {
587     time_start, time_end
588 } join_time;
589
590 struct join_event
591 {
592     ULONG heap;
593     join_time time;
594     join_type type;
595 };
596
597 class t_join
598 {
599     join_structure join_struct;
600
601     int id;
602     gc_join_flavor flavor;
603
604 #ifdef JOIN_STATS
605     unsigned int start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
606     // remember join id and last thread to arrive so restart can use these
607     int thd;
608     // we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval
609     DWORD start_tick;
610     // counters for joins, in 1000's of clock cycles
611     unsigned int elapsed_total[gc_join_max], seq_loss_total[gc_join_max], par_loss_total[gc_join_max], in_join_total[gc_join_max];
612 #endif //JOIN_STATS
613
614 public:
615     BOOL init (int n_th, gc_join_flavor f)
616     {
617         dprintf (JOIN_LOG, ("Initializing join structure"));
618         join_struct.n_threads = n_th;
619         join_struct.lock_color = 0;
620         for (int i = 0; i < 3; i++)
621         {
622             if (!join_struct.joined_event[i].IsValid())
623             {
624                 join_struct.joined_p = FALSE;
625                 dprintf (JOIN_LOG, ("Creating join event %d", i));
626                 // TODO - changing this to a non OS event
627                 // because this is also used by BGC threads which are 
628                 // managed threads and WaitEx does not allow you to wait
629                 // for an OS event on a managed thread.
630                 // But we are not sure if this plays well in the hosting 
631                 // environment.
632                 //join_struct.joined_event[i].CreateOSManualEvent(FALSE);
633                 join_struct.joined_event[i].CreateManualEvent(FALSE);
634                 if (!join_struct.joined_event[i].IsValid())
635                     return FALSE;
636             }
637         }
638         join_struct.join_lock = join_struct.n_threads;
639         join_struct.join_restart = join_struct.n_threads - 1;
640         join_struct.r_join_lock = join_struct.n_threads;
641         join_struct.r_join_restart = join_struct.n_threads - 1;
642         join_struct.wait_done = FALSE;
643         flavor = f;
644
645 #ifdef JOIN_STATS
646         start_tick = GetTickCount();
647 #endif //JOIN_STATS
648
649         return TRUE;
650     }
651     
652     void destroy ()
653     {
654         dprintf (JOIN_LOG, ("Destroying join structure"));
655         for (int i = 0; i < 3; i++)
656         {
657             if (join_struct.joined_event[i].IsValid())
658                 join_struct.joined_event[i].CloseEvent();
659         }
660     }
661
662     inline void fire_event (ULONG heap, join_time time, join_type type)
663     {
664         FireEtwGCJoin_V1(heap, time, type, GetClrInstanceId());
665     }
666
667     void join (gc_heap* gch, int join_id)
668     {
669 #ifdef JOIN_STATS
670         // parallel execution ends here
671         end[gch->heap_number] = GetCycleCount32();
672 #endif //JOIN_STATS
673
674         assert (!join_struct.joined_p);
675         int color = join_struct.lock_color;
676
677         if (FastInterlockDecrement(&join_struct.join_lock) != 0)
678         {
679             dprintf (JOIN_LOG, ("join%d(%d): Join() Waiting...join_lock is now %d", 
680                 flavor, join_id, (LONG)(join_struct.join_lock)));
681
682             fire_event (gch->heap_number, time_start, type_join);
683
684             //busy wait around the color
685             if (color == join_struct.lock_color)
686             {
687 respin:
688                 int spin_count = 4096 * g_SystemInfo.dwNumberOfProcessors;
689                 for (int j = 0; j < spin_count; j++)
690                 {
691                     if (color != join_struct.lock_color)
692                     {
693                         break;
694                     }
695                     YieldProcessor();           // indicate to the processor that we are spinning
696                 }
697
698                 // we've spun, and if color still hasn't changed, fall into hard wait
699                 if (color == join_struct.lock_color)
700                 {
701                     dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d", 
702                         flavor, join_id, color, (LONG)(join_struct.join_lock)));
703
704                     //Thread* current_thread = GetThread();
705                     //BOOL cooperative_mode = gc_heap::enable_preemptive (current_thread);
706                     DWORD dwJoinWait = join_struct.joined_event[color].Wait(INFINITE, FALSE);
707                     //gc_heap::disable_preemptive (current_thread, cooperative_mode);
708
709                     if (dwJoinWait != WAIT_OBJECT_0)
710                     {
711                         STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
712                         FATAL_GC_ERROR ();
713                     }
714                 }
715
716                 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
717                 if (color == join_struct.lock_color)
718                 {
719                     goto respin;
720                 }
721
722                 dprintf (JOIN_LOG, ("join%d(%d): Join() done, join_lock is %d", 
723                     flavor, join_id, (LONG)(join_struct.join_lock)));
724             }
725
726             fire_event (gch->heap_number, time_end, type_join);
727
728             // last thread out should reset event
729             if (FastInterlockDecrement(&join_struct.join_restart) == 0)
730             {
731                 // the joined event must be set at this point, because the restarting must have done this
732                 join_struct.join_restart = join_struct.n_threads - 1;
733 //                printf("Reset joined_event %d\n", color);
734             }
735
736 #ifdef JOIN_STATS
737             // parallel execution starts here
738             start[gch->heap_number] = GetCycleCount32();
739             FastInterlockExchangeAdd((int*)&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])/1000);
740 #endif //JOIN_STATS
741         }
742         else
743         {
744             fire_event (gch->heap_number, time_start, type_last_join);
745
746             join_struct.joined_p = TRUE;
747             dprintf (JOIN_LOG, ("join%d(%d): Last thread to complete the join, setting id", flavor, join_id));
748             join_struct.joined_event[!color].Reset();
749             id = join_id;
750             // this one is alone so it can proceed
751 #ifdef JOIN_STATS
752             // remember the join id, the last thread arriving, the start of the sequential phase,
753             // and keep track of the cycles spent waiting in the join
754             thd = gch->heap_number;
755             start_seq = GetCycleCount32();
756             FastInterlockExchangeAdd((int*)&in_join_total[join_id], (start_seq - end[gch->heap_number])/1000);
757 #endif //JOIN_STATS
758         }
759     }
760
761     // Reverse join - first thread gets here does the work; other threads will only proceed
762     // afte the work is done.
763     // Note that you cannot call this twice in a row on the same thread. Plus there's no 
764     // need to call it twice in row - you should just merge the work.
765     BOOL r_join (gc_heap* gch, int join_id)
766     {
767 #ifdef JOIN_STATS
768         // parallel execution ends here
769         end[gch->heap_number] = GetCycleCount32();
770 #endif //JOIN_STATS
771
772         if (join_struct.n_threads == 1)
773         {
774             return TRUE;
775         }
776
777         if (FastInterlockDecrement(&join_struct.r_join_lock) != (join_struct.n_threads - 1))
778         {
779             if (!join_struct.wait_done)
780             {
781                 dprintf (JOIN_LOG, ("r_join() Waiting..."));
782
783                 fire_event (gch->heap_number, time_start, type_join);
784
785                 //busy wait around the color
786                 if (!join_struct.wait_done)
787                 {
788         respin:
789                     int spin_count = 2 * 4096 * g_SystemInfo.dwNumberOfProcessors;
790                     for (int j = 0; j < spin_count; j++)
791                     {
792                         if (join_struct.wait_done)
793                         {
794                             break;
795                         }
796                         YieldProcessor();           // indicate to the processor that we are spinning
797                     }
798
799                     // we've spun, and if color still hasn't changed, fall into hard wait
800                     if (!join_struct.wait_done)
801                     {
802                         dprintf (JOIN_LOG, ("Join() hard wait on reset event %d", first_thread_arrived));
803                         DWORD dwJoinWait = join_struct.joined_event[first_thread_arrived].Wait(INFINITE, FALSE);
804                         if (dwJoinWait != WAIT_OBJECT_0)
805                         {
806                             STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
807                             FATAL_GC_ERROR ();
808                         }
809                     }
810
811                     // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
812                     if (!join_struct.wait_done)
813                     {
814                         goto respin;
815                     }
816
817                     dprintf (JOIN_LOG, ("r_join() done"));
818                 }
819
820                 fire_event (gch->heap_number, time_end, type_join);
821
822 #ifdef JOIN_STATS
823                 // parallel execution starts here
824                 start[gch->heap_number] = GetCycleCount32();
825                 FastInterlockExchangeAdd((volatile int *)&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])/1000);
826 #endif //JOIN_STATS
827             }
828
829             return FALSE;
830         }
831         else
832         {
833             return TRUE;
834         }
835     }
836
837     void restart()
838     {
839 #ifdef JOIN_STATS
840         unsigned int elapsed_seq = GetCycleCount32() - start_seq;
841         unsigned int max = 0, sum = 0;
842         for (int i = 0; i < join_struct.n_threads; i++)
843         {
844             unsigned int elapsed = end[i] - start[i];
845             if (max < elapsed)
846                 max = elapsed;
847             sum += elapsed;
848         }
849         unsigned int seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
850         unsigned int par_loss = join_struct.n_threads*max - sum;
851         double efficiency = 0.0;
852         if (max > 0)
853             efficiency = sum*100.0/(join_struct.n_threads*max);
854
855         // enable this printf to get statistics on each individual join as it occurs
856 //      printf("join #%3d  seq_loss = %5d   par_loss = %5d  efficiency = %3.0f%%\n", join_id, seq_loss/1000, par_loss/1000, efficiency);
857
858         elapsed_total[join_id] += sum/1000;
859         seq_loss_total[join_id] += seq_loss/1000;
860         par_loss_total[join_id] += par_loss/1000;
861
862         // every 10 seconds, print a summary of the time spent in each type of join, in 1000's of clock cycles
863         if (GetTickCount() - start_tick > 10*1000)
864         {
865             printf("**** summary *****\n");
866             for (int i = 0; i < 16; i++)
867             {
868                 printf("join #%3d  seq_loss = %8u  par_loss = %8u  in_join_total = %8u\n", i, seq_loss_total[i], par_loss_total[i], in_join_total[i]);
869                 elapsed_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
870             }
871             start_tick = GetTickCount();
872         }
873 #endif //JOIN_STATS
874
875         fire_event (100, time_start, type_restart);
876         assert (join_struct.joined_p);
877         join_struct.joined_p = FALSE;
878         join_struct.join_lock = join_struct.n_threads;
879         dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d", flavor, id, (LONG)(join_struct.join_lock)));
880 //        printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start);
881         int color = join_struct.lock_color;
882         join_struct.lock_color = !color;
883         join_struct.joined_event[color].Set();
884
885 //        printf("Set joined_event %d\n", !join_struct.lock_color);
886
887         fire_event (100, time_end, type_restart);
888
889 #ifdef JOIN_STATS
890         start[thd] = GetCycleCount32();
891 #endif //JOIN_STATS
892     }
893     
894     BOOL joined()
895     {
896         dprintf (JOIN_LOG, ("join%d(%d): joined, join_lock is %d", flavor, id, (LONG)(join_struct.join_lock)));
897         return join_struct.joined_p;
898     }
899
900     void r_restart()
901     {
902         if (join_struct.n_threads != 1)
903         {
904             join_struct.wait_done = TRUE;
905             join_struct.joined_event[first_thread_arrived].Set();
906         }
907     }
908
909     void r_init()
910     {
911         if (join_struct.n_threads != 1)
912         {
913             join_struct.r_join_lock = join_struct.n_threads;
914             join_struct.r_join_restart = join_struct.n_threads - 1;
915             join_struct.wait_done = FALSE;
916             join_struct.joined_event[first_thread_arrived].Reset();
917         }
918     }
919 };
920
921 t_join gc_t_join;
922
923 #ifdef BACKGROUND_GC
924 t_join bgc_t_join;
925 #endif //BACKGROUND_GC
926
927 #endif //MULTIPLE_HEAPS
928
929 #define spin_and_switch(count_to_spin, expr) \
930 { \
931     for (int j = 0; j < count_to_spin; j++) \
932     { \
933         if (expr) \
934         { \
935             break;\
936         } \
937         YieldProcessor(); \
938     } \
939     if (!(expr)) \
940     { \
941         __SwitchToThread(0, CALLER_LIMITS_SPINNING); \
942     } \
943 }
944
945 #ifndef DACCESS_COMPILE
946 #ifdef BACKGROUND_GC
947
948 #define max_pending_allocs 64
949
950 class exclusive_sync
951 {
952     // TODO - verify that this is the right syntax for Volatile.
953     VOLATILE(BYTE*) rwp_object;
954     VOLATILE(LONG) needs_checking;
955     
956     int spin_count;
957
958     BYTE cache_separator[HS_CACHE_LINE_SIZE - sizeof (int) - sizeof (LONG)];
959
960     // TODO - perhaps each object should be on its own cache line...
961     VOLATILE(BYTE*) alloc_objects[max_pending_allocs];
962
963     int find_free_index ()
964     {
965         for (int i = 0; i < max_pending_allocs; i++)
966         {
967             if (alloc_objects [i] == (BYTE*)0)
968             {
969                 return i;
970             }
971         }
972  
973         return -1;
974     }
975
976 public:
977     void init()
978     {
979         spin_count = 32 * (g_SystemInfo.dwNumberOfProcessors - 1);
980         rwp_object = 0;
981         needs_checking = 0;
982         for (int i = 0; i < max_pending_allocs; i++)
983         {
984             alloc_objects [i] = (BYTE*)0;
985         }
986     }
987
988     void check()
989     {
990         for (int i = 0; i < max_pending_allocs; i++)
991         {
992             if (alloc_objects [i] != (BYTE*)0)
993             {
994                 DebugBreak();
995             }
996         }
997     }
998
999     void bgc_mark_set (BYTE* obj)
1000     {
1001         dprintf (3, ("cm: probing %Ix", obj));
1002 retry:
1003         if (FastInterlockExchange (&needs_checking, 1) == 0)
1004         {
1005             // If we spend too much time spending all the allocs,
1006             // consider adding a high water mark and scan up
1007             // to that; we'll need to interlock in done when
1008             // we update the high watermark.
1009             for (int i = 0; i < max_pending_allocs; i++)
1010             {
1011                 if (obj == alloc_objects[i])
1012                 {
1013                     needs_checking = 0;
1014                     dprintf (3, ("cm: will spin", obj));
1015                     spin_and_switch (spin_count, (obj != alloc_objects[i]));
1016                     goto retry;
1017                 }
1018             }
1019
1020             rwp_object = obj;
1021             needs_checking = 0;
1022             dprintf (3, ("cm: set %Ix", obj));
1023             return;
1024         }
1025         else
1026         {
1027             spin_and_switch (spin_count, (needs_checking == 0));
1028             goto retry;
1029         }
1030     }
1031
1032     int loh_alloc_set (BYTE* obj)
1033     {
1034         if (!gc_heap::cm_in_progress)
1035         {
1036             return -1;
1037         }
1038
1039 retry:
1040         dprintf (3, ("loh alloc: probing %Ix", obj));
1041
1042         if (FastInterlockExchange (&needs_checking, 1) == 0)
1043         {
1044             if (obj == rwp_object)
1045             {
1046                 needs_checking = 0;
1047                 spin_and_switch (spin_count, (obj != rwp_object));
1048                 goto retry;
1049             }
1050             else
1051             {
1052                 int cookie = find_free_index();
1053
1054                 if (cookie != -1)
1055                 {
1056                     alloc_objects[cookie] = obj;
1057                     needs_checking = 0;
1058                     //if (cookie >= 4)
1059                     //{
1060                     //    DebugBreak();
1061                     //}
1062
1063                     dprintf (3, ("loh alloc: set %Ix at %d", obj, cookie));
1064                     return cookie;
1065                 } 
1066                 else 
1067                 {
1068                     needs_checking = 0;
1069                     dprintf (3, ("loh alloc: setting %Ix will spin to acquire a free index", obj));
1070                     spin_and_switch (spin_count, (find_free_index () != -1));
1071                     goto retry;
1072                 }
1073             }
1074         }
1075         else
1076         {
1077             dprintf (3, ("loh alloc: will spin on checking", obj));
1078             spin_and_switch (spin_count, (needs_checking == 0));
1079             goto retry;
1080         }
1081     }
1082
1083     void bgc_mark_done ()
1084     {
1085         dprintf (3, ("cm: release lock on %Ix", (BYTE *)rwp_object));
1086         rwp_object = 0;
1087     }
1088
1089     void loh_alloc_done_with_index (int index)
1090     {
1091         dprintf (3, ("loh alloc: release lock on %Ix based on %d", (BYTE *)alloc_objects[index], index));
1092         assert ((index >= 0) && (index < max_pending_allocs)); 
1093         alloc_objects[index] = (BYTE*)0;
1094     }
1095
1096     void loh_alloc_done (BYTE* obj)
1097     {
1098 #ifdef BACKGROUND_GC
1099         if (!gc_heap::cm_in_progress)
1100         {
1101             return;
1102         }
1103
1104         for (int i = 0; i < max_pending_allocs; i++)
1105         {
1106             if (alloc_objects [i] == obj)
1107             {
1108                 dprintf (3, ("loh alloc: release lock on %Ix at %d", (BYTE *)alloc_objects[i], i));
1109                 alloc_objects[i] = (BYTE*)0;
1110                 return;
1111             }
1112         }
1113 #endif //BACKGROUND_GC
1114     }
1115 };
1116
1117 // Note that this class was written assuming just synchronization between
1118 // one background GC thread and multiple user threads that might request 
1119 // an FGC - it does not take into account what kind of locks the multiple
1120 // user threads might be holding at the time (eg, there could only be one
1121 // user thread requesting an FGC because it needs to take gc_lock first)
1122 // so you'll see checks that may not be necessary if you take those conditions
1123 // into consideration.
1124 //
1125 // With the introduction of Server Background GC we no longer use this
1126 // class to do synchronization between FGCs and BGC.
1127 class recursive_gc_sync
1128 {
1129     static VOLATILE(LONG) foreground_request_count;//initial state 0
1130     static VOLATILE(BOOL) gc_background_running; //initial state FALSE
1131     static VOLATILE(LONG) foreground_count; // initial state 0;
1132     static VOLATILE(DWORD) foreground_gate; // initial state FALSE;
1133     static CLREvent foreground_complete;//Auto Reset
1134     static CLREvent foreground_allowed;//Auto Reset
1135 public:
1136     static void begin_background();
1137     static void end_background();
1138     static void begin_foreground();
1139     static void end_foreground();
1140     BOOL allow_foreground ();
1141     static BOOL init();
1142     static void shutdown();
1143     static BOOL background_running_p() {return gc_background_running;}
1144 };
1145
1146 VOLATILE(LONG) recursive_gc_sync::foreground_request_count = 0;//initial state 0
1147 VOLATILE(LONG) recursive_gc_sync::foreground_count = 0; // initial state 0;
1148 VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE
1149 VOLATILE(DWORD) recursive_gc_sync::foreground_gate = 0;
1150 CLREvent recursive_gc_sync::foreground_complete;//Auto Reset
1151 CLREvent recursive_gc_sync::foreground_allowed;//Manual Reset
1152
1153 BOOL recursive_gc_sync::init ()
1154 {
1155     foreground_request_count = 0;
1156     foreground_count = 0;
1157     gc_background_running = FALSE;
1158     foreground_gate = 0;
1159
1160     foreground_complete.CreateOSAutoEvent(FALSE);
1161     if (!foreground_complete.IsValid())
1162     {
1163         goto error;
1164     }
1165     foreground_allowed.CreateManualEvent(FALSE);
1166     if (!foreground_allowed.IsValid())
1167     {
1168         goto error;
1169     }
1170     return TRUE;
1171
1172 error:
1173     shutdown();
1174     return FALSE;
1175
1176 }
1177
1178 void recursive_gc_sync::shutdown()
1179 {
1180     if (foreground_complete.IsValid())
1181         foreground_complete.CloseEvent();
1182     if (foreground_allowed.IsValid())
1183         foreground_allowed.CloseEvent();
1184 }
1185
1186 void recursive_gc_sync::begin_background()
1187 {
1188     dprintf (2, ("begin background"));
1189     foreground_request_count = 1;
1190     foreground_count = 1;
1191     foreground_allowed.Reset();
1192     gc_background_running = TRUE;
1193 }
1194 void recursive_gc_sync::end_background()
1195 {
1196     dprintf (2, ("end background"));
1197     gc_background_running = FALSE;
1198     foreground_gate = 1;
1199     foreground_allowed.Set();
1200 }
1201
1202 void recursive_gc_sync::begin_foreground()
1203 {
1204     dprintf (2, ("begin_foreground"));
1205
1206     BOOL cooperative_mode = FALSE;
1207     Thread* current_thread = 0;
1208
1209     if (gc_background_running)
1210     {
1211         gc_heap::fire_alloc_wait_event_begin (awr_fgc_wait_for_bgc);
1212         gc_heap::alloc_wait_event_p = TRUE;
1213
1214 try_again_top:
1215
1216         FastInterlockIncrement (&foreground_request_count);
1217
1218 try_again_no_inc:
1219         dprintf(2, ("Waiting sync gc point"));
1220         assert (foreground_allowed.IsValid());
1221         assert (foreground_complete.IsValid());
1222
1223         current_thread = GetThread();
1224         cooperative_mode = gc_heap::enable_preemptive (current_thread);
1225
1226         foreground_allowed.Wait(INFINITE, FALSE);
1227
1228         dprintf(2, ("Waiting sync gc point is done"));
1229
1230         gc_heap::disable_preemptive (current_thread, cooperative_mode);
1231
1232         if (foreground_gate)
1233         {
1234             FastInterlockIncrement (&foreground_count);
1235             dprintf (2, ("foreground_count: %d", (LONG)foreground_count));
1236             if (foreground_gate)
1237             {
1238                 gc_heap::settings.concurrent = FALSE;
1239                 return;
1240             }
1241             else
1242             {
1243                 end_foreground();
1244                 goto try_again_top;
1245             }
1246         }
1247         else
1248         {
1249             goto try_again_no_inc;
1250         }
1251     }
1252 }
1253
1254 void recursive_gc_sync::end_foreground()
1255 {
1256     dprintf (2, ("end_foreground"));
1257     if (gc_background_running)
1258     {
1259         FastInterlockDecrement (&foreground_request_count);
1260         dprintf (2, ("foreground_count before decrement: %d", (LONG)foreground_count));
1261         if (FastInterlockDecrement (&foreground_count) == 0)
1262         {
1263             //c_write_volatile ((BOOL*)&foreground_gate, 0);
1264             // TODO - couldn't make the syntax work with Volatile<T>
1265             foreground_gate = 0;
1266             if (foreground_count == 0)
1267             {
1268                 foreground_allowed.Reset ();
1269                 dprintf(2, ("setting foreground complete event"));
1270                 foreground_complete.Set();
1271             }
1272         }
1273     }
1274 }
1275
1276 inline
1277 BOOL recursive_gc_sync::allow_foreground()
1278 {
1279     assert (gc_heap::settings.concurrent);
1280     dprintf (100, ("enter allow_foreground, f_req_count: %d, f_count: %d",
1281                    (LONG)foreground_request_count, (LONG)foreground_count));
1282
1283     BOOL did_fgc = FALSE;
1284
1285     //if we have suspended the EE, just return because
1286     //some thread could be waiting on this to proceed.
1287     if (!GCHeap::GcInProgress)
1288     {
1289         //TODO BACKGROUND_GC This is to stress the concurrency between
1290         //background and foreground
1291 //        gc_heap::disallow_new_allocation (0);
1292
1293         //__SwitchToThread(0, CALLER_LIMITS_SPINNING);
1294
1295         //END of TODO
1296         if (foreground_request_count != 0)
1297         {
1298             //foreground wants to run
1299             //save the important settings
1300             //TODO BACKGROUND_GC be more selective about the important settings.
1301             gc_mechanisms saved_settings = gc_heap::settings;
1302             do
1303             {
1304                 did_fgc = TRUE;
1305                 //c_write_volatile ((BOOL*)&foreground_gate, 1);
1306                 // TODO - couldn't make the syntax work with Volatile<T>
1307                 foreground_gate = 1;
1308                 foreground_allowed.Set ();
1309                 foreground_complete.Wait (INFINITE, FALSE);
1310             }while (/*foreground_request_count ||*/ foreground_gate);
1311
1312             assert (!foreground_gate);
1313
1314             //restore the important settings
1315             gc_heap::settings = saved_settings;
1316             GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
1317             //the background GC shouldn't be using gc_high and gc_low
1318             //gc_low = lowest_address;
1319             //gc_high = highest_address;
1320         }
1321
1322         //TODO BACKGROUND_GC This is to stress the concurrency between
1323         //background and foreground
1324 //        gc_heap::allow_new_allocation (0);
1325         //END of TODO
1326     }
1327
1328     dprintf (100, ("leave allow_foreground"));
1329     assert (gc_heap::settings.concurrent);
1330     return did_fgc;
1331 }
1332
1333 #endif //BACKGROUND_GC
1334 #endif //DACCESS_COMPILE
1335
1336 #ifdef _MSC_VER
1337 // disable new LKG8 warning
1338 #pragma warning(disable:4293)
1339 #endif //_MSC_VER
1340
1341 #if  defined(COUNT_CYCLES) || defined(JOIN_STATS) || defined(SYNCHRONIZATION_STATS)
1342 #ifdef _MSC_VER
1343 #pragma warning(disable:4035)
1344 #endif //_MSC_VER
1345
1346 static
1347 unsigned        GetCycleCount32()        // enough for about 40 seconds
1348 {
1349 __asm   push    EDX
1350 __asm   _emit   0x0F
1351 __asm   _emit   0x31
1352 __asm   pop     EDX
1353 };
1354
1355 #pragma warning(default:4035)
1356
1357 #endif //COUNT_CYCLES || JOIN_STATS || SYNCHRONIZATION_STATS
1358
1359 LARGE_INTEGER qpf;
1360
1361
1362 #ifdef TIME_GC
1363 int mark_time, plan_time, sweep_time, reloc_time, compact_time;
1364 #endif //TIME_GC
1365
1366 #ifndef MULTIPLE_HEAPS
1367
1368 #define ephemeral_low           g_ephemeral_low
1369 #define ephemeral_high          g_ephemeral_high
1370
1371 #endif // MULTIPLE_HEAPS
1372
1373 #ifdef TRACE_GC
1374
1375 int     print_level     = DEFAULT_GC_PRN_LVL;  //level of detail of the debug trace
1376 BOOL    trace_gc        = FALSE;
1377 int       gc_trace_fac = 0;
1378 hlet* hlet::bindings = 0;
1379
1380 #endif //TRACE_GC
1381
1382 void reset_memory (BYTE* o, size_t sizeo);
1383
1384 #ifdef WRITE_WATCH
1385
1386 #define MEM_WRITE_WATCH 0x200000
1387
1388 static DWORD mem_reserve = MEM_RESERVE;
1389
1390 #ifndef FEATURE_REDHAWK
1391 BOOL write_watch_capability = FALSE;
1392 #endif
1393
1394 #ifndef DACCESS_COMPILE
1395
1396 //check if the write watch APIs are supported.
1397
1398 void write_watch_api_supported()
1399 {
1400 #ifndef FEATURE_REDHAWK
1401     // check if the OS will accept the MEM_WRITE_WATCH flag at runtime. 
1402     // Drawbridge does not support write-watch so we still need to do the runtime detection for them.
1403     // Otherwise, all currently supported OSes do support write-watch.
1404     void* mem = VirtualAlloc (0, g_SystemInfo.dwAllocationGranularity, MEM_WRITE_WATCH|MEM_RESERVE,
1405                               PAGE_READWRITE);
1406     if (mem == 0)
1407     {
1408         dprintf (2,("WriteWatch not supported"));
1409     }
1410     else
1411     {
1412         write_watch_capability = TRUE;
1413         dprintf (2, ("WriteWatch supported"));
1414         VirtualFree (mem, 0, MEM_RELEASE);
1415     }
1416 #endif //FEATURE_REDHAWK
1417 }
1418
1419 #endif //!DACCESS_COMPILE
1420
1421 inline BOOL can_use_write_watch()
1422 {
1423 #ifdef FEATURE_REDHAWK
1424     return PalHasCapability(WriteWatchCapability);
1425 #else //FEATURE_REDHAWK
1426     return write_watch_capability;
1427 #endif //FEATURE_REDHAWK
1428 }
1429
1430 #else
1431 #define mem_reserve (MEM_RESERVE)
1432 #endif //WRITE_WATCH
1433
1434 //check if the low memory notification is supported
1435
1436 #ifndef DACCESS_COMPILE
1437
1438 void WaitLongerNoInstru (int i)
1439 {
1440     // every 8th attempt:
1441     Thread *pCurThread = GetThread();
1442     BOOL bToggleGC = FALSE;
1443     if (pCurThread)
1444     {
1445         bToggleGC = pCurThread->PreemptiveGCDisabled();
1446         if (bToggleGC)
1447             pCurThread->EnablePreemptiveGC();
1448     }
1449
1450     // if we're waiting for gc to finish, we should block immediately
1451     if (!g_TrapReturningThreads)
1452     {
1453         if  (g_SystemInfo.dwNumberOfProcessors > 1)
1454         {
1455             YieldProcessor();           // indicate to the processor that we are spining
1456             if  (i & 0x01f)
1457                 __SwitchToThread (0, CALLER_LIMITS_SPINNING);
1458             else
1459                 __SwitchToThread (5, CALLER_LIMITS_SPINNING);
1460         }
1461         else
1462             __SwitchToThread (5, CALLER_LIMITS_SPINNING);
1463     }
1464
1465     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1466     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1467     // It is important that the thread is going to wait for GC.  Otherwise the thread
1468     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD.
1469     if (pCurThread)
1470     {
1471         if (bToggleGC || g_TrapReturningThreads)
1472         {
1473             pCurThread->DisablePreemptiveGC();
1474             if (!bToggleGC)
1475             {
1476                 pCurThread->EnablePreemptiveGC();
1477             }
1478         }
1479     }
1480     else if (g_TrapReturningThreads)
1481     {
1482         GCHeap::GetGCHeap()->WaitUntilGCComplete();
1483     }
1484 }
1485
1486 inline
1487 static void safe_switch_to_thread()
1488 {
1489     Thread* current_thread = GetThread();
1490     BOOL cooperative_mode = gc_heap::enable_preemptive(current_thread);
1491
1492     __SwitchToThread(0, CALLER_LIMITS_SPINNING);
1493
1494     gc_heap::disable_preemptive(current_thread, cooperative_mode);
1495 }
1496
1497 //
1498 // We need the following methods to have volatile arguments, so that they can accept
1499 // raw pointers in addition to the results of the & operator on Volatile<T>.
1500 //
1501 inline
1502 static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) LONG* lock)
1503 {
1504 retry:
1505
1506     if (FastInterlockExchange (lock, 0) >= 0)
1507     {
1508         unsigned int i = 0;
1509         while (VolatileLoad(lock) >= 0)
1510         {
1511             if ((++i & 7) && !GCHeap::IsGCInProgress())
1512             {
1513                 if  (g_SystemInfo.dwNumberOfProcessors > 1)
1514                 {
1515 #ifndef MULTIPLE_HEAPS
1516                     int spin_count = 1024 * g_SystemInfo.dwNumberOfProcessors;
1517 #else //!MULTIPLE_HEAPS
1518                     int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
1519 #endif //!MULTIPLE_HEAPS
1520                     for (int j = 0; j < spin_count; j++)
1521                     {
1522                         if  (VolatileLoad(lock) < 0 || GCHeap::IsGCInProgress())
1523                             break;
1524                         YieldProcessor();           // indicate to the processor that we are spining
1525                     }
1526                     if  (VolatileLoad(lock) >= 0 && !GCHeap::IsGCInProgress())
1527                     {
1528                         safe_switch_to_thread();
1529                     }
1530                 }
1531                 else
1532                 {
1533                     safe_switch_to_thread();
1534                 }
1535             }
1536             else
1537             {
1538                 WaitLongerNoInstru(i);
1539             }
1540         }
1541         goto retry;
1542     }
1543 }
1544
1545 inline
1546 static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) LONG* lock)
1547 {
1548     return (FastInterlockExchange (&*lock, 0) < 0);
1549 }
1550
1551 inline
1552 static void leave_spin_lock_noinstru (RAW_KEYWORD(volatile) LONG* lock)
1553 {
1554     VolatileStore<LONG>((LONG*)lock, -1);
1555 }
1556
1557 #ifdef _DEBUG
1558
1559 inline
1560 static void enter_spin_lock(GCSpinLock *pSpinLock)
1561 {
1562     enter_spin_lock_noinstru(&pSpinLock->lock);
1563     assert (pSpinLock->holding_thread == (Thread*)-1);
1564     pSpinLock->holding_thread = GetThread();
1565 }
1566
1567 inline
1568 static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock)
1569 {
1570     BOOL ret = try_enter_spin_lock_noinstru(&pSpinLock->lock);
1571     if (ret)
1572         pSpinLock->holding_thread = GetThread();
1573     return ret;
1574 }
1575
1576 inline
1577 static void leave_spin_lock(GCSpinLock *pSpinLock)
1578 {
1579     BOOL gc_thread_p = IsGCSpecialThread();
1580 //    _ASSERTE((pSpinLock->holding_thread == GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p);
1581     pSpinLock->released_by_gc_p = gc_thread_p;
1582     pSpinLock->holding_thread = (Thread*) -1;
1583     if (pSpinLock->lock != -1)
1584         leave_spin_lock_noinstru(&pSpinLock->lock);
1585 }
1586
1587 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) \
1588     _ASSERTE((pSpinLock)->holding_thread == GetThread());
1589
1590 #define ASSERT_NOT_HOLDING_SPIN_LOCK(pSpinLock) \
1591     _ASSERTE((pSpinLock)->holding_thread != GetThread());
1592
1593 #else //_DEBUG
1594
1595 //In the concurrent version, the Enable/DisablePreemptiveGC is optional because
1596 //the gc thread call WaitLonger.
1597 void WaitLonger (int i
1598 #ifdef SYNCHRONIZATION_STATS
1599     , VOLATILE(GCSpinLock)* spin_lock
1600 #endif //SYNCHRONIZATION_STATS
1601     )
1602 {
1603 #ifdef SYNCHRONIZATION_STATS
1604     (spin_lock->num_wait_longer)++;
1605 #endif //SYNCHRONIZATION_STATS
1606
1607     // every 8th attempt:
1608     Thread *pCurThread = GetThread();
1609     BOOL bToggleGC = FALSE;
1610     if (pCurThread)
1611     {
1612         bToggleGC = pCurThread->PreemptiveGCDisabled();
1613         if (bToggleGC)
1614         {
1615             pCurThread->EnablePreemptiveGC();
1616         }
1617         else
1618         {
1619             assert (!"bToggleGC == TRUE");
1620         }
1621     }
1622
1623     // if we're waiting for gc to finish, we should block immediately
1624     if (!gc_heap::gc_started)
1625     {
1626 #ifdef SYNCHRONIZATION_STATS
1627         (spin_lock->num_switch_thread_w)++;
1628 #endif //SYNCHRONIZATION_STATS
1629         if  (g_SystemInfo.dwNumberOfProcessors > 1)
1630         {
1631             YieldProcessor();           // indicate to the processor that we are spining
1632             if  (i & 0x01f)
1633                 __SwitchToThread (0, CALLER_LIMITS_SPINNING);
1634             else
1635                 __SwitchToThread (5, CALLER_LIMITS_SPINNING);
1636         }
1637         else
1638             __SwitchToThread (5, CALLER_LIMITS_SPINNING);
1639     }
1640
1641     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1642     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1643     // It is important that the thread is going to wait for GC.  Otherwise the thread
1644     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD.
1645     if (pCurThread)
1646     {
1647         if (bToggleGC || gc_heap::gc_started)
1648         {
1649             if (gc_heap::gc_started)
1650             {
1651                 gc_heap::wait_for_gc_done();
1652             }
1653
1654 #ifdef SYNCHRONIZATION_STATS
1655             (spin_lock->num_disable_preemptive_w)++;
1656 #endif //SYNCHRONIZATION_STATS
1657             pCurThread->DisablePreemptiveGC();
1658         }
1659     }
1660 }
1661
1662 inline
1663 static void enter_spin_lock (GCSpinLock* spin_lock)
1664 {
1665 retry:
1666
1667     if (FastInterlockExchange (&spin_lock->lock, 0) >= 0)
1668     {
1669         unsigned int i = 0;
1670         while (spin_lock->lock >= 0)
1671         {
1672             if ((++i & 7) && !gc_heap::gc_started)
1673             {
1674                 if  (g_SystemInfo.dwNumberOfProcessors > 1)
1675                 {
1676 #ifndef MULTIPLE_HEAPS
1677                     int spin_count = 1024 * g_SystemInfo.dwNumberOfProcessors;
1678 #else //!MULTIPLE_HEAPS
1679                     int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
1680 #endif //!MULTIPLE_HEAPS
1681                     for (int j = 0; j < spin_count; j++)
1682                     {
1683                         if  (spin_lock->lock < 0 || gc_heap::gc_started)
1684                             break;
1685                         YieldProcessor();           // indicate to the processor that we are spining
1686                     }
1687                     if  (spin_lock->lock >= 0 && !gc_heap::gc_started)
1688                     {
1689 #ifdef SYNCHRONIZATION_STATS
1690                         (spin_lock->num_switch_thread)++;
1691 #endif //SYNCHRONIZATION_STATS
1692                         Thread* current_thread = GetThread();
1693                         BOOL cooperative_mode = gc_heap::enable_preemptive (current_thread);
1694
1695                         __SwitchToThread(0, CALLER_LIMITS_SPINNING);
1696
1697                         gc_heap::disable_preemptive (current_thread, cooperative_mode);
1698                     }
1699                 }
1700                 else
1701                     __SwitchToThread(0, CALLER_LIMITS_SPINNING);
1702             }
1703             else
1704             {
1705                 WaitLonger(i
1706 #ifdef SYNCHRONIZATION_STATS
1707                         , spin_lock
1708 #endif //SYNCHRONIZATION_STATS
1709                     );
1710             }
1711         }
1712         goto retry;
1713     }
1714 }
1715
1716 inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock)
1717 {
1718     return (FastInterlockExchange (&spin_lock->lock, 0) < 0);
1719 }
1720
1721 inline
1722 static void leave_spin_lock (GCSpinLock * spin_lock)
1723 {
1724     spin_lock->lock = -1;
1725 }
1726
1727 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock)
1728
1729 #endif //_DEBUG
1730
1731 #endif // !DACCESS_COMPILE
1732
1733 BOOL gc_heap::enable_preemptive (Thread* current_thread)
1734 {
1735     BOOL cooperative_mode = FALSE;
1736     if (current_thread)
1737     {
1738         cooperative_mode = current_thread->PreemptiveGCDisabled();
1739         if (cooperative_mode)
1740         {
1741             current_thread->EnablePreemptiveGC();    
1742         }
1743     }
1744
1745     return cooperative_mode;
1746 }
1747
1748 void gc_heap::disable_preemptive (Thread* current_thread, BOOL restore_cooperative)
1749 {
1750     if (current_thread)
1751     {
1752         if (restore_cooperative)
1753         {
1754             current_thread->DisablePreemptiveGC(); 
1755         }
1756     }
1757 }
1758
1759 typedef void **  PTR_PTR;
1760 //This function clears a piece of memory
1761 // size has to be Dword aligned
1762
1763 inline
1764 void memclr ( BYTE* mem, size_t size)
1765 {
1766     dprintf (3, ("MEMCLR: %Ix, %d", mem, size));
1767     assert ((size & (sizeof(PTR_PTR)-1)) == 0);
1768     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1769
1770 #if 0
1771     // The compiler will recognize this pattern and replace it with memset call. We can as well just call 
1772     // memset directly to make it obvious what's going on.
1773     PTR_PTR m = (PTR_PTR) mem;
1774     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
1775         *(m++) = 0;
1776 #endif
1777
1778     memset (mem, 0, size);
1779 }
1780
1781 void memcopy (BYTE* dmem, BYTE* smem, size_t size)
1782 {
1783     const size_t sz4ptr = sizeof(PTR_PTR)*4;
1784     const size_t sz2ptr = sizeof(PTR_PTR)*2;
1785     const size_t sz1ptr = sizeof(PTR_PTR)*1;
1786
1787     // size must be a multiple of the pointer size
1788     assert ((size & (sizeof (PTR_PTR)-1)) == 0);
1789     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1790
1791     // copy in groups of four pointer sized things at a time
1792     if (size >= sz4ptr)
1793     {
1794         do
1795         {
1796             ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1797             ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1798             ((PTR_PTR)dmem)[2] = ((PTR_PTR)smem)[2];
1799             ((PTR_PTR)dmem)[3] = ((PTR_PTR)smem)[3];
1800             dmem += sz4ptr;
1801             smem += sz4ptr;
1802         }
1803         while ((size -= sz4ptr) >= sz4ptr);
1804     }
1805
1806     // still two pointer sized things or more left to copy?
1807     if (size & sz2ptr)
1808     {
1809         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1810         ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1811         dmem += sz2ptr;
1812         smem += sz2ptr;
1813     }
1814
1815     // still one pointer sized thing left to copy?
1816     if (size & sz1ptr)
1817     {
1818         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1819         // dmem += sz1ptr;
1820         // smem += sz1ptr;
1821     }
1822
1823 }
1824
1825 inline
1826 ptrdiff_t round_down (ptrdiff_t add, int pitch)
1827 {
1828     return ((add / pitch) * pitch);
1829 }
1830
1831 #if defined(FEATURE_STRUCTALIGN) && defined(RESPECT_LARGE_ALIGNMENT)
1832 // FEATURE_STRUCTALIGN allows the compiler to dictate the alignment,
1833 // i.e, if a larger alignment matters or is beneficial, the compiler
1834 // generated info tells us so.  RESPECT_LARGE_ALIGNMENT is just the
1835 // converse - it's a heuristic for the GC to use a larger alignment.
1836 #error FEATURE_STRUCTALIGN should imply !RESPECT_LARGE_ALIGNMENT
1837 #endif
1838
1839 #if defined(FEATURE_STRUCTALIGN) && defined(FEATURE_LOH_COMPACTION)
1840 #error FEATURE_STRUCTALIGN and FEATURE_LOH_COMPACTION are mutually exclusive
1841 #endif
1842
1843 #if defined(GROWABLE_SEG_MAPPING_TABLE) && !defined(SEG_MAPPING_TABLE)
1844 #error if GROWABLE_SEG_MAPPING_TABLE is defined, SEG_MAPPING_TABLE must be defined
1845 #endif
1846
1847 inline
1848 BOOL same_large_alignment_p (BYTE* p1, BYTE* p2)
1849 {
1850 #ifdef RESPECT_LARGE_ALIGNMENT
1851     return ((((size_t)p1 ^ (size_t)p2) & 7) == 0);
1852 #else
1853     return TRUE;
1854 #endif //RESPECT_LARGE_ALIGNMENT
1855 }
1856
1857 inline 
1858 size_t switch_alignment_size (BOOL already_padded_p)
1859 {
1860     if (already_padded_p)
1861         return DATA_ALIGNMENT;
1862     else
1863         return (Align (min_obj_size) +((Align (min_obj_size)&DATA_ALIGNMENT)^DATA_ALIGNMENT));
1864 }
1865
1866
1867 #ifdef FEATURE_STRUCTALIGN
1868 void set_node_aligninfo (BYTE *node, int requiredAlignment, ptrdiff_t pad);
1869 void clear_node_aligninfo (BYTE *node);
1870 #else // FEATURE_STRUCTALIGN
1871 void set_node_realigned (BYTE* node);
1872 #endif // FEATURE_STRUCTALIGN
1873
1874 inline
1875 size_t AlignQword (size_t nbytes)
1876 {
1877 #ifdef FEATURE_STRUCTALIGN
1878     // This function is used to align everything on the large object
1879     // heap to an 8-byte boundary, to reduce the number of unaligned
1880     // accesses to (say) arrays of doubles.  With FEATURE_STRUCTALIGN,
1881     // the compiler dictates the optimal alignment instead of having
1882     // a heuristic in the GC.
1883     return Align (nbytes);
1884 #else // FEATURE_STRUCTALIGN
1885     return (nbytes + 7) & ~7;
1886 #endif // FEATURE_STRUCTALIGN
1887 }
1888
1889 inline
1890 BOOL Aligned (size_t n)
1891 {
1892     return (n & ALIGNCONST) == 0;
1893 }
1894
1895 #define OBJECT_ALIGNMENT_OFFSET (sizeof(MethodTable *))
1896
1897 #ifdef FEATURE_STRUCTALIGN
1898 #define MAX_STRUCTALIGN OS_PAGE_SIZE
1899 #else // FEATURE_STRUCTALIGN
1900 #define MAX_STRUCTALIGN 0
1901 #endif // FEATURE_STRUCTALIGN
1902
1903 #ifdef FEATURE_STRUCTALIGN
1904 inline
1905 ptrdiff_t AdjustmentForMinPadSize(ptrdiff_t pad, int requiredAlignment)
1906 {
1907     // The resulting alignpad must be either 0 or at least min_obj_size.
1908     // Note that by computing the following difference on unsigned types,
1909     // we can do the range check 0 < alignpad < min_obj_size with a
1910     // single conditional branch.
1911     if ((size_t)(pad - DATA_ALIGNMENT) < Align (min_obj_size) - DATA_ALIGNMENT)
1912     {
1913         return requiredAlignment;
1914     }
1915     return 0;
1916 }
1917
1918 inline
1919 BYTE* StructAlign (BYTE* origPtr, int requiredAlignment, ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
1920 {
1921     // required alignment must be a power of two
1922     _ASSERTE(((size_t)origPtr & ALIGNCONST) == 0);
1923     _ASSERTE(((requiredAlignment - 1) & requiredAlignment) == 0);
1924     _ASSERTE(requiredAlignment >= sizeof(void *));
1925     _ASSERTE(requiredAlignment <= MAX_STRUCTALIGN);
1926
1927     // When this method is invoked for individual objects (i.e., alignmentOffset
1928     // is just the size of the PostHeader), what needs to be aligned when
1929     // we're done is the pointer to the payload of the object (which means
1930     // the actual resulting object pointer is typically not aligned).
1931
1932     BYTE* result = (BYTE*)Align ((size_t)origPtr + alignmentOffset, requiredAlignment-1) - alignmentOffset;
1933     ptrdiff_t alignpad = result - origPtr;
1934
1935     return result + AdjustmentForMinPadSize (alignpad, requiredAlignment);
1936 }
1937
1938 inline
1939 ptrdiff_t ComputeStructAlignPad (BYTE* plug, int requiredAlignment, size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
1940 {
1941     return StructAlign (plug, requiredAlignment, alignmentOffset) - plug;
1942 }
1943
1944 BOOL IsStructAligned (BYTE *ptr, int requiredAlignment)
1945 {
1946     return StructAlign (ptr, requiredAlignment) == ptr;
1947 }
1948
1949 inline
1950 ptrdiff_t ComputeMaxStructAlignPad (int requiredAlignment)
1951 {
1952     if (requiredAlignment == DATA_ALIGNMENT)
1953         return 0;
1954     // Since a non-zero alignment padding cannot be less than min_obj_size (so we can fit the
1955     // alignment padding object), the worst-case alignment padding is correspondingly larger
1956     // than the required alignment.
1957     return requiredAlignment + Align (min_obj_size) - DATA_ALIGNMENT;
1958 }
1959
1960 inline
1961 ptrdiff_t ComputeMaxStructAlignPadLarge (int requiredAlignment)
1962 {
1963     if (requiredAlignment <= get_alignment_constant (TRUE)+1)
1964         return 0;
1965     // This is the same as ComputeMaxStructAlignPad, except that in addition to leaving space
1966     // for padding before the actual object, it also leaves space for filling a gap after the
1967     // actual object.  This is needed on the large object heap, as the outer allocation functions
1968     // don't operate on an allocation context (which would have left space for the final gap).
1969     return requiredAlignment + Align (min_obj_size) * 2 - DATA_ALIGNMENT;
1970 }
1971
1972 BYTE* gc_heap::pad_for_alignment (BYTE* newAlloc, int requiredAlignment, size_t size, alloc_context* acontext)
1973 {
1974     BYTE* alignedPtr = StructAlign (newAlloc, requiredAlignment);
1975     if (alignedPtr != newAlloc) {
1976         make_unused_array (newAlloc, alignedPtr - newAlloc);
1977     }
1978     acontext->alloc_ptr = alignedPtr + Align (size);
1979     return alignedPtr;
1980 }
1981
1982 BYTE* gc_heap::pad_for_alignment_large (BYTE* newAlloc, int requiredAlignment, size_t size)
1983 {
1984     BYTE* alignedPtr = StructAlign (newAlloc, requiredAlignment);
1985     if (alignedPtr != newAlloc) {
1986         make_unused_array (newAlloc, alignedPtr - newAlloc);
1987     }
1988     if (alignedPtr < newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment)) {
1989         make_unused_array (alignedPtr + AlignQword (size), newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment) - alignedPtr);
1990     }
1991     return alignedPtr;
1992 }
1993 #else // FEATURE_STRUCTALIGN
1994 #define ComputeMaxStructAlignPad(requiredAlignment) 0
1995 #define ComputeMaxStructAlignPadLarge(requiredAlignment) 0
1996 #endif // FEATURE_STRUCTALIGN
1997
1998 //CLR_SIZE  is the max amount of bytes from gen0 that is set to 0 in one chunk
1999 #ifdef SERVER_GC
2000 #define CLR_SIZE ((size_t)(8*1024))
2001 #else //SERVER_GC
2002 #define CLR_SIZE ((size_t)(8*1024))
2003 #endif //SERVER_GC
2004
2005 #define END_SPACE_AFTER_GC (LARGE_OBJECT_SIZE + MAX_STRUCTALIGN)
2006
2007 #ifdef BACKGROUND_GC
2008 #define SEGMENT_INITIAL_COMMIT (2*OS_PAGE_SIZE)
2009 #else
2010 #define SEGMENT_INITIAL_COMMIT (OS_PAGE_SIZE)
2011 #endif //BACKGROUND_GC
2012
2013 #ifdef SERVER_GC
2014
2015 #ifdef _WIN64
2016
2017 #define INITIAL_ALLOC ((size_t)((size_t)4*1024*1024*1024))
2018 #define LHEAP_ALLOC   ((size_t)(1024*1024*256))
2019
2020 #else
2021
2022 #define INITIAL_ALLOC ((size_t)(1024*1024*64))
2023 #define LHEAP_ALLOC   ((size_t)(1024*1024*32))
2024
2025 #endif  // _WIN64
2026
2027 #else //SERVER_GC
2028
2029 #ifdef _WIN64
2030
2031 #define INITIAL_ALLOC ((size_t)(1024*1024*256))
2032 #define LHEAP_ALLOC   ((size_t)(1024*1024*128))
2033
2034 #else
2035
2036 #define INITIAL_ALLOC ((size_t)(1024*1024*16))
2037 #define LHEAP_ALLOC   ((size_t)(1024*1024*16))
2038
2039 #endif  // _WIN64
2040
2041 #endif //SERVER_GC
2042
2043 //amount in bytes of the etw allocation tick
2044 const size_t etw_allocation_tick = 100*1024;
2045
2046 const size_t low_latency_alloc = 256*1024;
2047
2048 const size_t fgn_check_quantum = 2*1024*1024;
2049
2050 #ifdef MH_SC_MARK
2051 const int max_snoop_level = 128;
2052 #endif //MH_SC_MARK
2053
2054
2055 #ifdef CARD_BUNDLE
2056 //threshold of heap size to turn on card bundles.
2057 #define SH_TH_CARD_BUNDLE  (40*1024*1024)
2058 #define MH_TH_CARD_BUNDLE  (180*1024*1024)
2059 #endif //CARD_BUNDLE
2060
2061 #define page_size OS_PAGE_SIZE
2062
2063 #define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000
2064
2065 inline
2066 size_t align_on_page (size_t add)
2067 {
2068     return ((add + page_size - 1) & ~(page_size - 1));
2069 }
2070
2071 inline
2072 BYTE* align_on_page (BYTE* add)
2073 {
2074     return (BYTE*)align_on_page ((size_t) add);
2075 }
2076
2077 inline
2078 size_t align_lower_page (size_t add)
2079 {
2080     return (add & ~(page_size - 1));
2081 }
2082
2083 inline
2084 BYTE* align_lower_page (BYTE* add)
2085 {
2086     return (BYTE*)align_lower_page ((size_t)add);
2087 }
2088
2089 inline
2090 BOOL power_of_two_p (size_t integer)
2091 {
2092     return !(integer & (integer-1));
2093 }
2094
2095 inline
2096 BOOL oddp (size_t integer)
2097 {
2098     return (integer & 1) != 0;
2099 }
2100
2101 // we only ever use this for WORDs.
2102 size_t logcount (size_t word)
2103 {
2104     //counts the number of high bits in a 16 bit word.
2105     assert (word < 0x10000);
2106     size_t count;
2107     count = (word & 0x5555) + ( (word >> 1 ) & 0x5555);
2108     count = (count & 0x3333) + ( (count >> 2) & 0x3333);
2109     count = (count & 0x0F0F) + ( (count >> 4) & 0x0F0F);
2110     count = (count & 0x00FF) + ( (count >> 8) & 0x00FF);
2111     return count;
2112 }
2113
2114 //n!=0
2115 int log2(unsigned int n) 
2116 {
2117     int pos = 0;
2118     if (n >= 1<<16) { n >>= 16; pos += 16; }
2119     if (n >= 1<< 8) { n >>=  8; pos +=  8; }
2120     if (n >= 1<< 4) { n >>=  4; pos +=  4; }
2121     if (n >= 1<< 2) { n >>=  2; pos +=  2; }
2122     if (n >= 1<< 1) {           pos +=  1; }
2123     return pos;
2124 }
2125
2126 //extract the low bits [0,low[ of a DWORD
2127 #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1))
2128 //extract the high bits [high, 32] of a DWORD
2129 #define highbits(wrd, bits) ((wrd) & ~((1 << (bits))-1))
2130
2131
2132 class mark;
2133 class generation;
2134 class heap_segment;
2135 class CObjectHeader;
2136 class dynamic_data;
2137 class l_heap;
2138 class sorted_table;
2139 class c_synchronize;
2140
2141 #ifdef FEATURE_PREMORTEM_FINALIZATION
2142 #ifndef DACCESS_COMPILE
2143 static
2144 HRESULT AllocateCFinalize(CFinalize **pCFinalize);
2145 #endif //!DACCESS_COMPILE
2146 #endif // FEATURE_PREMORTEM_FINALIZATION
2147
2148 BYTE* tree_search (BYTE* tree, BYTE* old_address);
2149
2150
2151 #ifdef USE_INTROSORT
2152 #define _sort introsort::sort
2153 #else //USE_INTROSORT
2154 #define _sort qsort1
2155 void qsort1(BYTE** low, BYTE** high, unsigned int depth);
2156 #endif //USE_INTROSORT
2157
2158 void* virtual_alloc (size_t size);
2159 void virtual_free (void* add, size_t size);
2160
2161 /* per heap static initialization */
2162 #ifdef MARK_ARRAY
2163 #ifndef MULTIPLE_HEAPS
2164 SPTR_IMPL_NS(DWORD, WKS, gc_heap, mark_array);
2165 #endif //!MULTIPLE_HEAPS
2166 #endif //MARK_ARRAY
2167
2168 #ifdef MARK_LIST
2169 BYTE**      gc_heap::g_mark_list;
2170
2171 #ifdef PARALLEL_MARK_LIST_SORT
2172 BYTE**      gc_heap::g_mark_list_copy;
2173 #endif //PARALLEL_MARK_LIST_SORT
2174
2175 size_t      gc_heap::mark_list_size;
2176 #endif //MARK_LIST
2177
2178 #ifdef SEG_MAPPING_TABLE
2179 seg_mapping* seg_mapping_table;
2180 #endif //SEG_MAPPING_TABLE
2181
2182 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2183 sorted_table* gc_heap::seg_table;
2184 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
2185
2186 #ifdef MULTIPLE_HEAPS
2187 CLREvent    gc_heap::ee_suspend_event;
2188 #endif //MULTIPLE_HEAPS
2189
2190 VOLATILE(BOOL) gc_heap::gc_started;
2191
2192 #ifdef MULTIPLE_HEAPS
2193
2194 CLREvent    gc_heap::gc_start_event;
2195
2196 SVAL_IMPL_NS(int, SVR, gc_heap, n_heaps);
2197 SPTR_IMPL_NS(PTR_gc_heap, SVR, gc_heap, g_heaps);
2198
2199 HANDLE*     gc_heap::g_gc_threads;
2200
2201 size_t*     gc_heap::g_promoted;
2202
2203 #ifdef MH_SC_MARK
2204 BOOL*           gc_heap::g_mark_stack_busy;
2205 #endif //MH_SC_MARK
2206
2207
2208 #ifdef BACKGROUND_GC
2209 size_t*     gc_heap::g_bpromoted;
2210 #endif //BACKGROUND_GC
2211
2212 #else  //MULTIPLE_HEAPS
2213
2214 size_t      gc_heap::g_promoted;
2215
2216 #ifdef BACKGROUND_GC
2217 size_t      gc_heap::g_bpromoted;
2218 #endif //BACKGROUND_GC
2219
2220 #endif //MULTIPLE_HEAPS
2221
2222 size_t      gc_heap::reserved_memory = 0;
2223 size_t      gc_heap::reserved_memory_limit = 0;
2224 BOOL        gc_heap::g_low_memory_status;
2225
2226 #ifndef DACCESS_COMPILE
2227 static gc_reason gc_trigger_reason = reason_empty;
2228 #endif //DACCESS_COMPILE
2229
2230 gc_mechanisms  gc_heap::settings;
2231
2232 gc_history_global gc_heap::gc_data_global;
2233
2234 size_t      gc_heap::gc_last_ephemeral_decommit_time = 0;
2235
2236 size_t      gc_heap::gc_gen0_desired_high;
2237
2238 #if defined(_WIN64)
2239 #define MAX_ALLOWED_MEM_LOAD 85
2240
2241 // consider putting this in dynamic data -
2242 // we may want different values for workstation
2243 // and server GC.
2244 #define MIN_YOUNGEST_GEN_DESIRED (16*1024*1024)
2245
2246 size_t      gc_heap::youngest_gen_desired_th;
2247
2248 size_t      gc_heap::mem_one_percent;
2249
2250 ULONGLONG   gc_heap::total_physical_mem;
2251
2252 ULONGLONG   gc_heap::available_physical_mem;
2253 #endif //_WIN64
2254
2255 #ifdef BACKGROUND_GC
2256 CLREvent    gc_heap::bgc_start_event;
2257
2258 gc_mechanisms gc_heap::saved_bgc_settings;
2259
2260 CLREvent    gc_heap::background_gc_done_event;
2261
2262 CLREvent    gc_heap::ee_proceed_event;
2263
2264 BOOL        gc_heap::gc_can_use_concurrent = FALSE;
2265
2266 BOOL        gc_heap::temp_disable_concurrent_p = FALSE;
2267
2268 DWORD       gc_heap::cm_in_progress = FALSE;
2269
2270 BOOL        gc_heap::dont_restart_ee_p = FALSE;
2271
2272 BOOL        gc_heap::keep_bgc_threads_p = FALSE;
2273
2274 CLREvent    gc_heap::bgc_threads_sync_event;
2275
2276 BOOL        gc_heap::do_ephemeral_gc_p = FALSE;
2277
2278 BOOL        gc_heap::do_concurrent_p = FALSE;
2279
2280 size_t      gc_heap::ephemeral_fgc_counts[max_generation];
2281
2282 BOOL        gc_heap::alloc_wait_event_p = FALSE;
2283
2284 #if defined (DACCESS_COMPILE) && !defined (MULTIPLE_HEAPS)
2285 SVAL_IMPL_NS_INIT(gc_heap::c_gc_state, WKS, gc_heap, current_c_gc_state, c_gc_state_free);
2286 #else
2287 VOLATILE(gc_heap::c_gc_state) gc_heap::current_c_gc_state = c_gc_state_free;
2288 #endif //DACCESS_COMPILE && !MULTIPLE_HEAPS
2289
2290 #endif //BACKGROUND_GC
2291
2292 #ifndef MULTIPLE_HEAPS
2293 #ifdef SPINLOCK_HISTORY
2294 int         gc_heap::spinlock_info_index = 0;
2295 spinlock_info gc_heap::last_spinlock_info[max_saved_spinlock_info + 8];
2296 #endif //SPINLOCK_HISTORY
2297
2298 size_t      gc_heap::fgn_last_alloc = 0;
2299
2300 int         gc_heap::generation_skip_ratio = 100;
2301
2302 unsigned __int64 gc_heap::loh_alloc_since_cg = 0;
2303
2304 BOOL        gc_heap::elevation_requested = FALSE;
2305
2306 BOOL        gc_heap::last_gc_before_oom = FALSE;
2307
2308 #ifdef BACKGROUND_GC
2309 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, background_saved_lowest_address, 0);
2310 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, background_saved_highest_address, 0);
2311 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, next_sweep_obj, 0);
2312 BYTE*       gc_heap::current_sweep_pos = 0;
2313 exclusive_sync* gc_heap::bgc_alloc_lock;
2314 #endif //BACKGROUND_GC
2315
2316 SVAL_IMPL_NS(oom_history, WKS, gc_heap, oom_info);
2317
2318 fgm_history gc_heap::fgm_result;
2319
2320 BOOL        gc_heap::ro_segments_in_range;
2321
2322 size_t      gc_heap::gen0_big_free_spaces = 0;
2323
2324 BYTE*       gc_heap::lowest_address;
2325
2326 BYTE*       gc_heap::highest_address;
2327
2328 BOOL        gc_heap::ephemeral_promotion;
2329
2330 BYTE*       gc_heap::saved_ephemeral_plan_start[NUMBERGENERATIONS-1];
2331 size_t      gc_heap::saved_ephemeral_plan_start_size[NUMBERGENERATIONS-1];
2332
2333 short*      gc_heap::brick_table;
2334
2335 DWORD*      gc_heap::card_table;
2336
2337 #ifdef CARD_BUNDLE
2338 DWORD*      gc_heap::card_bundle_table;
2339 #endif //CARD_BUNDLE
2340
2341 BYTE*       gc_heap::gc_low;
2342
2343 BYTE*       gc_heap::gc_high;
2344
2345 BYTE*       gc_heap::demotion_low;
2346
2347 BYTE*       gc_heap::demotion_high;
2348
2349 BOOL        gc_heap::demote_gen1_p = TRUE;
2350
2351 BYTE*       gc_heap::last_gen1_pin_end;
2352
2353 gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons;
2354
2355 size_t      gc_heap::etw_allocation_running_amount[2];
2356
2357 int         gc_heap::gc_policy = 0;
2358
2359 size_t      gc_heap::allocation_running_time;
2360
2361 size_t      gc_heap::allocation_running_amount;
2362
2363 SPTR_IMPL_NS_INIT(heap_segment, WKS, gc_heap, ephemeral_heap_segment, 0);
2364
2365 BOOL        gc_heap::blocking_collection = FALSE;
2366
2367 heap_segment* gc_heap::freeable_large_heap_segment = 0;
2368
2369 size_t      gc_heap::time_bgc_last = 0;
2370
2371 size_t      gc_heap::mark_stack_tos = 0;
2372
2373 size_t      gc_heap::mark_stack_bos = 0;
2374
2375 size_t      gc_heap::mark_stack_array_length = 0;
2376
2377 mark*       gc_heap::mark_stack_array = 0;
2378
2379 BOOL        gc_heap::verify_pinned_queue_p = FALSE;
2380
2381 BYTE*       gc_heap::oldest_pinned_plug = 0;
2382
2383 #ifdef FEATURE_LOH_COMPACTION
2384 size_t      gc_heap::loh_pinned_queue_tos = 0;
2385
2386 size_t      gc_heap::loh_pinned_queue_bos = 0;
2387
2388 size_t      gc_heap::loh_pinned_queue_length = 0;
2389
2390 mark*       gc_heap::loh_pinned_queue = 0;
2391
2392 BOOL        gc_heap::loh_compacted_p = FALSE;
2393 #endif //FEATURE_LOH_COMPACTION
2394
2395 #ifdef BACKGROUND_GC
2396
2397 DWORD       gc_heap::bgc_thread_id = 0;
2398
2399 BYTE*       gc_heap::background_written_addresses [array_size+2];
2400
2401 heap_segment* gc_heap::freeable_small_heap_segment = 0;
2402
2403 size_t      gc_heap::bgc_overflow_count = 0;
2404
2405 size_t      gc_heap::bgc_begin_loh_size = 0;
2406 size_t      gc_heap::end_loh_size = 0;
2407
2408 DWORD       gc_heap::bgc_alloc_spin_loh = 0;
2409
2410 size_t      gc_heap::bgc_loh_size_increased = 0;
2411
2412 size_t      gc_heap::bgc_loh_allocated_in_free = 0;
2413
2414 size_t      gc_heap::background_soh_alloc_count = 0;
2415
2416 size_t      gc_heap::background_loh_alloc_count = 0;
2417
2418 BYTE**      gc_heap::background_mark_stack_tos = 0;
2419
2420 BYTE**      gc_heap::background_mark_stack_array = 0;
2421
2422 size_t      gc_heap::background_mark_stack_array_length = 0;
2423
2424 BYTE*       gc_heap::background_min_overflow_address =0;
2425
2426 BYTE*       gc_heap::background_max_overflow_address =0;
2427
2428 BOOL        gc_heap::processed_soh_overflow_p = FALSE;
2429
2430 BYTE*       gc_heap::background_min_soh_overflow_address =0;
2431
2432 BYTE*       gc_heap::background_max_soh_overflow_address =0;
2433
2434 SPTR_IMPL_NS_INIT(heap_segment, WKS, gc_heap, saved_sweep_ephemeral_seg, 0);
2435 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, saved_sweep_ephemeral_start, 0);
2436
2437 heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0;
2438
2439 Thread*     gc_heap::bgc_thread = 0;
2440
2441 BOOL        gc_heap::expanded_in_fgc = FALSE;
2442
2443 BYTE**      gc_heap::c_mark_list = 0;
2444
2445 size_t      gc_heap::c_mark_list_length = 0;
2446
2447 size_t      gc_heap::c_mark_list_index = 0;
2448
2449 gc_history_per_heap gc_heap::saved_bgc_data_per_heap;
2450
2451 BOOL        gc_heap::bgc_data_saved_p = FALSE;
2452
2453 BOOL    gc_heap::bgc_thread_running;
2454
2455 CLREvent gc_heap::background_gc_create_event;
2456
2457 CRITICAL_SECTION gc_heap::bgc_threads_timeout_cs;
2458
2459 CLREvent gc_heap::gc_lh_block_event;
2460
2461 #endif //BACKGROUND_GC
2462
2463 #ifdef MARK_LIST
2464 BYTE**      gc_heap::mark_list;
2465 BYTE**      gc_heap::mark_list_index;
2466 BYTE**      gc_heap::mark_list_end;
2467 #endif //MARK_LIST
2468
2469 #ifdef SNOOP_STATS
2470 snoop_stats_data gc_heap::snoop_stat;
2471 #endif //SNOOP_STATS
2472
2473 BYTE*       gc_heap::min_overflow_address = MAX_PTR;
2474
2475 BYTE*       gc_heap::max_overflow_address = 0;
2476
2477 BYTE*       gc_heap::shigh = 0;
2478
2479 BYTE*       gc_heap::slow = MAX_PTR;
2480
2481 size_t      gc_heap::ordered_free_space_indices[MAX_NUM_BUCKETS];
2482
2483 size_t      gc_heap::saved_ordered_free_space_indices[MAX_NUM_BUCKETS];
2484
2485 size_t      gc_heap::ordered_plug_indices[MAX_NUM_BUCKETS];
2486
2487 size_t      gc_heap::saved_ordered_plug_indices[MAX_NUM_BUCKETS];
2488
2489 BOOL        gc_heap::ordered_plug_indices_init = FALSE;
2490
2491 BOOL        gc_heap::use_bestfit = FALSE;
2492
2493 BYTE*       gc_heap::bestfit_first_pin = 0;
2494
2495 BOOL        gc_heap::commit_end_of_seg = FALSE;
2496
2497 size_t      gc_heap::max_free_space_items = 0;
2498
2499 size_t      gc_heap::free_space_buckets = 0;
2500
2501 size_t      gc_heap::free_space_items = 0;
2502
2503 int         gc_heap::trimmed_free_space_index = 0;
2504
2505 size_t      gc_heap::total_ephemeral_plugs = 0;
2506
2507 seg_free_spaces* gc_heap::bestfit_seg = 0;
2508
2509 size_t      gc_heap::total_ephemeral_size = 0;
2510
2511 #ifdef HEAP_ANALYZE
2512
2513 size_t      gc_heap::internal_root_array_length = initial_internal_roots;
2514
2515 SPTR_IMPL_NS_INIT(PTR_BYTE, WKS, gc_heap, internal_root_array, 0);
2516 SVAL_IMPL_NS_INIT(size_t, WKS, gc_heap, internal_root_array_index, 0);
2517 SVAL_IMPL_NS_INIT(BOOL, WKS, gc_heap, heap_analyze_success, TRUE);
2518
2519 BYTE*       gc_heap::current_obj = 0;
2520 size_t      gc_heap::current_obj_size = 0;
2521
2522 #endif //HEAP_ANALYZE
2523
2524 #endif //MULTIPLE_HEAPS
2525
2526 GCSpinLock gc_heap::gc_lock;
2527
2528 size_t gc_heap::eph_gen_starts_size = 0;
2529 heap_segment* gc_heap::segment_standby_list;
2530 size_t        gc_heap::last_gc_index = 0;
2531 size_t        gc_heap::min_segment_size = 0;
2532
2533 #ifdef FEATURE_LOH_COMPACTION
2534 BOOL                   gc_heap::loh_compaction_always_p = FALSE;
2535 gc_loh_compaction_mode gc_heap::loh_compaction_mode = loh_compaction_default;
2536 int                    gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY;
2537
2538 #endif //FEATURE_LOH_COMPACTION
2539
2540 CLREvent gc_heap::full_gc_approach_event;
2541
2542 CLREvent gc_heap::full_gc_end_event;
2543
2544 DWORD gc_heap::fgn_maxgen_percent = 0;
2545
2546 DWORD gc_heap::fgn_loh_percent = 0;
2547
2548 #ifdef BACKGROUND_GC
2549 BOOL gc_heap::fgn_last_gc_was_concurrent = FALSE;
2550 #endif //BACKGROUND_GC
2551
2552 VOLATILE(bool) gc_heap::full_gc_approach_event_set;
2553
2554 size_t gc_heap::full_gc_counts[gc_type_max];
2555
2556 BOOL gc_heap::should_expand_in_full_gc = FALSE;
2557
2558 #ifdef HEAP_ANALYZE
2559 BOOL        gc_heap::heap_analyze_enabled = FALSE;
2560 #endif //HEAP_ANALYZE
2561
2562 #ifndef MULTIPLE_HEAPS
2563
2564 #ifndef DACCESS_COMPILE
2565 extern "C" {
2566 #endif //!DACCESS_COMPILE
2567 GARY_IMPL(generation, generation_table,NUMBERGENERATIONS+1);
2568 #ifndef DACCESS_COMPILE
2569 }
2570 #endif //!DACCESS_COMPILE
2571
2572 #endif //MULTIPLE_HEAPS
2573
2574 #ifndef MULTIPLE_HEAPS
2575
2576 alloc_list gc_heap::loh_alloc_list [NUM_LOH_ALIST-1];
2577 alloc_list gc_heap::gen2_alloc_list[NUM_GEN2_ALIST-1];
2578
2579 dynamic_data gc_heap::dynamic_data_table [NUMBERGENERATIONS+1];
2580 gc_history_per_heap gc_heap::gc_data_per_heap;
2581
2582 SPTR_IMPL_NS_INIT(BYTE, WKS, gc_heap, alloc_allocated, 0);
2583
2584 size_t gc_heap::allocation_quantum = CLR_SIZE;
2585
2586 GCSpinLock gc_heap::more_space_lock;
2587
2588 #ifdef SYNCHRONIZATION_STATS
2589 unsigned int gc_heap::good_suspension = 0;
2590 unsigned int gc_heap::bad_suspension = 0;
2591 ULONGLONG     gc_heap::total_msl_acquire = 0;
2592 unsigned int gc_heap::num_msl_acquired = 0;
2593 unsigned int gc_heap::num_high_msl_acquire = 0;
2594 unsigned int gc_heap::num_low_msl_acquire = 0;
2595 #endif //SYNCHRONIZATION_STATS
2596
2597 size_t   gc_heap::alloc_contexts_used = 0;
2598
2599 #endif //MULTIPLE_HEAPS
2600
2601 #ifndef MULTIPLE_HEAPS
2602
2603 BOOL        gc_heap::gen0_bricks_cleared = FALSE;
2604
2605 #ifdef FFIND_OBJECT
2606 int         gc_heap::gen0_must_clear_bricks = 0;
2607 #endif //FFIND_OBJECT
2608
2609 #ifdef FEATURE_PREMORTEM_FINALIZATION
2610 SPTR_IMPL_NS_INIT(CFinalize, WKS, gc_heap, finalize_queue, 0);
2611 #endif // FEATURE_PREMORTEM_FINALIZATION
2612
2613 #endif // MULTIPLE_HEAPS
2614
2615 /* end of per heap static initialization */
2616
2617 /* end of static initialization */
2618
2619 #ifndef DACCESS_COMPILE
2620
2621 void gen_to_condemn_tuning::print (int heap_num)
2622 {
2623 #ifdef DT_LOG
2624     dprintf (DT_LOG_0, ("condemned reasons"));
2625     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_gen_header));
2626     gc_condemn_reason_gen r_gen;
2627     for (int i = 0; i < gcrg_max; i++)
2628     {
2629         r_gen = (gc_condemn_reason_gen)(i);
2630         str_reasons_gen[i * 2] = get_gen_char (get_gen (r_gen));
2631     }
2632     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_gen));
2633
2634     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_condition_header));
2635     gc_condemn_reason_condition r_condition;
2636     for (int i = 0; i < gcrc_max; i++)
2637     {
2638         r_condition = (gc_condemn_reason_condition)(i);
2639         str_reasons_condition[i * 2] = get_condition_char (get_condition (r_condition));
2640     }
2641
2642     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_condition));
2643 #endif //DT_LOG
2644 }
2645
2646 void gc_generation_data::print (int heap_num, int gen_num)
2647 {
2648 #ifdef SIMPLE_DPRINTF
2649 #ifdef DT_LOG
2650     dprintf (DT_LOG_0, ("[%2d]gen%d beg %Id fl %Id fo %Id end %Id fl %Id fo %Id in %Id out %Id surv %Id alloc %Id",
2651                 heap_num, gen_num, 
2652                 size_before, 
2653                 free_list_space_before, free_obj_space_before,
2654                 size_after, 
2655                 free_list_space_after, free_obj_space_after, 
2656                 in, out,
2657                 surv,
2658                 new_allocation));
2659 #endif //DT_LOG
2660 #endif //SIMPLE_DPRINTF
2661 }
2662
2663 void gc_history_per_heap::print (int heap_num)
2664 {
2665 #ifdef DT_LOG
2666     for (int i = 0; i < (sizeof (gen_data)/sizeof (gc_generation_data)); i++)
2667     {
2668         gen_data[i].print (heap_num, i);
2669     }
2670     dprintf (DT_LOG_0, ("[%2d]mp %d", heap_num, mem_pressure));
2671
2672     int mechanism = 0;
2673     gc_mechanism_descr* descr = 0;
2674
2675     for (int i = 0; i < max_mechanism_per_heap; i++)
2676     {
2677         mechanism = get_mechanism ((gc_mechanism_per_heap)i);
2678
2679         if (mechanism >= 0)
2680         {
2681             descr = &gc_mechanisms_descr[(gc_mechanism_per_heap)i];
2682             dprintf (DT_LOG_0, ("[%2d]%s%s", 
2683                         heap_num,
2684                         descr->name, 
2685                         (descr->descr)[mechanism]));
2686         }
2687     }
2688 #endif //DT_LOG
2689 }
2690
2691 void gc_history_global::print()
2692 {
2693 #ifdef DT_LOG
2694     char str_settings[64];
2695     memset (str_settings, '|', sizeof (char) * 64);
2696     str_settings[max_global_mechanism*2] = 0;
2697
2698     for (int i = 0; i < max_global_mechanism; i++)
2699     {
2700         str_settings[i * 2] = (get_mechanism_p ((gc_global_mechanism_p)i) ? 'Y' : 'N');
2701     }
2702
2703     dprintf (DT_LOG_0, ("[hp]|c|p|o|d|b|"));
2704     dprintf (DT_LOG_0, ("%4d|%s", num_heaps, str_settings));
2705     dprintf (DT_LOG_0, ("Condemned gen%d(%s), youngest budget %Id(%d)",
2706                         condemned_generation,
2707                         str_gc_reasons[reason],
2708                         final_youngest_desired,
2709                         gen0_reduction_count));
2710 #endif //DT_LOG
2711 }
2712
2713 void gc_heap::fire_pevents()
2714 {
2715 #ifndef CORECLR
2716     settings.record (&gc_data_global);
2717     gc_data_global.print();
2718
2719     FireEtwGCGlobalHeapHistory_V1(gc_data_global.final_youngest_desired, 
2720                                   gc_data_global.num_heaps, 
2721                                   gc_data_global.condemned_generation, 
2722                                   gc_data_global.gen0_reduction_count, 
2723                                   gc_data_global.reason, 
2724                                   gc_data_global.global_mechanims_p, 
2725                                   GetClrInstanceId());
2726
2727 #ifdef MULTIPLE_HEAPS
2728     for (int i = 0; i < gc_heap::n_heaps; i++)
2729     {
2730         gc_heap* hp = gc_heap::g_heaps[i];
2731         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
2732         current_gc_data_per_heap->print (i);
2733         current_gc_data_per_heap->gen_to_condemn_reasons.print (i);
2734         FireEtwGCPerHeapHistorySpecial(*current_gc_data_per_heap, sizeof(hp->gc_data_per_heap), (UINT8)GetClrInstanceId());        
2735     }
2736 #else
2737     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
2738     FireEtwGCPerHeapHistorySpecial(*current_gc_data_per_heap, sizeof(gc_data_per_heap), (UINT8)GetClrInstanceId());        
2739     current_gc_data_per_heap->print (0);
2740     current_gc_data_per_heap->gen_to_condemn_reasons.print (heap_number);
2741 #endif    
2742 #endif //!CORECLR
2743 }
2744
2745 inline BOOL
2746 gc_heap::dt_low_ephemeral_space_p (gc_tuning_point tp)
2747 {
2748     BOOL ret = FALSE;
2749
2750     switch (tp)
2751     {
2752         case tuning_deciding_condemned_gen:
2753         case tuning_deciding_compaction:
2754         case tuning_deciding_expansion:
2755         case tuning_deciding_full_gc:
2756         {
2757             ret = (!ephemeral_gen_fit_p (tp));
2758             break;
2759         }
2760         case tuning_deciding_promote_ephemeral:
2761         {
2762             size_t new_gen0size = approximate_new_allocation();
2763             ptrdiff_t plan_ephemeral_size = total_ephemeral_size;
2764             
2765             dprintf (GTC_LOG, ("h%d: plan eph size is %Id, new gen0 is %Id", 
2766                 heap_number, plan_ephemeral_size, new_gen0size));
2767             ret = ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - (heap_segment_mem (ephemeral_heap_segment))) <
2768                     (plan_ephemeral_size + new_gen0size));
2769             break;
2770         }
2771         default:
2772             break;
2773     }
2774
2775     return ret;
2776 }
2777
2778 BOOL 
2779 gc_heap::dt_high_frag_p (gc_tuning_point tp, 
2780                          int gen_number, 
2781                          BOOL elevate_p)
2782 {
2783     BOOL ret = FALSE;
2784
2785     switch (tp)
2786     {
2787         case tuning_deciding_condemned_gen:
2788         {
2789             dynamic_data* dd = dynamic_data_of (gen_number);
2790             float fragmentation_burden = 0;
2791
2792             if (elevate_p)
2793             {
2794                 ret = (dd_fragmentation (dynamic_data_of (max_generation)) >= dd_max_size(dd));
2795                 dprintf (GTC_LOG, ("h%d: frag is %Id, max size is %Id",
2796                     heap_number, dd_fragmentation (dd), dd_max_size(dd)));
2797             }
2798             else
2799             {
2800 #ifndef MULTIPLE_HEAPS
2801                 if (gen_number == max_generation)
2802                 {
2803                     float frag_ratio = (float)(dd_fragmentation (dynamic_data_of (max_generation))) / (float)generation_size (max_generation);
2804                     if (frag_ratio > 0.65)
2805                     {
2806                         dprintf (GTC_LOG, ("g2 FR: %d%%", (int)(frag_ratio*100)));
2807                         return TRUE;
2808                     }
2809                 }
2810 #endif //!MULTIPLE_HEAPS
2811                 size_t fr = generation_unusable_fragmentation (generation_of (gen_number));
2812                 ret = (fr > dd_fragmentation_limit(dd));
2813                 if (ret)
2814                 {
2815                     fragmentation_burden = (float)fr / generation_size (gen_number);
2816                     ret = (fragmentation_burden > dd_v_fragmentation_burden_limit (dd));
2817                 }
2818                 dprintf (GTC_LOG, ("h%d: gen%d, frag is %Id, alloc effi: %d%%, unusable frag is %Id, ratio is %d",
2819                     heap_number, gen_number, dd_fragmentation (dd), 
2820                     (int)(100*generation_allocator_efficiency (generation_of (gen_number))),
2821                     fr, (int)(fragmentation_burden*100)));
2822             }
2823             break;
2824         }
2825         default:
2826             break;
2827     }
2828
2829     return ret;
2830 }
2831
2832 inline BOOL 
2833 gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number, ULONGLONG total_mem)
2834 {
2835     BOOL ret = FALSE;
2836
2837     switch (tp)
2838     {
2839         case tuning_deciding_condemned_gen:
2840         {
2841             if (gen_number == max_generation)
2842             {
2843                 dynamic_data* dd = dynamic_data_of (gen_number);
2844                 size_t maxgen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd));
2845                 size_t maxgen_total_size = maxgen_allocated + dd_current_size (dd);
2846                 size_t est_maxgen_surv = (size_t)((float) (maxgen_total_size) * dd_surv (dd));
2847                 size_t est_maxgen_free = maxgen_total_size - est_maxgen_surv + dd_fragmentation (dd);
2848
2849 #ifdef SIMPLE_DPRINTF
2850                 dprintf (GTC_LOG, ("h%d: Total gen2 size: %Id(s: %d%%), est gen2 dead space: %Id (s: %d, allocated: %Id), frag: %Id, 3%% of physical mem is %Id bytes",
2851                             heap_number,
2852                             maxgen_total_size,
2853                             (int)(100*dd_surv (dd)),
2854                             est_maxgen_free, 
2855                             (int)(dd_surv (dd) * 100),
2856                             maxgen_allocated,
2857                             dd_fragmentation (dd),
2858                             (size_t)((float)total_mem * 0.03)));
2859 #endif //SIMPLE_DPRINTF
2860                 DWORD num_heaps = 1;
2861
2862 #ifdef MULTIPLE_HEAPS
2863                 num_heaps = gc_heap::n_heaps;
2864 #endif //MULTIPLE_HEAPS
2865
2866                 size_t min_frag_th = min_reclaim_fragmentation_threshold(total_mem, num_heaps);
2867                 dprintf (GTC_LOG, ("h%d, min frag is %Id", heap_number, min_frag_th));
2868                 ret = (est_maxgen_free >= min_frag_th);
2869             }
2870             else
2871             {
2872                 assert (0);
2873             }
2874             break;
2875         }
2876
2877         default:
2878             break;
2879     }
2880
2881     return ret;
2882 }
2883
2884 // DTREVIEW: Right now we only estimate gen2 fragmentation. 
2885 // on 64-bit though we should consider gen1 or even gen0 fragmentatioin as
2886 // well 
2887 inline BOOL 
2888 gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, ULONGLONG available_mem)
2889 {
2890     BOOL ret = FALSE;
2891
2892     switch (tp)
2893     {
2894         case tuning_deciding_condemned_gen:
2895         {
2896             if (gen_number == max_generation)
2897             {
2898                 dynamic_data* dd = dynamic_data_of (gen_number);
2899                 float est_frag_ratio = 0;
2900                 if (dd_current_size (dd) == 0)
2901                 {
2902                     est_frag_ratio = 1;
2903                 }
2904                 else if ((dd_fragmentation (dd) == 0) || (dd_fragmentation (dd) + dd_current_size (dd) == 0))
2905                 {
2906                     est_frag_ratio = 0;
2907                 }
2908                 else
2909                 {
2910                     est_frag_ratio = (float)dd_fragmentation (dd) / (float)(dd_fragmentation (dd) + dd_current_size (dd));
2911                 }
2912                 
2913                 size_t est_frag = (dd_fragmentation (dd) + (size_t)((dd_desired_allocation (dd) - dd_new_allocation (dd)) * est_frag_ratio));
2914                 dprintf (GTC_LOG, ("h%d: gen%d: current_size is %Id, frag is %Id, est_frag_ratio is %d%%, estimated frag is %Id", 
2915                     heap_number,
2916                     gen_number,
2917                     dd_current_size (dd),
2918                     dd_fragmentation (dd),
2919                     (int)(est_frag_ratio*100),
2920                     est_frag));
2921
2922                 DWORD num_heaps = 1;
2923
2924 #ifdef MULTIPLE_HEAPS
2925                 num_heaps = gc_heap::n_heaps;
2926 #endif //MULTIPLE_HEAPS
2927                 ULONGLONG min_frag_th = min_high_fragmentation_threshold(available_mem, num_heaps);
2928                 //dprintf (GTC_LOG, ("h%d, min frag is %I64d", heap_number, min_frag_th));
2929                 ret = (est_frag >= min_frag_th);
2930             }
2931             else
2932             {
2933                 assert (0);
2934             }
2935             break;
2936         }
2937
2938         default:
2939             break;
2940     }
2941
2942     return ret;
2943 }
2944
2945 inline BOOL 
2946 gc_heap::dt_low_card_table_efficiency_p (gc_tuning_point tp)
2947 {
2948     BOOL ret = FALSE;
2949
2950     switch (tp)
2951     {
2952     case tuning_deciding_condemned_gen:
2953     {
2954         /* promote into max-generation if the card table has too many
2955         * generation faults besides the n -> 0
2956         */
2957         ret = (generation_skip_ratio < 30);
2958         break;
2959     }
2960
2961     default:
2962         break;
2963     }
2964
2965     return ret;
2966 }
2967
2968 inline BOOL
2969 in_range_for_segment(BYTE* add, heap_segment* seg)
2970 {
2971     return ((add >= heap_segment_mem (seg)) && (add < heap_segment_reserved (seg)));
2972 }
2973
2974 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2975 // The array we allocate is organized as follows:
2976 // 0th element is the address of the last array we allocated.
2977 // starting from the 1st element are the segment addresses, that's
2978 // what buckets() returns.
2979 struct bk
2980 {
2981     BYTE* add;
2982 };
2983
2984 class sorted_table
2985 {
2986 private:
2987     ptrdiff_t size;
2988     ptrdiff_t count;
2989     bk* slots;
2990     bk* buckets() { return (slots + 1); }
2991     BYTE*& last_slot (bk* arr) { return arr[0].add; }
2992     bk* old_slots;
2993 public:
2994     static  sorted_table* make_sorted_table ();
2995     BOOL    insert (BYTE* add, size_t val);;
2996     size_t  lookup (BYTE*& add);
2997     void    remove (BYTE* add);
2998     void    clear ();
2999     void    delete_sorted_table();
3000     void    delete_old_slots();
3001     void    enqueue_old_slot(bk* sl);
3002     BOOL    insure_space_for_insert();
3003 };
3004
3005 sorted_table*
3006 sorted_table::make_sorted_table ()
3007 {
3008     size_t size = 400;
3009
3010     // allocate one more bk to store the older slot address.
3011     sorted_table* res = (sorted_table*)new char [sizeof (sorted_table) + (size + 1) * sizeof (bk)];
3012     if (!res)
3013         return 0;
3014     res->size = size;
3015     res->slots = (bk*)(res + 1);
3016     res->old_slots = 0;
3017     res->clear();
3018     return res;
3019 }
3020
3021 void
3022 sorted_table::delete_sorted_table()
3023 {
3024     if (slots != (bk*)(this+1))
3025     {
3026         delete slots;
3027     }
3028     delete_old_slots();
3029     delete this;
3030 }
3031 void
3032 sorted_table::delete_old_slots()
3033 {
3034     BYTE* sl = (BYTE*)old_slots;
3035     while (sl)
3036     {
3037         BYTE* dsl = sl;
3038         sl = last_slot ((bk*)sl);
3039         delete dsl;
3040     }
3041     old_slots = 0;
3042 }
3043 void
3044 sorted_table::enqueue_old_slot(bk* sl)
3045 {
3046     last_slot (sl) = (BYTE*)old_slots;
3047     old_slots = sl;
3048 }
3049
3050 inline
3051 size_t
3052 sorted_table::lookup (BYTE*& add)
3053 {
3054     ptrdiff_t high = (count-1);
3055     ptrdiff_t low = 0;
3056     ptrdiff_t ti;
3057     ptrdiff_t mid;
3058     bk* buck = buckets();
3059     while (low <= high)
3060     {
3061         mid = ((low + high)/2);
3062         ti = mid;
3063         if (buck[ti].add > add)
3064         {
3065             if ((ti > 0) && (buck[ti-1].add <= add))
3066             {
3067                 add = buck[ti-1].add;
3068                 return 0;
3069             }
3070             high = mid - 1;
3071         }
3072         else
3073         {
3074             if (buck[ti+1].add > add)
3075             {
3076                 add = buck[ti].add;
3077                 return 0;
3078             }
3079             low = mid + 1;
3080         }
3081     }
3082     add = 0;
3083     return 0;
3084 }
3085
3086 BOOL
3087 sorted_table::insure_space_for_insert()
3088 {
3089     if (count == size)
3090     {
3091         size = (size * 3)/2;
3092         assert((size * sizeof (bk)) > 0);
3093         bk* res = (bk*)new (nothrow) char [(size + 1) * sizeof (bk)];
3094         assert (res);
3095         if (!res)
3096             return FALSE;
3097
3098         last_slot (res) = 0;
3099         memcpy (((bk*)res + 1), buckets(), count * sizeof (bk));
3100         bk* last_old_slots = slots;
3101         slots = res;
3102         if (last_old_slots != (bk*)(this + 1))
3103             enqueue_old_slot (last_old_slots);
3104     }
3105     return TRUE;
3106 }
3107
3108 BOOL
3109 sorted_table::insert (BYTE* add, size_t val)
3110 {
3111     //val is ignored for non concurrent gc
3112     val = val;
3113     //grow if no more room
3114     assert (count < size);
3115
3116     //insert sorted
3117     ptrdiff_t high = (count-1);
3118     ptrdiff_t low = 0;
3119     ptrdiff_t ti;
3120     ptrdiff_t mid;
3121     bk* buck = buckets();
3122     while (low <= high)
3123     {
3124         mid = ((low + high)/2);
3125         ti = mid;
3126         if (buck[ti].add > add)
3127         {
3128             if ((ti == 0) || (buck[ti-1].add <= add))
3129             {
3130                 // found insertion point
3131                 for (ptrdiff_t k = count; k > ti;k--)
3132                 {
3133                     buck [k] = buck [k-1];
3134                 }
3135                 buck[ti].add = add;
3136                 count++;
3137                 return TRUE;
3138             }
3139             high = mid - 1;
3140         }
3141         else
3142         {
3143             if (buck[ti+1].add > add)
3144             {
3145                 //found the insertion point
3146                 for (ptrdiff_t k = count; k > ti+1;k--)
3147                 {
3148                     buck [k] = buck [k-1];
3149                 }
3150                 buck[ti+1].add = add;
3151                 count++;
3152                 return TRUE;
3153             }
3154             low = mid + 1;
3155         }
3156     }
3157     assert (0);
3158     return TRUE;
3159
3160 }
3161
3162 void
3163 sorted_table::remove (BYTE* add)
3164 {
3165     ptrdiff_t high = (count-1);
3166     ptrdiff_t low = 0;
3167     ptrdiff_t ti;
3168     ptrdiff_t mid;
3169     bk* buck = buckets();
3170     while (low <= high)
3171     {
3172         mid = ((low + high)/2);
3173         ti = mid;
3174         if (buck[ti].add > add)
3175         {
3176             if (buck[ti-1].add <= add)
3177             {
3178                 // found the guy to remove
3179                 for (ptrdiff_t k = ti; k < count; k++)
3180                     buck[k-1] = buck[k];
3181                 count--;
3182                 return;
3183             }
3184             high = mid - 1;
3185         }
3186         else
3187         {
3188             if (buck[ti+1].add > add)
3189             {
3190                 // found the guy to remove
3191                 for (ptrdiff_t k = ti+1; k < count; k++)
3192                     buck[k-1] = buck[k];
3193                 count--;
3194                 return;
3195             }
3196             low = mid + 1;
3197         }
3198     }
3199     assert (0);
3200 }
3201
3202 void
3203 sorted_table::clear()
3204 {
3205     count = 1;
3206     buckets()[0].add = MAX_PTR;
3207 }
3208 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
3209
3210 #ifdef SEG_MAPPING_TABLE
3211 #ifdef GROWABLE_SEG_MAPPING_TABLE
3212 inline
3213 BYTE* align_on_segment (BYTE* add)
3214 {
3215     return (BYTE*)((size_t)(add + (gc_heap::min_segment_size - 1)) & ~(gc_heap::min_segment_size - 1));
3216 }
3217
3218 inline
3219 BYTE* align_lower_segment (BYTE* add)
3220 {
3221     return (BYTE*)((size_t)(add) & ~(gc_heap::min_segment_size - 1));
3222 }
3223
3224 size_t size_seg_mapping_table_of (BYTE* from, BYTE* end)
3225 {
3226     from = align_lower_segment (from);
3227     end = align_on_segment (end);
3228     dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((end - from) / gc_heap::min_segment_size))));
3229     return sizeof (seg_mapping)*((end - from) / gc_heap::min_segment_size);
3230 }
3231
3232 inline
3233 size_t seg_mapping_word_of (BYTE* add)
3234 {
3235     return (size_t)add / gc_heap::min_segment_size;
3236 }
3237 #else //GROWABLE_SEG_MAPPING_TABLE
3238 BOOL seg_mapping_table_init()
3239 {
3240 #ifdef _WIN64
3241     ULONGLONG total_address_space = (ULONGLONG)8*1024*1024*1024*1024;
3242 #else
3243     ULONGLONG total_address_space = (ULONGLONG)4*1024*1024*1024;
3244 #endif //_WIN64
3245
3246     size_t num_entries = (size_t)(total_address_space / gc_heap::min_segment_size);
3247     seg_mapping_table = new seg_mapping[num_entries];
3248
3249     if (seg_mapping_table)
3250     {
3251         memset (seg_mapping_table, 0, num_entries * sizeof (seg_mapping));
3252         dprintf (1, ("created %d entries for heap mapping (%Id bytes)", 
3253                      num_entries, (num_entries * sizeof (seg_mapping))));
3254         return TRUE;
3255     }
3256     else
3257     {
3258         dprintf (1, ("failed to create %d entries for heap mapping (%Id bytes)", 
3259                      num_entries, (num_entries * sizeof (seg_mapping))));
3260         return FALSE;
3261     }
3262 }
3263 #endif //GROWABLE_SEG_MAPPING_TABLE
3264
3265 #ifdef FEATURE_BASICFREEZE
3266 inline
3267 size_t ro_seg_begin_index (heap_segment* seg)
3268 {
3269     size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3270     begin_index = max (begin_index, (size_t)g_lowest_address / gc_heap::min_segment_size);
3271     return begin_index;
3272 }
3273
3274 inline
3275 size_t ro_seg_end_index (heap_segment* seg)
3276 {
3277     size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) / gc_heap::min_segment_size;
3278     end_index = min (end_index, (size_t)g_highest_address / gc_heap::min_segment_size);
3279     return end_index;
3280 }
3281
3282 void seg_mapping_table_add_ro_segment (heap_segment* seg)
3283 {
3284 #ifdef GROWABLE_SEG_MAPPING_TABLE
3285     if ((heap_segment_reserved (seg) <= g_lowest_address) || (heap_segment_mem (seg) >= g_highest_address))
3286         return;
3287 #endif //GROWABLE_SEG_MAPPING_TABLE
3288
3289     for (size_t entry_index = ro_seg_begin_index (seg); entry_index <= ro_seg_end_index (seg); entry_index++)
3290         seg_mapping_table[entry_index].seg1 = (heap_segment*)((size_t)seg_mapping_table[entry_index].seg1 | ro_in_entry);
3291 }
3292
3293 void seg_mapping_table_remove_ro_segment (heap_segment* seg)
3294 {
3295 #if 0
3296 // POSSIBLE PERF TODO: right now we are not doing anything because we can't simply remove the flag. If it proves
3297 // to be a perf problem, we can search in the current ro segs and see if any lands in this range and only
3298 // remove the flag if none lands in this range.
3299 #endif //0
3300 }
3301
3302 heap_segment* ro_segment_lookup (BYTE* o)
3303 {
3304     BYTE* ro_seg = 0;
3305     gc_heap::seg_table->lookup (ro_seg);
3306
3307     if (ro_seg && in_range_for_segment (o, (heap_segment*)ro_seg))
3308         return (heap_segment*)ro_seg;
3309     else
3310         return 0;
3311 }
3312
3313 #endif //FEATURE_BASICFREEZE
3314
3315 void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
3316 {
3317     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3318     size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3319     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3320     size_t end_index = seg_end / gc_heap::min_segment_size;
3321     seg_mapping* end_entry = &seg_mapping_table[end_index];
3322     dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)", 
3323         seg, begin_index, heap_segment_reserved (seg), end_index));
3324
3325     dprintf (1, ("before add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3326         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3327         end_index, (seg_mapping_table[end_index].boundary + 1)));
3328
3329 #ifdef MULTIPLE_HEAPS
3330 #ifdef SIMPLE_DPRINTF
3331     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end %d: h0: %Ix(%d), h1: %Ix(%d)",
3332         begin_index, (BYTE*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3333         (BYTE*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3334         end_index, (BYTE*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3335         (BYTE*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3336 #endif //SIMPLE_DPRINTF
3337     assert (end_entry->boundary == 0);
3338     assert (end_entry->h0 == 0);
3339     end_entry->h0 = hp;
3340     assert (begin_entry->h1 == 0);
3341     begin_entry->h1 = hp;
3342 #endif //MULTIPLE_HEAPS
3343
3344     end_entry->boundary = (BYTE*)seg_end;
3345
3346     dprintf (1, ("set entry %d seg1 and %d seg0 to %Ix", begin_index, end_index, seg));
3347     assert ((begin_entry->seg1 == 0) || ((size_t)(begin_entry->seg1) == ro_in_entry));
3348     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) | (size_t)seg);
3349     end_entry->seg0 = seg;
3350
3351     // for every entry inbetween we need to set its heap too.
3352     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3353     {
3354         assert (seg_mapping_table[entry_index].boundary == 0);
3355 #ifdef MULTIPLE_HEAPS
3356         assert (seg_mapping_table[entry_index].h0 == 0);
3357         seg_mapping_table[entry_index].h1 = hp;
3358 #endif //MULTIPLE_HEAPS
3359         seg_mapping_table[entry_index].seg1 = seg;
3360     }
3361
3362     dprintf (1, ("after add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3363         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3364         end_index, (seg_mapping_table[end_index].boundary + 1)));
3365 #if defined(MULTIPLE_HEAPS) && defined(SIMPLE_DPRINTF)
3366     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end: %d h0: %Ix(%d), h1: %Ix(%d)",
3367         begin_index, (BYTE*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3368         (BYTE*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3369         end_index, (BYTE*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3370         (BYTE*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3371 #endif //MULTIPLE_HEAPS && SIMPLE_DPRINTF
3372 }
3373
3374 void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
3375 {
3376     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3377     size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3378     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3379     size_t end_index = seg_end / gc_heap::min_segment_size;
3380     seg_mapping* end_entry = &seg_mapping_table[end_index];
3381     dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)", 
3382         seg, begin_index, heap_segment_reserved (seg), end_index));
3383
3384     assert (end_entry->boundary == (BYTE*)seg_end);
3385     end_entry->boundary = 0;
3386
3387 #ifdef MULTIPLE_HEAPS
3388     gc_heap* hp = heap_segment_heap (seg);
3389     assert (end_entry->h0 == hp);
3390     end_entry->h0 = 0;
3391     assert (begin_entry->h1 == hp);
3392     begin_entry->h1 = 0;
3393 #endif //MULTIPLE_HEAPS
3394
3395     assert (begin_entry->seg1 != 0);
3396     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) & ro_in_entry);
3397     end_entry->seg0 = 0;
3398
3399     // for every entry inbetween we need to reset its heap too.
3400     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3401     {
3402         assert (seg_mapping_table[entry_index].boundary == 0);
3403 #ifdef MULTIPLE_HEAPS
3404         assert (seg_mapping_table[entry_index].h0 == 0);
3405         assert (seg_mapping_table[entry_index].h1 == hp);
3406         seg_mapping_table[entry_index].h1 = 0;
3407 #endif //MULTIPLE_HEAPS
3408         seg_mapping_table[entry_index].seg1 = 0;
3409     }
3410
3411     dprintf (1, ("after remove: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3412         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3413         end_index, (seg_mapping_table[end_index].boundary + 1)));
3414 #ifdef MULTIPLE_HEAPS
3415     dprintf (1, ("begin %d: h0: %Ix, h1: %Ix; end: %d h0: %Ix, h1: %Ix",
3416         begin_index, (BYTE*)(begin_entry->h0), (BYTE*)(begin_entry->h1), 
3417         end_index, (BYTE*)(end_entry->h0), (BYTE*)(end_entry->h1)));
3418 #endif //MULTIPLE_HEAPS
3419 }
3420
3421 #ifdef MULTIPLE_HEAPS
3422 inline
3423 gc_heap* seg_mapping_table_heap_of_worker (BYTE* o)
3424 {
3425     size_t index = (size_t)o / gc_heap::min_segment_size;
3426     seg_mapping* entry = &seg_mapping_table[index];
3427
3428     gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
3429
3430     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
3431         o, index, (entry->boundary + 1), 
3432         (BYTE*)(entry->h0), (BYTE*)(entry->seg0), 
3433         (BYTE*)(entry->h1), (BYTE*)(entry->seg1)));
3434
3435 #ifdef _DEBUG
3436     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3437 #ifdef FEATURE_BASICFREEZE
3438     if ((size_t)seg & ro_in_entry)
3439         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3440 #endif //FEATURE_BASICFREEZE
3441
3442     if (seg)
3443     {
3444         if (in_range_for_segment (o, seg))
3445         {
3446             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, seg, (BYTE*)heap_segment_allocated (seg)));
3447         }
3448         else
3449         {
3450             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg", 
3451                 seg, (BYTE*)heap_segment_allocated (seg), o));
3452         }
3453     }
3454     else
3455     {
3456         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3457     }
3458 #endif //_DEBUG
3459
3460     return hp;
3461 }
3462
3463 gc_heap* seg_mapping_table_heap_of (BYTE* o)
3464 {
3465 #ifdef GROWABLE_SEG_MAPPING_TABLE
3466     if ((o < g_lowest_address) || (o >= g_highest_address))
3467         return 0;
3468 #endif //GROWABLE_SEG_MAPPING_TABLE
3469
3470     return seg_mapping_table_heap_of_worker (o);
3471 }
3472
3473 gc_heap* seg_mapping_table_heap_of_gc (BYTE* o)
3474 {
3475 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3476     if ((o < g_lowest_address) || (o >= g_highest_address))
3477         return 0;
3478 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3479
3480     return seg_mapping_table_heap_of_worker (o);
3481 }
3482 #endif //MULTIPLE_HEAPS
3483
3484 // Only returns a valid seg if we can actually find o on the seg.
3485 heap_segment* seg_mapping_table_segment_of (BYTE* o)
3486 {
3487 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3488     if ((o < g_lowest_address) || (o >= g_highest_address))
3489 #ifdef FEATURE_BASICFREEZE
3490         return ro_segment_lookup (o);
3491 #else
3492         return 0;
3493 #endif //FEATURE_BASICFREEZE
3494 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3495
3496     size_t index = (size_t)o / gc_heap::min_segment_size;
3497     seg_mapping* entry = &seg_mapping_table[index];
3498
3499     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
3500         o, index, (entry->boundary + 1), 
3501         (BYTE*)(entry->seg0), (BYTE*)(entry->seg1)));
3502
3503     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3504 #ifdef FEATURE_BASICFREEZE
3505     if ((size_t)seg & ro_in_entry)
3506         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3507 #endif //FEATURE_BASICFREEZE
3508
3509     if (seg)
3510     {
3511         // Can't assert this when it's callled by everyone (it's true when it's called by mark cards).
3512         //assert (in_range_for_segment (o, seg));
3513         if (in_range_for_segment (o, seg))
3514         {
3515             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, (BYTE*)heap_segment_mem(seg), (BYTE*)heap_segment_reserved(seg)));
3516         }
3517         else
3518         {
3519             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg, setting it to 0", 
3520                 (BYTE*)heap_segment_mem(seg), (BYTE*)heap_segment_reserved(seg), o));
3521             seg = 0;
3522         }
3523     }
3524     else
3525     {
3526         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3527     }
3528
3529 #ifdef FEATURE_BASICFREEZE
3530     if (!seg && (size_t)(entry->seg1) & ro_in_entry)
3531     {
3532         seg = ro_segment_lookup (o);
3533         if (!in_range_for_segment (o, seg))
3534             seg = 0;
3535     }
3536 #endif //FEATURE_BASICFREEZE
3537
3538     return seg;
3539 }
3540 #endif //SEG_MAPPING_TABLE
3541
3542 size_t gcard_of ( BYTE*);
3543 void gset_card (size_t card);
3544
3545 #define memref(i) *(BYTE**)(i)
3546
3547 //GC Flags
3548 #define GC_MARKED       (size_t)0x1
3549 #define slot(i, j) ((BYTE**)(i))[j+1]
3550
3551 #define free_object_base_size (plug_skew + sizeof(ArrayBase))
3552
3553 class CObjectHeader : public Object
3554 {
3555 public:
3556
3557 #ifdef FEATURE_REDHAWK
3558     // The GC expects the following methods that are provided by the Object class in the CLR but not provided
3559     // by Redhawk's version of Object.
3560     DWORD GetNumComponents()
3561     {
3562         return ((ArrayBase *)this)->GetNumComponents();
3563     }
3564
3565     void Validate(BOOL bDeep=TRUE, BOOL bVerifyNextHeader = TRUE)
3566     {
3567         if (this == NULL)
3568             return;
3569
3570         BOOL fSmallObjectHeapPtr = FALSE, fLargeObjectHeapPtr = FALSE;
3571         fSmallObjectHeapPtr = GCHeap::GetGCHeap()->IsHeapPointer(this, TRUE);
3572         if (!fSmallObjectHeapPtr)
3573             fLargeObjectHeapPtr = GCHeap::GetGCHeap()->IsHeapPointer(this);
3574
3575         _ASSERTE(fSmallObjectHeapPtr || fLargeObjectHeapPtr);
3576         _ASSERTE(GetMethodTable()->GetBaseSize() >= 8);
3577
3578 #ifdef FEATURE_STRUCTALIGN
3579         _ASSERTE(IsStructAligned((BYTE *)this, GetMethodTable()->GetBaseAlignment()));
3580 #endif // FEATURE_STRUCTALIGN
3581
3582 #ifdef VERIFY_HEAP
3583         if (bDeep)
3584             GCHeap::GetGCHeap()->ValidateObjectMember(this);
3585 #endif
3586     }
3587
3588     void ValidatePromote(ScanContext *sc, DWORD flags)
3589     {
3590         Validate();
3591     }
3592
3593     void ValidateHeap(Object *from, BOOL bDeep)
3594     {
3595         Validate(bDeep, FALSE);
3596     }
3597
3598     ADIndex GetAppDomainIndex()
3599     {
3600         return (ADIndex)RH_DEFAULT_DOMAIN_ID;
3601     }
3602 #endif //FEATURE_REDHAWK
3603
3604     /////
3605     //
3606     // Header Status Information
3607     //
3608
3609     MethodTable    *GetMethodTable() const
3610     {
3611         return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED))));
3612     }
3613
3614     void SetMarked()
3615     {
3616         RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED));
3617     }
3618
3619     BOOL IsMarked() const
3620     {
3621         return !!(((size_t)RawGetMethodTable()) & GC_MARKED);
3622     }
3623
3624     void SetPinned()
3625     {
3626         assert (!(gc_heap::settings.concurrent));
3627         GetHeader()->SetGCBit();
3628     }
3629
3630     BOOL IsPinned() const
3631     {
3632         return !!((((CObjectHeader*)this)->GetHeader()->GetBits()) & BIT_SBLK_GC_RESERVE);
3633     }
3634
3635     void ClearMarked()
3636     {
3637         RawSetMethodTable( GetMethodTable() );
3638     }
3639
3640     CGCDesc *GetSlotMap ()
3641     {
3642         assert (GetMethodTable()->ContainsPointers());
3643         return CGCDesc::GetCGCDescFromMT(GetMethodTable());
3644     }
3645
3646     void SetFree(size_t size)
3647     {
3648         assert (size >= free_object_base_size);
3649
3650         assert (g_pFreeObjectMethodTable->GetBaseSize() == free_object_base_size);
3651         assert (g_pFreeObjectMethodTable->RawGetComponentSize() == 1);
3652
3653         RawSetMethodTable( g_pFreeObjectMethodTable );
3654
3655         SIZE_T* numComponentsPtr = (SIZE_T*) &((BYTE*) this)[ArrayBase::GetOffsetOfNumComponents()];
3656         *numComponentsPtr = size - free_object_base_size;
3657 #ifdef VERIFY_HEAP
3658         //This introduces a bug in the free list management. 
3659         //((void**) this)[-1] = 0;    // clear the sync block,
3660         assert (*numComponentsPtr >= 0);
3661         if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
3662             memset (((BYTE*)this)+sizeof(ArrayBase), 0xcc, *numComponentsPtr);
3663 #endif //VERIFY_HEAP
3664     }
3665
3666     void UnsetFree()
3667     {
3668         size_t size = free_object_base_size - plug_skew;
3669
3670         // since we only need to clear 2 ptr size, we do it manually
3671         PTR_PTR m = (PTR_PTR) this;
3672         for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
3673             *(m++) = 0;
3674     }
3675
3676     BOOL IsFree () const
3677     {
3678         return (GetMethodTable() == g_pFreeObjectMethodTable);
3679     }
3680
3681 #ifdef FEATURE_STRUCTALIGN
3682     int GetRequiredAlignment () const
3683     {
3684         return GetMethodTable()->GetRequiredAlignment();
3685     }
3686 #endif // FEATURE_STRUCTALIGN
3687
3688     BOOL ContainsPointers() const
3689     {
3690         return GetMethodTable()->ContainsPointers();
3691     }
3692
3693 #ifdef COLLECTIBLE_CLASS
3694     BOOL Collectible() const
3695     {
3696         return GetMethodTable()->Collectible();
3697     }
3698
3699     FORCEINLINE BOOL ContainsPointersOrCollectible() const
3700     {
3701         MethodTable *pMethodTable = GetMethodTable();
3702         return (pMethodTable->ContainsPointers() || pMethodTable->Collectible());
3703     }
3704 #endif //COLLECTIBLE_CLASS
3705
3706     Object* GetObjectBase() const
3707     {
3708         return (Object*) this;
3709     }
3710 };
3711
3712 #define header(i) ((CObjectHeader*)(i))
3713
3714 #define free_list_slot(x) ((BYTE**)(x))[2]
3715 #define free_list_undo(x) ((BYTE**)(x))[-1]
3716 #define UNDO_EMPTY ((BYTE*)1)
3717
3718 #ifdef SHORT_PLUGS
3719 inline 
3720 void set_plug_padded (BYTE* node)
3721 {
3722     header(node)->SetMarked();
3723 }
3724 inline
3725 void clear_plug_padded (BYTE* node)
3726 {
3727     header(node)->ClearMarked();
3728 }
3729 inline
3730 BOOL is_plug_padded (BYTE* node)
3731 {
3732     return header(node)->IsMarked();
3733 }
3734 #else //SHORT_PLUGS
3735 inline void set_plug_padded (BYTE* node){}
3736 inline void clear_plug_padded (BYTE* node){}
3737 inline
3738 BOOL is_plug_padded (BYTE* node){return FALSE;}
3739 #endif //SHORT_PLUGS
3740
3741
3742 inline size_t unused_array_size(BYTE * p)
3743 {
3744     assert(((CObjectHeader*)p)->IsFree());
3745
3746     SIZE_T* numComponentsPtr = (SIZE_T*)(p + ArrayBase::GetOffsetOfNumComponents());
3747     return free_object_base_size + *numComponentsPtr;
3748 }
3749
3750 heap_segment* heap_segment_rw (heap_segment* ns)
3751 {
3752     if ((ns == 0) || !heap_segment_read_only_p (ns))
3753     {
3754         return ns;
3755     }
3756     else
3757     {
3758         do
3759         {
3760             ns = heap_segment_next (ns);
3761         } while ((ns != 0) && heap_segment_read_only_p (ns));
3762         return ns;
3763     }
3764 }
3765
3766 //returns the next non ro segment.
3767 heap_segment* heap_segment_next_rw (heap_segment* seg)
3768 {
3769     heap_segment* ns = heap_segment_next (seg);
3770     return heap_segment_rw (ns);
3771 }
3772
3773 // returns the segment before seg.
3774 heap_segment* heap_segment_prev_rw (heap_segment* begin, heap_segment* seg)
3775 {
3776     assert (begin != 0);
3777     heap_segment* prev = begin;
3778     heap_segment* current = heap_segment_next_rw (begin);
3779
3780     while (current && current != seg)
3781     {
3782         prev = current;
3783         current = heap_segment_next_rw (current);
3784     }
3785
3786     if (current == seg)
3787     {
3788         return prev;
3789     }
3790     else
3791     {
3792         return 0;
3793     }
3794 }
3795
3796 // returns the segment before seg.
3797 heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg)
3798 {
3799     assert (begin != 0);
3800     heap_segment* prev = begin;
3801     heap_segment* current = heap_segment_next (begin);
3802
3803     while (current && current != seg)
3804     {
3805         prev = current;
3806         current = heap_segment_next (current);
3807     }
3808
3809     if (current == seg)
3810     {
3811         return prev;
3812     }
3813     else
3814     {
3815         return 0;
3816     }
3817 }
3818
3819 heap_segment* heap_segment_in_range (heap_segment* ns)
3820 {
3821     if ((ns == 0) || heap_segment_in_range_p (ns))
3822     {
3823         return ns;
3824     }
3825     else
3826     {
3827         do
3828         {
3829             ns = heap_segment_next (ns);
3830         } while ((ns != 0) && !heap_segment_in_range_p (ns));
3831         return ns;
3832     }
3833 }
3834
3835 heap_segment* heap_segment_next_in_range (heap_segment* seg)
3836 {
3837     heap_segment* ns = heap_segment_next (seg);
3838     return heap_segment_in_range (ns);
3839 }
3840
3841 typedef struct
3842 {
3843     BYTE* memory_base;
3844 } imemory_data;
3845
3846 typedef struct
3847 {
3848     imemory_data *initial_memory;
3849     imemory_data *initial_normal_heap; // points into initial_memory_array
3850     imemory_data *initial_large_heap;  // points into initial_memory_array
3851
3852     size_t block_size_normal;
3853     size_t block_size_large;
3854
3855     size_t block_count;                // # of blocks in each
3856     size_t current_block_normal;
3857     size_t current_block_large;
3858
3859     enum 
3860     { 
3861         ALLATONCE = 1, 
3862         TWO_STAGE, 
3863         EACH_BLOCK 
3864     };
3865
3866     size_t allocation_pattern;
3867 } initial_memory_details;
3868
3869 initial_memory_details memory_details;
3870
3871 BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
3872 {
3873     BOOL reserve_success = FALSE;
3874
3875     // should only be called once
3876     assert (memory_details.initial_memory == 0);
3877
3878     memory_details.initial_memory = new (nothrow) imemory_data[num_heaps*2];
3879     if (memory_details.initial_memory == 0)
3880     {
3881         dprintf (2, ("failed to reserve %Id bytes for imemory_data", num_heaps*2*sizeof(imemory_data)));
3882         return FALSE;
3883     }
3884
3885     memory_details.initial_normal_heap = memory_details.initial_memory;
3886     memory_details.initial_large_heap = memory_details.initial_memory + num_heaps;
3887     memory_details.block_size_normal = normal_size;
3888     memory_details.block_size_large = large_size;
3889     memory_details.block_count = num_heaps;
3890
3891     memory_details.current_block_normal = 0;
3892     memory_details.current_block_large = 0;
3893
3894     g_lowest_address = MAX_PTR;
3895     g_highest_address = 0;
3896
3897     // Try to get the data all at once
3898     ptrdiff_t allatonce_delta;
3899
3900     if (((size_t)MAX_PTR - large_size) < normal_size)
3901     {
3902         // we are already overflowing with just one heap.
3903         dprintf (2, ("0x%Ix + 0x%Ix already overflow", normal_size, large_size));
3904         return FALSE;
3905     }
3906
3907     if (((size_t)MAX_PTR / memory_details.block_count) < (normal_size + large_size))
3908     {
3909         dprintf (2, ("(0x%Ix + 0x%Ix)*0x%Ix overflow", normal_size, large_size, memory_details.block_count));
3910         return FALSE;
3911     }
3912
3913     size_t requestedMemory = memory_details.block_count * (normal_size + large_size);
3914
3915     BYTE* allatonce_block = (BYTE*)virtual_alloc (requestedMemory);
3916     if (allatonce_block)
3917     {
3918         g_lowest_address =  allatonce_block;
3919         g_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size));
3920         memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
3921
3922         for(size_t i = 0; i < memory_details.block_count; i++)
3923         {
3924             memory_details.initial_normal_heap[i].memory_base = allatonce_block + (i*normal_size);
3925             memory_details.initial_large_heap[i].memory_base = allatonce_block +
3926                             (memory_details.block_count*normal_size) + (i*large_size);
3927             reserve_success = TRUE;
3928         }
3929     }
3930     else
3931     {
3932         // try to allocate 2 blocks
3933         BYTE* b1 = 0;
3934         BYTE* b2 = 0;
3935         b1 = (BYTE*)virtual_alloc (memory_details.block_count * normal_size);
3936         if (b1)
3937         {
3938             b2 = (BYTE*)virtual_alloc (memory_details.block_count * large_size);
3939             if (b2)
3940             {
3941                 memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
3942                 g_lowest_address = min(b1,b2);
3943                 g_highest_address = max(b1 + memory_details.block_count*normal_size,
3944                                         b2 + memory_details.block_count*large_size);
3945                 for(size_t i = 0; i < memory_details.block_count; i++)
3946                 {
3947                     memory_details.initial_normal_heap[i].memory_base = b1 + (i*normal_size);
3948                     memory_details.initial_large_heap[i].memory_base = b2 + (i*large_size);
3949                     reserve_success = TRUE;
3950                 }
3951             }
3952             else
3953             {
3954                 // b2 allocation failed, we'll go on to try allocating each block.
3955                 // We could preserve the b1 alloc, but code complexity increases
3956                 virtual_free (b1, memory_details.block_count * normal_size);
3957             }
3958         }
3959
3960         if ((b2==NULL) && ( memory_details.block_count > 1))
3961         {
3962             memory_details.allocation_pattern = initial_memory_details::EACH_BLOCK;
3963
3964             imemory_data *current_block = memory_details.initial_memory;
3965             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
3966             {
3967                 size_t block_size = ((i < memory_details.block_count) ?
3968                                      memory_details.block_size_normal :
3969                                      memory_details.block_size_large);
3970                 current_block->memory_base =
3971                     (BYTE*)virtual_alloc (block_size);
3972                 if (current_block->memory_base == 0)
3973                 {
3974                     // Free the blocks that we've allocated so far
3975                     current_block = memory_details.initial_memory;
3976                     for(size_t j = 0; j < i; j++, current_block++){
3977                         if (current_block->memory_base != 0){
3978                             block_size = ((j < memory_details.block_count) ?
3979                                      memory_details.block_size_normal :
3980                                      memory_details.block_size_large);
3981                              virtual_free (current_block->memory_base , block_size);
3982                         }
3983                     }
3984                     reserve_success = FALSE;
3985                     break;
3986                 }
3987                 else
3988                 {
3989                     if (current_block->memory_base < g_lowest_address)
3990                         g_lowest_address =  current_block->memory_base;
3991                     if (((BYTE *) current_block->memory_base + block_size) > g_highest_address)
3992                         g_highest_address = (current_block->memory_base + block_size);
3993                 }
3994                 reserve_success = TRUE;
3995             }
3996         }
3997     }
3998
3999     return reserve_success;
4000 }
4001
4002 void destroy_initial_memory()
4003 {
4004     if (memory_details.initial_memory != NULL)
4005     {
4006         if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE)
4007         {
4008             virtual_free(memory_details.initial_memory[0].memory_base,
4009                 memory_details.block_count*(memory_details.block_size_normal +
4010                 memory_details.block_size_large));
4011         }
4012         else if (memory_details.allocation_pattern == initial_memory_details::TWO_STAGE)
4013         {
4014             virtual_free (memory_details.initial_normal_heap[0].memory_base,
4015                 memory_details.block_count*memory_details.block_size_normal);
4016
4017             virtual_free (memory_details.initial_large_heap[0].memory_base,
4018                 memory_details.block_count*memory_details.block_size_large);
4019         }
4020         else
4021         {
4022             assert (memory_details.allocation_pattern == initial_memory_details::EACH_BLOCK);
4023             imemory_data *current_block = memory_details.initial_memory;
4024             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4025             {
4026                 size_t block_size = (i < memory_details.block_count) ? memory_details.block_size_normal :
4027                                                                        memory_details.block_size_large;
4028                 if (current_block->memory_base != NULL)
4029                 {
4030                     virtual_free (current_block->memory_base, block_size);
4031                 }
4032             }
4033         }
4034
4035         delete [] memory_details.initial_memory;
4036         memory_details.initial_memory = NULL;
4037         memory_details.initial_normal_heap = NULL;
4038         memory_details.initial_large_heap = NULL;
4039     }
4040 }
4041
4042 void* next_initial_memory (size_t size)
4043 {
4044     assert ((size == memory_details.block_size_normal) || (size == memory_details.block_size_large));
4045     void *res = NULL;
4046
4047     if ((size != memory_details.block_size_normal) ||
4048         ((memory_details.current_block_normal == memory_details.block_count) &&
4049          (memory_details.block_size_normal == memory_details.block_size_large)))
4050     {
4051         // If the block sizes are the same, flow block requests from normal to large
4052         assert (memory_details.current_block_large < memory_details.block_count);
4053         assert (memory_details.initial_large_heap != 0);
4054
4055         res = memory_details.initial_large_heap[memory_details.current_block_large].memory_base;
4056         memory_details.current_block_large++;
4057     }
4058     else
4059     {
4060         assert (memory_details.current_block_normal < memory_details.block_count);
4061         assert (memory_details.initial_normal_heap != NULL);
4062
4063         res = memory_details.initial_normal_heap[memory_details.current_block_normal].memory_base;
4064         memory_details.current_block_normal++;
4065     }
4066
4067     return res;
4068 }
4069
4070 heap_segment* get_initial_segment (size_t size, int h_number)
4071 {
4072     void* mem = next_initial_memory (size);
4073     heap_segment* res = gc_heap::make_heap_segment ((BYTE*)mem, size , h_number);
4074
4075     return res;
4076 }
4077
4078 void* virtual_alloc (size_t size)
4079 {
4080     size_t requested_size = size;
4081
4082     if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4083     {
4084         gc_heap::reserved_memory_limit =
4085             CNameSpace::AskForMoreReservedMemory (gc_heap::reserved_memory_limit, requested_size);
4086         if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4087         {
4088             return 0;
4089         }
4090     }
4091
4092     void* prgmem = ClrVirtualAllocAligned (0, requested_size, mem_reserve, PAGE_READWRITE, card_size * card_word_width);
4093     void *aligned_mem = prgmem;
4094
4095     // We don't want (prgmem + size) to be right at the end of the address space 
4096     // because we'd have to worry about that everytime we do (address + size).
4097     // We also want to make sure that we leave LARGE_OBJECT_SIZE at the end 
4098     // so we allocate a small object we don't need to worry about overflow there
4099     // when we do alloc_ptr+size.
4100     if (prgmem)
4101     {
4102         BYTE* end_mem = (BYTE*)prgmem + requested_size;
4103
4104         if ((end_mem == 0) || ((size_t)(MAX_PTR - end_mem) <= END_SPACE_AFTER_GC))
4105         {
4106             VirtualFree (prgmem, 0, MEM_RELEASE);
4107             dprintf (2, ("Virtual Alloc size %Id returned memory right against 4GB [%Ix, %Ix[ - discarding",
4108                         requested_size, (size_t)prgmem, (size_t)((BYTE*)prgmem+requested_size)));
4109             prgmem = 0;
4110             aligned_mem = 0;
4111         }
4112     }
4113
4114     if (prgmem)
4115     {
4116         gc_heap::reserved_memory += requested_size;
4117     }
4118
4119     dprintf (2, ("Virtual Alloc size %Id: [%Ix, %Ix[",
4120                  requested_size, (size_t)prgmem, (size_t)((BYTE*)prgmem+requested_size)));
4121
4122     return aligned_mem;
4123 }
4124
4125 void virtual_free (void* add, size_t size)
4126 {
4127     VirtualFree (add, 0, MEM_RELEASE);
4128     gc_heap::reserved_memory -= size;
4129     dprintf (2, ("Virtual Free size %Id: [%Ix, %Ix[",
4130                  size, (size_t)add, (size_t)((BYTE*)add+size)));
4131 }
4132
4133 // We have a few places that call this but the seg size doesn't change so call it
4134 // once and save the result.
4135 // TODO: move back after we do this.
4136 static size_t get_valid_segment_size (BOOL large_seg=FALSE)
4137 {
4138     size_t seg_size, initial_seg_size;
4139
4140     if (!large_seg)
4141     {
4142         initial_seg_size = INITIAL_ALLOC;
4143         seg_size = g_pConfig->GetSegmentSize();
4144     }
4145     else
4146     {
4147         initial_seg_size = LHEAP_ALLOC;
4148         seg_size = g_pConfig->GetSegmentSize() / 2;
4149     }
4150
4151 #ifdef MULTIPLE_HEAPS
4152     if (g_SystemInfo.dwNumberOfProcessors > 4)
4153         initial_seg_size /= 2;
4154     if (g_SystemInfo.dwNumberOfProcessors > 8)
4155         initial_seg_size /= 2;
4156 #endif //MULTIPLE_HEAPS
4157
4158     // if seg_size is small but not 0 (0 is default if config not set)
4159     // then set the segment to the minimum size
4160     if (!GCHeap::IsValidSegmentSize(seg_size))
4161     {
4162         // if requested size is between 1 byte and 4MB, use min
4163         if ((seg_size >> 1) && !(seg_size >> 22))
4164             seg_size = 1024*1024*4;
4165         else
4166             seg_size = initial_seg_size;
4167     }
4168
4169     return (seg_size);
4170 }
4171
4172 void
4173 gc_heap::compute_new_ephemeral_size()
4174 {
4175     int eph_gen_max = max_generation - 1 - (settings.promotion ? 1 : 0);
4176     size_t padding_size = 0;
4177
4178     for (int i = 0; i <= eph_gen_max; i++)
4179     {
4180         dynamic_data* dd = dynamic_data_of (i);
4181         total_ephemeral_size += (dd_survived_size (dd) - dd_pinned_survived_size (dd));
4182 #ifdef RESPECT_LARGE_ALIGNMENT
4183         total_ephemeral_size += dd_num_npinned_plugs (dd) * switch_alignment_size (FALSE);
4184 #endif //RESPECT_LARGE_ALIGNMENT
4185 #ifdef FEATURE_STRUCTALIGN
4186         total_ephemeral_size += dd_num_npinned_plugs (dd) * MAX_STRUCTALIGN;
4187 #endif //FEATURE_STRUCTALIGN
4188
4189 #ifdef SHORT_PLUGS
4190         padding_size += dd_padding_size (dd);
4191 #endif //SHORT_PLUGS
4192     }
4193
4194     total_ephemeral_size += eph_gen_starts_size;
4195
4196 #ifdef RESPECT_LARGE_ALIGNMENT
4197     size_t planned_ephemeral_size = heap_segment_plan_allocated (ephemeral_heap_segment) -
4198                                        generation_plan_allocation_start (generation_of (max_generation-1));
4199     total_ephemeral_size = min (total_ephemeral_size, planned_ephemeral_size);
4200 #endif //RESPECT_LARGE_ALIGNMENT
4201
4202 #ifdef SHORT_PLUGS
4203     float pad_ratio = (float)24 / (float)DESIRED_PLUG_LENGTH;
4204     total_ephemeral_size += (size_t)((float)total_ephemeral_size * pad_ratio) + Align (min_obj_size);
4205 #endif //SHORT_PLUGS
4206
4207     dprintf (3, ("total ephemeral size is %Ix, padding %Ix(%Ix)", 
4208         total_ephemeral_size,
4209         padding_size, (total_ephemeral_size - padding_size)));
4210 }
4211
4212 #ifdef _MSC_VER
4213 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
4214 #endif // _MSC_VER
4215
4216 heap_segment*
4217 gc_heap::soh_get_segment_to_expand()
4218 {
4219     size_t size = get_valid_segment_size();
4220
4221     ordered_plug_indices_init = FALSE;
4222     use_bestfit = FALSE;
4223
4224     //compute the size of the new ephemeral heap segment.
4225     compute_new_ephemeral_size();
4226
4227     if ((settings.pause_mode != pause_low_latency)
4228 #ifdef BACKGROUND_GC
4229         && (!recursive_gc_sync::background_running_p())
4230 #endif //BACKGROUND_GC
4231         )
4232     {
4233         allocator*  gen_alloc = ((settings.condemned_generation == max_generation) ? 0 :
4234                               generation_allocator (generation_of (max_generation)));
4235         dprintf (2, ("(gen%d)soh_get_segment_to_expand", settings.condemned_generation));
4236
4237         // try to find one in the gen 2 segment list, search backwards because the first segments
4238         // tend to be more compact than the later ones.
4239         heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
4240
4241         PREFIX_ASSUME(fseg != NULL);
4242
4243 #ifdef SEG_REUSE_STATS
4244         int try_reuse = 0;
4245 #endif //SEG_REUSE_STATS
4246
4247         heap_segment* seg = ephemeral_heap_segment;
4248         while ((seg = heap_segment_prev_rw (fseg, seg)) && (seg != fseg))
4249         {
4250 #ifdef SEG_REUSE_STATS
4251         try_reuse++;
4252 #endif //SEG_REUSE_STATS
4253
4254             if (can_expand_into_p (seg, size/3, total_ephemeral_size, gen_alloc))
4255             {
4256                 gc_data_per_heap.set_mechanism (gc_heap_expand, 
4257                     (use_bestfit ? expand_reuse_bestfit : expand_reuse_normal));
4258                 if (settings.condemned_generation == max_generation)
4259                 {
4260                     if (use_bestfit)
4261                     {
4262                         build_ordered_free_spaces (seg);
4263                         dprintf (GTC_LOG, ("can use best fit"));
4264                     }
4265
4266 #ifdef SEG_REUSE_STATS
4267                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse", 
4268                         settings.condemned_generation, try_reuse));
4269 #endif //SEG_REUSE_STATS
4270                     dprintf (GTC_LOG, ("max_gen: Found existing segment to expand into %Ix", (size_t)seg));
4271                     return seg;
4272                 }
4273                 else
4274                 {
4275 #ifdef SEG_REUSE_STATS
4276                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse - returning", 
4277                         settings.condemned_generation, try_reuse));
4278 #endif //SEG_REUSE_STATS
4279                     dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix", (size_t)seg));
4280
4281                     // If we return 0 here, the allocator will think since we are short on end
4282                     // of seg we neeed to trigger a full compacting GC. So if sustained low latency 
4283                     // is set we should acquire a new seg instead, that way we wouldn't be short.
4284                     // The real solution, of course, is to actually implement seg reuse in gen1.
4285                     if (settings.pause_mode != pause_sustained_low_latency)
4286                     {
4287                         dprintf (GTC_LOG, ("max_gen-1: SustainedLowLatency is set, acquire a new seg"));
4288                         return 0;
4289                     }
4290                 }
4291             }
4292         }
4293     }
4294
4295     heap_segment* result = get_segment (size, FALSE);
4296
4297     if(result)
4298     {
4299 #ifdef BACKGROUND_GC
4300         if (current_c_gc_state == c_gc_state_planning)
4301         {
4302             // When we expand heap during bgc sweep, we set the seg to be swept so 
4303             // we'll always look at cards for objects on the new segment.
4304             result->flags |= heap_segment_flags_swept;
4305         }
4306 #endif //BACKGROUND_GC
4307
4308         FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(result), 
4309                                   (size_t)(heap_segment_reserved (result) - heap_segment_mem(result)), 
4310                                   ETW::GCLog::ETW_GC_INFO::SMALL_OBJECT_HEAP, 
4311                                   GetClrInstanceId());
4312     }
4313
4314     gc_data_per_heap.set_mechanism (gc_heap_expand, (result ? expand_new_seg : expand_no_memory));
4315
4316     if (result == 0)
4317     {
4318         dprintf (2, ("h%d: failed to allocate a new segment!", heap_number));
4319     }
4320     else
4321     {
4322 #ifdef MULTIPLE_HEAPS
4323         heap_segment_heap (result) = this;
4324 #endif //MULTIPLE_HEAPS
4325     }
4326
4327     dprintf (GTC_LOG, ("(gen%d)creating new segment %Ix", settings.condemned_generation, result));
4328     return result;
4329 }
4330
4331 #ifdef _MSC_VER
4332 #pragma warning(default:4706)
4333 #endif // _MSC_VER
4334
4335 //returns 0 in case of allocation failure
4336 heap_segment*
4337 gc_heap::get_segment (size_t size, BOOL loh_p)
4338 {
4339     heap_segment* result = 0;
4340
4341     if (segment_standby_list != 0)
4342     {
4343         result = segment_standby_list;
4344         heap_segment* last = 0;
4345         while (result)
4346         {
4347             size_t hs = (size_t)(heap_segment_reserved (result) - (BYTE*)result);
4348             if ((hs >= size) && ((hs / 2) < size))
4349             {
4350                 dprintf (2, ("Hoarded segment %Ix found", (size_t) result));
4351                 if (last)
4352                 {
4353                     heap_segment_next (last) = heap_segment_next (result);
4354                 }
4355                 else
4356                 {
4357                     segment_standby_list = heap_segment_next (result);
4358                 }
4359                 break;
4360             }
4361             else
4362             {
4363                 last = result;
4364                 result = heap_segment_next (result);
4365             }
4366         }
4367     }
4368
4369     if (result)
4370     {
4371         init_heap_segment (result);
4372 #ifdef BACKGROUND_GC
4373         if (should_commit_mark_array())
4374         {
4375             dprintf (GC_TABLE_LOG, ("hoarded seg %Ix, mark_array is %Ix", result, mark_array));
4376             if (!commit_mark_array_new_seg (__this, result))
4377             {
4378                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for hoarded seg"));
4379                 // If we can't use it we need to thread it back.
4380                 if (segment_standby_list != 0)
4381                 {
4382                     heap_segment_next (result) = segment_standby_list;
4383                     segment_standby_list = result;
4384                 }
4385                 else
4386                 {
4387                     segment_standby_list = result;
4388                 }
4389
4390                 result = 0;
4391             }
4392         }
4393 #endif //BACKGROUND_GC
4394
4395 #ifdef SEG_MAPPING_TABLE
4396         if (result)
4397             seg_mapping_table_add_segment (result, __this);
4398 #endif //SEG_MAPPING_TABLE
4399     }
4400
4401     if (!result)
4402     {
4403 #ifndef SEG_MAPPING_TABLE
4404         if (!seg_table->insure_space_for_insert ())
4405             return 0;
4406 #endif //SEG_MAPPING_TABLE
4407         void* mem = virtual_alloc (size);
4408         if (!mem)
4409         {
4410             fgm_result.set_fgm (fgm_reserve_segment, size, loh_p);
4411             return 0;
4412         }
4413
4414         result = gc_heap::make_heap_segment ((BYTE*)mem, size, heap_number);
4415
4416         if (result)
4417         {
4418             BYTE* start;
4419             BYTE* end;
4420             if (mem < g_lowest_address)
4421             {
4422                 start =  (BYTE*)mem;
4423             }
4424             else
4425             {
4426                 start = (BYTE*)g_lowest_address;
4427             }
4428
4429             if (((BYTE*)mem + size) > g_highest_address)
4430             {
4431                 end = (BYTE*)mem + size;
4432             }
4433             else
4434             {
4435                 end = (BYTE*)g_highest_address;
4436             }
4437
4438             if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0)
4439             {
4440                 virtual_free (mem, size);
4441                 return 0;
4442             }
4443         }
4444         else
4445         {
4446             fgm_result.set_fgm (fgm_commit_segment_beg, SEGMENT_INITIAL_COMMIT, loh_p);
4447             virtual_free (mem, size);
4448         }
4449
4450         if (result)
4451         {
4452 #ifdef SEG_MAPPING_TABLE
4453             seg_mapping_table_add_segment (result, __this);
4454 #else //SEG_MAPPING_TABLE
4455             gc_heap::seg_table->insert ((BYTE*)result, delta);
4456 #endif //SEG_MAPPING_TABLE
4457         }
4458     }
4459
4460 #ifdef BACKGROUND_GC
4461     if (result)
4462     {
4463         ::record_changed_seg ((BYTE*)result, heap_segment_reserved (result), 
4464                             settings.gc_index, current_bgc_state,
4465                             seg_added);
4466         bgc_verify_mark_array_cleared (result);
4467     }
4468 #endif //BACKGROUND_GC
4469
4470     dprintf (GC_TABLE_LOG, ("h%d: new seg: %Ix-%Ix (%Id)", heap_number, result, ((BYTE*)result + size), size));
4471     return result;
4472 }
4473
4474 void release_segment (heap_segment* sg)
4475 {
4476     ptrdiff_t delta = 0;
4477     FireEtwGCFreeSegment_V1((size_t)heap_segment_mem(sg), GetClrInstanceId());
4478     virtual_free (sg, (BYTE*)heap_segment_reserved (sg)-(BYTE*)sg);
4479 }
4480
4481 heap_segment* gc_heap::get_segment_for_loh (size_t size
4482 #ifdef MULTIPLE_HEAPS
4483                                            , gc_heap* hp
4484 #endif //MULTIPLE_HEAPS
4485                                            )
4486 {
4487 #ifndef MULTIPLE_HEAPS
4488     gc_heap* hp = 0;
4489 #endif //MULTIPLE_HEAPS
4490     heap_segment* res = hp->get_segment (size, TRUE);
4491     if (res != 0)
4492     {
4493 #ifdef MULTIPLE_HEAPS
4494         heap_segment_heap (res) = hp;
4495 #endif //MULTIPLE_HEAPS
4496         res->flags |= heap_segment_flags_loh;
4497
4498         FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(res), (size_t)(heap_segment_reserved (res) - heap_segment_mem(res)), ETW::GCLog::ETW_GC_INFO::LARGE_OBJECT_HEAP, GetClrInstanceId());
4499
4500 #ifdef GC_PROFILING
4501         if (CORProfilerTrackGC())
4502             UpdateGenerationBounds();
4503 #endif // GC_PROFILING
4504
4505 #ifdef MULTIPLE_HEAPS
4506         hp->thread_loh_segment (res);
4507 #else
4508         thread_loh_segment (res);
4509 #endif //MULTIPLE_HEAPS
4510     }
4511
4512     return res;
4513 }
4514
4515 void gc_heap::thread_loh_segment (heap_segment* new_seg)
4516 {
4517     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
4518
4519     while (heap_segment_next_rw (seg))
4520         seg = heap_segment_next_rw (seg);
4521     heap_segment_next (seg) = new_seg;
4522 }
4523
4524 heap_segment*
4525 gc_heap::get_large_segment (size_t size, BOOL* did_full_compact_gc)
4526 {
4527     *did_full_compact_gc = FALSE;
4528     size_t last_full_compact_gc_count = get_full_compact_gc_count();
4529
4530     //access to get_segment needs to be serialized
4531     add_saved_spinlock_info (me_release, mt_get_large_seg);
4532
4533     dprintf (SPINLOCK_LOG, ("[%d]Seg: Lmsl", heap_number));
4534     leave_spin_lock (&more_space_lock);
4535     enter_spin_lock (&gc_heap::gc_lock);
4536     dprintf (SPINLOCK_LOG, ("[%d]Seg: Egc", heap_number));
4537     // if a GC happened between here and before we ask for a segment in 
4538     // get_large_segment, we need to count that GC.
4539     size_t current_full_compact_gc_count = get_full_compact_gc_count();
4540
4541     if (current_full_compact_gc_count > last_full_compact_gc_count)
4542     {
4543         *did_full_compact_gc = TRUE;
4544     }
4545
4546 #ifdef BACKGROUND_GC
4547     while (current_c_gc_state == c_gc_state_planning)
4548     {
4549         dprintf (3, ("lh state planning, waiting to get a large seg"));
4550
4551         dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Lgc", heap_number));
4552         leave_spin_lock (&gc_lock);
4553         background_gc_wait_lh (awr_get_loh_seg);
4554         enter_spin_lock (&gc_lock);
4555         dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Egc", heap_number));
4556     }
4557     assert ((current_c_gc_state == c_gc_state_free) ||
4558             (current_c_gc_state == c_gc_state_marking));
4559 #endif //BACKGROUND_GC
4560
4561     heap_segment* res = get_segment_for_loh (size
4562 #ifdef MULTIPLE_HEAPS
4563                                             , this
4564 #endif //MULTIPLE_HEAPS
4565                                             );
4566
4567     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Lgc", heap_number));
4568     leave_spin_lock (&gc_heap::gc_lock);
4569     enter_spin_lock (&more_space_lock);
4570     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Emsl", heap_number));
4571     add_saved_spinlock_info (me_acquire, mt_get_large_seg);
4572     
4573 #ifdef BACKGROUND_GC
4574     wait_for_background_planning (awr_get_loh_seg);
4575 #endif //BACKGROUND_GC
4576
4577     return res;
4578 }
4579
4580 BOOL gc_heap::unprotect_segment (heap_segment* seg)
4581 {
4582     BYTE* start = align_lower_page (heap_segment_mem (seg));
4583     ptrdiff_t region_size = heap_segment_allocated (seg) - start;
4584
4585     if (region_size != 0 )
4586     {
4587         DWORD old_protection;
4588         dprintf (3, ("unprotecting segment %Ix:", (size_t)seg));
4589
4590         BOOL status = VirtualProtect (start, region_size,
4591                                       PAGE_READWRITE, &old_protection);
4592         assert (status);
4593         return status;
4594     }
4595     return FALSE;
4596 }
4597
4598 #ifdef MULTIPLE_HEAPS
4599 #ifdef _X86_
4600 #ifdef _MSC_VER
4601 #pragma warning(disable:4035)
4602     static SSIZE_T  get_cycle_count()
4603     {
4604         __asm   rdtsc
4605     }
4606 #pragma warning(default:4035)
4607 #elif defined(__GNUC__)
4608     static SSIZE_T  get_cycle_count()
4609     {
4610         SSIZE_T cycles;
4611         SSIZE_T cyclesHi;
4612         __asm__ __volatile__
4613         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4614         return cycles;
4615     }
4616 #else //_MSC_VER
4617 #error Unknown compiler
4618 #endif //_MSC_VER
4619 #elif defined(_TARGET_AMD64_) 
4620 #ifdef _MSC_VER
4621 extern "C" unsigned __int64 __rdtsc();
4622 #pragma intrinsic(__rdtsc)
4623     static SSIZE_T get_cycle_count()
4624     {
4625         return (SSIZE_T)__rdtsc();
4626     }
4627 #elif defined(__clang__)    
4628     static SSIZE_T get_cycle_count()
4629     {
4630         SSIZE_T cycles;
4631         SSIZE_T cyclesHi;
4632         __asm__ __volatile__
4633         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4634         return (cyclesHi << 32) | cycles;
4635     }
4636 #else // _MSC_VER
4637     extern "C" SSIZE_T get_cycle_count(void);
4638 #endif // _MSC_VER
4639 #elif defined(_TARGET_ARM_)
4640     static SSIZE_T get_cycle_count()
4641     {
4642         // @ARMTODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
4643         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
4644         // all buffer access times being reported as equal in access_time().
4645         return 0;
4646     }
4647 #elif defined(_TARGET_ARM64_)
4648     static SSIZE_T get_cycle_count()
4649     {
4650         // @ARM64TODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
4651         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
4652         // all buffer access times being reported as equal in access_time().
4653         return 0;
4654     }
4655 #else
4656 #error NYI platform: get_cycle_count
4657 #endif //_TARGET_X86_
4658
4659 // The purpose of this whole class is to guess the right heap to use for a given thread.
4660 typedef
4661 DWORD (WINAPI *GetCurrentProcessorNumber_t)(VOID);
4662
4663 class heap_select
4664 {
4665     heap_select() {}
4666     static BYTE* sniff_buffer;
4667     static unsigned n_sniff_buffers;
4668     static unsigned cur_sniff_index;
4669
4670     static BYTE proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
4671     static BYTE heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
4672     static BYTE heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
4673     static BYTE heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
4674     static BYTE heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
4675     static BYTE numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
4676
4677     static int access_time(BYTE *sniff_buffer, int heap_number, unsigned sniff_index, unsigned n_sniff_buffers)
4678     {
4679         SSIZE_T start_cycles = get_cycle_count();
4680         BYTE sniff = sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE];
4681         assert (sniff == 0);
4682         SSIZE_T elapsed_cycles = get_cycle_count() - start_cycles;
4683         // add sniff here just to defeat the optimizer
4684         elapsed_cycles += sniff;
4685         return (int) elapsed_cycles;
4686     }
4687
4688     static
4689     GetCurrentProcessorNumber_t GCGetCurrentProcessorNumber;
4690
4691     //check if the new APIs are supported.
4692     static
4693     BOOL api_supported()
4694     {
4695 #ifdef FEATURE_REDHAWK
4696         BOOL fSupported = PalHasCapability(GetCurrentProcessorNumberCapability);
4697         GCGetCurrentProcessorNumber = fSupported ? PalGetCurrentProcessorNumber : NULL;
4698         return fSupported;
4699 #elif !defined(FEATURE_PAL)
4700         // on all platforms we support this API exists.
4701         GCGetCurrentProcessorNumber = (GetCurrentProcessorNumber_t)&GetCurrentProcessorNumber;
4702         return TRUE;
4703 #else 
4704         return FALSE;
4705 #endif //FEATURE_REDHAWK
4706     }
4707
4708 public:
4709     static BOOL init(int n_heaps)
4710     {
4711         assert (sniff_buffer == NULL && n_sniff_buffers == 0);
4712         if (!api_supported())
4713         {
4714             n_sniff_buffers = n_heaps*2+1;
4715             size_t sniff_buf_size = 0;
4716 #ifdef FEATURE_REDHAWK
4717             size_t n_cache_lines = 1 + n_heaps*n_sniff_buffers + 1;
4718             sniff_buf_size = n_cache_lines * HS_CACHE_LINE_SIZE;
4719 #else
4720             S_SIZE_T safe_sniff_buf_size = S_SIZE_T(1 + n_heaps*n_sniff_buffers + 1);
4721             safe_sniff_buf_size *= HS_CACHE_LINE_SIZE;
4722             if (safe_sniff_buf_size.IsOverflow())
4723             {
4724                 return FALSE;
4725             }
4726             sniff_buf_size = safe_sniff_buf_size.Value();
4727 #endif //FEATURE_REDHAWK
4728             sniff_buffer = new (nothrow) BYTE[sniff_buf_size];
4729             if (sniff_buffer == 0)
4730                 return FALSE;
4731             memset(sniff_buffer, 0, sniff_buf_size*sizeof(BYTE));
4732         }
4733
4734         //can not enable gc numa aware, force all heaps to be in
4735         //one numa node by filling the array with all 0s
4736         if (!NumaNodeInfo::CanEnableGCNumaAware())
4737             memset(heap_no_to_numa_node, 0, MAX_SUPPORTED_CPUS); 
4738
4739         return TRUE;
4740     }
4741
4742     static void init_cpu_mapping(gc_heap *heap, int heap_number)
4743     {
4744         if (GCGetCurrentProcessorNumber != 0)
4745         {
4746             DWORD proc_no = GCGetCurrentProcessorNumber() % gc_heap::n_heaps;
4747             // We can safely cast heap_number to a BYTE 'cause GetCurrentProcessCpuCount
4748             // only returns up to MAX_SUPPORTED_CPUS procs right now. We only ever create at most
4749             // MAX_SUPPORTED_CPUS GC threads.
4750             proc_no_to_heap_no[proc_no] = (BYTE)heap_number;
4751         }
4752     }
4753
4754     static void mark_heap(int heap_number)
4755     {
4756         if (GCGetCurrentProcessorNumber != 0)
4757             return;
4758
4759         for (unsigned sniff_index = 0; sniff_index < n_sniff_buffers; sniff_index++)
4760             sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
4761     }
4762
4763     static int select_heap(alloc_context* acontext, int hint)
4764     {
4765         if (GCGetCurrentProcessorNumber)
4766             return proc_no_to_heap_no[GCGetCurrentProcessorNumber() % gc_heap::n_heaps];
4767
4768         unsigned sniff_index = FastInterlockIncrement((LONG *)&cur_sniff_index);
4769         sniff_index %= n_sniff_buffers;
4770
4771         int best_heap = 0;
4772         int best_access_time = 1000*1000*1000;
4773         int second_best_access_time = best_access_time;
4774
4775         BYTE *l_sniff_buffer = sniff_buffer;
4776         unsigned l_n_sniff_buffers = n_sniff_buffers;
4777         for (int heap_number = 0; heap_number < gc_heap::n_heaps; heap_number++)
4778         {
4779             int this_access_time = access_time(l_sniff_buffer, heap_number, sniff_index, l_n_sniff_buffers);
4780             if (this_access_time < best_access_time)
4781             {
4782                 second_best_access_time = best_access_time;
4783                 best_access_time = this_access_time;
4784                 best_heap = heap_number;
4785             }
4786             else if (this_access_time < second_best_access_time)
4787             {
4788                 second_best_access_time = this_access_time;
4789             }
4790         }
4791
4792         if (best_access_time*2 < second_best_access_time)
4793         {
4794             sniff_buffer[(1 + best_heap*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
4795
4796             dprintf (3, ("select_heap yields crisp %d for context %p\n", best_heap, (void *)acontext));
4797         }
4798         else
4799         {
4800             dprintf (3, ("select_heap yields vague %d for context %p\n", best_heap, (void *)acontext ));
4801         }
4802
4803         return best_heap;
4804     }
4805
4806     static BOOL can_find_heap_fast()
4807     {
4808         if (GCGetCurrentProcessorNumber)
4809             return TRUE;
4810         else
4811             return FALSE;
4812     }
4813
4814     static BYTE find_proc_no_from_heap_no(int heap_number)
4815     {
4816         return heap_no_to_proc_no[heap_number];
4817     }
4818
4819     static void set_proc_no_for_heap(int heap_number, BYTE proc_no)
4820     {
4821         heap_no_to_proc_no[heap_number] = proc_no;
4822     }
4823
4824     static BYTE find_numa_node_from_heap_no(int heap_number)
4825     {
4826         return heap_no_to_numa_node[heap_number];
4827     }
4828
4829     static void set_numa_node_for_heap(int heap_number, BYTE numa_node)
4830     {
4831         heap_no_to_numa_node[heap_number] = numa_node;
4832     }
4833
4834     static BYTE find_cpu_group_from_heap_no(int heap_number)
4835     {
4836         return heap_no_to_cpu_group[heap_number];
4837     }
4838
4839     static void set_cpu_group_for_heap(int heap_number, BYTE group_number)
4840     {
4841         heap_no_to_cpu_group[heap_number] = group_number;
4842     }
4843
4844     static BYTE find_group_proc_from_heap_no(int heap_number)
4845     {
4846         return heap_no_to_group_proc[heap_number];
4847     }
4848
4849     static void set_group_proc_for_heap(int heap_number, BYTE group_proc)
4850     {
4851         heap_no_to_group_proc[heap_number] = group_proc;
4852     }
4853
4854     static void init_numa_node_to_heap_map(int nheaps)
4855     {   // called right after GCHeap::Init() for each heap is finished
4856         // when numa is not enabled, heap_no_to_numa_node[] are all filled
4857         // with 0s during initialization, and will be treated as one node
4858         numa_node_to_heap_map[0] = 0;
4859         int node_index = 1;
4860
4861         for (int i=1; i < nheaps; i++)
4862         {
4863             if (heap_no_to_numa_node[i] != heap_no_to_numa_node[i-1])
4864                 numa_node_to_heap_map[node_index++] = (BYTE)i;
4865         }
4866         numa_node_to_heap_map[node_index] = (BYTE)nheaps; //mark the end with nheaps
4867     }
4868
4869     static void get_heap_range_for_heap(int hn, int* start, int* end)
4870     {   // 1-tier/no numa case: heap_no_to_numa_node[] all zeros, 
4871         // and treated as in one node. thus: start=0, end=n_heaps
4872         BYTE numa_node = heap_no_to_numa_node[hn];
4873         *start = (int)numa_node_to_heap_map[numa_node];
4874         *end   = (int)(numa_node_to_heap_map[numa_node+1]);
4875     }
4876 };
4877 BYTE* heap_select::sniff_buffer;
4878 unsigned heap_select::n_sniff_buffers;
4879 unsigned heap_select::cur_sniff_index;
4880 GetCurrentProcessorNumber_t heap_select::GCGetCurrentProcessorNumber;
4881 BYTE heap_select::proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
4882 BYTE heap_select::heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
4883 BYTE heap_select::heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
4884 BYTE heap_select::heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
4885 BYTE heap_select::heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
4886 BYTE heap_select::numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
4887
4888 BOOL gc_heap::create_thread_support (unsigned number_of_heaps)
4889 {
4890     BOOL ret = FALSE;
4891     gc_start_event.CreateOSManualEvent (FALSE);
4892     if (!gc_start_event.IsValid())
4893     {
4894         goto cleanup;
4895     }
4896     ee_suspend_event.CreateOSAutoEvent (FALSE);
4897     if (!ee_suspend_event.IsValid())
4898     {
4899         goto cleanup;
4900     }
4901     if (!gc_t_join.init (number_of_heaps, join_flavor_server_gc))
4902     {
4903         goto cleanup;
4904     }
4905
4906     ret = TRUE;
4907
4908 cleanup:
4909
4910     if (!ret)
4911     {
4912         destroy_thread_support();
4913     }
4914
4915     return ret;
4916 }
4917
4918 void gc_heap::destroy_thread_support ()
4919 {
4920     if (ee_suspend_event.IsValid())
4921     {
4922         ee_suspend_event.CloseEvent();
4923     }
4924     if (gc_start_event.IsValid())
4925     {
4926         gc_start_event.CloseEvent();
4927     }
4928 }
4929
4930 void set_thread_group_affinity_for_heap(HANDLE gc_thread, int heap_number)
4931 {
4932 #if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORECLR)
4933     GROUP_AFFINITY ga;
4934     WORD gn, gpn;
4935
4936     CPUGroupInfo::GetGroupForProcessor((WORD)heap_number, &gn, &gpn);
4937     ga.Group  = gn;
4938     ga.Reserved[0] = 0; // reserve must be filled with zero
4939     ga.Reserved[1] = 0; // otherwise call may fail
4940     ga.Reserved[2] = 0;
4941
4942     int bit_number = 0;
4943     for (DWORD_PTR mask = 1; mask !=0; mask <<=1) 
4944     {
4945         if (bit_number == gpn)
4946         {
4947             dprintf(3, ("using processor group %d, mask %x%Ix for heap %d\n", gn, mask, heap_number));
4948             ga.Mask = mask;
4949             CPUGroupInfo::SetThreadGroupAffinity(gc_thread, &ga, NULL);
4950             heap_select::set_cpu_group_for_heap(heap_number, (BYTE)gn);
4951             heap_select::set_group_proc_for_heap(heap_number, (BYTE)gpn);
4952             if (NumaNodeInfo::CanEnableGCNumaAware())
4953             {  
4954                 PROCESSOR_NUMBER proc_no;
4955                 proc_no.Group    = gn;
4956                 proc_no.Number   = (BYTE)gpn;
4957                 proc_no.Reserved = 0;
4958
4959                 WORD node_no = 0;
4960                 if (NumaNodeInfo::GetNumaProcessorNodeEx(&proc_no, &node_no))
4961                     heap_select::set_numa_node_for_heap(heap_number, (BYTE)node_no);
4962             }
4963             else
4964             {   // no numa setting, each cpu group is treated as a node
4965                 heap_select::set_numa_node_for_heap(heap_number, (BYTE)gn);
4966             }
4967             return;
4968         }
4969         bit_number++;
4970     }
4971 #endif
4972 }
4973
4974 void set_thread_affinity_mask_for_heap(HANDLE gc_thread, int heap_number)
4975 {
4976 #if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORECLR)
4977     DWORD_PTR pmask, smask;
4978
4979     if (GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
4980     {
4981         pmask &= smask;
4982         int bit_number = 0; 
4983         BYTE proc_number = 0;
4984         for (DWORD_PTR mask = 1; mask != 0; mask <<= 1)
4985         {
4986             if ((mask & pmask) != 0)
4987             {
4988                 if (bit_number == heap_number)
4989                 {
4990                     dprintf (3, ("Using processor mask 0x%Ix for heap %d\n", mask, heap_number));
4991                     SetThreadAffinityMask(gc_thread, mask);
4992                     heap_select::set_proc_no_for_heap(heap_number, proc_number);
4993                     if (NumaNodeInfo::CanEnableGCNumaAware())
4994                     { // have the processor number, find the numa node
4995 #if !defined(FEATURE_CORESYSTEM)
4996                         BYTE node_no = 0;
4997                         if (NumaNodeInfo::GetNumaProcessorNode(proc_number, &node_no))
4998                             heap_select::set_numa_node_for_heap(heap_number, node_no);
4999 #else
5000                         WORD gn, gpn;
5001                         WORD node_no = 0;
5002                         CPUGroupInfo::GetGroupForProcessor((WORD)heap_number, &gn, &gpn);
5003                         
5004                         PROCESSOR_NUMBER proc_no;
5005                         proc_no.Group = gn;
5006                         proc_no.Number = (BYTE)gpn;
5007                         proc_no.Reserved = 0;
5008                         if (NumaNodeInfo::GetNumaProcessorNodeEx(&proc_no, &node_no))
5009                         {
5010                             heap_select::set_numa_node_for_heap(heap_number, (BYTE)node_no);
5011                         }
5012 #endif
5013                     }
5014                     return;
5015                 }
5016                 bit_number++;
5017             }
5018             proc_number++;
5019         }
5020     }
5021 #endif
5022 }
5023
5024 HANDLE gc_heap::create_gc_thread ()
5025 {
5026     DWORD thread_id;
5027     dprintf (3, ("Creating gc thread\n"));
5028
5029 #ifdef FEATURE_REDHAWK
5030     HANDLE gc_thread = CreateThread(0, 4096, gc_thread_stub,this, CREATE_SUSPENDED, &thread_id);
5031 #else //FEATURE_REDHAWK
5032     HANDLE gc_thread = Thread::CreateUtilityThread(Thread::StackSize_Medium, gc_thread_stub, this, CREATE_SUSPENDED, &thread_id);
5033 #endif //FEATURE_REDHAWK
5034
5035     if (!gc_thread)
5036     {
5037         return 0;;
5038     }
5039     SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST );
5040
5041     //We are about to set affinity for GC threads, it is a good place to setup NUMA and
5042     //CPU groups, because the process mask, processor number, group number are all
5043     //readyly available.
5044     if (CPUGroupInfo::CanEnableGCCPUGroups()) 
5045         set_thread_group_affinity_for_heap(gc_thread, heap_number);
5046     else
5047         set_thread_affinity_mask_for_heap(gc_thread, heap_number);
5048
5049     ResumeThread(gc_thread);
5050     return gc_thread;
5051 }
5052
5053 #ifdef _MSC_VER
5054 #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
5055 #endif //_MSC_VER
5056 DWORD gc_heap::gc_thread_function ()
5057 {
5058     assert (gc_done_event.IsValid());
5059     assert (gc_start_event.IsValid());
5060     dprintf (3, ("gc thread started"));
5061
5062     heap_select::init_cpu_mapping(this, heap_number);
5063
5064     while (1)
5065     {
5066         assert (!gc_t_join.joined());
5067
5068         if (heap_number == 0)
5069         {
5070             gc_heap::ee_suspend_event.Wait(INFINITE, FALSE);
5071
5072             BEGIN_TIMING(suspend_ee_during_log);
5073             GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC);
5074             END_TIMING(suspend_ee_during_log);
5075
5076             settings.init_mechanisms();
5077             dprintf (3, ("%d gc thread waiting...", heap_number));
5078             gc_start_event.Set();
5079         }
5080         else
5081         {
5082             gc_start_event.Wait(INFINITE, FALSE);
5083             dprintf (3, ("%d gc thread waiting... Done", heap_number));
5084         }
5085
5086         garbage_collect (GCHeap::GcCondemnedGeneration);
5087
5088         if (heap_number == 0)
5089         {
5090             if (!settings.concurrent)
5091             {
5092                 do_post_gc();
5093             }
5094
5095 #ifdef BACKGROUND_GC
5096             recover_bgc_settings();
5097 #endif //BACKGROUND_GC
5098
5099 #ifdef MULTIPLE_HEAPS
5100             for (int i = 0; i < gc_heap::n_heaps; i++)
5101             {
5102                 gc_heap* hp = gc_heap::g_heaps[i];
5103                 hp->add_saved_spinlock_info (me_release, mt_block_gc);
5104                 dprintf (SPINLOCK_LOG, ("[%d]GC Lmsl", i));
5105                 leave_spin_lock(&hp->more_space_lock);
5106             }
5107 #endif //MULTIPLE_HEAPS
5108
5109             gc_heap::gc_started = FALSE;
5110
5111             BEGIN_TIMING(restart_ee_during_log);
5112             GCToEEInterface::RestartEE(TRUE);
5113             END_TIMING(restart_ee_during_log);
5114             process_sync_log_stats();
5115
5116             dprintf (SPINLOCK_LOG, ("GC Lgc"));
5117             leave_spin_lock (&gc_heap::gc_lock);
5118
5119             gc_heap::internal_gc_done = true;
5120
5121             set_gc_done();
5122         }
5123         else
5124         {
5125             int spin_count = 32 * (g_SystemInfo.dwNumberOfProcessors - 1);
5126
5127             // wait until RestartEE has progressed to a stage where we can restart user threads
5128             while (!gc_heap::internal_gc_done && !GCHeap::SafeToRestartManagedThreads())
5129             {
5130                 spin_and_switch (spin_count, (gc_heap::internal_gc_done || GCHeap::SafeToRestartManagedThreads()));
5131             }
5132             set_gc_done();
5133         }
5134     }
5135     return 0;
5136 }
5137 #ifdef _MSC_VER
5138 #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
5139 #endif //_MSC_VER
5140
5141 #endif //MULTIPLE_HEAPS
5142
5143 void* virtual_alloc_commit_for_heap(void* addr, size_t size, DWORD type, 
5144                                                             DWORD prot, int h_number)
5145 {
5146 #if defined(MULTIPLE_HEAPS) && !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL)
5147     // Currently there is no way for us to specific the numa node to allocate on via hosting interfaces to
5148     // a host. This will need to be added later.
5149     if (!CLRMemoryHosted())
5150     {
5151         if (NumaNodeInfo::CanEnableGCNumaAware())
5152         {
5153             DWORD numa_node = heap_select::find_numa_node_from_heap_no(h_number);
5154             void * ret = NumaNodeInfo::VirtualAllocExNuma(GetCurrentProcess(), addr, size, 
5155                                                         type, prot, numa_node);
5156             if (ret != NULL)
5157                 return ret;
5158         }
5159     }
5160 #endif
5161
5162     //numa aware not enabled, or call failed --> fallback to VirtualAlloc()
5163     return VirtualAlloc(addr, size, type, prot);
5164 }
5165
5166 #ifndef SEG_MAPPING_TABLE
5167 inline
5168 heap_segment* gc_heap::segment_of (BYTE* add, ptrdiff_t& delta, BOOL verify_p)
5169 {
5170     BYTE* sadd = add;
5171     heap_segment* hs = 0;
5172     heap_segment* hs1 = 0;
5173     if (!((add >= g_lowest_address) && (add < g_highest_address)))
5174     {
5175         delta = 0;
5176         return 0;
5177     }
5178     //repeat in case there is a concurrent insertion in the table.
5179     do
5180     {
5181         hs = hs1;
5182         sadd = add;
5183         seg_table->lookup (sadd);
5184         hs1 = (heap_segment*)sadd;
5185     } while (hs1 && !in_range_for_segment (add, hs1) && (hs != hs1));
5186
5187     hs = hs1;
5188
5189     if ((hs == 0) ||
5190         (verify_p && (add > heap_segment_reserved ((heap_segment*)(sadd + delta)))))
5191         delta = 0;
5192     return hs;
5193 }
5194 #endif //SEG_MAPPING_TABLE
5195
5196 class mark
5197 {
5198 public:
5199     BYTE* first;
5200     size_t len;
5201
5202     // If we want to save space we can have a pool of plug_and_gap's instead of 
5203     // always having 2 allocated for each pinned plug.
5204     gap_reloc_pair saved_pre_plug;
5205     // If we decide to not compact, we need to restore the original values.
5206     gap_reloc_pair saved_pre_plug_reloc;
5207
5208     gap_reloc_pair saved_post_plug;
5209
5210     // Supposedly Pinned objects cannot have references but we are seeing some from pinvoke 
5211     // frames. Also if it's an artificially pinned plug created by us, it can certainly 
5212     // have references. 
5213     // We know these cases will be rare so we can optimize this to be only allocated on decommand. 
5214     gap_reloc_pair saved_post_plug_reloc;
5215
5216     // We need to calculate this after we are done with plan phase and before compact
5217     // phase because compact phase will change the bricks so relocate_address will no 
5218     // longer work.
5219     BYTE* saved_pre_plug_info_reloc_start;
5220
5221     // We need to save this because we will have no way to calculate it, unlike the 
5222     // pre plug info start which is right before this plug.
5223     BYTE* saved_post_plug_info_start;
5224
5225 #ifdef SHORT_PLUGS
5226     BYTE* allocation_context_start_region;
5227 #endif //SHORT_PLUGS
5228
5229     // How the bits in these bytes are organized:
5230     // MSB --> LSB
5231     // 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
5232     // 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.
5233     BOOL saved_pre_p;
5234     BOOL saved_post_p;
5235
5236 #ifdef _DEBUG
5237     // We are seeing this is getting corrupted for a PP with a NP after.
5238     // Save it when we first set it and make sure it doesn't change.
5239     gap_reloc_pair saved_post_plug_debug;
5240 #endif //_DEBUG
5241
5242     size_t get_max_short_bits()
5243     {
5244         return (sizeof (gap_reloc_pair) / sizeof (BYTE*));
5245     }
5246
5247     // pre bits
5248     size_t get_pre_short_start_bit ()
5249     {
5250         return (sizeof (saved_pre_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (BYTE*)));
5251     }
5252
5253     BOOL pre_short_p()
5254     {
5255         return (saved_pre_p & (1 << (sizeof (saved_pre_p) * 8 - 1)));
5256     }
5257
5258     void set_pre_short()
5259     {
5260         saved_pre_p |= (1 << (sizeof (saved_pre_p) * 8 - 1));
5261     }
5262
5263     void set_pre_short_bit (size_t bit)
5264     {
5265         saved_pre_p |= 1 << (get_pre_short_start_bit() + bit);
5266     }
5267
5268     BOOL pre_short_bit_p (size_t bit)
5269     {
5270         return (saved_pre_p & (1 << (get_pre_short_start_bit() + bit)));
5271     }
5272
5273 #ifdef COLLECTIBLE_CLASS
5274     void set_pre_short_collectible()
5275     {
5276         saved_pre_p |= 2;
5277     }
5278
5279     BOOL pre_short_collectible_p()
5280     {
5281         return (saved_pre_p & 2);
5282     }
5283 #endif //COLLECTIBLE_CLASS
5284
5285     // post bits
5286     size_t get_post_short_start_bit ()
5287     {
5288         return (sizeof (saved_post_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (BYTE*)));
5289     }
5290
5291     BOOL post_short_p()
5292     {
5293         return (saved_post_p & (1 << (sizeof (saved_post_p) * 8 - 1)));
5294     }
5295
5296     void set_post_short()
5297     {
5298         saved_post_p |= (1 << (sizeof (saved_post_p) * 8 - 1));
5299     }
5300
5301     void set_post_short_bit (size_t bit)
5302     {
5303         saved_post_p |= 1 << (get_post_short_start_bit() + bit);
5304     }
5305
5306     BOOL post_short_bit_p (size_t bit)
5307     {
5308         return (saved_post_p & (1 << (get_post_short_start_bit() + bit)));
5309     }
5310
5311 #ifdef COLLECTIBLE_CLASS
5312     void set_post_short_collectible()
5313     {
5314         saved_post_p |= 2;
5315     }
5316
5317     BOOL post_short_collectible_p()
5318     {
5319         return (saved_post_p & 2);
5320     }
5321 #endif //COLLECTIBLE_CLASS
5322
5323     BYTE* get_plug_address() { return first; }
5324
5325     BOOL has_pre_plug_info() { return saved_pre_p; }
5326     BOOL has_post_plug_info() { return saved_post_p; }
5327
5328     gap_reloc_pair* get_pre_plug_reloc_info() { return &saved_pre_plug_reloc; }
5329     gap_reloc_pair* get_post_plug_reloc_info() { return &saved_post_plug_reloc; }
5330     void set_pre_plug_info_reloc_start (BYTE* reloc) { saved_pre_plug_info_reloc_start = reloc; }
5331     BYTE* get_post_plug_info_start() { return saved_post_plug_info_start; }
5332
5333     // We need to temporarily recover the shortened plugs for compact phase so we can
5334     // copy over the whole plug and their related info (mark bits/cards). But we will
5335     // need to set the artificial gap back so compact phase can keep reading the plug info.
5336     // We also need to recover the saved info because we'll need to recover it later.
5337     // 
5338     // So we would call swap_p*_plug_and_saved once to recover the object info; then call 
5339     // it again to recover the artifical gap.
5340     void swap_pre_plug_and_saved()
5341     {
5342         gap_reloc_pair temp;
5343         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5344         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5345         saved_pre_plug_reloc = temp;
5346     }
5347
5348     void swap_post_plug_and_saved()
5349     {
5350         gap_reloc_pair temp;
5351         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5352         memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5353         saved_post_plug_reloc = temp;
5354     }
5355
5356 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
5357     void swap_pre_plug_and_saved_for_profiler()
5358     {
5359         gap_reloc_pair temp;
5360         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5361         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5362         saved_pre_plug = temp;
5363     }
5364
5365     void swap_post_plug_and_saved_for_profiler()
5366     {
5367         gap_reloc_pair temp;
5368         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5369         memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5370         saved_post_plug = temp;
5371     }
5372 #endif //GC_PROFILING || //FEATURE_EVENT_TRACE
5373
5374     // We should think about whether it's really necessary to have to copy back the pre plug
5375     // info since it was already copied during compacting plugs. But if a plug doesn't move
5376     // by < 3 ptr size, it means we'd have to recover pre plug info.
5377     void recover_plug_info() 
5378     {
5379         if (saved_pre_p)
5380         {
5381             if (gc_heap::settings.compaction)
5382             {
5383                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
5384                     first,
5385                     &saved_pre_plug_reloc, 
5386                     saved_pre_plug_info_reloc_start));
5387                 memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5388             }
5389             else
5390             {
5391                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
5392                     first,
5393                     &saved_pre_plug, 
5394                     (first - sizeof (plug_and_gap))));
5395                 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5396             }
5397         }
5398
5399         if (saved_post_p)
5400         {
5401             if (gc_heap::settings.compaction)
5402             {
5403                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
5404                     first,
5405                     &saved_post_plug_reloc, 
5406                     saved_post_plug_info_start));
5407                 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5408             }
5409             else
5410             {
5411                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
5412                     first,
5413                     &saved_post_plug, 
5414                     saved_post_plug_info_start));
5415                 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5416             }
5417         }
5418     }
5419 };
5420
5421
5422 void gc_mechanisms::init_mechanisms()
5423 {
5424     condemned_generation = 0;
5425     promotion = FALSE;//TRUE;
5426     compaction = TRUE;
5427 #ifdef FEATURE_LOH_COMPACTION
5428     loh_compaction = gc_heap::should_compact_loh();
5429 #else
5430     loh_compaction = FALSE;
5431 #endif //FEATURE_LOH_COMPACTION
5432     heap_expansion = FALSE;
5433     concurrent = FALSE;
5434     demotion = FALSE;
5435     found_finalizers = FALSE;
5436 #ifdef BACKGROUND_GC
5437     background_p = recursive_gc_sync::background_running_p() != FALSE;
5438     allocations_allowed = TRUE;
5439 #endif //BACKGROUND_GC
5440
5441 #ifdef _WIN64
5442     entry_memory_load = 0;
5443 #endif //_WIN64
5444
5445 #ifdef STRESS_HEAP
5446     stress_induced = FALSE;
5447 #endif // STRESS_HEAP
5448 }
5449
5450 void gc_mechanisms::first_init()
5451 {
5452     gc_index = 0;
5453     gen0_reduction_count = 0;
5454     should_lock_elevation = FALSE;
5455     elevation_locked_count = 0;
5456     reason = reason_empty;
5457 #ifdef BACKGROUND_GC
5458     pause_mode = gc_heap::gc_can_use_concurrent ? pause_interactive : pause_batch;
5459 #ifdef _DEBUG
5460     int debug_pause_mode = g_pConfig->GetGCLatencyMode();
5461     if (debug_pause_mode >= 0)
5462     {
5463         assert (debug_pause_mode <= pause_sustained_low_latency);
5464         pause_mode = (gc_pause_mode)debug_pause_mode;
5465     }
5466 #endif //_DEBUG
5467 #else //BACKGROUND_GC
5468     pause_mode = pause_batch;
5469 #endif //BACKGROUND_GC
5470
5471     init_mechanisms();
5472 }
5473
5474 void gc_mechanisms::record (gc_history_global* history)
5475 {
5476 #ifdef MULTIPLE_HEAPS
5477     history->num_heaps = gc_heap::n_heaps;
5478 #else
5479     history->num_heaps = 1;
5480 #endif //MULTIPLE_HEAPS
5481
5482     history->condemned_generation = condemned_generation;
5483     history->gen0_reduction_count = gen0_reduction_count;
5484     history->reason = reason;
5485     history->global_mechanims_p = 0;
5486
5487     // start setting the boolean values.
5488     if (concurrent)
5489         history->set_mechanism_p (global_concurrent);
5490     
5491     if (compaction)
5492         history->set_mechanism_p (global_compaction);
5493
5494     if (promotion)
5495         history->set_mechanism_p (global_promotion);
5496     
5497     if (demotion)
5498         history->set_mechanism_p (global_demotion);
5499
5500     if (card_bundles)
5501         history->set_mechanism_p (global_card_bundles);
5502 }
5503
5504 /**********************************
5505    called at the beginning of GC to fix the allocated size to
5506    what is really allocated, or to turn the free area into an unused object
5507    It needs to be called after all of the other allocation contexts have been
5508    fixed since it relies on alloc_allocated.
5509  ********************************/
5510
5511 //for_gc_p indicates that the work is being done for GC,
5512 //as opposed to concurrent heap verification
5513 void gc_heap::fix_youngest_allocation_area (BOOL for_gc_p)
5514 {
5515     assert (alloc_allocated);
5516     alloc_context* acontext = generation_alloc_context (youngest_generation);
5517     dprintf (3, ("generation 0 alloc context: ptr: %Ix, limit %Ix",
5518                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5519     fix_allocation_context (acontext, for_gc_p, get_alignment_constant (TRUE));
5520     if (for_gc_p)
5521     {
5522         acontext->alloc_ptr = alloc_allocated;
5523         acontext->alloc_limit = acontext->alloc_ptr;
5524     }
5525     heap_segment_allocated (ephemeral_heap_segment) =
5526         alloc_allocated;
5527 }
5528
5529 void gc_heap::fix_large_allocation_area (BOOL for_gc_p)
5530 {
5531 #ifdef _DEBUG
5532     alloc_context* acontext = 
5533 #endif // DEBUG    
5534         generation_alloc_context (large_object_generation);
5535     assert (acontext->alloc_ptr == 0);
5536     assert (acontext->alloc_limit == 0); 
5537 #if 0
5538     dprintf (3, ("Large object alloc context: ptr: %Ix, limit %Ix",
5539                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5540     fix_allocation_context (acontext, FALSE, get_alignment_constant (FALSE));
5541     if (for_gc_p)
5542     {
5543         acontext->alloc_ptr = 0;
5544         acontext->alloc_limit = acontext->alloc_ptr;
5545     }
5546 #endif //0
5547 }
5548
5549 //for_gc_p indicates that the work is being done for GC,
5550 //as opposed to concurrent heap verification
5551 void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
5552                                       int align_const)
5553 {
5554     dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
5555                  (size_t)acontext,
5556                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5557
5558     if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) ||
5559         !for_gc_p)
5560     {
5561         BYTE*  point = acontext->alloc_ptr;
5562         if (point != 0)
5563         {
5564             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
5565             // the allocation area was from the free list
5566             // it was shortened by Align (min_obj_size) to make room for
5567             // at least the shortest unused object
5568             size += Align (min_obj_size, align_const);
5569             assert ((size >= Align (min_obj_size)));
5570
5571             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point,
5572                        (size_t)point + size ));
5573             make_unused_array (point, size);
5574
5575             if (for_gc_p)
5576             {
5577                 generation_free_obj_space (generation_of (0)) += size;
5578                 alloc_contexts_used ++;
5579             }
5580         }
5581     }
5582     else if (for_gc_p)
5583     {
5584         alloc_allocated = acontext->alloc_ptr;
5585         assert (heap_segment_allocated (ephemeral_heap_segment) <=
5586                 heap_segment_committed (ephemeral_heap_segment));
5587         alloc_contexts_used ++;
5588     }
5589
5590
5591     if (for_gc_p)
5592     {
5593         acontext->alloc_ptr = 0;
5594         acontext->alloc_limit = acontext->alloc_ptr;
5595     }
5596 }
5597
5598 //used by the heap verification for concurrent gc.
5599 //it nulls out the words set by fix_allocation_context for heap_verification
5600 void repair_allocation (alloc_context* acontext)
5601 {
5602     BYTE*  point = acontext->alloc_ptr;
5603
5604     if (point != 0)
5605     {
5606         dprintf (3, ("Clearing [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5607                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
5608         memclr (acontext->alloc_ptr - plug_skew,
5609                 (acontext->alloc_limit - acontext->alloc_ptr)+Align (min_obj_size));
5610     }
5611 }
5612
5613 void void_allocation (alloc_context* acontext)
5614 {
5615     BYTE*  point = acontext->alloc_ptr;
5616
5617     if (point != 0)
5618     {
5619         dprintf (3, ("Void [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5620                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
5621         acontext->alloc_ptr = 0;
5622         acontext->alloc_limit = acontext->alloc_ptr;
5623     }
5624 }
5625
5626 void gc_heap::fix_allocation_contexts (BOOL for_gc_p)
5627 {
5628     CNameSpace::GcFixAllocContexts ((void*)for_gc_p, __this);
5629     fix_youngest_allocation_area (for_gc_p);
5630     fix_large_allocation_area (for_gc_p);
5631 }
5632
5633 void gc_heap::repair_allocation_contexts (BOOL repair_p)
5634 {
5635     CNameSpace::GcEnumAllocContexts (repair_p ? repair_allocation : void_allocation);
5636
5637     alloc_context* acontext = generation_alloc_context (youngest_generation);
5638     if (repair_p)
5639         repair_allocation (acontext);
5640     else
5641         void_allocation (acontext);
5642 }
5643
5644 void gc_heap::fix_older_allocation_area (generation* older_gen)
5645 {
5646     heap_segment* older_gen_seg = generation_allocation_segment (older_gen);
5647     if (generation_allocation_limit (older_gen) !=
5648         heap_segment_plan_allocated (older_gen_seg))
5649     {
5650         BYTE*  point = generation_allocation_pointer (older_gen);
5651
5652         size_t  size = (generation_allocation_limit (older_gen) -
5653                                generation_allocation_pointer (older_gen));
5654         if (size != 0)
5655         {
5656             assert ((size >= Align (min_obj_size)));
5657             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, (size_t)point+size));
5658             make_unused_array (point, size);
5659         }
5660     }
5661     else
5662     {
5663         assert (older_gen_seg != ephemeral_heap_segment);
5664         heap_segment_plan_allocated (older_gen_seg) =
5665             generation_allocation_pointer (older_gen);
5666         generation_allocation_limit (older_gen) =
5667             generation_allocation_pointer (older_gen);
5668     }
5669 }
5670
5671 void gc_heap::set_allocation_heap_segment (generation* gen)
5672 {
5673     BYTE* p = generation_allocation_start (gen);
5674     assert (p);
5675     heap_segment* seg = generation_allocation_segment (gen);
5676     if (in_range_for_segment (p, seg))
5677         return;
5678
5679     // try ephemeral heap segment in case of heap expansion
5680     seg = ephemeral_heap_segment;
5681     if (!in_range_for_segment (p, seg))
5682     {
5683         seg = heap_segment_rw (generation_start_segment (gen));
5684
5685         PREFIX_ASSUME(seg != NULL);
5686
5687         while (!in_range_for_segment (p, seg))
5688         {
5689             seg = heap_segment_next_rw (seg);
5690             PREFIX_ASSUME(seg != NULL);
5691         }
5692     }
5693
5694     generation_allocation_segment (gen) = seg;
5695 }
5696
5697 void gc_heap::reset_allocation_pointers (generation* gen, BYTE* start)
5698 {
5699     assert (start);
5700     assert (Align ((size_t)start) == (size_t)start);
5701     generation_allocation_start (gen) = start;
5702     generation_allocation_pointer (gen) =  0;//start + Align (min_obj_size);
5703     generation_allocation_limit (gen) = 0;//generation_allocation_pointer (gen);
5704     set_allocation_heap_segment (gen);
5705 }
5706
5707 #ifdef BACKGROUND_GC
5708 //TODO BACKGROUND_GC this is for test only
5709 void
5710 gc_heap::disallow_new_allocation (int gen_number)
5711 {
5712     settings.allocations_allowed = FALSE;
5713 }
5714 void
5715 gc_heap::allow_new_allocation (int gen_number)
5716 {
5717     settings.allocations_allowed = TRUE;
5718 }
5719
5720 #endif //BACKGROUND_GC
5721
5722 bool gc_heap::new_allocation_allowed (int gen_number)
5723 {
5724 #ifdef BACKGROUND_GC
5725     //TODO BACKGROUND_GC this is for test only
5726     if (!settings.allocations_allowed)
5727     {
5728         dprintf (2, ("new allocation not allowed"));
5729         return FALSE;
5730     }
5731 #endif //BACKGROUND_GC
5732
5733     if (dd_new_allocation (dynamic_data_of (gen_number)) < 0)
5734     {
5735         if (gen_number != 0)
5736         {
5737             // For LOH we will give it more budget before we try a GC.
5738             if (settings.concurrent)
5739             {
5740                 dynamic_data* dd2 = dynamic_data_of (max_generation + 1 );
5741
5742                 if (dd_new_allocation (dd2) <= (SSIZE_T)(-2 * dd_desired_allocation (dd2)))
5743                 {
5744                     return TRUE;
5745                 }
5746             }
5747         }
5748         return FALSE;
5749     }
5750 #ifndef MULTIPLE_HEAPS
5751     else if ((gen_number == 0))
5752     {
5753         dprintf (3, ("evaluating allocation rate"));
5754         dynamic_data* dd0 = dynamic_data_of (0);
5755         if ((allocation_running_amount - dd_new_allocation (dd0)) >
5756             dd_min_gc_size (dd0))
5757         {
5758             DWORD ctime = GetTickCount();
5759             if ((ctime - allocation_running_time) > 1000)
5760             {
5761                 dprintf (2, (">1s since last gen0 gc"));
5762                 return FALSE;
5763             }
5764             else
5765             {
5766                 allocation_running_amount = dd_new_allocation (dd0);
5767             }
5768         }
5769     }
5770 #endif //MULTIPLE_HEAPS
5771     return TRUE;
5772 }
5773
5774 inline
5775 ptrdiff_t gc_heap::get_desired_allocation (int gen_number)
5776 {
5777     return dd_desired_allocation (dynamic_data_of (gen_number));
5778 }
5779
5780 inline
5781 ptrdiff_t  gc_heap::get_new_allocation (int gen_number)
5782 {
5783     return dd_new_allocation (dynamic_data_of (gen_number));
5784 }
5785
5786 //return the amount allocated so far in gen_number
5787 inline
5788 ptrdiff_t  gc_heap::get_allocation (int gen_number)
5789 {
5790     dynamic_data* dd = dynamic_data_of (gen_number);
5791
5792     return dd_desired_allocation (dd) - dd_new_allocation (dd);
5793 }
5794
5795 inline
5796 BOOL grow_mark_stack (mark*& m, size_t& len, size_t init_len)
5797 {
5798     size_t new_size = max (init_len, 2*len);
5799     mark* tmp = new (nothrow) (mark [new_size]);
5800     if (tmp)
5801     {
5802         memcpy (tmp, m, len * sizeof (mark));
5803         delete m;
5804         m = tmp;
5805         len = new_size;
5806         return TRUE;
5807     }
5808     else
5809     {
5810         dprintf (1, ("Failed to allocate %Id bytes for mark stack", (len * sizeof (mark))));
5811         return FALSE;
5812     }
5813 }
5814
5815 inline
5816 BYTE* pinned_plug (mark* m)
5817 {
5818    return m->first;
5819 }
5820
5821 inline
5822 size_t& pinned_len (mark* m)
5823 {
5824     return m->len;
5825 }
5826
5827 inline
5828 void set_new_pin_info (mark* m, BYTE* pin_free_space_start)
5829 {
5830     m->len = pinned_plug (m) - pin_free_space_start;
5831 #ifdef SHORT_PLUGS
5832     m->allocation_context_start_region = pin_free_space_start;
5833 #endif //SHORT_PLUGS
5834 }
5835
5836 #ifdef SHORT_PLUGS
5837 inline
5838 BYTE*& pin_allocation_context_start_region (mark* m)
5839 {
5840     return m->allocation_context_start_region;
5841 }
5842
5843 inline
5844 void set_padding_in_expand (BYTE* old_loc, 
5845                        BOOL set_padding_on_saved_p,
5846                        mark* pinned_plug_entry)
5847 {
5848     if (set_padding_on_saved_p)
5849     {
5850         BYTE* saved_pre_plug_info = (BYTE*)(pinned_plug_entry->get_pre_plug_reloc_info());
5851         BYTE* plug_start_in_saved = saved_pre_plug_info + (old_loc - (pinned_plug (pinned_plug_entry) - sizeof (plug_and_gap)));
5852         //dprintf (1, ("detected a very short plug: %Ix before PP %Ix, pad %Ix", 
5853         //    old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
5854         dprintf (1, ("EP: %Ix(%Ix)", old_loc, (BYTE)pinned_plug_entry));
5855         set_plug_padded (plug_start_in_saved);
5856     }
5857     else
5858     {
5859         set_plug_padded (old_loc);
5860     }
5861 }
5862 #endif //SHORT_PLUGS
5863
5864 void gc_heap::reset_pinned_queue()
5865 {
5866     mark_stack_tos = 0;
5867     mark_stack_bos = 0;
5868 }
5869
5870 void gc_heap::reset_pinned_queue_bos()
5871 {
5872     mark_stack_bos = 0;
5873 }
5874
5875 // last_pinned_plug is only for asserting purpose.
5876 void gc_heap::merge_with_last_pinned_plug (BYTE* last_pinned_plug, size_t plug_size)
5877 {
5878     if (last_pinned_plug)
5879     {
5880         mark& last_m = mark_stack_array[mark_stack_tos - 1];
5881         assert (last_pinned_plug == last_m.first);
5882         if (last_m.saved_post_p)
5883         {
5884             last_m.saved_post_p = FALSE;
5885             dprintf (3, ("setting last plug %Ix post to false", last_m.first));
5886             // We need to recover what the gap has overwritten.
5887             memcpy ((last_m.first + last_m.len - sizeof (plug_and_gap)), &(last_m.saved_post_plug), sizeof (gap_reloc_pair));
5888         }
5889         last_m.len += plug_size;
5890         dprintf (3, ("recovered the last part of plug %Ix, setting its plug size to %Ix", last_m.first, last_m.len));
5891     }
5892 }
5893
5894 void gc_heap::set_allocator_next_pin (BYTE* alloc_pointer, BYTE*& alloc_limit)
5895 {
5896     dprintf (3, ("sanp: ptr: %Ix, limit: %Ix", alloc_pointer, alloc_limit));
5897     dprintf (3, ("oldest %Id: %Ix", mark_stack_bos, pinned_plug (oldest_pin())));
5898     if (!(pinned_plug_que_empty_p()))
5899     {
5900         mark*  oldest_entry = oldest_pin();
5901         BYTE* plug = pinned_plug (oldest_entry);
5902         if ((plug >= alloc_pointer) && (plug < alloc_limit))
5903         {
5904             alloc_limit = pinned_plug (oldest_entry);
5905             dprintf (3, ("now setting alloc context: %Ix->%Ix(%Id)",
5906                 alloc_pointer, alloc_limit, (alloc_limit - alloc_pointer)));
5907         }
5908     }
5909 }
5910
5911 void gc_heap::set_allocator_next_pin (generation* gen)
5912 {
5913     dprintf (3, ("SANP: gen%d, ptr; %Ix, limit: %Ix", gen->gen_num, generation_allocation_pointer (gen), generation_allocation_limit (gen)));
5914     if (!(pinned_plug_que_empty_p()))
5915     {
5916         mark*  oldest_entry = oldest_pin();
5917         BYTE* plug = pinned_plug (oldest_entry);
5918         if ((plug >= generation_allocation_pointer (gen)) &&
5919             (plug <  generation_allocation_limit (gen)))
5920         {
5921             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
5922             dprintf (3, ("SANP: get next pin free space in gen%d for alloc: %Ix->%Ix(%Id)", 
5923                 gen->gen_num,
5924                 generation_allocation_pointer (gen), generation_allocation_limit (gen),
5925                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
5926         }
5927         else
5928             assert (!((plug < generation_allocation_pointer (gen)) &&
5929                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
5930     }
5931 }
5932
5933 // After we set the info, we increase tos.
5934 void gc_heap::set_pinned_info (BYTE* last_pinned_plug, size_t plug_len, BYTE* alloc_pointer, BYTE*& alloc_limit)
5935 {
5936     mark& m = mark_stack_array[mark_stack_tos];
5937     assert (m.first == last_pinned_plug);
5938
5939     m.len = plug_len;
5940     mark_stack_tos++;
5941     set_allocator_next_pin (alloc_pointer, alloc_limit);
5942 }
5943
5944 // After we set the info, we increase tos.
5945 void gc_heap::set_pinned_info (BYTE* last_pinned_plug, size_t plug_len, generation* gen)
5946 {
5947     mark& m = mark_stack_array[mark_stack_tos];
5948     assert (m.first == last_pinned_plug);
5949
5950     m.len = plug_len;
5951     mark_stack_tos++;
5952     assert (gen != 0);
5953     // Why are we checking here? gen is never 0.
5954     if (gen != 0)
5955     {
5956         set_allocator_next_pin (gen);
5957     }
5958 }
5959
5960 size_t gc_heap::deque_pinned_plug ()
5961 {
5962     dprintf (3, ("dequed: %Id", mark_stack_bos));
5963     size_t m = mark_stack_bos;
5964     mark_stack_bos++;
5965     return m;
5966 }
5967
5968 inline
5969 mark* gc_heap::pinned_plug_of (size_t bos)
5970 {
5971     return &mark_stack_array [ bos ];
5972 }
5973
5974 inline
5975 mark* gc_heap::oldest_pin ()
5976 {
5977     return pinned_plug_of (mark_stack_bos);
5978 }
5979
5980 inline
5981 BOOL gc_heap::pinned_plug_que_empty_p ()
5982 {
5983     return (mark_stack_bos == mark_stack_tos);
5984 }
5985
5986 inline
5987 mark* gc_heap::before_oldest_pin()
5988 {
5989     if (mark_stack_bos >= 1)
5990         return pinned_plug_of (mark_stack_bos-1);
5991     else
5992         return 0;
5993 }
5994
5995 inline
5996 BOOL gc_heap::ephemeral_pointer_p (BYTE* o)
5997 {
5998     return ((o >= ephemeral_low) && (o < ephemeral_high));
5999 }
6000
6001 #ifdef MH_SC_MARK
6002 inline
6003 int& gc_heap::mark_stack_busy()
6004 {
6005     return  g_mark_stack_busy [(heap_number+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
6006 }
6007 #endif //MH_SC_MARK
6008
6009 void gc_heap::make_mark_stack (mark* arr)
6010 {
6011     reset_pinned_queue();
6012     mark_stack_array = arr;
6013     mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6014 #ifdef MH_SC_MARK
6015     mark_stack_busy() = 0;
6016 #endif //MH_SC_MARK
6017 }
6018
6019 #ifdef BACKGROUND_GC
6020 inline
6021 size_t& gc_heap::bpromoted_bytes(int thread)
6022 {
6023 #ifdef MULTIPLE_HEAPS
6024     return g_bpromoted [thread*16];
6025 #else //MULTIPLE_HEAPS
6026     thread = thread;
6027     return g_bpromoted;
6028 #endif //MULTIPLE_HEAPS
6029 }
6030
6031 void gc_heap::make_background_mark_stack (BYTE** arr)
6032 {
6033     background_mark_stack_array = arr;
6034     background_mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6035     background_mark_stack_tos = arr;
6036 }
6037
6038 void gc_heap::make_c_mark_list (BYTE** arr)
6039 {
6040     c_mark_list = arr;
6041     c_mark_list_index = 0;
6042     c_mark_list_length = 1 + (page_size / MIN_OBJECT_SIZE);
6043 }
6044 #endif //BACKGROUND_GC
6045
6046 #if defined (_TARGET_AMD64_)
6047 #define brick_size ((size_t)4096)
6048 #else
6049 #define brick_size ((size_t)2048)
6050 #endif //_TARGET_AMD64_
6051
6052 inline
6053 size_t gc_heap::brick_of (BYTE* add)
6054 {
6055     return (size_t)(add - lowest_address) / brick_size;
6056 }
6057
6058 inline
6059 BYTE* gc_heap::brick_address (size_t brick)
6060 {
6061     return lowest_address + (brick_size * brick);
6062 }
6063
6064
6065 void gc_heap::clear_brick_table (BYTE* from, BYTE* end)
6066 {
6067     for (size_t i = brick_of (from);i < brick_of (end); i++)
6068         brick_table[i] = 0;
6069 }
6070
6071 //codes for the brick entries:
6072 //entry == 0 -> not assigned
6073 //entry >0 offset is entry-1
6074 //entry <0 jump back entry bricks
6075
6076
6077 inline
6078 void gc_heap::set_brick (size_t index, ptrdiff_t val)
6079 {
6080     if (val < -32767)
6081     {
6082         val = -32767;
6083     }
6084     assert (val < 32767);
6085     if (val >= 0)
6086         brick_table [index] = (short)val+1;
6087     else
6088         brick_table [index] = (short)val;
6089 }
6090
6091 inline
6092 int gc_heap::brick_entry (size_t index)
6093 {
6094     int val = brick_table [index];
6095     if (val == 0)
6096     {
6097         return -32768;
6098     }
6099     else if (val < 0)
6100     {
6101         return val;
6102     }
6103     else
6104         return val-1;
6105 }
6106
6107
6108 inline
6109 BYTE* align_on_brick (BYTE* add)
6110 {
6111     return (BYTE*)((size_t)(add + brick_size - 1) & ~(brick_size - 1));
6112 }
6113
6114 inline
6115 BYTE* align_lower_brick (BYTE* add)
6116 {
6117     return (BYTE*)(((size_t)add) & ~(brick_size - 1));
6118 }
6119
6120 size_t size_brick_of (BYTE* from, BYTE* end)
6121 {
6122     assert (((size_t)from & (brick_size-1)) == 0);
6123     assert (((size_t)end  & (brick_size-1)) == 0);
6124
6125     return ((end - from) / brick_size) * sizeof (short);
6126 }
6127
6128 inline
6129 BYTE* gc_heap::card_address (size_t card)
6130 {
6131     return  (BYTE*) (card_size * card);
6132 }
6133
6134 inline
6135 size_t gc_heap::card_of ( BYTE* object)
6136 {
6137     return (size_t)(object) / card_size;
6138 }
6139
6140 inline
6141 size_t gc_heap::card_to_brick (size_t card)
6142 {
6143     return brick_of (card_address (card));
6144 }
6145
6146 inline
6147 BYTE* align_on_card (BYTE* add)
6148 {
6149     return (BYTE*)((size_t)(add + card_size - 1) & ~(card_size - 1 ));
6150 }
6151 inline
6152 BYTE* align_on_card_word (BYTE* add)
6153 {
6154     return (BYTE*) ((size_t)(add + (card_size*card_word_width)-1) & ~(card_size*card_word_width - 1));
6155 }
6156
6157 inline
6158 BYTE* align_lower_card (BYTE* add)
6159 {
6160     return (BYTE*)((size_t)add & ~(card_size-1));
6161 }
6162
6163 inline
6164 void gc_heap::clear_card (size_t card)
6165 {
6166     card_table [card_word (card)] =
6167         (card_table [card_word (card)] & ~(1 << card_bit (card)));
6168     dprintf (3,("Cleared card %Ix [%Ix, %Ix[", card, (size_t)card_address (card),
6169               (size_t)card_address (card+1)));
6170 }
6171
6172 inline
6173 void gc_heap::set_card (size_t card)
6174 {
6175     card_table [card_word (card)] =
6176         (card_table [card_word (card)] | (1 << card_bit (card)));
6177 }
6178
6179 inline
6180 void gset_card (size_t card)
6181 {
6182     g_card_table [card_word (card)] |= (1 << card_bit (card));
6183 }
6184
6185 inline
6186 BOOL  gc_heap::card_set_p (size_t card)
6187 {
6188     return ( card_table [ card_word (card) ] & (1 << card_bit (card)));
6189 }
6190
6191 // Returns the number of DWORDs in the card table that cover the
6192 // range of addresses [from, end[.
6193 size_t count_card_of (BYTE* from, BYTE* end)
6194 {
6195     return card_word (gcard_of (end - 1)) - card_word (gcard_of (from)) + 1;
6196 }
6197
6198 // Returns the number of bytes to allocate for a card table
6199 // that covers the range of addresses [from, end[.
6200 size_t size_card_of (BYTE* from, BYTE* end)
6201 {
6202     return count_card_of (from, end) * sizeof(DWORD);
6203 }
6204
6205 #ifdef CARD_BUNDLE
6206
6207 //The card bundle keeps track of groups of card words
6208 #define card_bundle_word_width ((size_t)32)
6209 //how do we express the fact that 32 bits (card_word_width) is one DWORD?
6210 #define card_bundle_size ((size_t)(OS_PAGE_SIZE/(sizeof (DWORD)*card_bundle_word_width)))
6211
6212 inline
6213 size_t card_bundle_word (size_t cardb)
6214 {
6215     return cardb / card_bundle_word_width;
6216 }
6217
6218 inline
6219 DWORD card_bundle_bit (size_t cardb)
6220 {
6221     return (DWORD)(cardb % card_bundle_word_width);
6222 }
6223
6224 size_t align_cardw_on_bundle (size_t cardw)
6225 {
6226     return ((size_t)(cardw + card_bundle_size - 1) & ~(card_bundle_size - 1 ));
6227 }
6228
6229 size_t cardw_card_bundle (size_t cardw)
6230 {
6231     return cardw/card_bundle_size;
6232 }
6233
6234 size_t card_bundle_cardw (size_t cardb)
6235 {
6236     return cardb*card_bundle_size;
6237 }
6238
6239 void gc_heap::card_bundle_clear(size_t cardb)
6240 {
6241     card_bundle_table [card_bundle_word (cardb)] &= ~(1 << card_bundle_bit (cardb));
6242     dprintf (3,("Cleared card bundle %Ix [%Ix, %Ix[", cardb, (size_t)card_bundle_cardw (cardb),
6243               (size_t)card_bundle_cardw (cardb+1)));
6244 //    printf ("Cleared card bundle %Ix\n", cardb);
6245 }
6246
6247 void gc_heap::card_bundles_set (size_t start_cardb, size_t end_cardb)
6248 {
6249     size_t start_word = card_bundle_word (start_cardb);
6250     size_t end_word = card_bundle_word (end_cardb);
6251     if (start_word < end_word)
6252     {
6253         //set the partial words
6254         card_bundle_table [start_word] |= highbits (~0u, card_bundle_bit (start_cardb));
6255
6256         if (card_bundle_bit (end_cardb))
6257             card_bundle_table [end_word] |= lowbits (~0u, card_bundle_bit (end_cardb));
6258
6259         for (size_t i = start_word+1; i < end_word; i++)
6260             card_bundle_table [i] = ~0u;
6261
6262     }
6263     else
6264     {
6265         card_bundle_table [start_word] |= (highbits (~0u, card_bundle_bit (start_cardb)) &
6266                                            lowbits (~0u, card_bundle_bit (end_cardb)));
6267
6268     }
6269
6270 }
6271
6272 BOOL gc_heap::card_bundle_set_p (size_t cardb)
6273 {
6274     return ( card_bundle_table [ card_bundle_word (cardb) ] & (1 << card_bundle_bit (cardb)));
6275 }
6276
6277 size_t size_card_bundle_of (BYTE* from, BYTE* end)
6278 {
6279     //align from to lower
6280     from = (BYTE*)((size_t)from & ~(card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1));
6281     //align to to upper
6282     end = (BYTE*)((size_t)(end + (card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1)) &
6283                   ~(card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1));
6284
6285     assert (((size_t)from & ((card_size*card_word_width*card_bundle_size*card_bundle_word_width)-1)) == 0);
6286     assert (((size_t)end  & ((card_size*card_word_width*card_bundle_size*card_bundle_word_width)-1)) == 0);
6287
6288     return ((end - from) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (DWORD);
6289 }
6290
6291 DWORD* translate_card_bundle_table (DWORD* cb)
6292 {
6293     return (DWORD*)((BYTE*)cb - ((((size_t)g_lowest_address) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (DWORD)));
6294 }
6295
6296 void gc_heap::enable_card_bundles ()
6297 {
6298     if (can_use_write_watch() && (!card_bundles_enabled()))
6299     {
6300         dprintf (3, ("Enabling card bundles"));
6301         //set all of the card bundles
6302         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
6303                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
6304         settings.card_bundles = TRUE;
6305     }
6306 }
6307
6308 BOOL gc_heap::card_bundles_enabled ()
6309 {
6310     return settings.card_bundles;
6311 }
6312
6313 #endif //CARD_BUNDLE
6314
6315 // We don't store seg_mapping_table in card_table_info because there's only always one view.
6316 class card_table_info
6317 {
6318 public:
6319     unsigned    recount;
6320     BYTE*       lowest_address;
6321     BYTE*       highest_address;
6322     short*      brick_table;
6323
6324 #ifdef CARD_BUNDLE
6325     DWORD*      card_bundle_table;
6326 #endif //CARD_BUNDLE
6327
6328     // mark_array is always at the end of the data structure because we
6329     // want to be able to make one commit call for everything before it.
6330 #ifdef MARK_ARRAY
6331     DWORD*      mark_array;
6332 #endif //MARK_ARRAY
6333
6334     DWORD*      next_card_table;
6335 };
6336
6337 //These are accessors on untranslated cardtable
6338 inline
6339 unsigned& card_table_refcount (DWORD* c_table)
6340 {
6341     return *(unsigned*)((char*)c_table - sizeof (card_table_info));
6342 }
6343
6344 inline
6345 BYTE*& card_table_lowest_address (DWORD* c_table)
6346 {
6347     return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->lowest_address;
6348 }
6349
6350 DWORD* translate_card_table (DWORD* ct)
6351 {
6352     return (DWORD*)((BYTE*)ct - size_card_of (0, card_table_lowest_address( ct)));
6353 }
6354
6355 inline
6356 BYTE*& card_table_highest_address (DWORD* c_table)
6357 {
6358     return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->highest_address;
6359 }
6360
6361 inline
6362 short*& card_table_brick_table (DWORD* c_table)
6363 {
6364     return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->brick_table;
6365 }
6366
6367 #ifdef CARD_BUNDLE
6368 inline
6369 DWORD*& card_table_card_bundle_table (DWORD* c_table)
6370 {
6371     return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->card_bundle_table;
6372 }
6373 #endif //CARD_BUNDLE
6374
6375 #ifdef MARK_ARRAY
6376 /* Support for mark_array */
6377
6378 inline
6379 DWORD*& card_table_mark_array (DWORD* c_table)
6380 {
6381     return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->mark_array;
6382 }
6383
6384 #if defined (_TARGET_AMD64_)
6385 #define mark_bit_pitch ((size_t)16)
6386 #else
6387 #define mark_bit_pitch ((size_t)8)
6388 #endif //AMD64
6389 #define mark_word_width ((size_t)32)
6390 #define mark_word_size (mark_word_width * mark_bit_pitch)
6391
6392 inline
6393 BYTE* align_on_mark_bit (BYTE* add)
6394 {
6395     return (BYTE*)((size_t)(add + (mark_bit_pitch - 1)) & ~(mark_bit_pitch - 1));
6396 }
6397
6398 inline
6399 BYTE* align_lower_mark_bit (BYTE* add)
6400 {
6401     return (BYTE*)((size_t)(add) & ~(mark_bit_pitch - 1));
6402 }
6403
6404 inline
6405 BOOL is_aligned_on_mark_word (BYTE* add)
6406 {
6407     return ((size_t)add == ((size_t)(add) & ~(mark_word_size - 1)));
6408 }
6409
6410 inline
6411 BYTE* align_on_mark_word (BYTE* add)
6412 {
6413     return (BYTE*)((size_t)(add + mark_word_size - 1) & ~(mark_word_size - 1));
6414 }
6415
6416 inline
6417 BYTE* align_lower_mark_word (BYTE* add)
6418 {
6419     return (BYTE*)((size_t)(add) & ~(mark_word_size - 1));
6420 }
6421
6422 inline
6423 size_t mark_bit_of (BYTE* add)
6424 {
6425     return ((size_t)add / mark_bit_pitch);
6426 }
6427
6428 inline
6429 unsigned int mark_bit_bit (size_t mark_bit)
6430 {
6431     return (unsigned int)(mark_bit % mark_word_width);
6432 }
6433
6434 inline
6435 size_t mark_bit_word (size_t mark_bit)
6436 {
6437     return (mark_bit / mark_word_width);
6438 }
6439
6440 inline
6441 size_t mark_word_of (BYTE* add)
6442 {
6443     return ((size_t)add) / mark_word_size;
6444 }
6445
6446 BYTE* mark_word_address (size_t wd)
6447 {
6448     return (BYTE*)(wd*mark_word_size);
6449 }
6450
6451 BYTE* mark_bit_address (size_t mark_bit)
6452 {
6453     return (BYTE*)(mark_bit*mark_bit_pitch);
6454 }
6455
6456 inline
6457 size_t mark_bit_bit_of (BYTE* add)
6458 {
6459     return  (((size_t)add / mark_bit_pitch) % mark_word_width);
6460 }
6461
6462 inline
6463 unsigned int gc_heap::mark_array_marked(BYTE* add)
6464 {
6465     return mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add));
6466 }
6467
6468 inline
6469 BOOL gc_heap::is_mark_bit_set (BYTE* add)
6470 {
6471     return (mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add)));
6472 }
6473
6474 inline
6475 void gc_heap::mark_array_set_marked (BYTE* add)
6476 {
6477     size_t index = mark_word_of (add);
6478     DWORD val = (1 << mark_bit_bit_of (add));
6479 #ifdef MULTIPLE_HEAPS
6480     InterlockedOr ((LONG*)&(mark_array [index]), val);
6481 #else
6482     mark_array [index] |= val;
6483 #endif 
6484 }
6485
6486 inline
6487 void gc_heap::mark_array_clear_marked (BYTE* add)
6488 {
6489     mark_array [mark_word_of (add)] &= ~(1 << mark_bit_bit_of (add));
6490 }
6491
6492 size_t size_mark_array_of (BYTE* from, BYTE* end)
6493 {
6494     assert (((size_t)from & ((mark_word_size)-1)) == 0);
6495     assert (((size_t)end  & ((mark_word_size)-1)) == 0);
6496     return sizeof (DWORD)*(((end - from) / mark_word_size));
6497 }
6498
6499 //In order to eliminate the lowest_address in the mark array
6500 //computations (mark_word_of, etc) mark_array is offset
6501 // according to the lowest_address.
6502 DWORD* translate_mark_array (DWORD* ma)
6503 {
6504     return (DWORD*)((BYTE*)ma - size_mark_array_of (0, g_lowest_address));
6505 }
6506
6507 // from and end must be page aligned addresses. 
6508 void gc_heap::clear_mark_array (BYTE* from, BYTE* end, BOOL check_only/*=TRUE*/)
6509 {
6510     if(!gc_can_use_concurrent)
6511         return;
6512
6513     assert (from == align_on_mark_word (from));
6514     assert (end == align_on_mark_word (end));
6515
6516 #ifdef BACKGROUND_GC
6517     BYTE* current_lowest_address = background_saved_lowest_address;
6518     BYTE* current_highest_address = background_saved_highest_address;
6519 #else
6520     BYTE* current_lowest_address = lowest_address;
6521     BYTE* current_highest_address = highest_address;
6522 #endif //BACKGROUND_GC
6523
6524     //there is a possibility of the addresses to be
6525     //outside of the covered range because of a newly allocated
6526     //large object segment
6527     if ((end <= current_highest_address) && (from >= current_lowest_address))
6528     {
6529         size_t beg_word = mark_word_of (align_on_mark_word (from));
6530         MAYBE_UNUSED_VAR(beg_word);
6531         //align end word to make sure to cover the address
6532         size_t end_word = mark_word_of (align_on_mark_word (end));
6533         MAYBE_UNUSED_VAR(end_word);
6534         dprintf (3, ("Calling clearing mark array [%Ix, %Ix[ for addresses [%Ix, %Ix[(%s)",
6535                      (size_t)mark_word_address (beg_word),
6536                      (size_t)mark_word_address (end_word),
6537                      (size_t)from, (size_t)end,
6538                      (check_only ? "check_only" : "clear")));
6539         if (!check_only)
6540         {
6541             BYTE* op = from;
6542             while (op < mark_word_address (beg_word))
6543             {
6544                 mark_array_clear_marked (op);
6545                 op += mark_bit_pitch;
6546             }
6547
6548             memset (&mark_array[beg_word], 0, (end_word - beg_word)*sizeof (DWORD));
6549         }
6550 #ifdef _DEBUG
6551         else
6552         {
6553             //Beware, it is assumed that the mark array word straddling
6554             //start has been cleared before
6555             //verify that the array is empty.
6556             size_t  markw = mark_word_of (align_on_mark_word (from));
6557             size_t  markw_end = mark_word_of (align_on_mark_word (end));
6558             while (markw < markw_end)
6559             {
6560                 assert (!(mark_array [markw]));
6561                 markw++;
6562             }
6563             BYTE* p = mark_word_address (markw_end);
6564             while (p < end)
6565             {
6566                 assert (!(mark_array_marked (p)));
6567                 p++;
6568             }
6569         }
6570 #endif //_DEBUG
6571     }
6572 }
6573 #endif //MARK_ARRAY
6574
6575 //These work on untranslated card tables
6576 inline
6577 DWORD*& card_table_next (DWORD* c_table)
6578 {
6579     return ((card_table_info*)((BYTE*)c_table - sizeof (card_table_info)))->next_card_table;
6580 }
6581
6582 void own_card_table (DWORD* c_table)
6583 {
6584     card_table_refcount (c_table) += 1;
6585 }
6586
6587 void destroy_card_table (DWORD* c_table);
6588
6589 void delete_next_card_table (DWORD* c_table)
6590 {
6591     DWORD* n_table = card_table_next (c_table);
6592     if (n_table)
6593     {
6594         if (card_table_next (n_table))
6595         {
6596             delete_next_card_table (n_table);
6597         }
6598         if (card_table_refcount (n_table) == 0)
6599         {
6600             destroy_card_table (n_table);
6601             card_table_next (c_table) = 0;
6602         }
6603     }
6604 }
6605
6606 void release_card_table (DWORD* c_table)
6607 {
6608     assert (card_table_refcount (c_table) >0);
6609     card_table_refcount (c_table) -= 1;
6610     if (card_table_refcount (c_table) == 0)
6611     {
6612         delete_next_card_table (c_table);
6613         if (card_table_next (c_table) == 0)
6614         {
6615             destroy_card_table (c_table);
6616             // sever the link from the parent
6617             if (&g_card_table[card_word (gcard_of(g_lowest_address))] == c_table)
6618                 g_card_table = 0;
6619             DWORD* p_table = &g_card_table[card_word (gcard_of(g_lowest_address))];
6620             if (p_table)
6621             {
6622                 while (p_table && (card_table_next (p_table) != c_table))
6623                     p_table = card_table_next (p_table);
6624                 card_table_next (p_table) = 0;
6625             }
6626         }
6627     }
6628 }
6629
6630 void destroy_card_table (DWORD* c_table)
6631 {
6632 //  delete (DWORD*)&card_table_refcount(c_table);
6633     VirtualFree (&card_table_refcount(c_table), 0, MEM_RELEASE);
6634     dprintf (2, ("Table Virtual Free : %Ix", (size_t)&card_table_refcount(c_table)));
6635 }
6636
6637 DWORD* gc_heap::make_card_table (BYTE* start, BYTE* end)
6638 {
6639     assert (g_lowest_address == start);
6640     assert (g_highest_address == end);
6641
6642     DWORD mem_flags = MEM_RESERVE;
6643
6644     size_t bs = size_brick_of (start, end);
6645     size_t cs = size_card_of (start, end);
6646 #ifdef MARK_ARRAY
6647     size_t ms = (gc_can_use_concurrent ? 
6648                  size_mark_array_of (start, end) :
6649                  0);
6650 #else
6651     size_t ms = 0;
6652 #endif //MARK_ARRAY
6653
6654     size_t cb = 0;
6655
6656 #ifdef CARD_BUNDLE
6657     if (can_use_write_watch())
6658     {
6659         mem_flags |= MEM_WRITE_WATCH;
6660         cb = size_card_bundle_of (g_lowest_address, g_highest_address);
6661     }
6662 #endif //CARD_BUNDLE
6663
6664 #ifdef GROWABLE_SEG_MAPPING_TABLE
6665     size_t st = size_seg_mapping_table_of (g_lowest_address, g_highest_address);
6666 #else //GROWABLE_SEG_MAPPING_TABLE
6667     size_t st = 0;
6668 #endif //GROWABLE_SEG_MAPPING_TABLE
6669
6670     // it is impossible for alloc_size to overflow due bounds on each of 
6671     // its components.
6672     size_t alloc_size = sizeof (BYTE)*(bs + cs + cb + ms + st + sizeof (card_table_info));
6673     size_t alloc_size_aligned = Align (alloc_size, g_SystemInfo.dwAllocationGranularity-1);
6674
6675     DWORD* ct = (DWORD*)VirtualAlloc (0, alloc_size_aligned,
6676                                       mem_flags, PAGE_READWRITE);
6677
6678     if (!ct)
6679         return 0;
6680
6681     dprintf (2, ("init - table alloc for %Id bytes: [%Ix, %Ix[",
6682                  alloc_size, (size_t)ct, (size_t)((BYTE*)ct+alloc_size)));
6683
6684     // mark array will be committed separately (per segment).
6685     size_t commit_size = alloc_size - ms;
6686         
6687     if (!VirtualAlloc ((BYTE*)ct, commit_size, MEM_COMMIT, PAGE_READWRITE))
6688     {
6689         dprintf (2, ("Table commit failed: %d", GetLastError()));
6690         VirtualFree ((BYTE*)ct, 0, MEM_RELEASE);
6691         return 0;
6692     }
6693
6694     // initialize the ref count
6695     ct = (DWORD*)((BYTE*)ct+sizeof (card_table_info));
6696     card_table_refcount (ct) = 0;
6697     card_table_lowest_address (ct) = start;
6698     card_table_highest_address (ct) = end;
6699     card_table_brick_table (ct) = (short*)((BYTE*)ct + cs);
6700     card_table_next (ct) = 0;
6701
6702 #ifdef CARD_BUNDLE
6703     card_table_card_bundle_table (ct) = (DWORD*)((BYTE*)card_table_brick_table (ct) + bs);
6704 #endif //CARD_BUNDLE
6705
6706 #ifdef GROWABLE_SEG_MAPPING_TABLE
6707     seg_mapping_table = (seg_mapping*)((BYTE*)card_table_brick_table (ct) + bs + cb);
6708     seg_mapping_table = (seg_mapping*)((BYTE*)seg_mapping_table - 
6709                                         size_seg_mapping_table_of (0, (align_lower_segment (g_lowest_address))));
6710 #endif //GROWABLE_SEG_MAPPING_TABLE
6711
6712 #ifdef MARK_ARRAY
6713     if (gc_can_use_concurrent)
6714         card_table_mark_array (ct) = (DWORD*)((BYTE*)card_table_brick_table (ct) + bs + cb + st);
6715     else
6716         card_table_mark_array (ct) = NULL;
6717 #endif //MARK_ARRAY
6718
6719     return translate_card_table(ct);
6720 }
6721
6722 void gc_heap::set_fgm_result (failure_get_memory f, size_t s, BOOL loh_p)
6723 {
6724 #ifdef MULTIPLE_HEAPS
6725     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
6726     {
6727         gc_heap* hp = gc_heap::g_heaps [hn];
6728         hp->fgm_result.set_fgm (f, s, loh_p);
6729     }
6730 #else //MULTIPLE_HEAPS
6731     fgm_result.set_fgm (f, s, loh_p);
6732 #endif //MULTIPLE_HEAPS
6733 }
6734
6735 //returns 0 for success, -1 otherwise
6736 // We are doing all the decommitting here because we want to make sure we have
6737 // enough memory to do so - if we do this during copy_brick_card_table and 
6738 // and fail to decommit it would make the failure case very complicated to 
6739 // handle. This way we can waste some decommit if we call this multiple 
6740 // times before the next FGC but it's easier to handle the failure case.
6741 int gc_heap::grow_brick_card_tables (BYTE* start, 
6742                                      BYTE* end, 
6743                                      size_t size,
6744                                      heap_segment* new_seg, 
6745                                      gc_heap* hp, 
6746                                      BOOL loh_p)
6747 {
6748     BYTE* la = g_lowest_address;
6749     BYTE* ha = g_highest_address;
6750     BYTE* saved_g_lowest_address = min (start, g_lowest_address);
6751     BYTE* saved_g_highest_address = max (end, g_highest_address);
6752 #ifdef BACKGROUND_GC
6753     // This value is only for logging purpose - it's not necessarily exactly what we 
6754     // would commit for mark array but close enough for diagnostics purpose.
6755     size_t logging_ma_commit_size = size_mark_array_of (0, (BYTE*)size);
6756 #endif //BACKGROUND_GC
6757
6758     // See if the address is already covered
6759     if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address))
6760     {
6761         {
6762             //modify the higest address so the span covered
6763             //is twice the previous one.
6764             MEMORYSTATUSEX st;
6765             GetProcessMemoryLoad (&st);
6766             BYTE* top = (BYTE*)0 + Align ((size_t)(st.ullTotalVirtual));
6767             size_t ps = ha-la;
6768             BYTE* highest = max ((saved_g_lowest_address + 2*ps), saved_g_highest_address);
6769             //BYTE* highest = saved_g_highest_address;
6770             if (highest > top)
6771             {
6772                 highest = top;
6773             }
6774             if (highest > saved_g_highest_address)
6775             {
6776                 saved_g_highest_address = highest;
6777             }
6778         }
6779         dprintf (GC_TABLE_LOG, ("Growing card table [%Ix, %Ix[",
6780                                 (size_t)saved_g_lowest_address,
6781                                 (size_t)saved_g_highest_address));
6782
6783         DWORD mem_flags = MEM_RESERVE;
6784         DWORD* saved_g_card_table = g_card_table;
6785         DWORD* ct = 0;
6786         short* bt = 0;
6787
6788         size_t cs = size_card_of (saved_g_lowest_address, saved_g_highest_address);
6789         size_t bs = size_brick_of (saved_g_lowest_address, saved_g_highest_address);
6790
6791 #ifdef MARK_ARRAY
6792         size_t ms = (gc_heap::gc_can_use_concurrent ? 
6793                     size_mark_array_of (saved_g_lowest_address, saved_g_highest_address) :
6794                     0);
6795 #else
6796         size_t ms = 0;
6797 #endif //MARK_ARRAY
6798
6799         size_t cb = 0;
6800
6801 #ifdef CARD_BUNDLE
6802         if (can_use_write_watch())
6803         {
6804             mem_flags |= MEM_WRITE_WATCH;
6805             cb = size_card_bundle_of (saved_g_lowest_address, saved_g_highest_address);
6806         }
6807 #endif //CARD_BUNDLE
6808
6809 #ifdef GROWABLE_SEG_MAPPING_TABLE
6810         size_t st = size_seg_mapping_table_of (saved_g_lowest_address, saved_g_highest_address);
6811 #else //GROWABLE_SEG_MAPPING_TABLE
6812         size_t st = 0;
6813 #endif //GROWABLE_SEG_MAPPING_TABLE
6814
6815         // it is impossible for alloc_size to overflow due bounds on each of 
6816         // its components.
6817         size_t alloc_size = sizeof (BYTE)*(bs + cs + cb + ms +st + sizeof (card_table_info));
6818         size_t alloc_size_aligned = Align (alloc_size, g_SystemInfo.dwAllocationGranularity-1);
6819         dprintf (GC_TABLE_LOG, ("brick table: %Id; card table: %Id; mark array: %Id, card bundle: %Id, seg table: %Id",
6820                                   bs, cs, ms, cb, st));
6821
6822         BYTE* mem = (BYTE*)VirtualAlloc (0, alloc_size_aligned, mem_flags, PAGE_READWRITE);
6823
6824         if (!mem)
6825         {
6826             set_fgm_result (fgm_grow_table, alloc_size, loh_p);
6827             goto fail;
6828         }
6829
6830         dprintf (GC_TABLE_LOG, ("Table alloc for %Id bytes: [%Ix, %Ix[",
6831                                  alloc_size, (size_t)mem, (size_t)((BYTE*)mem+alloc_size)));
6832
6833         {   
6834             // mark array will be committed separately (per segment).
6835             size_t commit_size = alloc_size - ms;
6836
6837             if (!VirtualAlloc (mem, commit_size, MEM_COMMIT, PAGE_READWRITE))
6838             {
6839                 dprintf (GC_TABLE_LOG, ("Table commit failed"));
6840                 set_fgm_result (fgm_commit_table, commit_size, loh_p);
6841                 goto fail;
6842             }
6843         }
6844
6845         ct = (DWORD*)(mem + sizeof (card_table_info));
6846         card_table_refcount (ct) = 0;
6847         card_table_lowest_address (ct) = saved_g_lowest_address;
6848         card_table_highest_address (ct) = saved_g_highest_address;
6849         card_table_next (ct) = &g_card_table[card_word (gcard_of (la))];
6850
6851         //clear the card table
6852 /*
6853         memclr ((BYTE*)ct,
6854                 (((saved_g_highest_address - saved_g_lowest_address)*sizeof (DWORD) /
6855                   (card_size * card_word_width))
6856                  + sizeof (DWORD)));
6857 */
6858
6859         bt = (short*)((BYTE*)ct + cs);
6860
6861         // No initialization needed, will be done in copy_brick_card
6862
6863         card_table_brick_table (ct) = bt;
6864
6865 #ifdef CARD_BUNDLE
6866         card_table_card_bundle_table (ct) = (DWORD*)((BYTE*)card_table_brick_table (ct) + bs);
6867         //set all bundle to look at all of the cards
6868         memset(card_table_card_bundle_table (ct), 0xFF, cb);
6869 #endif //CARD_BUNDLE
6870
6871 #ifdef GROWABLE_SEG_MAPPING_TABLE
6872         {
6873             seg_mapping* new_seg_mapping_table = (seg_mapping*)((BYTE*)card_table_brick_table (ct) + bs + cb);
6874             new_seg_mapping_table = (seg_mapping*)((BYTE*)new_seg_mapping_table - 
6875                                               size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address))));
6876             memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_lowest_address)],
6877                 &seg_mapping_table[seg_mapping_word_of(g_lowest_address)],
6878                 size_seg_mapping_table_of(g_lowest_address, g_highest_address));
6879
6880             seg_mapping_table = new_seg_mapping_table;
6881         }
6882 #endif //GROWABLE_SEG_MAPPING_TABLE
6883
6884 #ifdef MARK_ARRAY
6885         if(gc_can_use_concurrent)
6886             card_table_mark_array (ct) = (DWORD*)((BYTE*)card_table_brick_table (ct) + bs + cb + st);
6887         else
6888             card_table_mark_array (ct) = NULL;
6889 #endif //MARK_ARRAY
6890
6891         g_card_table = translate_card_table (ct);
6892
6893         dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix", 
6894             (size_t)ct, (size_t)g_card_table, (size_t)seg_mapping_table, (size_t)card_table_mark_array (ct)));
6895
6896 #ifdef BACKGROUND_GC
6897         if (hp->should_commit_mark_array())
6898         {
6899             dprintf (GC_TABLE_LOG, ("new low: %Ix, new high: %Ix, latest mark array is %Ix(translate: %Ix)", 
6900                                     saved_g_lowest_address, saved_g_highest_address,
6901                                     card_table_mark_array (ct),
6902                                     translate_mark_array (card_table_mark_array (ct))));
6903             DWORD* new_mark_array = (DWORD*)((BYTE*)card_table_mark_array (ct) - size_mark_array_of (0, saved_g_lowest_address));
6904             if (!commit_new_mark_array_global (new_mark_array))
6905             {
6906                 dprintf (GC_TABLE_LOG, ("failed to commit portions in the mark array for existing segments"));
6907                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
6908                 goto fail;
6909             }
6910
6911             if (!commit_mark_array_new_seg (hp, new_seg, saved_g_lowest_address))
6912             {
6913                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg"));
6914                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
6915                 goto fail;
6916             }
6917         }
6918         else
6919         {
6920             clear_commit_flag_global();
6921         }
6922 #endif //BACKGROUND_GC
6923
6924         // This passes a bool telling whether we need to switch to the post
6925         // grow version of the write barrier.  This test tells us if the new
6926         // segment was allocated at a lower address than the old, requiring
6927         // that we start doing an upper bounds check in the write barrier.
6928         StompWriteBarrierResize(la != saved_g_lowest_address);
6929
6930         // We need to make sure that other threads executing checked write barriers
6931         // will see the g_card_table update before g_lowest/highest_address updates.
6932         // Otherwise, the checked write barrier may AV accessing the old card table
6933         // with address that it does not cover. Write barriers access card table 
6934         // without memory barriers for performance reasons, so we need to flush 
6935         // the store buffers here.
6936         FlushProcessWriteBuffers();
6937
6938         g_lowest_address = saved_g_lowest_address;
6939         VolatileStore(&g_highest_address, saved_g_highest_address);
6940
6941         return 0;
6942         
6943 fail:
6944         //cleanup mess and return -1;
6945
6946         if (mem)
6947         {
6948             if (g_card_table != saved_g_card_table)
6949             {
6950                 g_card_table = saved_g_card_table; 
6951             }
6952
6953             //delete (DWORD*)((BYTE*)ct - sizeof(card_table_info));
6954             if (!VirtualFree (mem, 0, MEM_RELEASE))
6955             {
6956                 dprintf (GC_TABLE_LOG, ("VirtualFree failed: %d", GetLastError()));
6957                 assert (!"release failed");
6958             }
6959         }
6960
6961         return -1;
6962     }
6963     else
6964     {
6965 #ifdef BACKGROUND_GC
6966         if (hp->should_commit_mark_array())
6967         {
6968             dprintf (GC_TABLE_LOG, ("in range new seg %Ix, mark_array is %Ix", new_seg, hp->mark_array));
6969             if (!commit_mark_array_new_seg (hp, new_seg))
6970             {
6971                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg in range"));
6972                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
6973                 return -1;
6974             }
6975         }
6976 #endif //BACKGROUND_GC
6977     }
6978
6979     return 0;
6980 }
6981
6982 //copy all of the arrays managed by the card table for a page aligned range
6983 void gc_heap::copy_brick_card_range (BYTE* la, DWORD* old_card_table,
6984                                      short* old_brick_table,
6985                                      heap_segment* seg,
6986                                      BYTE* start, BYTE* end, BOOL heap_expand)
6987 {
6988     ptrdiff_t brick_offset = brick_of (start) - brick_of (la);
6989
6990
6991     dprintf (2, ("copying tables for range [%Ix %Ix[", (size_t)start, (size_t)end));
6992
6993     // copy brick table
6994     short* brick_start = &brick_table [brick_of (start)];
6995     if (old_brick_table)
6996     {
6997         // segments are always on page boundaries
6998         memcpy (brick_start, &old_brick_table[brick_offset],
6999                 size_brick_of (start, end));
7000
7001     }
7002     else
7003     {
7004         // This is a large heap, just clear the brick table
7005     }
7006
7007     DWORD* old_ct = &old_card_table[card_word (card_of (la))];
7008 #ifdef MARK_ARRAY
7009 #ifdef BACKGROUND_GC
7010     if (recursive_gc_sync::background_running_p())
7011     {
7012         DWORD* old_mark_array = card_table_mark_array (old_ct);
7013
7014         // We don't need to go through all the card tables here because 
7015         // we only need to copy from the GC version of the mark array - when we
7016         // mark (even in allocate_large_object) we always use that mark array.
7017         if ((card_table_highest_address (old_ct) >= start) &&
7018             (card_table_lowest_address (old_ct) <= end))
7019         {
7020             if ((background_saved_highest_address >= start) &&
7021                 (background_saved_lowest_address <= end))
7022             {
7023                 //copy the mark bits
7024                 // segments are always on page boundaries
7025                 BYTE* m_start = max (background_saved_lowest_address, start);
7026                 BYTE* m_end = min (background_saved_highest_address, end);
7027                 memcpy (&mark_array[mark_word_of (m_start)],
7028                         &old_mark_array[mark_word_of (m_start) - mark_word_of (la)],
7029                         size_mark_array_of (m_start, m_end));
7030             }
7031         }
7032         else
7033         {
7034             //only large segments can be out of range
7035             assert (old_brick_table == 0);
7036         }
7037     }
7038 #else //BACKGROUND_GC
7039     assert (seg != 0);
7040     clear_mark_array (start, heap_segment_committed(seg));
7041 #endif //BACKGROUND_GC
7042 #endif //MARK_ARRAY
7043
7044     // n way merge with all of the card table ever used in between
7045     DWORD* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]);
7046
7047     assert (ct);
7048     while (card_table_next (old_ct) != ct)
7049     {
7050         //copy if old card table contained [start, end[
7051         if ((card_table_highest_address (ct) >= end) &&
7052             (card_table_lowest_address (ct) <= start))
7053         {
7054             // or the card_tables
7055             DWORD* dest = &card_table [card_word (card_of (start))];
7056             DWORD* src = &((translate_card_table (ct)) [card_word (card_of (start))]);
7057             ptrdiff_t count = count_card_of (start, end);
7058             for (int x = 0; x < count; x++)
7059             {
7060                 *dest |= *src;
7061                 dest++;
7062                 src++;
7063             }
7064         }
7065         ct = card_table_next (ct);
7066     }
7067
7068 }
7069
7070 //initialize all of the arrays managed by the card table for a page aligned range when an existing ro segment becomes in range
7071 void gc_heap::init_brick_card_range (heap_segment* seg)
7072 {
7073     dprintf (2, ("initialising tables for range [%Ix %Ix[",
7074                  (size_t)heap_segment_mem (seg),
7075                  (size_t)heap_segment_allocated (seg)));
7076
7077     // initialize the brick table
7078     for (size_t b = brick_of (heap_segment_mem (seg));
7079          b < brick_of (align_on_brick (heap_segment_allocated (seg)));
7080          b++)
7081     {
7082         set_brick (b, -1);
7083     }
7084
7085 #ifdef MARK_ARRAY
7086     if (recursive_gc_sync::background_running_p() && (seg->flags & heap_segment_flags_ma_committed))
7087     {
7088         assert (seg != 0);
7089         clear_mark_array (heap_segment_mem (seg), heap_segment_committed(seg));
7090     }
7091 #endif //MARK_ARRAY
7092
7093     clear_card_for_addresses (heap_segment_mem (seg),
7094                               heap_segment_allocated (seg));
7095 }
7096
7097 void gc_heap::copy_brick_card_table(BOOL heap_expand)
7098 {
7099     BYTE* la = lowest_address;
7100     BYTE* ha = highest_address;
7101     MAYBE_UNUSED_VAR(ha);
7102     DWORD* old_card_table = card_table;
7103     short* old_brick_table = brick_table;
7104
7105     assert (la == card_table_lowest_address (&old_card_table[card_word (card_of (la))]));
7106     assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))]));
7107
7108     /* todo: Need a global lock for this */
7109     DWORD* ct = &g_card_table[card_word (gcard_of (g_lowest_address))];
7110     own_card_table (ct);
7111     card_table = translate_card_table (ct);
7112     /* End of global lock */
7113     highest_address = card_table_highest_address (ct);
7114     lowest_address = card_table_lowest_address (ct);
7115
7116     brick_table = card_table_brick_table (ct);
7117
7118 #ifdef MARK_ARRAY
7119     if (gc_can_use_concurrent)
7120     {
7121         mark_array = translate_mark_array (card_table_mark_array (ct));
7122         assert (mark_word_of (g_highest_address) ==
7123             mark_word_of (align_on_mark_word (g_highest_address)));
7124     }
7125     else
7126         mark_array = NULL;
7127 #endif //MARK_ARRAY
7128
7129 #ifdef CARD_BUNDLE
7130 #if defined(MARK_ARRAY) && defined(_DEBUG)
7131 #ifdef GROWABLE_SEG_MAPPING_TABLE
7132     size_t st = size_seg_mapping_table_of (g_lowest_address, g_highest_address);
7133 #else  //GROWABLE_SEG_MAPPING_TABLE
7134     size_t st = 0;
7135 #endif //GROWABLE_SEG_MAPPING_TABLE
7136     assert (!gc_can_use_concurrent || 
7137             (((BYTE*)card_table_card_bundle_table (ct) + size_card_bundle_of (g_lowest_address, g_highest_address) + st) == (BYTE*)card_table_mark_array (ct)));
7138 #endif //MARK_ARRAY && _DEBUG
7139     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct));
7140     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_lowest_address))))] ==
7141             card_table_card_bundle_table (ct));
7142
7143     //set the card table if we are in a heap growth scenario
7144     if (card_bundles_enabled())
7145     {
7146         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
7147                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
7148     }
7149     //check if we need to turn on card_bundles.
7150 #ifdef MULTIPLE_HEAPS
7151     // use __int64 arithmetic here because of possible overflow on 32p
7152     unsigned __int64 th = (unsigned __int64)MH_TH_CARD_BUNDLE*gc_heap::n_heaps;
7153 #else
7154     // use __int64 arithmetic here because of possible overflow on 32p
7155     unsigned __int64 th = (unsigned __int64)SH_TH_CARD_BUNDLE;
7156 #endif //MULTIPLE_HEAPS
7157     if (reserved_memory >= th)
7158     {
7159         enable_card_bundles();
7160     }
7161
7162 #endif //CARD_BUNDLE
7163
7164     // for each of the segments and heaps, copy the brick table and
7165     // or the card table
7166     heap_segment* seg = generation_start_segment (generation_of (max_generation));
7167     while (seg)
7168     {
7169         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7170         {
7171             //check if it became in range
7172             if ((heap_segment_reserved (seg) > lowest_address) &&
7173                 (heap_segment_mem (seg) < highest_address))
7174             {
7175                 set_ro_segment_in_range (seg);
7176             }
7177         }
7178         else
7179         {
7180
7181             BYTE* end = align_on_page (heap_segment_allocated (seg));
7182             copy_brick_card_range (la, old_card_table,
7183                                    old_brick_table,
7184                                    seg,
7185                                    align_lower_page (heap_segment_mem (seg)),
7186                                    end,
7187                                    heap_expand);
7188         }
7189         seg = heap_segment_next (seg);
7190     }
7191
7192     seg = generation_start_segment (large_object_generation);
7193     while (seg)
7194     {
7195         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7196         {
7197             //check if it became in range
7198             if ((heap_segment_reserved (seg) > lowest_address) &&
7199                 (heap_segment_mem (seg) < highest_address))
7200             {
7201                 set_ro_segment_in_range (seg);
7202             }
7203         }
7204         else
7205         {
7206             BYTE* end = align_on_page (heap_segment_allocated (seg));
7207             copy_brick_card_range (la, old_card_table,
7208                                    0,
7209                                    seg,
7210                                    align_lower_page (heap_segment_mem (seg)),
7211                                    end,
7212                                    heap_expand);
7213         }
7214         seg = heap_segment_next (seg);
7215     }
7216
7217     release_card_table (&old_card_table[card_word (card_of(la))]);
7218 }
7219
7220 #ifdef FEATURE_BASICFREEZE
7221 BOOL gc_heap::insert_ro_segment (heap_segment* seg)
7222 {
7223     enter_spin_lock (&gc_heap::gc_lock);
7224
7225     if (!gc_heap::seg_table->insure_space_for_insert () ||
7226         (!(should_commit_mark_array() && commit_mark_array_new_seg (__this, seg))))
7227     {
7228         leave_spin_lock (&gc_heap::gc_lock);
7229         return FALSE;
7230     }
7231
7232     //insert at the head of the segment list
7233     generation* gen2 = generation_of (max_generation);
7234     heap_segment* oldhead = generation_start_segment (gen2);
7235     heap_segment_next (seg) = oldhead;
7236     generation_start_segment (gen2) = seg;
7237
7238     ptrdiff_t sdelta = 0;
7239     seg_table->insert ((BYTE*)seg, sdelta);
7240
7241 #ifdef SEG_MAPPING_TABLE
7242     seg_mapping_table_add_ro_segment (seg);
7243 #endif //SEG_MAPPING_TABLE
7244
7245     //test if in range
7246     if ((heap_segment_reserved (seg) > lowest_address) &&
7247         (heap_segment_mem (seg) < highest_address))
7248     {
7249         set_ro_segment_in_range (seg);
7250     }
7251
7252     FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(seg), (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), ETW::GCLog::ETW_GC_INFO::READ_ONLY_HEAP, GetClrInstanceId());
7253
7254     leave_spin_lock (&gc_heap::gc_lock);
7255     return TRUE;
7256 }
7257
7258 // No one is calling this function right now. If this is getting called we need
7259 // to take care of decommitting the mark array for it - we will need to remember
7260 // which portion of the mark array was committed and only decommit that.
7261 void gc_heap::remove_ro_segment (heap_segment* seg)
7262 {
7263 //clear the mark bits so a new segment allocated in its place will have a clear mark bits
7264 #ifdef MARK_ARRAY
7265     if (gc_can_use_concurrent)
7266     {
7267         clear_mark_array (align_lower_mark_word (max (heap_segment_mem (seg), lowest_address)),
7268                       align_on_card_word (min (heap_segment_allocated (seg), highest_address)),
7269                       false); // read_only segments need the mark clear
7270     }
7271 #endif //MARK_ARRAY
7272
7273     enter_spin_lock (&gc_heap::gc_lock);
7274
7275     seg_table->remove ((BYTE*)seg);
7276
7277 #ifdef SEG_MAPPING_TABLE
7278     seg_mapping_table_remove_ro_segment (seg);
7279 #endif //SEG_MAPPING_TABLE
7280
7281     // Locate segment (and previous segment) in the list.
7282     generation* gen2 = generation_of (max_generation);
7283     heap_segment* curr_seg = generation_start_segment (gen2);
7284     heap_segment* prev_seg = NULL;
7285
7286     while (curr_seg && curr_seg != seg)
7287     {
7288         prev_seg = curr_seg;
7289         curr_seg = heap_segment_next (curr_seg);
7290     }
7291     assert (curr_seg == seg);
7292
7293     // Patch previous segment (or list head if there is none) to skip the removed segment.
7294     if (prev_seg)
7295         heap_segment_next (prev_seg) = heap_segment_next (curr_seg);
7296     else
7297         generation_start_segment (gen2) = heap_segment_next (curr_seg);
7298
7299     leave_spin_lock (&gc_heap::gc_lock);
7300 }
7301 #endif //FEATURE_BASICFREEZE
7302
7303 BOOL gc_heap::set_ro_segment_in_range (heap_segment* seg)
7304 {
7305     //set it in range
7306     seg->flags |= heap_segment_flags_inrange;
7307 //    init_brick_card_range (seg);
7308     ro_segments_in_range = TRUE;
7309     //right now, segments aren't protected
7310     //unprotect_segment (seg);
7311     return TRUE;
7312 }
7313
7314 #ifdef MARK_LIST
7315
7316 BYTE** make_mark_list (size_t size)
7317 {
7318     BYTE** mark_list = new (nothrow) BYTE* [size];
7319     return mark_list;
7320 }
7321
7322 #define swap(a,b){BYTE* t; t = a; a = b; b = t;}
7323
7324 void verify_qsort_array (BYTE* *low, BYTE* *high)
7325 {
7326     BYTE **i = 0;
7327
7328     for (i = low+1; i <= high; i++)
7329     {
7330         if (*i < *(i-1))
7331         {
7332             FATAL_GC_ERROR();
7333         }
7334     }
7335 }
7336
7337 #ifndef USE_INTROSORT
7338 void qsort1( BYTE* *low, BYTE* *high, unsigned int depth)
7339 {
7340     if (((low + 16) >= high) || (depth > 100))
7341     {
7342         //insertion sort
7343         BYTE **i, **j;
7344         for (i = low+1; i <= high; i++)
7345         {
7346             BYTE* val = *i;
7347             for (j=i;j >low && val<*(j-1);j--)
7348             {
7349                 *j=*(j-1);
7350             }
7351             *j=val;
7352         }
7353     }
7354     else
7355     {
7356         BYTE *pivot, **left, **right;
7357
7358         //sort low middle and high
7359         if (*(low+((high-low)/2)) < *low)
7360             swap (*(low+((high-low)/2)), *low);
7361         if (*high < *low)
7362             swap (*low, *high);
7363         if (*high < *(low+((high-low)/2)))
7364             swap (*(low+((high-low)/2)), *high);
7365
7366         swap (*(low+((high-low)/2)), *(high-1));
7367         pivot =  *(high-1);
7368         left = low; right = high-1;
7369         while (1) {
7370             while (*(--right) > pivot);
7371             while (*(++left)  < pivot);
7372             if (left < right)
7373             {
7374                 swap(*left, *right);
7375             }
7376             else
7377                 break;
7378         }
7379         swap (*left, *(high-1));
7380         qsort1(low, left-1, depth+1);
7381         qsort1(left+1, high, depth+1);
7382     }
7383 }
7384 #endif //USE_INTROSORT
7385 void rqsort1( BYTE* *low, BYTE* *high)
7386 {
7387     if ((low + 16) >= high)
7388     {
7389         //insertion sort
7390         BYTE **i, **j;
7391         for (i = low+1; i <= high; i++)
7392         {
7393             BYTE* val = *i;
7394             for (j=i;j >low && val>*(j-1);j--)
7395             {
7396                 *j=*(j-1);
7397             }
7398             *j=val;
7399         }
7400     }
7401     else
7402     {
7403         BYTE *pivot, **left, **right;
7404
7405         //sort low middle and high
7406         if (*(low+((high-low)/2)) > *low)
7407             swap (*(low+((high-low)/2)), *low);
7408         if (*high > *low)
7409             swap (*low, *high);
7410         if (*high > *(low+((high-low)/2)))
7411             swap (*(low+((high-low)/2)), *high);
7412
7413         swap (*(low+((high-low)/2)), *(high-1));
7414         pivot =  *(high-1);
7415         left = low; right = high-1;
7416         while (1) {
7417             while (*(--right) < pivot);
7418             while (*(++left)  > pivot);
7419             if (left < right)
7420             {
7421                 swap(*left, *right);
7422             }
7423             else
7424                 break;
7425         }
7426         swap (*left, *(high-1));
7427         rqsort1(low, left-1);
7428         rqsort1(left+1, high);
7429     }
7430 }
7431
7432 #ifdef USE_INTROSORT
7433 class introsort 
7434 {
7435
7436 private: 
7437     static const int size_threshold = 64;
7438     static const int max_depth = 100;
7439
7440
7441 inline static void swap_elements(BYTE** i,BYTE** j) 
7442     {
7443         BYTE* t=*i; 
7444         *i=*j; 
7445         *j=t;
7446     }
7447
7448 public:
7449     static void sort (BYTE** begin, BYTE** end, int ignored)
7450     {
7451         ignored = 0;
7452         introsort_loop (begin, end, max_depth);
7453         insertionsort (begin, end);
7454     }
7455
7456 private: 
7457
7458     static void introsort_loop (BYTE** lo, BYTE** hi, int depth_limit)
7459     {
7460         while (hi-lo >= size_threshold)
7461         {
7462             if (depth_limit == 0)
7463             {
7464                 heapsort (lo, hi);
7465                 return;
7466             }
7467             BYTE** p=median_partition (lo, hi);
7468             depth_limit=depth_limit-1;
7469             introsort_loop (p, hi, depth_limit);
7470             hi=p-1;
7471         }        
7472     }
7473
7474     static BYTE** median_partition (BYTE** low, BYTE** high)
7475     {
7476         BYTE *pivot, **left, **right;
7477
7478         //sort low middle and high
7479         if (*(low+((high-low)/2)) < *low)
7480             swap_elements ((low+((high-low)/2)), low);
7481         if (*high < *low)
7482             swap_elements (low, high);
7483         if (*high < *(low+((high-low)/2)))
7484             swap_elements ((low+((high-low)/2)), high);
7485
7486         swap_elements ((low+((high-low)/2)), (high-1));
7487         pivot =  *(high-1);
7488         left = low; right = high-1;
7489         while (1) {
7490             while (*(--right) > pivot);
7491             while (*(++left)  < pivot);
7492             if (left < right)
7493             {
7494                 swap_elements(left, right);
7495             }
7496             else
7497                 break;
7498         }
7499         swap_elements (left, (high-1));
7500         return left;
7501     }
7502
7503
7504     static void insertionsort (BYTE** lo, BYTE** hi)
7505     {
7506         for (BYTE** i=lo+1; i <= hi; i++)
7507         {
7508             BYTE** j = i;
7509             BYTE* t = *i;
7510             while((j > lo) && (t <*(j-1)))
7511             {
7512                 *j = *(j-1);
7513                 j--;
7514             }
7515             *j = t;
7516         }
7517     }
7518
7519     static void heapsort (BYTE** lo, BYTE** hi)
7520     { 
7521         size_t n = hi - lo + 1;
7522         for (size_t i=n / 2; i >= 1; i--)
7523         {
7524             downheap (i,n,lo);
7525         }
7526         for (size_t i = n; i > 1; i--)
7527         {
7528             swap_elements (lo, lo + i - 1);
7529             downheap(1, i - 1,  lo);
7530         }
7531     }
7532
7533     static void downheap (size_t i, size_t n, BYTE** lo)
7534     {
7535         BYTE* d = *(lo + i - 1);
7536         size_t child;
7537         while (i <= n / 2)
7538         {
7539             child = 2*i;
7540             if (child < n && *(lo + child - 1)<(*(lo + child)))
7541             {
7542                 child++;
7543             }
7544             if (!(d<*(lo + child - 1))) 
7545             {
7546                 break;
7547             }
7548             *(lo + i - 1) = *(lo + child - 1);
7549             i = child;
7550         }
7551         *(lo + i - 1) = d;
7552     }
7553
7554 };
7555
7556 #endif //USE_INTROSORT    
7557
7558 #ifdef MULTIPLE_HEAPS
7559 #ifdef PARALLEL_MARK_LIST_SORT
7560 void gc_heap::sort_mark_list()
7561 {
7562     // if this heap had a mark list overflow, we don't do anything
7563     if (mark_list_index > mark_list_end)
7564     {
7565 //        printf("sort_mark_list: overflow on heap %d\n", heap_number);
7566         return;
7567     }
7568
7569     // if any other heap had a mark list overflow, we fake one too,
7570     // so we don't use an incomplete mark list by mistake
7571     for (int i = 0; i < n_heaps; i++)
7572     {
7573         if (g_heaps[i]->mark_list_index > g_heaps[i]->mark_list_end)
7574         {
7575             mark_list_index = mark_list_end + 1;
7576 //            printf("sort_mark_list: overflow on heap %d\n", i);
7577             return;
7578         }
7579     }
7580
7581 //    unsigned long start = GetCycleCount32();
7582
7583     dprintf (3, ("Sorting mark lists"));
7584     if (mark_list_index > mark_list)
7585         _sort (mark_list, mark_list_index - 1, 0);
7586
7587 //    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);
7588 //    start = GetCycleCount32();
7589
7590     // first set the pieces for all heaps to empty
7591     int heap_num;
7592     for (heap_num = 0; heap_num < n_heaps; heap_num++)
7593     {
7594         mark_list_piece_start[heap_num] = NULL;
7595         mark_list_piece_end[heap_num] = NULL;
7596     }
7597
7598     BYTE** x = mark_list;
7599
7600 // predicate means: x is still within the mark list, and within the bounds of this heap
7601 #define predicate(x) (((x) < mark_list_index) && (*(x) < heap->ephemeral_high))
7602
7603     heap_num = -1;
7604     while (x < mark_list_index)
7605     {
7606         gc_heap* heap;
7607         // find the heap x points into - searching cyclically from the last heap,
7608         // because in many cases the right heap is the next one or comes soon after
7609         int last_heap_num = heap_num;
7610         MAYBE_UNUSED_VAR(last_heap_num);
7611         do
7612         {
7613             heap_num++;
7614             if (heap_num >= n_heaps)
7615                 heap_num = 0;
7616             assert(heap_num != last_heap_num); // we should always find the heap - infinite loop if not!
7617             heap = g_heaps[heap_num];
7618         }
7619         while (!(*x >= heap->ephemeral_low && *x < heap->ephemeral_high));
7620
7621         // x is the start of the mark list piece for this heap
7622         mark_list_piece_start[heap_num] = x;
7623
7624         // to find the end of the mark list piece for this heap, find the first x
7625         // that has !predicate(x), i.e. that is either not in this heap, or beyond the end of the list
7626         if (predicate(x))
7627         {
7628             // let's see if we get lucky and the whole rest belongs to this piece
7629             if (predicate(mark_list_index-1))
7630             {
7631                 x = mark_list_index;
7632                 mark_list_piece_end[heap_num] = x;
7633                 break;
7634             }
7635
7636             // we play a variant of binary search to find the point sooner.
7637             // the first loop advances by increasing steps until the predicate turns false.
7638             // then we retreat the last step, and the second loop advances by decreasing steps, keeping the predicate true.
7639             unsigned inc = 1;
7640             do
7641             {
7642                 inc *= 2;
7643                 BYTE** temp_x = x;
7644                 x += inc;
7645                 if (temp_x > x)
7646                 {
7647                     break;
7648                 }
7649             }
7650             while (predicate(x));
7651             // we know that only the last step was wrong, so we undo it
7652             x -= inc;
7653             do
7654             {
7655                 // loop invariant - predicate holds at x, but not x + inc
7656                 assert (predicate(x) && !(((x + inc) > x) && predicate(x + inc)));
7657                 inc /= 2;
7658                 if (((x + inc) > x) && predicate(x + inc))
7659                 {
7660                     x += inc;
7661                 }
7662             }
7663             while (inc > 1);
7664             // the termination condition and the loop invariant together imply this:
7665             assert(predicate(x) && !predicate(x + inc) && (inc == 1));
7666             // so the spot we're looking for is one further
7667             x += 1;
7668         }
7669         mark_list_piece_end[heap_num] = x;
7670     }
7671
7672 #undef predicate
7673
7674 //    printf("second phase of sort_mark_list for heap %d took %u cycles\n", this->heap_number, GetCycleCount32() - start);
7675 }
7676
7677 void gc_heap::append_to_mark_list(BYTE **start, BYTE **end)
7678 {
7679     size_t slots_needed = end - start;
7680     size_t slots_available = mark_list_end + 1 - mark_list_index;
7681     size_t slots_to_copy = min(slots_needed, slots_available);
7682     memcpy(mark_list_index, start, slots_to_copy*sizeof(*start));
7683     mark_list_index += slots_to_copy;
7684 //    printf("heap %d: appended %Id slots to mark_list\n", heap_number, slots_to_copy);
7685 }
7686
7687 void gc_heap::merge_mark_lists()
7688 {
7689     BYTE** source[MAX_SUPPORTED_CPUS];
7690     BYTE** source_end[MAX_SUPPORTED_CPUS];
7691     int source_heap[MAX_SUPPORTED_CPUS];
7692     int source_count = 0;
7693
7694     // in case of mark list overflow, don't bother
7695     if (mark_list_index >  mark_list_end)
7696     {
7697 //        printf("merge_mark_lists: overflow\n");
7698         return;
7699     }
7700
7701     dprintf(3, ("merge_mark_lists: heap_number = %d  starts out with %Id entries", heap_number, mark_list_index - mark_list));
7702 //    unsigned long start = GetCycleCount32();
7703     for (int i = 0; i < n_heaps; i++)
7704     {
7705         gc_heap* heap = g_heaps[i];
7706         if (heap->mark_list_piece_start[heap_number] < heap->mark_list_piece_end[heap_number])
7707         {
7708             source[source_count] = heap->mark_list_piece_start[heap_number];
7709             source_end[source_count] = heap->mark_list_piece_end[heap_number];
7710             source_heap[source_count] = i;
7711             if (source_count < MAX_SUPPORTED_CPUS)
7712                 source_count++;
7713         }
7714     }
7715 //    printf("first phase of merge_mark_lists for heap %d took %u cycles\n", heap_number, GetCycleCount32() - start);
7716
7717     dprintf(3, ("heap_number = %d  has %d sources\n", heap_number, source_count));
7718 #if defined(_DEBUG) || defined(TRACE_GC)
7719     for (int j = 0; j < source_count; j++)
7720     {
7721         dprintf(3, ("heap_number = %d  ", heap_number));
7722         dprintf(3, (" source from heap %d = %Ix .. %Ix (%Id entries)",
7723             (size_t)(source_heap[j]), (size_t)(source[j][0]), (size_t)(source_end[j][-1]), (size_t)(source_end[j] - source[j])));
7724        // the sources should all be sorted
7725         for (BYTE **x = source[j]; x < source_end[j] - 1; x++)
7726         {
7727             if (x[0] > x[1])
7728             {
7729                 dprintf(3, ("oops, mark_list from source %d for heap %d isn't sorted\n", j, heap_number));
7730                 assert (0);
7731             }
7732         }
7733     }
7734 #endif //_DEBUG || TRACE_GC
7735
7736 //    start = GetCycleCount32();
7737
7738     mark_list = &g_mark_list_copy [heap_number*mark_list_size];
7739     mark_list_index = mark_list;
7740     mark_list_end = &mark_list [mark_list_size-1];
7741     int piece_count = 0;
7742     if (source_count == 0)
7743     {
7744         ; // nothing to do
7745     }
7746     else if (source_count == 1)
7747     {
7748         mark_list = source[0];
7749         mark_list_index = source_end[0];
7750         mark_list_end = mark_list_index;
7751         piece_count++;
7752     }
7753     else
7754     {
7755         while (source_count > 1)
7756         {
7757             // find the lowest and second lowest value in the sources we're merging from
7758             int lowest_source = 0;
7759             BYTE *lowest = *source[0];
7760             BYTE *second_lowest = *source[1];
7761             for (int i = 1; i < source_count; i++)
7762             {
7763                 if (lowest > *source[i])
7764                 {
7765                     second_lowest = lowest;
7766                     lowest = *source[i];
7767                     lowest_source = i;
7768                 }
7769                 else if (second_lowest > *source[i])
7770                 {
7771                     second_lowest = *source[i];
7772                 }
7773             }
7774
7775             // find the point in the lowest source where it either runs out or is not <= second_lowest anymore
7776
7777             // let's first try to get lucky and see if the whole source is <= second_lowest -- this is actually quite common
7778             BYTE **x;
7779             if (source_end[lowest_source][-1] <= second_lowest)
7780                 x = source_end[lowest_source];
7781             else
7782             {
7783                 // use linear search to find the end -- could also use binary search as in sort_mark_list,
7784                 // but saw no improvement doing that
7785                 for (x = source[lowest_source]; x < source_end[lowest_source] && *x <= second_lowest; x++)
7786                     ;
7787             }
7788
7789             // blast this piece to the mark list
7790             append_to_mark_list(source[lowest_source], x);
7791             piece_count++;
7792
7793             source[lowest_source] = x;
7794
7795             // check whether this source is now exhausted
7796             if (x >= source_end[lowest_source])
7797             {
7798                 // if it's not the source with the highest index, copy the source with the highest index
7799                 // over it so the non-empty sources are always at the beginning
7800                 if (lowest_source < source_count-1)
7801                 {
7802                     source[lowest_source] = source[source_count-1];
7803                     source_end[lowest_source] = source_end[source_count-1];
7804                 }
7805                 source_count--;
7806             }
7807         }
7808         // we're left with just one source that we copy
7809         append_to_mark_list(source[0], source_end[0]);
7810         piece_count++;
7811     }
7812
7813 //    printf("second phase of merge_mark_lists for heap %d took %u cycles to merge %d pieces\n", heap_number, GetCycleCount32() - start, piece_count);
7814
7815 #if defined(_DEBUG) || defined(TRACE_GC)
7816     // the final mark list must be sorted
7817     for (BYTE **x = mark_list; x < mark_list_index - 1; x++)
7818     {
7819         if (x[0] > x[1])
7820         {
7821             dprintf(3, ("oops, mark_list for heap %d isn't sorted at the end of merge_mark_lists", heap_number));
7822             assert (0);
7823         }
7824     }
7825 #endif //defined(_DEBUG) || defined(TRACE_GC)
7826 }
7827 #else //PARALLEL_MARK_LIST_SORT
7828 void gc_heap::combine_mark_lists()
7829 {
7830     dprintf (3, ("Combining mark lists"));
7831     //verify if a heap has overflowed its mark list
7832     BOOL use_mark_list = TRUE;
7833     for (int i = 0; i < n_heaps; i++)
7834     {
7835         if (g_heaps [i]->mark_list_index >  g_heaps [i]->mark_list_end)
7836         {
7837             use_mark_list = FALSE;
7838             break;
7839         }
7840     }
7841
7842     if (use_mark_list)
7843     {
7844         dprintf (3, ("Using mark list"));
7845         //compact the gaps out of the mark list
7846         int gn = 0;
7847         BYTE** current_gap = g_heaps [gn]->mark_list_index;
7848         BYTE** current_gap_end = g_heaps[gn]->mark_list_end + 1;
7849         BYTE** dst_last = current_gap-1;
7850
7851         int srcn = n_heaps-1;
7852         gc_heap* srch = g_heaps [srcn];
7853         BYTE** src = srch->mark_list_index - 1;
7854         BYTE** src_beg = srch->mark_list;
7855
7856         while (current_gap <= src)
7857         {
7858             while ((gn < n_heaps-1) && (current_gap >= current_gap_end))
7859             {
7860                 //go to the next gap
7861                 gn++;
7862                 dprintf (3, ("Going to the next gap %d", gn));
7863                 assert (gn < n_heaps);
7864                 current_gap = g_heaps [gn]->mark_list_index;
7865                 current_gap_end = g_heaps[gn]->mark_list_end + 1;
7866                 assert ((gn == (n_heaps-1)) || (current_gap_end == g_heaps[gn+1]->mark_list));
7867             }
7868             while ((srcn > 0) && (src < src_beg))
7869             {
7870                 //go to the previous source
7871                 srcn--;
7872                 dprintf (3, ("going to the previous source %d", srcn));
7873                 assert (srcn>=0);
7874                 gc_heap* srch = g_heaps [srcn];
7875                 src = srch->mark_list_index - 1;
7876                 src_beg = srch->mark_list;
7877             }
7878             if (current_gap < src)
7879             {
7880                 dst_last = current_gap;
7881                 *current_gap++ = *src--;
7882             }
7883         }
7884         dprintf (3, ("src: %Ix dst_last: %Ix", (size_t)src, (size_t)dst_last));
7885
7886         BYTE** end_of_list = max (src, dst_last);
7887
7888         //sort the resulting compacted list
7889         assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]);
7890         if (end_of_list > &g_mark_list[0])
7891             _sort (&g_mark_list[0], end_of_list, 0);
7892         //adjust the mark_list to the begining of the resulting mark list.
7893         for (int i = 0; i < n_heaps; i++)
7894         {
7895             g_heaps [i]->mark_list = g_mark_list;
7896             g_heaps [i]->mark_list_index = end_of_list + 1;
7897             g_heaps [i]->mark_list_end = end_of_list + 1;
7898         }
7899     }
7900     else
7901     {
7902         BYTE** end_of_list = g_mark_list;
7903         //adjust the mark_list to the begining of the resulting mark list.
7904         //put the index beyond the end to turn off mark list processing
7905         for (int i = 0; i < n_heaps; i++)
7906         {
7907             g_heaps [i]->mark_list = g_mark_list;
7908             g_heaps [i]->mark_list_index = end_of_list + 1;
7909             g_heaps [i]->mark_list_end = end_of_list;
7910         }
7911     }
7912 }
7913 #endif // PARALLEL_MARK_LIST_SORT
7914 #endif //MULTIPLE_HEAPS
7915 #endif //MARK_LIST
7916
7917 #ifdef _WIN64
7918 #define TOTAL_TIMES_TO_SHIFT 6
7919 #else
7920 #define TOTAL_TIMES_TO_SHIFT 5
7921 #endif // _WIN64
7922
7923 size_t round_up_power2 (size_t size)
7924 {
7925     unsigned short shift = 1;
7926     size_t shifted = 0;
7927
7928     size--;
7929     for (unsigned short i = 0; i < TOTAL_TIMES_TO_SHIFT; i++)
7930     {
7931         shifted = size | (size >> shift);
7932         if (shifted == size)
7933         {
7934             break;
7935         }
7936
7937         size = shifted;
7938         shift <<= 1;
7939     }
7940     shifted++;
7941
7942     return shifted;
7943 }
7944
7945 inline
7946 size_t round_down_power2 (size_t size)
7947 {
7948     size_t power2 = round_up_power2 (size);
7949
7950     if (power2 != size)
7951     {
7952         power2 >>= 1;
7953     }
7954
7955     return power2;
7956 }
7957
7958 // the index starts from 0.
7959 int index_of_set_bit (size_t power2)
7960 {
7961     int low = 0;
7962     int high = sizeof (size_t) * 8 - 1;
7963     int mid; 
7964     while (low <= high)
7965     {
7966         mid = ((low + high)/2);
7967         size_t temp = 1 << mid;
7968         if (power2 & temp)
7969         {
7970             return mid;
7971         }
7972         else if (power2 < temp)
7973         {
7974             high = mid - 1;
7975         }
7976         else
7977         {
7978             low = mid + 1;
7979         }
7980     }
7981
7982     return -1;
7983 }
7984
7985 inline
7986 int relative_index_power2_plug (size_t power2)
7987 {
7988     int index = index_of_set_bit (power2);
7989     assert (index <= MAX_INDEX_POWER2);
7990
7991     return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
7992 }
7993
7994 inline
7995 int relative_index_power2_free_space (size_t power2)
7996 {
7997     int index = index_of_set_bit (power2);
7998     assert (index <= MAX_INDEX_POWER2);
7999
8000     return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
8001 }
8002
8003 class seg_free_spaces
8004 {
8005     struct seg_free_space
8006     {
8007         BOOL is_plug;
8008         void* start;
8009     };
8010
8011     struct free_space_bucket
8012     {
8013         seg_free_space* free_space;
8014         SSIZE_T count_add; // Assigned when we first contruct the array.
8015         SSIZE_T count_fit; // How many items left when we are fitting plugs.
8016     };
8017
8018     void move_bucket (int old_power2, int new_power2)
8019     {
8020         // PREFAST warning 22015: old_power2 could be negative
8021         assert (old_power2 >= 0);
8022         assert (old_power2 >= new_power2);
8023
8024         if (old_power2 == new_power2)
8025         {
8026             return;
8027         }
8028
8029         seg_free_space* src_index = free_space_buckets[old_power2].free_space;
8030         for (int i = old_power2; i > new_power2; i--)
8031         {
8032             seg_free_space** dest = &(free_space_buckets[i].free_space);
8033             (*dest)++;
8034
8035             seg_free_space* dest_index = free_space_buckets[i - 1].free_space;
8036             if (i > (new_power2 + 1))
8037             {
8038                 seg_free_space temp = *src_index;
8039                 *src_index = *dest_index;
8040                 *dest_index = temp;
8041             }
8042             src_index = dest_index;
8043         }
8044
8045         free_space_buckets[old_power2].count_fit--;
8046         free_space_buckets[new_power2].count_fit++;
8047     }
8048
8049 #ifdef _DEBUG
8050
8051     void dump_free_space (seg_free_space* item)
8052     {
8053         BYTE* addr = 0;
8054         size_t len = 0;
8055
8056         if (item->is_plug)
8057         {
8058             mark* m = (mark*)(item->start);
8059             len = pinned_len (m);
8060             addr = pinned_plug (m) - len;
8061         }
8062         else
8063         {
8064             heap_segment* seg = (heap_segment*)(item->start);
8065             addr = heap_segment_plan_allocated (seg);
8066             len = heap_segment_committed (seg) - addr;
8067         }
8068
8069         dprintf (SEG_REUSE_LOG_1, ("[%d]0x%Ix %Id", heap_num, addr, len));
8070     }
8071
8072     void dump()
8073     {
8074         seg_free_space* item = NULL;
8075         int i = 0;
8076
8077         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------\nnow the free spaces look like:", heap_num));
8078         for (i = 0; i < (free_space_bucket_count - 1); i++)
8079         {
8080             dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8081             dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8082             item = free_space_buckets[i].free_space;
8083             while (item < free_space_buckets[i + 1].free_space)
8084             {
8085                 dump_free_space (item);
8086                 item++;
8087             }
8088             dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8089         }
8090
8091         dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8092         dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8093         item = free_space_buckets[i].free_space;
8094
8095         while (item <= &seg_free_space_array[free_space_item_count - 1])
8096         {
8097             dump_free_space (item);
8098             item++;
8099         }
8100         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8101     }
8102
8103 #endif //_DEBUG
8104
8105     free_space_bucket* free_space_buckets;
8106     seg_free_space* seg_free_space_array;
8107     SSIZE_T free_space_bucket_count;
8108     SSIZE_T free_space_item_count;
8109     int base_power2;
8110     int heap_num;
8111 #ifdef _DEBUG
8112     BOOL has_end_of_seg;
8113 #endif //_DEBUG
8114
8115 public:
8116
8117     seg_free_spaces (int h_number)
8118     {
8119         heap_num = h_number;
8120     }
8121
8122     BOOL alloc ()
8123     {
8124         size_t total_prealloc_size = 
8125             MAX_NUM_BUCKETS * sizeof (free_space_bucket) +
8126             MAX_NUM_FREE_SPACES * sizeof (seg_free_space);
8127
8128         free_space_buckets = (free_space_bucket*) new (nothrow) (BYTE[total_prealloc_size]);
8129
8130         return (!!free_space_buckets);
8131     }
8132
8133     // We take the ordered free space array we got from the 1st pass,
8134     // and feed the portion that we decided to use to this method, ie,
8135     // the largest item_count free spaces.
8136     void add_buckets (int base, size_t* ordered_free_spaces, int bucket_count, size_t item_count)
8137     {
8138         assert (free_space_buckets);
8139         assert (item_count <= (size_t)MAX_PTR);
8140
8141         free_space_bucket_count = bucket_count;
8142         free_space_item_count = item_count;
8143         base_power2 = base;
8144 #ifdef _DEBUG
8145         has_end_of_seg = FALSE;
8146 #endif //_DEBUG
8147
8148         SSIZE_T total_item_count = 0;
8149         SSIZE_T i = 0;
8150
8151         seg_free_space_array = (seg_free_space*)(free_space_buckets + free_space_bucket_count);
8152
8153         for (i = 0; i < (SSIZE_T)item_count; i++)
8154         {
8155             seg_free_space_array[i].start = 0;
8156             seg_free_space_array[i].is_plug = FALSE;
8157         }
8158
8159         for (i = 0; i < bucket_count; i++)
8160         {
8161             free_space_buckets[i].count_add = ordered_free_spaces[i];
8162             free_space_buckets[i].count_fit = ordered_free_spaces[i];
8163             free_space_buckets[i].free_space = &seg_free_space_array[total_item_count];
8164             total_item_count += free_space_buckets[i].count_add;
8165         }
8166
8167         assert (total_item_count == (SSIZE_T)item_count);
8168     }
8169
8170     // If we are adding a free space before a plug we pass the
8171     // mark stack position so we can update the length; we could
8172     // also be adding the free space after the last plug in which
8173     // case start is the segment which we'll need to update the 
8174     // heap_segment_plan_allocated.
8175     void add (void* start, BOOL plug_p, BOOL first_p)
8176     {
8177         size_t size = (plug_p ? 
8178                        pinned_len ((mark*)start) : 
8179                        (heap_segment_committed ((heap_segment*)start) - 
8180                            heap_segment_plan_allocated ((heap_segment*)start)));
8181         
8182         if (plug_p)
8183         {
8184             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space before plug: %Id", heap_num, size));
8185         }
8186         else
8187         {
8188             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space at end of seg: %Id", heap_num, size));
8189 #ifdef _DEBUG
8190             has_end_of_seg = TRUE;
8191 #endif //_DEBUG
8192         }
8193                   
8194         if (first_p)
8195         {
8196             size_t eph_gen_starts = gc_heap::eph_gen_starts_size;
8197             size -= eph_gen_starts;
8198             if (plug_p)
8199             {
8200                 mark* m = (mark*)(start);
8201                 pinned_len (m) -= eph_gen_starts;
8202             }
8203             else
8204             {
8205                 heap_segment* seg = (heap_segment*)start;
8206                 heap_segment_plan_allocated (seg) += eph_gen_starts;
8207             }
8208         }
8209
8210         int bucket_power2 = index_of_set_bit (round_down_power2 (size));
8211         if (bucket_power2 < base_power2)
8212         {
8213             return;
8214         }
8215
8216         free_space_bucket* bucket = &free_space_buckets[bucket_power2 - base_power2];
8217
8218         seg_free_space* bucket_free_space = bucket->free_space;
8219         assert (plug_p || (!plug_p && bucket->count_add));
8220
8221         if (bucket->count_add == 0)
8222         {
8223             dprintf (SEG_REUSE_LOG_1, ("[%d]Already have enough of 2^%d", heap_num, bucket_power2));
8224             return;
8225         }
8226
8227         SSIZE_T index = bucket->count_add - 1;
8228
8229         dprintf (SEG_REUSE_LOG_1, ("[%d]Building free spaces: adding %Ix; len: %Id (2^%d)", 
8230                     heap_num, 
8231                     (plug_p ? 
8232                         (pinned_plug ((mark*)start) - pinned_len ((mark*)start)) : 
8233                         heap_segment_plan_allocated ((heap_segment*)start)),
8234                     size,
8235                     bucket_power2));
8236
8237         if (plug_p)
8238         {
8239             bucket_free_space[index].is_plug = TRUE;
8240         }
8241
8242         bucket_free_space[index].start = start;
8243         bucket->count_add--;
8244     }
8245
8246 #ifdef _DEBUG
8247
8248     // Do a consistency check after all free spaces are added.
8249     void check()
8250     {
8251         SSIZE_T i = 0;
8252         int end_of_seg_count = 0;
8253
8254         for (i = 0; i < free_space_item_count; i++)
8255         {
8256             assert (seg_free_space_array[i].start);
8257             if (!(seg_free_space_array[i].is_plug))
8258             {
8259                 end_of_seg_count++;
8260             }
8261         }
8262         
8263         if (has_end_of_seg)
8264         {
8265             assert (end_of_seg_count == 1);
8266         }
8267         else
8268         {
8269             assert (end_of_seg_count == 0);
8270         }
8271
8272         for (i = 0; i < free_space_bucket_count; i++)
8273         {
8274             assert (free_space_buckets[i].count_add == 0);
8275         }
8276     }
8277
8278 #endif //_DEBUG
8279
8280     BYTE* fit (BYTE* old_loc, 
8281 #ifdef SHORT_PLUGS
8282                BOOL set_padding_on_saved_p,
8283                mark* pinned_plug_entry,
8284 #endif //SHORT_PLUGS
8285                size_t plug_size
8286                REQD_ALIGN_AND_OFFSET_DCL)
8287     {
8288 #ifdef FEATURE_STRUCTALIGN
8289         // BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account
8290         _ASSERTE(requiredAlignment == DATA_ALIGNMENT && false);
8291 #endif // FEATURE_STRUCTALIGN
8292         // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the 
8293         // the bucket.
8294
8295         size_t plug_size_to_fit = plug_size;
8296
8297         int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
8298
8299 #ifdef SHORT_PLUGS
8300         plug_size_to_fit += (pad_in_front ? Align(min_obj_size) : 0);
8301 #endif //SHORT_PLUGS
8302
8303         int plug_power2 = index_of_set_bit (round_up_power2 (plug_size_to_fit + Align(min_obj_size)));
8304         SSIZE_T i;
8305         BYTE* new_address = 0;
8306
8307         if (plug_power2 < base_power2)
8308         {
8309             plug_power2 = base_power2;
8310         }
8311
8312         int chosen_power2 = plug_power2 - base_power2;
8313 retry:
8314         for (i = chosen_power2; i < free_space_bucket_count; i++)
8315         {
8316             if (free_space_buckets[i].count_fit != 0)
8317             {
8318                 break;
8319             }
8320             chosen_power2++;
8321         }
8322
8323         dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting plug len %Id (2^%d) using 2^%d free space", 
8324             heap_num, 
8325             plug_size, 
8326             plug_power2, 
8327             (chosen_power2 + base_power2)));
8328
8329         assert (i < free_space_bucket_count);
8330         
8331         seg_free_space* bucket_free_space = free_space_buckets[chosen_power2].free_space;
8332         SSIZE_T free_space_count = free_space_buckets[chosen_power2].count_fit;
8333         size_t new_free_space_size = 0;
8334         BOOL can_fit = FALSE;
8335         size_t pad = 0;
8336         
8337         for (i = 0; i < free_space_count; i++)
8338         {
8339             size_t free_space_size = 0;
8340
8341             if (bucket_free_space[i].is_plug)
8342             {
8343                 mark* m = (mark*)(bucket_free_space[i].start);
8344                 BYTE* plug_free_space_start = pinned_plug (m) - pinned_len (m);
8345                 
8346 #ifdef SHORT_PLUGS
8347                 if ((pad_in_front & USE_PADDING_FRONT) &&
8348                     (((plug_free_space_start - pin_allocation_context_start_region (m))==0) ||
8349                     ((plug_free_space_start - pin_allocation_context_start_region (m))>=DESIRED_PLUG_LENGTH)))
8350                 {
8351                     pad = Align (min_obj_size);
8352                     set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
8353                 }
8354 #endif //SHORT_PLUGS
8355
8356                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, plug_free_space_start+pad)))
8357                 {
8358                     pad += switch_alignment_size (FALSE);
8359                     set_node_realigned (old_loc);
8360                 }
8361
8362                 plug_size += pad;
8363
8364                 free_space_size = pinned_len (m);
8365                 new_address = pinned_plug (m) - pinned_len (m);
8366
8367                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8368                     free_space_size == plug_size)
8369                 {
8370                     new_free_space_size = free_space_size - plug_size;
8371                     pinned_len (m) = new_free_space_size;
8372 #ifdef SIMPLE_DPRINTF
8373                     dprintf (SEG_REUSE_LOG_0, ("[%d]free space before pin: [0x%Ix, [0x%Ix (2^%d) -> [0x%Ix, [0x%Ix (2^%d)",
8374                                 heap_num, 
8375                                 new_address, pinned_plug (m), 
8376                                 index_of_set_bit (round_down_power2 (free_space_size)),
8377                                 (pinned_plug (m) - pinned_len (m)), pinned_plug (m),
8378                                 index_of_set_bit (round_down_power2 (new_free_space_size))));
8379 #endif //SIMPLE_DPRINTF
8380
8381                     if (pad)
8382                     {
8383                         pin_allocation_context_start_region (m) = plug_free_space_start;
8384                     }
8385
8386                     can_fit = TRUE;
8387                 }
8388             }
8389             else
8390             {
8391                 heap_segment* seg = (heap_segment*)(bucket_free_space[i].start);
8392                 free_space_size = heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
8393
8394                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, heap_segment_plan_allocated (seg))))
8395                 {
8396                     pad = switch_alignment_size (FALSE);
8397                     set_node_realigned (old_loc);
8398                 }
8399
8400                 plug_size += pad;
8401
8402                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8403                     free_space_size == plug_size)
8404                 {
8405                     new_address = heap_segment_plan_allocated (seg);
8406                     new_free_space_size = free_space_size - plug_size;
8407                     heap_segment_plan_allocated (seg) = new_address + plug_size;
8408 #ifdef SIMPLE_DPRINTF
8409                     dprintf (SEG_REUSE_LOG_0, ("[%d]free space at the end of seg 0x%Ix (2^%d) -> 0x%Ix (2^%d)",
8410                                 heap_num, 
8411                                 new_address, 
8412                                 index_of_set_bit (round_down_power2 (free_space_size)),
8413                                 heap_segment_plan_allocated (seg), 
8414                                 index_of_set_bit (round_down_power2 (new_free_space_size))));
8415 #endif //SIMPLE_DPRINTF
8416                     can_fit = TRUE;
8417                 }
8418             }
8419
8420             if (can_fit)
8421             {
8422                 break;
8423             }
8424         }
8425
8426         if (!can_fit)
8427         {
8428             assert (chosen_power2 == 0);
8429             chosen_power2 = 1;
8430             goto retry;
8431         }
8432         else
8433         {
8434             if (pad)
8435             {
8436                 new_address += pad;
8437             }
8438             assert ((chosen_power2 && (i == 0)) ||
8439                     (!chosen_power2) && (i < free_space_count));
8440         }
8441
8442         int new_bucket_power2 = index_of_set_bit (round_down_power2 (new_free_space_size));
8443
8444         if (new_bucket_power2 < base_power2)
8445         {
8446             new_bucket_power2 = base_power2;
8447         }
8448
8449         move_bucket (chosen_power2, new_bucket_power2 - base_power2);
8450
8451         //dump();
8452
8453         return new_address;
8454     }
8455
8456     void cleanup ()
8457     {
8458         if (free_space_buckets)
8459         {
8460             delete [] free_space_buckets;
8461         }
8462         if (seg_free_space_array)
8463         {
8464             delete [] seg_free_space_array;
8465         }
8466     }
8467 };
8468
8469
8470 #define marked(i) header(i)->IsMarked()
8471 #define set_marked(i) header(i)->SetMarked()
8472 #define clear_marked(i) header(i)->ClearMarked()
8473 #define pinned(i) header(i)->IsPinned()
8474 #define set_pinned(i) header(i)->SetPinned()
8475 #define clear_pinned(i) header(i)->GetHeader()->ClrGCBit();
8476
8477 inline size_t my_get_size (Object* ob)
8478 {
8479     MethodTable* mT = header(ob)->GetMethodTable();
8480     return (mT->GetBaseSize() +
8481             (mT->HasComponentSize() ?
8482              ((size_t)((CObjectHeader*)ob)->GetNumComponents() * mT->RawGetComponentSize()) : 0));
8483 }
8484
8485 //#define size(i) header(i)->GetSize()
8486 #define size(i) my_get_size (header(i))
8487
8488 #define contain_pointers(i) header(i)->ContainsPointers()
8489 #ifdef COLLECTIBLE_CLASS
8490 #define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible()
8491
8492 #define get_class_object(i) method_table(i)->GetLoaderAllocatorObjectForGC()
8493 #define is_collectible(i) method_table(i)->Collectible()
8494 #else //COLLECTIBLE_CLASS
8495 #define contain_pointers_or_collectible(i) header(i)->ContainsPointers()
8496 #endif //COLLECTIBLE_CLASS
8497
8498 #if defined (MARK_ARRAY) && defined (BACKGROUND_GC)
8499 inline
8500 void gc_heap::seg_clear_mark_array_bits_soh (heap_segment* seg)
8501 {
8502     BYTE* range_beg = 0;
8503     BYTE* range_end = 0;
8504     if (bgc_mark_array_range (seg, FALSE, &range_beg, &range_end))
8505     {
8506         clear_mark_array (range_beg, align_on_mark_word (range_end), FALSE);
8507     }
8508 }
8509
8510 void gc_heap::clear_batch_mark_array_bits (BYTE* start, BYTE* end)
8511 {
8512     if ((start < background_saved_highest_address) &&
8513         (end > background_saved_lowest_address))
8514     {
8515         start = max (start, background_saved_lowest_address);
8516         end = min (end, background_saved_highest_address);
8517
8518         size_t start_mark_bit = mark_bit_of (start);
8519         size_t end_mark_bit = mark_bit_of (end);
8520         unsigned int startbit = mark_bit_bit (start_mark_bit);
8521         unsigned int endbit = mark_bit_bit (end_mark_bit);
8522         size_t startwrd = mark_bit_word (start_mark_bit);
8523         size_t endwrd = mark_bit_word (end_mark_bit);
8524
8525         dprintf (3, ("Clearing all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
8526             (size_t)start, (size_t)start_mark_bit, 
8527             (size_t)end, (size_t)end_mark_bit));
8528
8529         unsigned int firstwrd = lowbits (~0, startbit);
8530         unsigned int lastwrd = highbits (~0, endbit);
8531
8532         if (startwrd == endwrd)
8533         {
8534             unsigned int wrd = firstwrd | lastwrd;
8535             mark_array[startwrd] &= wrd;
8536             return;
8537         }
8538
8539         // clear the first mark word.
8540         if (startbit)
8541         {
8542             mark_array[startwrd] &= firstwrd;
8543             startwrd++;
8544         }
8545
8546         for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
8547         {
8548             mark_array[wrdtmp] = 0;
8549         }
8550
8551         // clear the last mark word.
8552         if (endbit)
8553         {
8554             mark_array[endwrd] &= lastwrd;
8555         }
8556     }
8557 }
8558
8559 void gc_heap::bgc_clear_batch_mark_array_bits (BYTE* start, BYTE* end)
8560 {
8561     if ((start < background_saved_highest_address) &&
8562         (end > background_saved_lowest_address))
8563     {
8564         start = max (start, background_saved_lowest_address);
8565         end = min (end, background_saved_highest_address);
8566
8567         clear_batch_mark_array_bits (start, end);
8568     }
8569 }
8570
8571 void gc_heap::clear_mark_array_by_objects (BYTE* from, BYTE* end, BOOL loh_p)
8572 {
8573     dprintf (3, ("clearing mark array bits by objects for addr [%Ix,[%Ix", 
8574                   from, end));
8575     int align_const = get_alignment_constant (!loh_p);
8576
8577     BYTE* o = from;
8578
8579     while (o < end)
8580     {
8581         BYTE*  next_o = o + Align (size (o), align_const);
8582
8583         if (background_object_marked (o, TRUE))
8584         {
8585             dprintf (3, ("%Ix was marked by bgc, is now cleared", o));
8586         }
8587
8588         o = next_o;
8589     }
8590 }
8591 #endif //MARK_ARRAY && BACKGROUND_GC
8592
8593 inline
8594 BOOL gc_heap::is_mark_set (BYTE* o)
8595 {
8596     return marked (o);
8597 }
8598
8599 #if defined (_MSC_VER) && defined (_TARGET_X86_)
8600 #pragma optimize("y", on)        // Small critical routines, don't put in EBP frame 
8601 #endif //_MSC_VER && _TARGET_X86_
8602
8603 // return the generation number of an object.
8604 // It is assumed that the object is valid.
8605 //Note that this will return max_generation for a LOH object
8606 int gc_heap::object_gennum (BYTE* o)
8607 {
8608     if (in_range_for_segment (o, ephemeral_heap_segment) &&
8609         (o >= generation_allocation_start (generation_of (max_generation-1))))
8610     {
8611         // in an ephemeral generation.
8612         for ( int i = 0; i < max_generation-1; i++)
8613         {
8614             if ((o >= generation_allocation_start (generation_of (i))))
8615                 return i;
8616         }
8617         return max_generation-1;
8618     }
8619     else
8620     {
8621         return max_generation;
8622     }
8623 }
8624
8625 int gc_heap::object_gennum_plan (BYTE* o)
8626 {
8627     if (in_range_for_segment (o, ephemeral_heap_segment))
8628     {
8629         for (int i = 0; i <= max_generation-1; i++)
8630         {
8631             BYTE* plan_start = generation_plan_allocation_start (generation_of (i));
8632             if (plan_start && (o >= plan_start))
8633             {
8634                 return i;
8635             }
8636         }
8637     }
8638     return max_generation;
8639 }
8640
8641 #if defined(_MSC_VER) && defined(_TARGET_X86_)
8642 #pragma optimize("", on)        // Go back to command line default optimizations
8643 #endif //_MSC_VER && _TARGET_X86_
8644
8645 heap_segment* gc_heap::make_heap_segment (BYTE* new_pages, size_t size, int h_number)
8646 {
8647     void * res;
8648     size_t initial_commit = SEGMENT_INITIAL_COMMIT;
8649
8650     //Commit the first page
8651     if ((res = virtual_alloc_commit_for_heap (new_pages, initial_commit,
8652                               MEM_COMMIT, PAGE_READWRITE, h_number)) == 0)
8653     {
8654         return 0;
8655     }
8656
8657     //overlay the heap_segment
8658     heap_segment* new_segment = (heap_segment*)new_pages;
8659
8660     BYTE* start = 0;
8661 #ifdef BACKGROUND_GC
8662     //leave the first page to contain only segment info
8663     //because otherwise we could need to revisit the first page frequently in 
8664     // background GC.
8665     start = new_pages + OS_PAGE_SIZE;
8666 #else
8667     start = new_pages +
8668         Align (sizeof (heap_segment), get_alignment_constant (FALSE));
8669 #endif //BACKGROUND_GC
8670     heap_segment_mem (new_segment) = start;
8671     heap_segment_used (new_segment) = start;
8672     heap_segment_reserved (new_segment) = new_pages + size;
8673     heap_segment_committed (new_segment) = new_pages + initial_commit;
8674     init_heap_segment (new_segment);
8675     dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
8676     return new_segment;
8677 }
8678
8679 void gc_heap::init_heap_segment (heap_segment* seg)
8680 {
8681     seg->flags = 0;
8682     heap_segment_next (seg) = 0;
8683     heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
8684     heap_segment_allocated (seg) = heap_segment_mem (seg);
8685 #ifdef BACKGROUND_GC
8686     heap_segment_background_allocated (seg) = 0;
8687     heap_segment_saved_bg_allocated (seg) = 0;
8688 #endif //BACKGROUND_GC
8689 }
8690
8691 //Releases the segment to the OS.
8692 // this is always called on one thread only so calling seg_table->remove is fine.
8693 void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding)
8694 {
8695     if (!heap_segment_loh_p (seg))
8696     {
8697         //cleanup the brick table back to the empty value
8698         clear_brick_table (heap_segment_mem (seg), heap_segment_reserved (seg));
8699     }
8700
8701     if (consider_hoarding)
8702     {
8703         assert ((heap_segment_mem (seg) - (BYTE*)seg) <= 2*OS_PAGE_SIZE);
8704         size_t ss = (size_t) (heap_segment_reserved (seg) - (BYTE*)seg);
8705         //Don't keep the big ones.
8706         if (ss <= INITIAL_ALLOC)
8707         {
8708             dprintf (2, ("Hoarding segment %Ix", (size_t)seg));
8709 #ifdef BACKGROUND_GC
8710             // We don't need to clear the decommitted flag because when this segment is used
8711             // for a new segment the flags will be cleared.
8712             if (!heap_segment_decommitted_p (seg))
8713 #endif //BACKGROUND_GC
8714             {
8715                 decommit_heap_segment (seg);
8716             }
8717
8718 #ifdef SEG_MAPPING_TABLE
8719             seg_mapping_table_remove_segment (seg);
8720 #endif //SEG_MAPPING_TABLE
8721
8722             heap_segment_next (seg) = segment_standby_list;
8723             segment_standby_list = seg;
8724             seg = 0;
8725         }
8726     }
8727
8728     if (seg != 0)
8729     {
8730         dprintf (2, ("h%d: del seg: [%Ix, %Ix[", 
8731                      heap_number, (size_t)seg,
8732                      (size_t)(heap_segment_reserved (seg))));
8733
8734 #ifdef BACKGROUND_GC
8735         ::record_changed_seg ((BYTE*)seg, heap_segment_reserved (seg), 
8736                             settings.gc_index, current_bgc_state,
8737                             seg_deleted);
8738         decommit_mark_array_by_seg (seg);
8739 #endif //BACKGROUND_GC
8740
8741 #ifdef SEG_MAPPING_TABLE
8742         seg_mapping_table_remove_segment (seg);
8743 #else //SEG_MAPPING_TABLE
8744         seg_table->remove ((BYTE*)seg);
8745 #endif //SEG_MAPPING_TABLE
8746
8747         release_segment (seg);
8748     }
8749 }
8750
8751 //resets the pages beyond alloctes size so they won't be swapped out and back in
8752
8753 void gc_heap::reset_heap_segment_pages (heap_segment* seg)
8754 {
8755 #ifndef FEATURE_PAL // No MEM_RESET support in PAL VirtualAlloc
8756     size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg));
8757     size_t size = (size_t)heap_segment_committed (seg) - page_start;
8758     if (size != 0)
8759         VirtualAlloc ((char*)page_start, size, MEM_RESET, PAGE_READWRITE);
8760 #endif //!FEATURE_PAL
8761 }
8762
8763 void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
8764                                            size_t extra_space)
8765 {
8766     BYTE*  page_start = align_on_page (heap_segment_allocated(seg));
8767     size_t size = heap_segment_committed (seg) - page_start;
8768     extra_space = align_on_page (extra_space);
8769     if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE))
8770     {
8771         page_start += max(extra_space, 32*OS_PAGE_SIZE);
8772         size -= max (extra_space, 32*OS_PAGE_SIZE);
8773
8774         VirtualFree (page_start, size, MEM_DECOMMIT);
8775         dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)", 
8776             (size_t)page_start, 
8777             (size_t)(page_start + size),
8778             size));
8779         heap_segment_committed (seg) = page_start;
8780         if (heap_segment_used (seg) > heap_segment_committed (seg))
8781         {
8782             heap_segment_used (seg) = heap_segment_committed (seg);
8783         }
8784     }
8785 }
8786
8787 //decommit all pages except one or 2
8788 void gc_heap::decommit_heap_segment (heap_segment* seg)
8789 {
8790     BYTE*  page_start = align_on_page (heap_segment_mem (seg));
8791
8792     dprintf (3, ("Decommitting heap segment %Ix", (size_t)seg));
8793
8794 #ifdef BACKGROUND_GC
8795     page_start += OS_PAGE_SIZE;
8796 #endif //BACKGROUND_GC
8797
8798     size_t size = heap_segment_committed (seg) - page_start;
8799     VirtualFree (page_start, size, MEM_DECOMMIT);
8800
8801     //re-init the segment object
8802     heap_segment_committed (seg) = page_start;
8803     if (heap_segment_used (seg) > heap_segment_committed (seg))
8804     {
8805         heap_segment_used (seg) = heap_segment_committed (seg);
8806     }
8807 }
8808
8809 void gc_heap::clear_gen0_bricks()
8810 {
8811     if (!gen0_bricks_cleared)
8812     {
8813         gen0_bricks_cleared = TRUE;
8814         //initialize brick table for gen 0
8815         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
8816                 b < brick_of (align_on_brick
8817                             (heap_segment_allocated (ephemeral_heap_segment)));
8818                 b++)
8819         {
8820             set_brick (b, -1);
8821         }
8822     }
8823 }
8824
8825 #ifdef BACKGROUND_GC
8826 void gc_heap::rearrange_small_heap_segments()
8827 {
8828     heap_segment* seg = freeable_small_heap_segment;
8829     while (seg)
8830     {
8831         heap_segment* next_seg = heap_segment_next (seg);
8832         // TODO: we need to consider hoarding here.
8833         delete_heap_segment (seg, FALSE);
8834         seg = next_seg;
8835     }
8836     freeable_small_heap_segment = 0;
8837 }
8838 #endif //BACKGROUND_GC
8839
8840 void gc_heap::rearrange_large_heap_segments()
8841 {
8842     dprintf (2, ("deleting empty large segments"));
8843     heap_segment* seg = freeable_large_heap_segment;
8844     while (seg)
8845     {
8846         heap_segment* next_seg = heap_segment_next (seg);
8847         delete_heap_segment (seg, (g_pConfig->GetGCRetainVM() != 0));
8848         seg = next_seg;
8849     }
8850     freeable_large_heap_segment = 0;
8851 }
8852
8853 void gc_heap::rearrange_heap_segments(BOOL compacting)
8854 {
8855     heap_segment* seg =
8856         generation_start_segment (generation_of (max_generation));
8857
8858     heap_segment* prev_seg = 0;
8859     heap_segment* next_seg = 0;
8860     while (seg)
8861     {
8862         next_seg = heap_segment_next (seg);
8863
8864         //link ephemeral segment when expanding
8865         if ((next_seg == 0) && (seg != ephemeral_heap_segment))
8866         {
8867             seg->next = ephemeral_heap_segment;
8868             next_seg = heap_segment_next (seg);
8869         }
8870
8871         //re-used expanded heap segment
8872         if ((seg == ephemeral_heap_segment) && next_seg)
8873         {
8874             heap_segment_next (prev_seg) = next_seg;
8875             heap_segment_next (seg) = 0;
8876         }
8877         else
8878         {
8879             BYTE* end_segment = (compacting ? 
8880                                  heap_segment_plan_allocated (seg) : 
8881                                  heap_segment_allocated (seg));
8882             // check if the segment was reached by allocation
8883             if ((end_segment == heap_segment_mem (seg))&&
8884                 !heap_segment_read_only_p (seg))
8885             {
8886                 //if not, unthread and delete
8887                 assert (prev_seg);
8888                 assert (seg != ephemeral_heap_segment);
8889                 heap_segment_next (prev_seg) = next_seg;
8890                 delete_heap_segment (seg, (g_pConfig->GetGCRetainVM() != 0));
8891
8892                 dprintf (2, ("Deleting heap segment %Ix", (size_t)seg));
8893             }
8894             else
8895             {
8896                 if (!heap_segment_read_only_p (seg))
8897                 {
8898                     if (compacting)
8899                     {
8900                         heap_segment_allocated (seg) =
8901                             heap_segment_plan_allocated (seg);
8902                     }
8903
8904                     // reset the pages between allocated and committed.
8905                     if (seg != ephemeral_heap_segment)
8906                     {
8907                         decommit_heap_segment_pages (seg, 0);
8908                     }
8909                 }
8910                 prev_seg = seg;
8911             }
8912         }
8913
8914         seg = next_seg;
8915     }
8916 }
8917
8918
8919 #ifdef WRITE_WATCH
8920
8921 BYTE* g_addresses [array_size+2]; // to get around the bug in GetWriteWatch
8922
8923 #ifdef TIME_WRITE_WATCH
8924 static unsigned int tot_cycles = 0;
8925 #endif //TIME_WRITE_WATCH
8926
8927 #ifdef CARD_BUNDLE
8928
8929 void gc_heap::update_card_table_bundle()
8930 {
8931     if (card_bundles_enabled())
8932     {
8933         BYTE* base_address = (BYTE*)(&card_table[card_word (card_of (lowest_address))]);
8934         BYTE* saved_base_address = base_address;
8935         ULONG_PTR bcount = array_size;
8936         ULONG granularity = 0;
8937         BYTE* high_address = (BYTE*)(&card_table[card_word (card_of (highest_address))]);
8938         size_t saved_region_size = align_on_page (high_address) - saved_base_address;
8939
8940         do
8941         {
8942             size_t region_size = align_on_page (high_address) - base_address;
8943             dprintf (3,("Probing card table pages [%Ix, %Ix[", (size_t)base_address, (size_t)base_address+region_size));
8944             UINT status = GetWriteWatch (0, base_address, region_size,
8945                                          (PVOID*)g_addresses,
8946                                          &bcount, &granularity);
8947             assert (status == 0);
8948             assert (granularity == OS_PAGE_SIZE);
8949             dprintf (3,("Found %d pages written", bcount));
8950             for (unsigned  i = 0; i < bcount; i++)
8951             {
8952                 size_t bcardw = (DWORD*)(max(g_addresses[i],base_address)) - &card_table[0];
8953                 size_t ecardw = (DWORD*)(min(g_addresses[i]+granularity, high_address)) - &card_table[0];
8954                 assert (bcardw >= card_word (card_of (g_lowest_address)));
8955
8956                 card_bundles_set (cardw_card_bundle (bcardw),
8957                                   cardw_card_bundle (align_cardw_on_bundle (ecardw)));
8958
8959                 dprintf (3,("Set Card bundle [%Ix, %Ix[",
8960                             cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))));
8961
8962 #ifdef _DEBUG
8963                 for (size_t x = cardw_card_bundle (bcardw); x < cardw_card_bundle (ecardw); x++)
8964                 {
8965                     if (!card_bundle_set_p (x))
8966                     {
8967                         assert (!"Card bundle not set");
8968                         dprintf (3, ("Card bundle %Ix not set", x));
8969                     }
8970                 }
8971 #endif //_DEBUG
8972
8973             }
8974             if (bcount >= array_size){
8975                 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
8976                 bcount = array_size;
8977             }
8978         } while ((bcount >= array_size) && (base_address < high_address));
8979
8980         ResetWriteWatch (saved_base_address, saved_region_size);
8981
8982 #ifdef _DEBUG
8983
8984         size_t lowest_card = card_word (card_of (lowest_address));
8985         size_t highest_card = card_word (card_of (highest_address));
8986         size_t cardb = cardw_card_bundle (lowest_card);
8987         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (highest_card));
8988
8989         //find a non null bundle
8990         while (cardb < end_cardb)
8991         {
8992             if (card_bundle_set_p (cardb)==0)
8993             {
8994                 //verify that the cards are indeed empty
8995                 DWORD* card_word = &card_table[max(card_bundle_cardw (cardb), lowest_card)];
8996                 DWORD* card_word_end = &card_table[min(card_bundle_cardw (cardb+1), highest_card)];
8997                 while (card_word < card_word_end)
8998                 {
8999                     if ((*card_word) != 0)
9000                     {
9001                         dprintf  (3, ("gc: %d, Card word %Ix for address %Ix set, card_bundle %Ix clear",
9002                                 dd_collection_count (dynamic_data_of (0)), 
9003                                 (size_t)(card_word-&card_table[0]),
9004                                 (size_t)(card_address ((size_t)(card_word-&card_table[0]) * card_word_width)), cardb));
9005                     }
9006                     assert((*card_word)==0);
9007                     card_word++;
9008                 }
9009             }
9010             //end of verification
9011             cardb++;
9012         }
9013 #endif //_DEBUG
9014     }
9015 }
9016 #endif //CARD_BUNDLE
9017
9018 const size_t ww_reset_quantum = 128*1024*1024;
9019
9020 inline
9021 void gc_heap::switch_one_quantum()
9022 {
9023     Thread* current_thread = GetThread();
9024     enable_preemptive (current_thread);
9025     __SwitchToThread (1, CALLER_LIMITS_SPINNING);
9026     disable_preemptive (current_thread, TRUE);
9027 }
9028
9029 void gc_heap::reset_ww_by_chunk (BYTE* start_address, size_t total_reset_size)
9030 {
9031     size_t reset_size = 0;
9032     size_t remaining_reset_size = 0;
9033     size_t next_reset_size = 0;
9034
9035     while (reset_size != total_reset_size)
9036     {
9037         remaining_reset_size = total_reset_size - reset_size;
9038         next_reset_size = ((remaining_reset_size >= ww_reset_quantum) ? ww_reset_quantum : remaining_reset_size);
9039         if (next_reset_size)
9040         {
9041             ResetWriteWatch (start_address, next_reset_size);
9042             reset_size += next_reset_size;
9043
9044             switch_one_quantum();
9045         }
9046     }
9047
9048     assert (reset_size == total_reset_size);
9049 }
9050
9051 // This does a __SwitchToThread for every reset ww_reset_quantum bytes of reset 
9052 // we do concurrently.
9053 void gc_heap::switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size)
9054 {
9055     if (concurrent_p)
9056     {
9057         *current_total_reset_size += last_reset_size;
9058
9059         dprintf (2, ("reset %Id bytes so far", *current_total_reset_size));
9060
9061         if (*current_total_reset_size > ww_reset_quantum)
9062         {
9063             switch_one_quantum();
9064
9065             *current_total_reset_size = 0;
9066         }
9067     }
9068 }
9069
9070 void gc_heap::reset_write_watch (BOOL concurrent_p)
9071 {
9072     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
9073
9074     PREFIX_ASSUME(seg != NULL);
9075
9076     size_t reset_size = 0;
9077     size_t region_size = 0;
9078
9079     dprintf (2, ("bgc lowest: %Ix, bgc highest: %Ix", background_saved_lowest_address, background_saved_highest_address));
9080
9081     while (seg)
9082     {
9083         BYTE* base_address = align_lower_page (heap_segment_mem (seg));
9084
9085         if (concurrent_p)
9086         {
9087             base_address = max (base_address, background_saved_lowest_address);
9088         }
9089
9090         BYTE* high_address = 0;
9091         if (concurrent_p)
9092         {
9093             high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
9094             high_address = min (high_address, background_saved_highest_address);
9095         }
9096         else
9097         {
9098             high_address = heap_segment_allocated (seg);
9099         }
9100         
9101         if (base_address < high_address)
9102         {
9103             region_size = high_address - base_address;
9104
9105 #ifdef TIME_WRITE_WATCH
9106             unsigned int time_start = GetCycleCount32();
9107 #endif //TIME_WRITE_WATCH
9108             dprintf (3, ("h%d: soh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9109             //reset_ww_by_chunk (base_address, region_size);
9110             ResetWriteWatch (base_address, region_size);
9111
9112 #ifdef TIME_WRITE_WATCH
9113             unsigned int time_stop = GetCycleCount32();
9114             tot_cycles += time_stop - time_start;
9115             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9116                     time_stop - time_start, tot_cycles);
9117 #endif //TIME_WRITE_WATCH
9118
9119             switch_on_reset (concurrent_p, &reset_size, region_size);
9120         }
9121
9122         seg = heap_segment_next_rw (seg);
9123
9124         concurrent_print_time_delta ("CRWW soh");
9125     }
9126
9127     //concurrent_print_time_delta ("CRW soh");
9128
9129     seg = heap_segment_rw (generation_start_segment (large_object_generation));
9130
9131     PREFIX_ASSUME(seg != NULL);
9132
9133     while (seg)
9134     {
9135         BYTE* base_address = align_lower_page (heap_segment_mem (seg));
9136         BYTE* high_address =  heap_segment_allocated (seg);
9137
9138         if (concurrent_p)
9139         {
9140             base_address = max (base_address, background_saved_lowest_address);
9141             high_address = min (high_address, background_saved_highest_address);
9142         }
9143
9144         if (base_address < high_address)
9145         {
9146             region_size = high_address - base_address;
9147             
9148 #ifdef TIME_WRITE_WATCH
9149             unsigned int time_start = GetCycleCount32();
9150 #endif //TIME_WRITE_WATCH
9151             dprintf (3, ("h%d: loh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9152             //reset_ww_by_chunk (base_address, region_size);
9153             ResetWriteWatch (base_address, region_size);
9154
9155 #ifdef TIME_WRITE_WATCH
9156             unsigned int time_stop = GetCycleCount32();
9157             tot_cycles += time_stop - time_start;
9158             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9159                     time_stop - time_start, tot_cycles);
9160 #endif //TIME_WRITE_WATCH
9161     
9162             switch_on_reset (concurrent_p, &reset_size, region_size);
9163         }
9164
9165         seg = heap_segment_next_rw (seg);
9166
9167         concurrent_print_time_delta ("CRWW loh");
9168     }
9169
9170 #ifdef DEBUG_WRITE_WATCH
9171     debug_write_watch = (BYTE**)~0;
9172 #endif //DEBUG_WRITE_WATCH
9173 }
9174
9175 #endif //WRITE_WATCH
9176
9177 #ifdef BACKGROUND_GC
9178 void gc_heap::restart_vm()
9179 {
9180     //assert (generation_allocation_pointer (youngest_generation) == 0);
9181     dprintf (3, ("Restarting EE"));
9182     STRESS_LOG0(LF_GC, LL_INFO10000, "Concurrent GC: Retarting EE\n");
9183     ee_proceed_event.Set();
9184 }
9185
9186 inline
9187 void fire_alloc_wait_event (alloc_wait_reason awr, BOOL begin_p)
9188 {
9189     if (awr != awr_ignored)
9190     {
9191         if (begin_p)
9192         {
9193             FireEtwBGCAllocWaitBegin (awr, GetClrInstanceId());
9194         }
9195         else
9196         {
9197             FireEtwBGCAllocWaitEnd (awr, GetClrInstanceId());
9198         }
9199     }
9200 }
9201
9202
9203 void gc_heap::fire_alloc_wait_event_begin (alloc_wait_reason awr)
9204 {
9205     fire_alloc_wait_event (awr, TRUE);
9206 }
9207
9208
9209 void gc_heap::fire_alloc_wait_event_end (alloc_wait_reason awr)
9210 {
9211     fire_alloc_wait_event (awr, FALSE);
9212 }
9213 #endif //BACKGROUND_GC
9214 void gc_heap::make_generation (generation& gen, heap_segment* seg, BYTE* start, BYTE* pointer)
9215 {
9216     gen.allocation_start = start;
9217     gen.allocation_context.alloc_ptr = pointer;
9218     gen.allocation_context.alloc_limit = pointer;
9219     gen.allocation_context.alloc_bytes = 0;
9220     gen.allocation_context.alloc_bytes_loh = 0;
9221     gen.allocation_context_start_region = pointer;
9222     gen.start_segment = seg;
9223     gen.allocation_segment = seg;
9224     gen.plan_allocation_start = 0;
9225     gen.free_list_space = 0;
9226     gen.pinned_allocated = 0; 
9227     gen.free_list_allocated = 0; 
9228     gen.end_seg_allocated = 0;
9229     gen.condemned_allocated = 0; 
9230     gen.free_obj_space = 0;
9231     gen.allocation_size = 0;
9232     gen.pinned_allocation_sweep_size = 0;
9233     gen.pinned_allocation_compact_size = 0;
9234     gen.allocate_end_seg_p = FALSE;
9235     gen.free_list_allocator.clear();
9236
9237 #ifdef FREE_USAGE_STATS
9238     memset (gen.gen_free_spaces, 0, sizeof (gen.gen_free_spaces));
9239     memset (gen.gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces));
9240     memset (gen.gen_plugs, 0, sizeof (gen.gen_plugs));
9241 #endif //FREE_USAGE_STATS
9242 }
9243
9244 void gc_heap::adjust_ephemeral_limits ()
9245 {
9246     ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
9247     ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);
9248
9249     dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
9250                  (size_t)ephemeral_low, (size_t)ephemeral_high))
9251
9252     // This updates the write barrier helpers with the new info.
9253     StompWriteBarrierEphemeral();
9254 }
9255
9256 HRESULT gc_heap::initialize_gc (size_t segment_size,
9257                                 size_t heap_size
9258 #ifdef MULTIPLE_HEAPS
9259                                 ,unsigned number_of_heaps
9260 #endif //MULTIPLE_HEAPS
9261 )
9262 {
9263 #ifdef TRACE_GC
9264     int log_last_gcs = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCLogEnabled);
9265     if (log_last_gcs)
9266     {
9267         LPWSTR  temp_logfile_name = NULL;
9268         CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCLogFile, &temp_logfile_name);
9269
9270 #ifdef FEATURE_REDHAWK
9271         gc_log = PalCreateFileW(
9272             temp_logfile_name,
9273             GENERIC_WRITE,
9274             FILE_SHARE_READ,
9275             NULL,
9276             CREATE_ALWAYS,
9277             FILE_ATTRIBUTE_NORMAL,
9278             NULL);
9279 #else // FEATURE_REDHAWK
9280         char logfile_name[MAX_PATH+1];
9281         if (temp_logfile_name != 0)
9282         {
9283             int ret;
9284             ret = WszWideCharToMultiByte(CP_ACP, 0, temp_logfile_name, -1, logfile_name, sizeof(logfile_name)-1, NULL, NULL);
9285             _ASSERTE(ret != 0);
9286             delete temp_logfile_name;
9287         }
9288
9289         char szPid[20];
9290         sprintf_s(szPid, _countof(szPid), ".%d", GetCurrentProcessId());
9291         strcat_s(logfile_name, _countof(logfile_name), szPid);
9292         strcat_s(logfile_name, _countof(logfile_name), ".log");
9293
9294         gc_log = CreateFileA(
9295             logfile_name,
9296             GENERIC_WRITE,
9297             FILE_SHARE_READ,
9298             NULL,
9299             CREATE_ALWAYS,
9300             FILE_ATTRIBUTE_NORMAL,
9301             NULL);
9302 #endif // FEATURE_REDHAWK
9303
9304         if (gc_log == INVALID_HANDLE_VALUE)
9305         {
9306             return E_FAIL;
9307         }
9308
9309         // GCLogFileSize in MBs.
9310         gc_log_file_size = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCLogFileSize);
9311
9312         if (gc_log_file_size < 0 || gc_log_file_size > 500)
9313         {
9314             CloseHandle (gc_log);
9315             return E_FAIL;
9316         }
9317
9318         gc_log_lock = ClrCreateMutex(NULL, FALSE, NULL);
9319         gc_log_buffer = new (nothrow) BYTE [gc_log_buffer_size];
9320         if (!gc_log_buffer)
9321         {
9322             return E_FAIL;
9323         }
9324         memset (gc_log_buffer, '*', gc_log_buffer_size);
9325
9326         max_gc_buffers = gc_log_file_size * 1024 * 1024 / gc_log_buffer_size;
9327         //max_gc_buffers = gc_log_file_size * 1024 * 5/ gc_log_buffer_size;
9328
9329     }
9330 #endif // TRACE_GC
9331
9332 #ifdef GC_STATS
9333     GCStatistics::logFileName = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCMixLog);
9334     if (GCStatistics::logFileName != NULL)
9335     {
9336         GCStatistics::logFile = _wfopen((LPCWSTR)GCStatistics::logFileName, W("a"));
9337     }
9338
9339 #endif // GC_STATS
9340
9341     HRESULT hres = S_OK;
9342
9343 #ifdef WRITE_WATCH
9344     write_watch_api_supported();
9345 #ifdef BACKGROUND_GC
9346     if (can_use_write_watch () && g_pConfig->GetGCconcurrent()!=0)
9347     {
9348         gc_can_use_concurrent = TRUE;
9349         mem_reserve = MEM_WRITE_WATCH | MEM_RESERVE;
9350     }
9351     else
9352     {
9353         gc_can_use_concurrent = FALSE;
9354     }
9355 #endif //BACKGROUND_GC
9356 #endif //WRITE_WATCH
9357
9358     reserved_memory = 0;
9359     unsigned block_count;
9360 #ifdef MULTIPLE_HEAPS
9361     reserved_memory_limit = (segment_size + heap_size) * number_of_heaps;
9362     block_count = number_of_heaps;
9363 #else //MULTIPLE_HEAPS
9364     reserved_memory_limit = segment_size + heap_size;
9365     block_count = 1;
9366 #endif //MULTIPLE_HEAPS
9367
9368     if (!reserve_initial_memory(segment_size,heap_size,block_count))
9369         return E_OUTOFMEMORY;
9370
9371 #ifdef CARD_BUNDLE
9372     //check if we need to turn on card_bundles.
9373 #ifdef MULTIPLE_HEAPS
9374     // use __int64 arithmetic here because of possible overflow on 32p
9375     unsigned __int64 th = (unsigned __int64)MH_TH_CARD_BUNDLE*number_of_heaps;
9376 #else
9377     // use __int64 arithmetic here because of possible overflow on 32p
9378     unsigned __int64 th = (unsigned __int64)SH_TH_CARD_BUNDLE;
9379 #endif //MULTIPLE_HEAPS
9380
9381     if ((can_use_write_watch() && reserved_memory >= th))
9382     {
9383         settings.card_bundles = TRUE;
9384     } else
9385     {
9386         settings.card_bundles = FALSE;
9387     }
9388 #endif //CARD_BUNDLE
9389
9390     //Init the gc_mechanisms
9391     settings.first_init();
9392
9393     //g_highest_address = (BYTE*)0x7ffe0000;
9394     g_card_table = make_card_table (g_lowest_address, g_highest_address);
9395
9396     if (!g_card_table)
9397         return E_OUTOFMEMORY;
9398
9399     gc_started = FALSE;
9400
9401 #ifdef MULTIPLE_HEAPS
9402     n_heaps = number_of_heaps;
9403
9404     g_heaps = new (nothrow) gc_heap* [number_of_heaps];
9405     if (!g_heaps)
9406         return E_OUTOFMEMORY;
9407
9408 #ifdef _PREFAST_ 
9409 #pragma warning(push)
9410 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
9411 #endif // _PREFAST_
9412     g_promoted = new (nothrow) size_t [number_of_heaps*16];
9413     g_bpromoted = new (nothrow) size_t [number_of_heaps*16];
9414 #ifdef MH_SC_MARK
9415     g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
9416 #endif //MH_SC_MARK
9417 #ifdef _PREFAST_ 
9418 #pragma warning(pop)
9419 #endif // _PREFAST_
9420     if (!g_promoted || !g_bpromoted)
9421         return E_OUTOFMEMORY;
9422
9423 #ifdef MH_SC_MARK
9424     if (!g_mark_stack_busy)
9425         return E_OUTOFMEMORY;
9426 #endif //MH_SC_MARK
9427
9428     g_gc_threads = new (nothrow) HANDLE [number_of_heaps];
9429     if (!g_gc_threads)
9430         return E_OUTOFMEMORY;
9431
9432     if (!create_thread_support (number_of_heaps))
9433         return E_OUTOFMEMORY;
9434
9435     if (!heap_select::init (number_of_heaps))
9436         return E_OUTOFMEMORY;
9437
9438 #endif //MULTIPLE_HEAPS
9439
9440 #ifdef TRACE_GC
9441     print_level = g_pConfig->GetGCprnLvl();
9442     gc_trace_fac = g_pConfig->GetGCtraceFac();
9443 #endif //TRACE_GC
9444
9445     if (!init_semi_shared())
9446     {
9447         hres = E_FAIL;
9448     }
9449
9450     return hres;
9451 }
9452
9453 //Initializes PER_HEAP_ISOLATED data members.
9454 int
9455 gc_heap::init_semi_shared()
9456 {
9457     int ret = 0;
9458
9459     // This is used for heap expansion - it's to fix exactly the start for gen 0
9460     // through (max_generation-1). When we expand the heap we allocate all these
9461     // gen starts at the beginning of the new ephemeral seg. 
9462     eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
9463
9464 #ifdef MARK_LIST
9465     size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
9466     MAYBE_UNUSED_VAR(gen0size);
9467
9468 #ifdef MULTIPLE_HEAPS
9469
9470     mark_list_size = min (150*1024, max (8192, get_valid_segment_size()/(2*10*32)));
9471     g_mark_list = make_mark_list (mark_list_size*n_heaps);
9472
9473 #ifdef PARALLEL_MARK_LIST_SORT
9474     g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
9475     if (!g_mark_list_copy)
9476     {
9477         goto cleanup;
9478     }
9479 #endif //PARALLEL_MARK_LIST_SORT
9480
9481 #else //MULTIPLE_HEAPS
9482
9483     mark_list_size = max (8192, get_valid_segment_size()/(64*32));
9484     g_mark_list = make_mark_list (mark_list_size);
9485
9486 #endif //MULTIPLE_HEAPS
9487
9488     dprintf (3, ("gen0 size: %d, mark_list_size: %d",
9489                  gen0size, mark_list_size));
9490
9491     if (!g_mark_list)
9492     {
9493         goto cleanup;
9494     }
9495 #endif //MARK_LIST
9496
9497 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
9498     if (!seg_mapping_table_init())
9499         goto cleanup;
9500 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
9501
9502 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
9503     seg_table = sorted_table::make_sorted_table();
9504
9505     if (!seg_table)
9506         goto cleanup;
9507 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
9508
9509     segment_standby_list = 0;
9510
9511     full_gc_approach_event.CreateManualEvent(FALSE);
9512     if (!full_gc_approach_event.IsValid())
9513     {
9514         goto cleanup;
9515     }
9516     full_gc_end_event.CreateManualEvent(FALSE);
9517     if (!full_gc_end_event.IsValid())
9518     {
9519         goto cleanup;
9520     }
9521
9522     fgn_maxgen_percent = 0;
9523     fgn_loh_percent = 0;
9524     full_gc_approach_event_set = false;
9525
9526     memset (full_gc_counts, 0, sizeof (full_gc_counts));
9527
9528     last_gc_index = 0;
9529     should_expand_in_full_gc = FALSE;
9530
9531 #ifdef FEATURE_LOH_COMPACTION
9532     loh_compaction_always_p = (g_pConfig->GetGCLOHCompactionMode() != 0);
9533     loh_compaction_mode = loh_compaction_default;
9534 #endif //FEATURE_LOH_COMPACTION
9535
9536 #ifdef BACKGROUND_GC
9537     memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
9538     bgc_alloc_spin_count = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BGCSpinCount);
9539     bgc_alloc_spin = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BGCSpin);
9540
9541     {   
9542         int number_bgc_threads = 1;
9543 #ifdef MULTIPLE_HEAPS
9544         number_bgc_threads = n_heaps;
9545 #endif //MULTIPLE_HEAPS
9546         if (!create_bgc_threads_support (number_bgc_threads))
9547         {
9548             goto cleanup;
9549         }
9550 #endif //BACKGROUND_GC
9551     }
9552
9553     ret = 1;
9554
9555 cleanup:
9556
9557     if (!ret)
9558     {
9559         if (full_gc_approach_event.IsValid())
9560         {
9561             full_gc_approach_event.CloseEvent();
9562         }
9563         if (full_gc_end_event.IsValid())
9564         {
9565             full_gc_end_event.CloseEvent();
9566         }
9567     }
9568
9569     return ret;
9570 }
9571
9572 gc_heap* gc_heap::make_gc_heap (
9573 #ifdef MULTIPLE_HEAPS
9574                                 GCHeap* vm_hp,
9575                                 int heap_number
9576 #endif //MULTIPLE_HEAPS
9577                                 )
9578 {
9579     gc_heap* res = 0;
9580
9581 #ifdef MULTIPLE_HEAPS
9582     res = new (nothrow) gc_heap;
9583     if (!res)
9584         return 0;
9585
9586     res->vm_heap = vm_hp;
9587     res->alloc_context_count = 0;
9588
9589 #ifdef MARK_LIST
9590 #ifdef PARALLEL_MARK_LIST_SORT
9591     res->mark_list_piece_start = new (nothrow) BYTE**[n_heaps];
9592     if (!res->mark_list_piece_start)
9593         return 0;
9594
9595 #ifdef _PREFAST_ 
9596 #pragma warning(push)
9597 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
9598 #endif // _PREFAST_
9599     res->mark_list_piece_end = new (nothrow) BYTE**[n_heaps + 32]; // +32 is padding to reduce false sharing
9600 #ifdef _PREFAST_ 
9601 #pragma warning(pop)
9602 #endif // _PREFAST_
9603
9604     if (!res->mark_list_piece_end)
9605         return 0;
9606 #endif //PARALLEL_MARK_LIST_SORT
9607 #endif //MARK_LIST
9608
9609
9610 #endif //MULTIPLE_HEAPS
9611
9612     if (res->init_gc_heap (
9613 #ifdef MULTIPLE_HEAPS
9614         heap_number
9615 #else  //MULTIPLE_HEAPS
9616         0
9617 #endif //MULTIPLE_HEAPS
9618         )==0)
9619     {
9620         return 0;
9621     }
9622
9623 #ifdef MULTIPLE_HEAPS
9624     return res;
9625 #else
9626     return (gc_heap*)1;
9627 #endif //MULTIPLE_HEAPS
9628 }
9629
9630 DWORD 
9631 gc_heap::wait_for_gc_done(INT32 timeOut)
9632 {
9633     Thread* current_thread = GetThread();
9634     BOOL cooperative_mode = enable_preemptive (current_thread);
9635
9636     DWORD dwWaitResult = NOERROR;
9637
9638     gc_heap* wait_heap = NULL;
9639     while (gc_heap::gc_started)
9640     {       
9641 #ifdef MULTIPLE_HEAPS
9642         wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
9643         dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
9644 #endif // MULTIPLE_HEAPS
9645
9646 #ifdef _PREFAST_
9647         PREFIX_ASSUME(wait_heap != NULL);
9648 #endif // _PREFAST_
9649
9650         dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE); 
9651     }
9652     disable_preemptive (current_thread, cooperative_mode);
9653
9654     return dwWaitResult;
9655 }
9656
9657 void 
9658 gc_heap::set_gc_done()
9659 {
9660     enter_gc_done_event_lock();
9661     if (!gc_done_event_set)
9662     {
9663         gc_done_event_set = true;
9664         dprintf (2, ("heap %d: setting gc_done_event", heap_number));
9665         gc_done_event.Set();
9666     }
9667     exit_gc_done_event_lock();
9668 }
9669
9670 void 
9671 gc_heap::reset_gc_done()
9672 {
9673     enter_gc_done_event_lock();
9674     if (gc_done_event_set)
9675     {
9676         gc_done_event_set = false;
9677         dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
9678         gc_done_event.Reset();
9679     }
9680     exit_gc_done_event_lock();
9681 }
9682
9683 void 
9684 gc_heap::enter_gc_done_event_lock()
9685 {
9686     DWORD dwSwitchCount = 0;
9687 retry:
9688
9689     if (FastInterlockExchange (&gc_done_event_lock, 0) >= 0)
9690     {
9691         while (gc_done_event_lock >= 0)
9692         {
9693             if  (g_SystemInfo.dwNumberOfProcessors > 1)
9694             {
9695                 int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
9696                 for (int j = 0; j < spin_count; j++)
9697                 {
9698                     if  (gc_done_event_lock < 0)
9699                         break;
9700                     YieldProcessor();           // indicate to the processor that we are spining
9701                 }
9702                 if  (gc_done_event_lock >= 0)
9703                     __SwitchToThread(0, ++dwSwitchCount);
9704             }
9705             else
9706                 __SwitchToThread(0, ++dwSwitchCount);
9707         }
9708         goto retry;
9709     }
9710 }
9711
9712 void 
9713 gc_heap::exit_gc_done_event_lock()
9714 {
9715     gc_done_event_lock = -1;
9716 }
9717
9718 #ifndef MULTIPLE_HEAPS
9719
9720 #ifdef RECORD_LOH_STATE
9721 int gc_heap::loh_state_index = 0;
9722 gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
9723 #endif //RECORD_LOH_STATE
9724
9725 VOLATILE(LONG) gc_heap::gc_done_event_lock;
9726 VOLATILE(bool) gc_heap::gc_done_event_set;
9727 CLREvent gc_heap::gc_done_event;
9728 #endif //!MULTIPLE_HEAPS
9729 VOLATILE(bool) gc_heap::internal_gc_done;
9730
9731 void gc_heap::add_saved_spinlock_info (
9732             msl_enter_state enter_state, 
9733             msl_take_state take_state)
9734
9735 {
9736 #ifdef SPINLOCK_HISTORY
9737     spinlock_info* current = &last_spinlock_info[spinlock_info_index];
9738
9739     current->enter_state = enter_state;
9740     current->take_state = take_state;
9741     current->thread_id = GetCurrentThreadId();
9742
9743     spinlock_info_index++;
9744
9745     assert (spinlock_info_index <= max_saved_spinlock_info);
9746
9747     if (spinlock_info_index >= max_saved_spinlock_info)
9748     {
9749         spinlock_info_index = 0;
9750     }
9751 #else
9752     MAYBE_UNUSED_VAR(enter_state);
9753     MAYBE_UNUSED_VAR(take_state);
9754 #endif //SPINLOCK_HISTORY
9755 }
9756
9757 int
9758 gc_heap::init_gc_heap (int  h_number)
9759 {
9760 #ifdef MULTIPLE_HEAPS
9761
9762     time_bgc_last = 0;
9763
9764 #ifdef SPINLOCK_HISTORY
9765     spinlock_info_index = 0;
9766     memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
9767 #endif //SPINLOCK_HISTORY
9768
9769     // initialize per heap members.
9770     ephemeral_low = (BYTE*)1;
9771
9772     ephemeral_high = MAX_PTR;
9773
9774     ephemeral_heap_segment = 0;
9775
9776     freeable_large_heap_segment = 0;
9777
9778     condemned_generation_num = 0;
9779
9780     blocking_collection = FALSE;
9781
9782     generation_skip_ratio = 100;
9783
9784     mark_stack_tos = 0;
9785
9786     mark_stack_bos = 0;
9787
9788     mark_stack_array_length = 0;
9789
9790     mark_stack_array = 0;
9791
9792     verify_pinned_queue_p = FALSE;
9793
9794     loh_pinned_queue_tos = 0;
9795
9796     loh_pinned_queue_bos = 0;
9797
9798     loh_pinned_queue_length = 0;
9799
9800     loh_pinned_queue_decay = LOH_PIN_DECAY;
9801
9802     loh_pinned_queue = 0;
9803
9804     min_overflow_address = MAX_PTR;
9805
9806     max_overflow_address = 0;
9807
9808     gen0_bricks_cleared = FALSE;
9809
9810     gen0_must_clear_bricks = 0;
9811
9812     allocation_quantum = CLR_SIZE;
9813
9814     more_space_lock = gc_lock;
9815
9816     ro_segments_in_range = FALSE;
9817
9818     loh_alloc_since_cg = 0;
9819
9820     new_heap_segment = NULL;
9821
9822 #ifdef RECORD_LOH_STATE
9823     loh_state_index = 0;
9824 #endif //RECORD_LOH_STATE
9825 #endif //MULTIPLE_HEAPS
9826
9827 #ifdef MULTIPLE_HEAPS
9828     if (h_number > n_heaps)
9829     {
9830         assert (!"Number of heaps exceeded");
9831         return 0;
9832     }
9833
9834     heap_number = h_number;
9835 #endif //MULTIPLE_HEAPS
9836
9837     memset (&oom_info, 0, sizeof (oom_info));
9838     memset (&fgm_result, 0, sizeof (fgm_result));
9839     gc_done_event.CreateManualEvent(FALSE);
9840     if (!gc_done_event.IsValid())
9841     {
9842         return 0;
9843     }
9844     gc_done_event_lock = -1;
9845     gc_done_event_set = false;
9846
9847 #ifndef SEG_MAPPING_TABLE
9848     if (!gc_heap::seg_table->insure_space_for_insert ())
9849     {
9850         return 0;
9851     }
9852 #endif //!SEG_MAPPING_TABLE
9853
9854     heap_segment* seg = get_initial_segment (get_valid_segment_size(), h_number);
9855     if (!seg)
9856         return 0;
9857
9858     FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(seg), 
9859                               (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), 
9860                               ETW::GCLog::ETW_GC_INFO::SMALL_OBJECT_HEAP, 
9861                               GetClrInstanceId());
9862     
9863 #ifdef SEG_MAPPING_TABLE
9864     seg_mapping_table_add_segment (seg, __this);
9865 #else //SEG_MAPPING_TABLE
9866     seg_table->insert ((BYTE*)seg, sdelta);
9867 #endif //SEG_MAPPING_TABLE
9868
9869 #ifdef MULTIPLE_HEAPS
9870     heap_segment_heap (seg) = this;
9871 #endif //MULTIPLE_HEAPS
9872
9873     /* todo: Need a global lock for this */
9874     DWORD* ct = &g_card_table [card_word (card_of (g_lowest_address))];
9875     own_card_table (ct);
9876     card_table = translate_card_table (ct);
9877     /* End of global lock */
9878
9879     brick_table = card_table_brick_table (ct);
9880     highest_address = card_table_highest_address (ct);
9881     lowest_address = card_table_lowest_address (ct);
9882
9883 #ifdef CARD_BUNDLE
9884     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct));
9885     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_lowest_address))))] ==
9886             card_table_card_bundle_table (ct));
9887 #endif //CARD_BUNDLE
9888
9889 #ifdef MARK_ARRAY
9890     if (gc_can_use_concurrent)
9891         mark_array = translate_mark_array (card_table_mark_array (&g_card_table[card_word (card_of (g_lowest_address))]));
9892     else
9893         mark_array = NULL;
9894 #endif //MARK_ARRAY
9895
9896     BYTE*  start = heap_segment_mem (seg);
9897
9898     for (int i = 0; i < 1 + max_generation; i++)
9899     {
9900         make_generation (generation_table [ (max_generation - i) ],
9901                          seg, start, 0);
9902         generation_table [(max_generation - i)].gen_num = max_generation - i;
9903         start += Align (min_obj_size);
9904     }
9905
9906     heap_segment_allocated (seg) = start;
9907     alloc_allocated = start;
9908     heap_segment_used (seg) = start - plug_skew;
9909
9910     ephemeral_heap_segment = seg;
9911
9912 #ifndef SEG_MAPPING_TABLE
9913     if (!gc_heap::seg_table->insure_space_for_insert ())
9914     {
9915         return 0;
9916     }
9917 #endif //!SEG_MAPPING_TABLE
9918     //Create the large segment generation
9919     heap_segment* lseg = get_initial_segment(get_valid_segment_size(TRUE), h_number);
9920     if (!lseg)
9921         return 0;
9922     lseg->flags |= heap_segment_flags_loh;
9923
9924     FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(lseg), 
9925                               (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)), 
9926                               ETW::GCLog::ETW_GC_INFO::LARGE_OBJECT_HEAP, 
9927                               GetClrInstanceId());
9928 #ifdef SEG_MAPPING_TABLE
9929     seg_mapping_table_add_segment (lseg, __this);
9930 #else //SEG_MAPPING_TABLE
9931     seg_table->insert ((BYTE*)lseg, sdelta);
9932 #endif //SEG_MAPPING_TABLE
9933
9934     generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
9935     //assign the alloc_list for the large generation 
9936     generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
9937     generation_table [max_generation+1].gen_num = max_generation+1;
9938     make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
9939     heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
9940     heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
9941
9942     for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
9943     {
9944         generation*  gen = generation_of (gen_num);
9945         make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
9946     }
9947
9948 #ifdef MULTIPLE_HEAPS
9949     heap_segment_heap (lseg) = this;
9950
9951     //initialize the alloc context heap
9952     generation_alloc_context (generation_of (0))->alloc_heap = vm_heap;
9953
9954     //initialize the alloc context heap
9955     generation_alloc_context (generation_of (max_generation+1))->alloc_heap = vm_heap;
9956
9957 #endif //MULTIPLE_HEAPS
9958
9959     //Do this only once
9960 #ifdef MULTIPLE_HEAPS
9961     if (h_number == 0)
9962 #endif //MULTIPLE_HEAPS
9963     {
9964 #ifndef INTERIOR_POINTERS
9965         //set the brick_table for large objects
9966         //but default value is clearded
9967         //clear_brick_table ((BYTE*)heap_segment_mem (lseg),
9968         //                   (BYTE*)heap_segment_reserved (lseg));
9969
9970 #else //INTERIOR_POINTERS
9971
9972         //Because of the interior pointer business, we have to clear
9973         //the whole brick table
9974         //but the default value is cleared
9975         // clear_brick_table (lowest_address, highest_address);
9976 #endif //INTERIOR_POINTERS
9977     }
9978
9979     if (!init_dynamic_data())
9980     {
9981         return 0;
9982     }
9983
9984     etw_allocation_running_amount[0] = 0;
9985     etw_allocation_running_amount[1] = 0;
9986
9987     //needs to be done after the dynamic data has been initialized
9988 #ifndef MULTIPLE_HEAPS
9989     allocation_running_amount = dd_min_gc_size (dynamic_data_of (0));
9990 #endif //!MULTIPLE_HEAPS
9991
9992     fgn_last_alloc = dd_min_gc_size (dynamic_data_of (0));
9993
9994     mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
9995     if (!arr)
9996         return 0;
9997
9998     make_mark_stack(arr);
9999
10000 #ifdef BACKGROUND_GC
10001     freeable_small_heap_segment = 0;
10002     gchist_index_per_heap = 0;
10003     BYTE** b_arr = new (nothrow) (BYTE* [MARK_STACK_INITIAL_LENGTH]);
10004     if (!b_arr)
10005         return 0;
10006
10007     make_background_mark_stack (b_arr);
10008 #endif //BACKGROUND_GC
10009
10010     adjust_ephemeral_limits();
10011
10012 #ifdef MARK_ARRAY
10013     // why would we clear the mark array for this page? it should be cleared..
10014     // clear the first committed page
10015     //if(gc_can_use_concurrent)
10016     //{
10017     //    clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10018     //}
10019 #endif //MARK_ARRAY
10020
10021 #ifdef MULTIPLE_HEAPS
10022     //register the heap in the heaps array
10023
10024     g_gc_threads [heap_number] = create_gc_thread ();
10025     if (!g_gc_threads [heap_number])
10026         return 0;
10027
10028     g_heaps [heap_number] = this;
10029
10030 #endif //MULTIPLE_HEAPS
10031
10032 #ifdef FEATURE_PREMORTEM_FINALIZATION
10033     HRESULT hr = AllocateCFinalize(&finalize_queue);
10034     if (FAILED(hr))
10035         return 0;
10036 #endif // FEATURE_PREMORTEM_FINALIZATION
10037
10038     max_free_space_items = MAX_NUM_FREE_SPACES;
10039
10040     bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10041
10042     if (!bestfit_seg)
10043     {
10044         return 0;
10045     }
10046
10047     if (!bestfit_seg->alloc())
10048     {
10049         return 0;
10050     }
10051
10052     last_gc_before_oom = FALSE;
10053
10054 #ifdef MULTIPLE_HEAPS
10055
10056 #ifdef HEAP_ANALYZE
10057
10058     heap_analyze_success = TRUE;
10059
10060     internal_root_array  = 0;
10061
10062     internal_root_array_index = 0;
10063
10064     internal_root_array_length = initial_internal_roots;
10065
10066     current_obj          = 0;
10067
10068     current_obj_size     = 0;
10069
10070 #endif //HEAP_ANALYZE
10071
10072 #endif // MULTIPLE_HEAPS
10073
10074 #ifdef BACKGROUND_GC
10075     bgc_thread_id = 0;
10076
10077     if (!create_bgc_thread_support())
10078     {
10079         return 0;
10080     }
10081
10082     bgc_alloc_lock = new (nothrow) exclusive_sync;
10083     if (!bgc_alloc_lock)
10084     {
10085         return 0;
10086     }
10087
10088     bgc_alloc_lock->init();
10089
10090     if (h_number == 0)
10091     {
10092         if (!recursive_gc_sync::init())
10093             return 0;
10094     }
10095
10096     bgc_thread_running = 0;
10097     bgc_thread = 0;
10098     InitializeCriticalSection (&bgc_threads_timeout_cs);
10099     expanded_in_fgc = 0;
10100     current_bgc_state = bgc_not_in_process;
10101     background_soh_alloc_count = 0;
10102     background_loh_alloc_count = 0;
10103     bgc_overflow_count = 0;
10104     end_loh_size = dd_min_gc_size (dynamic_data_of (max_generation + 1));
10105 #endif //BACKGROUND_GC
10106     return 1;
10107 }
10108
10109 void
10110 gc_heap::destroy_semi_shared()
10111 {
10112 //TODO: will need to move this to per heap
10113 //#ifdef BACKGROUND_GC
10114 //    if (c_mark_list)
10115 //        delete c_mark_list;
10116 //#endif //BACKGROUND_GC
10117
10118 #ifdef MARK_LIST
10119     if (g_mark_list)
10120         delete g_mark_list;
10121 #endif //MARK_LIST
10122
10123 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10124     if (seg_mapping_table)
10125         delete seg_mapping_table;
10126 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10127
10128 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10129     //destroy the segment map
10130     seg_table->delete_sorted_table();
10131 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10132 }
10133
10134 void
10135 gc_heap::self_destroy()
10136 {
10137 #ifdef BACKGROUND_GC
10138     kill_gc_thread();
10139 #endif //BACKGROUND_GC
10140
10141     if (gc_done_event.IsValid())
10142     {
10143         gc_done_event.CloseEvent();
10144     }
10145
10146     // destroy every segment.
10147     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10148
10149     PREFIX_ASSUME(seg != NULL);
10150
10151     heap_segment* next_seg;
10152     while (seg)
10153     {
10154         next_seg = heap_segment_next_rw (seg);
10155         delete_heap_segment (seg);
10156         seg = next_seg;
10157     }
10158
10159     seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10160
10161     PREFIX_ASSUME(seg != NULL);
10162
10163     while (seg)
10164     {
10165         next_seg = heap_segment_next_rw (seg);
10166         delete_heap_segment (seg);
10167         seg = next_seg;
10168     }
10169
10170     // get rid of the card table
10171     release_card_table (card_table);
10172
10173     // destroy the mark stack
10174     delete mark_stack_array;
10175
10176 #ifdef FEATURE_PREMORTEM_FINALIZATION
10177     if (finalize_queue)
10178         delete finalize_queue;
10179 #endif // FEATURE_PREMORTEM_FINALIZATION
10180 }
10181
10182 void
10183 gc_heap::destroy_gc_heap(gc_heap* heap)
10184 {
10185     heap->self_destroy();
10186     delete heap;
10187 }
10188
10189 // Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10190 // the finalizer queue has been drained.
10191 void gc_heap::shutdown_gc()
10192 {
10193     destroy_semi_shared();
10194
10195 #ifdef MULTIPLE_HEAPS
10196     //delete the heaps array
10197     delete g_heaps;
10198     for (int i = 0; i < n_heaps; i++)
10199     {
10200         CloseHandle (g_gc_threads [i]);
10201     }
10202     delete g_gc_threads;
10203     destroy_thread_support();
10204     n_heaps = 0;
10205 #endif //MULTIPLE_HEAPS
10206     //destroy seg_manager
10207
10208     destroy_initial_memory();
10209 }
10210
10211 inline
10212 BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, BYTE* alloc_pointer, BYTE* alloc_limit,
10213                           BYTE* old_loc, int use_padding)
10214 {
10215     BOOL already_padded = FALSE;
10216 #ifdef SHORT_PLUGS
10217     if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
10218     {
10219         alloc_pointer = alloc_pointer + Align (min_obj_size);
10220         already_padded = TRUE;
10221     }
10222 #endif //SHORT_PLUGS
10223
10224     // TODO: this is incorrect - if we don't pad, we would have a different alignment so
10225     // calculating the alignment requirement here is incorrect.
10226     if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
10227         size = size + switch_alignment_size (already_padded);
10228
10229 #ifdef FEATURE_STRUCTALIGN
10230     alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
10231 #endif // FEATURE_STRUCTALIGN
10232
10233     // in allocate_in_condemned_generation we can have this when we
10234     // set the alloc_limit to plan_allocated which could be less than 
10235     // alloc_ptr
10236     if (alloc_limit < alloc_pointer)
10237     {
10238         return FALSE;
10239     }
10240
10241     if (old_loc != 0)
10242     {
10243         return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0))) 
10244 #ifdef SHORT_PLUGS
10245                 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
10246 #else //SHORT_PLUGS
10247                 ||((alloc_pointer + size) == alloc_limit)
10248 #endif //SHORT_PLUGS
10249             );
10250     }
10251     else
10252     {
10253         assert (size == Align (min_obj_size));
10254         return ((size_t)(alloc_limit - alloc_pointer) >= size);
10255     }
10256 }
10257
10258 inline
10259 BOOL gc_heap::a_size_fit_p (size_t size, BYTE* alloc_pointer, BYTE* alloc_limit,
10260                             int align_const)
10261 {
10262     // We could have run into cases where this is true when alloc_allocated is the 
10263     // the same as the seg committed.
10264     if (alloc_limit < alloc_pointer)
10265     {
10266         return FALSE;
10267     }
10268
10269     return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
10270 }
10271
10272 // Grow by committing more pages
10273 BOOL gc_heap::grow_heap_segment (heap_segment* seg, BYTE* high_address)
10274 {
10275     assert (high_address <= heap_segment_reserved (seg));
10276
10277     //return 0 if we are at the end of the segment.
10278     if (align_on_page (high_address) > heap_segment_reserved (seg))
10279         return FALSE;
10280
10281     if (high_address <= heap_segment_committed (seg))
10282         return TRUE;
10283
10284     size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
10285     c_size = max (c_size, 16*OS_PAGE_SIZE);
10286     c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
10287
10288     if (c_size == 0)
10289         return FALSE;
10290
10291     STRESS_LOG2(LF_GC, LL_INFO10000,
10292                 "Growing heap_segment: %Ix high address: %Ix\n",
10293                 (size_t)seg, (size_t)high_address);
10294
10295     dprintf(3, ("Growing segment allocation %Ix %Ix", (size_t)heap_segment_committed(seg),c_size));
10296     
10297     if (!virtual_alloc_commit_for_heap(heap_segment_committed (seg), c_size,
10298                                        MEM_COMMIT, PAGE_READWRITE, heap_number))
10299     {
10300         dprintf(3, ("Cannot grow heap segment"));
10301         return FALSE;
10302     }
10303 #ifdef MARK_ARRAY
10304 #ifndef BACKGROUND_GC
10305     clear_mark_array (heap_segment_committed (seg),
10306                       heap_segment_committed (seg)+c_size, TRUE);
10307 #endif //BACKGROUND_GC
10308 #endif //MARK_ARRAY
10309     heap_segment_committed (seg) += c_size;
10310     STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
10311                 (size_t)heap_segment_committed (seg));
10312
10313     assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
10314
10315     assert (high_address <= heap_segment_committed (seg));
10316
10317     return TRUE;
10318 }
10319
10320 inline
10321 int gc_heap::grow_heap_segment (heap_segment* seg, BYTE* allocated, BYTE* old_loc, size_t size, BOOL pad_front_p  REQD_ALIGN_AND_OFFSET_DCL)
10322 {
10323 #ifdef SHORT_PLUGS
10324     if ((old_loc != 0) && pad_front_p)
10325     {
10326         allocated = allocated + Align (min_obj_size);
10327     }
10328 #endif //SHORT_PLUGS
10329
10330     if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
10331         size = size + switch_alignment_size (FALSE);
10332 #ifdef FEATURE_STRUCTALIGN
10333     size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
10334     return grow_heap_segment (seg, allocated + pad + size);
10335 #else // FEATURE_STRUCTALIGN
10336     return grow_heap_segment (seg, allocated + size);
10337 #endif // FEATURE_STRUCTALIGN
10338 }
10339
10340 //used only in older generation allocation (i.e during gc).
10341 void gc_heap::adjust_limit (BYTE* start, size_t limit_size, generation* gen,
10342                             int gennum)
10343 {
10344     dprintf (3, ("gc Expanding segment allocation"));
10345     heap_segment* seg = generation_allocation_segment (gen);
10346     if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
10347     {
10348         if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
10349         {
10350             assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
10351             assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
10352             heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
10353         }
10354         else
10355         {
10356             BYTE*  hole = generation_allocation_pointer (gen);
10357             size_t  size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
10358
10359             if (size != 0)
10360             {
10361                 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
10362                 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
10363                 if (size >= Align (min_free_list))
10364                 {
10365                     if (allocated_size < min_free_list)
10366                     {
10367                         if (size >= (Align (min_free_list) + Align (min_obj_size)))
10368                         {
10369                             //split hole into min obj + threadable free item
10370                             make_unused_array (hole, min_obj_size);
10371                             generation_free_obj_space (gen) += Align (min_obj_size);
10372                             make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
10373                             generation_free_list_space (gen) += size - Align (min_obj_size);
10374                             generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size), 
10375                                                                           size - Align (min_obj_size));
10376                             add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
10377                         }
10378                         else
10379                         {
10380                             dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
10381                             make_unused_array (hole, size);
10382                             generation_free_obj_space (gen) += size;
10383                         }
10384                     }
10385                     else 
10386                     {
10387                         dprintf (3, ("threading hole in front of free list"));
10388                         make_unused_array (hole, size);
10389                         generation_free_list_space (gen) += size;
10390                         generation_allocator(gen)->thread_item_front (hole, size);
10391                         add_gen_free (gen->gen_num, size);
10392                     }
10393                 }
10394                 else
10395                 {
10396                     make_unused_array (hole, size);
10397                     generation_free_obj_space (gen) += size;
10398                 }
10399             }
10400         }
10401         generation_allocation_pointer (gen) = start;
10402         generation_allocation_context_start_region (gen) = start;
10403     }
10404     generation_allocation_limit (gen) = (start + limit_size);
10405 }
10406
10407 void verify_mem_cleared (BYTE* start, size_t size)
10408 {
10409     if (!Aligned (size))
10410     {
10411         FATAL_GC_ERROR();
10412     }
10413
10414     PTR_PTR curr_ptr = (PTR_PTR) start;
10415     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
10416     {
10417         if (*(curr_ptr++) != 0)
10418         {
10419             FATAL_GC_ERROR();
10420         }
10421     }
10422 }
10423
10424 #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
10425 void gc_heap::set_batch_mark_array_bits (BYTE* start, BYTE* end)
10426 {
10427     size_t start_mark_bit = mark_bit_of (start);
10428     size_t end_mark_bit = mark_bit_of (end);
10429     unsigned int startbit = mark_bit_bit (start_mark_bit);
10430     unsigned int endbit = mark_bit_bit (end_mark_bit);
10431     size_t startwrd = mark_bit_word (start_mark_bit);
10432     size_t endwrd = mark_bit_word (end_mark_bit);
10433
10434     dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
10435         (size_t)start, (size_t)start_mark_bit, 
10436         (size_t)end, (size_t)end_mark_bit));
10437
10438     unsigned int firstwrd = ~(lowbits (~0, startbit));
10439     unsigned int lastwrd = ~(highbits (~0, endbit));
10440
10441     if (startwrd == endwrd)
10442     {
10443         unsigned int wrd = firstwrd & lastwrd;
10444         mark_array[startwrd] |= wrd;
10445         return;
10446     }
10447
10448     // set the first mark word.
10449     if (startbit)
10450     {
10451         mark_array[startwrd] |= firstwrd;
10452         startwrd++;
10453     }
10454
10455     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
10456     {
10457         mark_array[wrdtmp] = ~(unsigned int)0;
10458     }
10459
10460     // set the last mark word.
10461     if (endbit)
10462     {
10463         mark_array[endwrd] |= lastwrd;
10464     }
10465 }
10466
10467 // makes sure that the mark array bits between start and end are 0.
10468 void gc_heap::check_batch_mark_array_bits (BYTE* start, BYTE* end)
10469 {
10470     size_t start_mark_bit = mark_bit_of (start);
10471     size_t end_mark_bit = mark_bit_of (end);
10472     unsigned int startbit = mark_bit_bit (start_mark_bit);
10473     unsigned int endbit = mark_bit_bit (end_mark_bit);
10474     size_t startwrd = mark_bit_word (start_mark_bit);
10475     size_t endwrd = mark_bit_word (end_mark_bit);
10476
10477     //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
10478     //    (size_t)start, (size_t)start_mark_bit, 
10479     //    (size_t)end, (size_t)end_mark_bit));
10480
10481     unsigned int firstwrd = ~(lowbits (~0, startbit));
10482     unsigned int lastwrd = ~(highbits (~0, endbit));
10483
10484     if (startwrd == endwrd)
10485     {
10486         unsigned int wrd = firstwrd & lastwrd;
10487         if (mark_array[startwrd] & wrd)
10488         {
10489             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
10490                             wrd, startwrd, 
10491                             mark_array [startwrd], mark_word_address (startwrd)));
10492             FATAL_GC_ERROR();
10493         }
10494         return;
10495     }
10496
10497     // set the first mark word.
10498     if (startbit)
10499     {
10500         if (mark_array[startwrd] & firstwrd)
10501         {
10502             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
10503                             firstwrd, startwrd, 
10504                             mark_array [startwrd], mark_word_address (startwrd)));
10505             FATAL_GC_ERROR();
10506         }
10507
10508         startwrd++;
10509     }
10510
10511     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
10512     {
10513         if (mark_array[wrdtmp])
10514         {
10515             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
10516                             wrdtmp, 
10517                             mark_array [wrdtmp], mark_word_address (wrdtmp)));
10518             FATAL_GC_ERROR();
10519         }
10520     }
10521
10522     // set the last mark word.
10523     if (endbit)
10524     {
10525         if (mark_array[endwrd] & lastwrd)
10526         {
10527             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
10528                             lastwrd, lastwrd, 
10529                             mark_array [lastwrd], mark_word_address (lastwrd)));
10530             FATAL_GC_ERROR();
10531         }
10532     }
10533 }
10534 #endif //VERIFY_HEAP && BACKGROUND_GC
10535
10536 allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
10537 {
10538     assert (num_b < MAX_BUCKET_COUNT);
10539     num_buckets = num_b;
10540     frst_bucket_size = fbs;
10541     buckets = b;
10542 }
10543
10544 alloc_list& allocator::alloc_list_of (unsigned int bn)
10545 {
10546     assert (bn < num_buckets);
10547     if (bn == 0)
10548         return first_bucket;
10549     else
10550         return buckets [bn-1];
10551 }
10552
10553 void allocator::unlink_item (unsigned int bn, BYTE* item, BYTE* prev_item, BOOL use_undo_p)
10554 {
10555     //unlink the free_item
10556     alloc_list* al = &alloc_list_of (bn);
10557     if (prev_item)
10558     {
10559         if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
10560         {
10561             free_list_undo (prev_item) = item;
10562         }
10563         free_list_slot (prev_item) = free_list_slot(item);
10564     }
10565     else
10566     {
10567         al->alloc_list_head() = (BYTE*)free_list_slot(item);
10568     }
10569     if (al->alloc_list_tail() == item)
10570     {
10571         al->alloc_list_tail() = prev_item;
10572     }
10573 }
10574
10575 void allocator::clear()
10576 {
10577     for (unsigned int i = 0; i < num_buckets; i++)
10578     {
10579         alloc_list_head_of (i) = 0;
10580         alloc_list_tail_of (i) = 0;
10581     }
10582 }
10583
10584 //always thread to the end.
10585 void allocator::thread_free_item (BYTE* item, BYTE*& head, BYTE*& tail)
10586 {
10587     free_list_slot (item) = 0;
10588     free_list_undo (item) = UNDO_EMPTY;
10589     assert (item != head);
10590
10591     if (head == 0)
10592     {
10593        head = item;
10594     }
10595     //TODO: This shouldn't happen anymore - verify that's the case.
10596     //the following is necessary because the last free element
10597     //may have been truncated, and tail isn't updated.
10598     else if (free_list_slot (head) == 0)
10599     {
10600         free_list_slot (head) = item;
10601     }
10602     else
10603     {
10604         assert (item != tail);
10605         assert (free_list_slot(tail) == 0);
10606         free_list_slot (tail) = item;
10607     }
10608     tail = item;
10609 }
10610
10611 void allocator::thread_item (BYTE* item, size_t size)
10612 {
10613     size_t sz = frst_bucket_size;
10614     unsigned int a_l_number = 0; 
10615
10616     for (; a_l_number < (num_buckets-1); a_l_number++)
10617     {
10618         if (size < sz)
10619         {
10620             break;
10621         }
10622         sz = sz * 2;
10623     }
10624     alloc_list* al = &alloc_list_of (a_l_number);
10625     thread_free_item (item, 
10626                       al->alloc_list_head(),
10627                       al->alloc_list_tail());
10628 }
10629
10630 void allocator::thread_item_front (BYTE* item, size_t size)
10631 {
10632     //find right free list
10633     size_t sz = frst_bucket_size;
10634     unsigned int a_l_number = 0; 
10635     for (; a_l_number < (num_buckets-1); a_l_number++)
10636     {
10637         if (size < sz)
10638         {
10639             break;
10640         }
10641         sz = sz * 2;
10642     }
10643     alloc_list* al = &alloc_list_of (a_l_number);
10644     free_list_slot (item) = al->alloc_list_head();
10645     free_list_undo (item) = UNDO_EMPTY;
10646     if (al->alloc_list_tail() == 0)
10647     {
10648         al->alloc_list_tail() = al->alloc_list_head();
10649     }
10650     al->alloc_list_head() = item;
10651     if (al->alloc_list_tail() == 0)
10652     {
10653         al->alloc_list_tail() = item;
10654     }
10655 }
10656
10657 void allocator::copy_to_alloc_list (alloc_list* toalist)
10658 {
10659     for (unsigned int i = 0; i < num_buckets; i++)
10660     {
10661         toalist [i] = alloc_list_of (i);
10662     }
10663 }
10664
10665 void allocator::copy_from_alloc_list (alloc_list* fromalist)
10666 {
10667     BOOL repair_list = !discard_if_no_fit_p ();
10668     for (unsigned int i = 0; i < num_buckets; i++)
10669     {
10670         alloc_list_of (i) = fromalist [i];
10671         if (repair_list)
10672         {
10673             //repair the the list
10674             //new items may have been added during the plan phase 
10675             //items may have been unlinked. 
10676             BYTE* free_item = alloc_list_head_of (i);
10677             while (free_item)
10678             {
10679                 assert (((CObjectHeader*)free_item)->IsFree());
10680                 if ((free_list_undo (free_item) != UNDO_EMPTY))
10681                 {
10682                     free_list_slot (free_item) = free_list_undo (free_item);
10683                     free_list_undo (free_item) = UNDO_EMPTY;
10684                 }
10685
10686                 free_item = free_list_slot (free_item);
10687             }
10688         }
10689 #ifdef DEBUG
10690         BYTE* tail_item = alloc_list_tail_of (i);
10691         assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
10692 #endif
10693     }
10694 }
10695
10696 void allocator::commit_alloc_list_changes()
10697 {
10698     BOOL repair_list = !discard_if_no_fit_p ();
10699     if (repair_list)
10700     {
10701         for (unsigned int i = 0; i < num_buckets; i++)
10702         {
10703             //remove the undo info from list. 
10704             BYTE* free_item = alloc_list_head_of (i);
10705             while (free_item)
10706             {
10707                 assert (((CObjectHeader*)free_item)->IsFree());
10708                 free_list_undo (free_item) = UNDO_EMPTY;
10709                 free_item = free_list_slot (free_item);
10710             }
10711         }
10712     }
10713 }
10714
10715 void gc_heap::adjust_limit_clr (BYTE* start, size_t limit_size,
10716                                 alloc_context* acontext, heap_segment* seg,
10717                                 int align_const)
10718 {
10719     //probably should pass seg==0 for free lists.
10720     if (seg)
10721     {
10722         assert (heap_segment_used (seg) <= heap_segment_committed (seg));
10723     }
10724
10725     dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
10726                (size_t)start + limit_size - Align (min_obj_size, align_const)));
10727
10728     if ((acontext->alloc_limit != start) &&
10729         (acontext->alloc_limit + Align (min_obj_size, align_const))!= start)
10730     {
10731         BYTE*  hole = acontext->alloc_ptr;
10732         if (hole != 0)
10733         {
10734             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
10735             dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
10736             // when we are finishing an allocation from a free list
10737             // we know that the free area was Align(min_obj_size) larger
10738             make_unused_array (hole, size + Align (min_obj_size, align_const));
10739         }
10740         acontext->alloc_ptr = start;
10741     }
10742     acontext->alloc_limit = (start + limit_size - Align (min_obj_size, align_const));
10743     acontext->alloc_bytes += limit_size;
10744
10745 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
10746     if (g_fEnableARM)
10747     {
10748         AppDomain* alloc_appdomain = GetAppDomain();
10749         alloc_appdomain->RecordAllocBytes (limit_size, heap_number);
10750     }
10751 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
10752
10753     BYTE* saved_used = 0;
10754
10755     if (seg)
10756     {
10757         saved_used = heap_segment_used (seg);
10758     }
10759
10760     if (seg == ephemeral_heap_segment)
10761     {
10762         //Sometimes the allocated size is advanced without clearing the
10763         //memory. Let's catch up here
10764         if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
10765         {
10766 #ifdef MARK_ARRAY
10767 #ifndef BACKGROUND_GC
10768             clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
10769 #endif //BACKGROUND_GC
10770 #endif //MARK_ARRAY
10771             heap_segment_used (seg) = alloc_allocated - plug_skew;
10772         }
10773     }
10774 #ifdef BACKGROUND_GC
10775     else if (seg)
10776     {
10777         BYTE* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
10778 #ifdef FEATURE_LOH_COMPACTION
10779         old_allocated -= Align (loh_padding_obj_size, align_const);
10780 #endif //FEATURE_LOH_COMPACTION
10781
10782         assert (heap_segment_used (seg) >= old_allocated);
10783     }
10784 #endif //BACKGROUND_GC
10785     if ((seg == 0) ||
10786         (start - plug_skew + limit_size) <= heap_segment_used (seg))
10787     {
10788         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory(1)", heap_number));
10789         add_saved_spinlock_info (me_release, mt_clr_mem);
10790         leave_spin_lock (&more_space_lock);
10791         dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
10792         memclr (start - plug_skew, limit_size);
10793     }
10794     else
10795     {
10796         BYTE* used = heap_segment_used (seg);
10797         heap_segment_used (seg) = start + limit_size - plug_skew;
10798
10799         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory", heap_number));
10800         add_saved_spinlock_info (me_release, mt_clr_mem);
10801         leave_spin_lock (&more_space_lock);
10802         if ((start - plug_skew) < used)
10803         {
10804             if (used != saved_used)
10805             {
10806                 FATAL_GC_ERROR ();
10807             }
10808
10809             dprintf (2, ("clearing memory before used at %Ix for %Id bytes", 
10810                 (start - plug_skew), (plug_skew + used - start)));
10811             memclr (start - plug_skew, used - (start - plug_skew));
10812         }
10813     }
10814
10815     //this portion can be done after we release the lock
10816     if (seg == ephemeral_heap_segment)
10817     {
10818 #ifdef FFIND_OBJECT
10819         if (gen0_must_clear_bricks > 0)
10820         {
10821             //set the brick table to speed up find_object
10822             size_t b = brick_of (acontext->alloc_ptr);
10823             set_brick (b, acontext->alloc_ptr - brick_address (b));
10824             b++;
10825             dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
10826                          b, brick_of (align_on_brick (start + limit_size))));
10827             short* x = &brick_table [b];
10828             short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
10829
10830             for (;x < end_x;x++)
10831                 *x = -1;
10832         }
10833         else
10834 #endif //FFIND_OBJECT
10835         {
10836             gen0_bricks_cleared = FALSE;
10837         }
10838     }
10839
10840     // verifying the memory is completely cleared.
10841     //verify_mem_cleared (start - plug_skew, limit_size);
10842 }
10843
10844 /* in order to make the allocator faster, allocate returns a
10845  * 0 filled object. Care must be taken to set the allocation limit to the
10846  * allocation pointer after gc
10847  */
10848
10849 size_t gc_heap::limit_from_size (size_t size, size_t room, int gen_number,
10850                                  int align_const)
10851 {
10852     size_t new_limit = new_allocation_limit ((size + Align (min_obj_size, align_const)),
10853                                              min (room,max (size + Align (min_obj_size, align_const),
10854                                                             ((gen_number < max_generation+1) ?
10855                                                              allocation_quantum :
10856                                                              0))),
10857                                              gen_number);
10858     assert (new_limit >= (size + Align (min_obj_size, align_const)));
10859     dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
10860     return new_limit;
10861 }
10862
10863 void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size, 
10864                           BYTE* allocated, BYTE* reserved)
10865 {
10866     if (reason == oom_budget)
10867     {
10868         alloc_size = dd_min_gc_size (dynamic_data_of (0)) / 2;
10869     }
10870
10871     if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
10872     {
10873         // This means during the last GC we needed to reserve and/or commit more memory
10874         // but we couldn't. We proceeded with the GC and ended up not having enough
10875         // memory at the end. This is a legitimate OOM situtation. Otherwise we 
10876         // probably made a mistake and didn't expand the heap when we should have.
10877         reason = oom_low_mem;
10878     }
10879
10880     oom_info.reason = reason;
10881     oom_info.allocated = allocated;
10882     oom_info.reserved = reserved;
10883     oom_info.alloc_size = alloc_size;
10884     oom_info.gc_index = settings.gc_index;
10885     oom_info.fgm = fgm_result.fgm;
10886     oom_info.size = fgm_result.size;
10887     oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
10888     oom_info.loh_p = fgm_result.loh_p;
10889
10890     fgm_result.fgm = fgm_no_failure;
10891
10892     // Break early - before the more_space_lock is release so no other threads
10893     // could have allocated on the same heap when OOM happened.
10894     if (g_pConfig->IsGCBreakOnOOMEnabled())
10895     {
10896         DebugBreak();
10897     }
10898 }
10899
10900 #ifdef BACKGROUND_GC
10901 BOOL gc_heap::background_allowed_p()
10902 {
10903     return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
10904 }
10905 #endif //BACKGROUND_GC
10906
10907 void gc_heap::check_for_full_gc (int gen_num, size_t size)
10908 {
10909     BOOL should_notify = FALSE;
10910     // if we detect full gc because of the allocation budget specified this is TRUE;
10911     // it's FALSE if it's due to other factors.
10912     BOOL alloc_factor = TRUE; 
10913     int i = 0;
10914     int n = 0;
10915     int n_initial = gen_num;
10916     BOOL local_blocking_collection = FALSE;
10917     BOOL local_elevation_requested = FALSE;
10918     int new_alloc_remain_percent = 0;
10919
10920     if (full_gc_approach_event_set)
10921     {
10922         return;
10923     }
10924     
10925     if (gen_num != (max_generation + 1))
10926     {
10927         gen_num = max_generation;
10928     }
10929
10930     dynamic_data* dd_full = dynamic_data_of (gen_num);
10931     SSIZE_T new_alloc_remain = 0;
10932     DWORD pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
10933
10934     for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
10935     {
10936         dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)", 
10937                      heap_number, gen_index,
10938                      dd_new_allocation (dynamic_data_of (gen_index)),
10939                      dd_desired_allocation (dynamic_data_of (gen_index))));
10940     }
10941
10942     // For small object allocations we only check every fgn_check_quantum bytes.
10943     if (n_initial == 0)
10944     {
10945         dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
10946         dynamic_data* dd_0 = dynamic_data_of (n_initial);
10947         if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
10948             (dd_new_allocation (dd_0) >= 0))
10949         {
10950             return;
10951         }
10952         else
10953         {
10954             fgn_last_alloc = dd_new_allocation (dd_0);
10955             dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
10956         }
10957
10958         // We don't consider the size that came from soh 'cause it doesn't contribute to the
10959         // gen2 budget.
10960         size = 0;
10961     }
10962
10963     for (i = n+1; i <= max_generation; i++)
10964     {
10965         if (get_new_allocation (i) <= 0)
10966         {
10967             n = min (i, max_generation);
10968         }
10969         else
10970             break;
10971     }
10972
10973     dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
10974     if (gen_num == max_generation)
10975     {
10976         // If it's small object heap we should first see if we will even be looking at gen2 budget
10977         // in the next GC or not. If not we should go directly to checking other factors.
10978         if (n < (max_generation - 1))
10979         {
10980             goto check_other_factors;
10981         }
10982     }
10983
10984     new_alloc_remain = dd_new_allocation (dd_full) - size;
10985
10986     new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
10987
10988     dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%", 
10989                  gen_num, pct, new_alloc_remain_percent));
10990
10991     if (new_alloc_remain_percent <= (int)pct)
10992     {
10993 #ifdef BACKGROUND_GC
10994         // If background GC is enabled, we still want to check whether this will
10995         // be a blocking GC or not because we only want to notify when it's a 
10996         // blocking full GC.
10997         if (background_allowed_p())
10998         {
10999             goto check_other_factors;
11000         }
11001 #endif //BACKGROUND_GC
11002
11003         should_notify = TRUE;
11004         goto done;
11005     }
11006
11007 check_other_factors:
11008
11009     dprintf (2, ("FGC: checking other factors"));
11010     n = generation_to_condemn (n, 
11011                                &local_blocking_collection, 
11012                                &local_elevation_requested, 
11013                                TRUE);
11014
11015     if (local_elevation_requested && (n == max_generation))
11016     {
11017         if (settings.should_lock_elevation)
11018         {
11019             int local_elevation_locked_count = settings.elevation_locked_count + 1;
11020             if (local_elevation_locked_count != 6)
11021             {
11022                 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1", 
11023                     local_elevation_locked_count));
11024                 n = max_generation - 1;
11025             }
11026         }
11027     }
11028
11029     dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11030
11031 #ifdef BACKGROUND_GC
11032     // When background GC is enabled it decreases the accurancy of our predictability -
11033     // by the time the GC happens, we may not be under BGC anymore. If we try to 
11034     // predict often enough it should be ok.
11035     if ((n == max_generation) &&
11036         (recursive_gc_sync::background_running_p()))
11037     {
11038         n = max_generation - 1;
11039         dprintf (2, ("FGN: bgc - 1 instead of 2"));
11040     }
11041
11042     if ((n == max_generation) && !local_blocking_collection)
11043     {
11044         if (!background_allowed_p())
11045         {
11046             local_blocking_collection = TRUE;
11047         }
11048     }
11049 #endif //BACKGROUND_GC
11050
11051     dprintf (2, ("FGN: we estimate gen%d will be collected: %s", 
11052                        n, 
11053                        (local_blocking_collection ? "blocking" : "background")));
11054
11055     if ((n == max_generation) && local_blocking_collection)
11056     {
11057         alloc_factor = FALSE;
11058         should_notify = TRUE;
11059         goto done;
11060     }
11061
11062 done:
11063
11064     if (should_notify)
11065     {
11066         dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)", 
11067                      n_initial,
11068                      (alloc_factor ? "alloc" : "other"),
11069                      dd_collection_count (dynamic_data_of (0)),
11070                      new_alloc_remain_percent, 
11071                      gen_num));
11072
11073         send_full_gc_notification (n_initial, alloc_factor);
11074     }
11075 }
11076
11077 void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11078 {
11079     if (!full_gc_approach_event_set)
11080     {
11081         assert (full_gc_approach_event.IsValid());
11082         FireEtwGCFullNotify_V1 (gen_num, due_to_alloc_p, GetClrInstanceId());
11083
11084         full_gc_end_event.Reset();
11085         full_gc_approach_event.Set();
11086         full_gc_approach_event_set = true;
11087     }
11088 }
11089
11090 wait_full_gc_status gc_heap::full_gc_wait (CLREvent *event, int time_out_ms)
11091 {
11092     if (fgn_maxgen_percent == 0)
11093     {
11094         return wait_full_gc_na;
11095     }
11096
11097     DWORD wait_result = user_thread_wait(event, FALSE, time_out_ms);
11098
11099     if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11100     {
11101         if (fgn_maxgen_percent == 0)
11102         {
11103             return wait_full_gc_cancelled;
11104         }
11105         
11106         if (wait_result == WAIT_OBJECT_0)
11107         {
11108 #ifdef BACKGROUND_GC
11109             if (fgn_last_gc_was_concurrent)
11110             {
11111                 fgn_last_gc_was_concurrent = FALSE;
11112                 return wait_full_gc_na;
11113             }
11114             else
11115 #endif //BACKGROUND_GC
11116             {
11117                 return wait_full_gc_success;
11118             }
11119         }
11120         else
11121         {
11122             return wait_full_gc_timeout;
11123         }
11124     }
11125     else
11126     {
11127         return wait_full_gc_failed;
11128     }
11129 }
11130
11131 size_t gc_heap::get_full_compact_gc_count()
11132 {
11133     return full_gc_counts[gc_type_compacting];
11134 }
11135
11136 // DTREVIEW - we should check this in dt_low_ephemeral_space_p
11137 // as well.
11138 inline
11139 BOOL gc_heap::short_on_end_of_seg (int gen_number,
11140                                    heap_segment* seg,
11141                                    int align_const)
11142 {
11143     BYTE* allocated = heap_segment_allocated(seg);
11144
11145     return (!a_size_fit_p (end_space_after_gc(),
11146                           allocated,
11147                           heap_segment_reserved (seg), 
11148                           align_const));
11149 }
11150
11151 #ifdef _MSC_VER
11152 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
11153 #endif // _MSC_VER
11154
11155 inline
11156 BOOL gc_heap::a_fit_free_list_p (int gen_number, 
11157                                  size_t size, 
11158                                  alloc_context* acontext,
11159                                  int align_const)
11160 {
11161     BOOL can_fit = FALSE;
11162     generation* gen = generation_of (gen_number);
11163     allocator* gen_allocator = generation_allocator (gen);
11164     size_t sz_list = gen_allocator->first_bucket_size();
11165     for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
11166     {
11167         if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
11168         {
11169             BYTE* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
11170             BYTE* prev_free_item = 0; 
11171
11172             while (free_list != 0)
11173             {
11174                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11175                 size_t free_list_size = unused_array_size (free_list);
11176                 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
11177                 {
11178                     dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
11179                                  (size_t)free_list, free_list_size));
11180
11181                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11182                     // We ask for more Align (min_obj_size)
11183                     // to make sure that we can insert a free object
11184                     // in adjust_limit will set the limit lower
11185                     size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
11186
11187                     BYTE*  remain = (free_list + limit);
11188                     size_t remain_size = (free_list_size - limit);
11189                     if (remain_size >= Align(min_free_list, align_const))
11190                     {
11191                         make_unused_array (remain, remain_size);
11192                         gen_allocator->thread_item_front (remain, remain_size);
11193                         assert (remain_size >= Align (min_obj_size, align_const));
11194                     }
11195                     else
11196                     {
11197                         //absorb the entire free list
11198                         limit += remain_size;
11199                     }
11200                     generation_free_list_space (gen) -= limit;
11201
11202                     adjust_limit_clr (free_list, limit, acontext, 0, align_const);
11203
11204                     can_fit = TRUE;
11205                     goto end;
11206                 }
11207                 else if (gen_allocator->discard_if_no_fit_p())
11208                 {
11209                     assert (prev_free_item == 0);
11210                     dprintf (3, ("couldn't use this free area, discarding"));
11211                     generation_free_obj_space (gen) += free_list_size;
11212
11213                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11214                     generation_free_list_space (gen) -= free_list_size;
11215                 }
11216                 else
11217                 {
11218                     prev_free_item = free_list;
11219                 }
11220                 free_list = free_list_slot (free_list); 
11221             }
11222         }
11223         sz_list = sz_list * 2;
11224     }
11225 end:
11226     return can_fit;
11227 }
11228
11229
11230 #ifdef BACKGROUND_GC
11231 void gc_heap::bgc_loh_alloc_clr (BYTE* alloc_start, 
11232                                  size_t size, 
11233                                  alloc_context* acontext,
11234                                  int align_const, 
11235                                  int lock_index,
11236                                  BOOL check_used_p,
11237                                  heap_segment* seg)
11238 {
11239     make_unused_array (alloc_start, size);
11240
11241 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11242     if (g_fEnableARM)
11243     {
11244         AppDomain* alloc_appdomain = GetAppDomain();
11245         alloc_appdomain->RecordAllocBytes (size, heap_number);
11246     }
11247 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11248
11249     size_t size_of_array_base = sizeof(ArrayBase);
11250
11251     bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
11252
11253     // clear memory while not holding the lock. 
11254     size_t size_to_skip = size_of_array_base;
11255     size_t size_to_clear = size - size_to_skip - plug_skew;
11256     size_t saved_size_to_clear = size_to_clear;
11257     if (check_used_p)
11258     {
11259         BYTE* end = alloc_start + size - plug_skew;
11260         BYTE* used = heap_segment_used (seg);
11261         if (used < end)
11262         {
11263             if ((alloc_start + size_to_skip) < used)
11264             {
11265                 size_to_clear = used - (alloc_start + size_to_skip);
11266             }
11267             else
11268             {
11269                 size_to_clear = 0;
11270             }
11271             dprintf (2, ("bgc loh: setting used to %Ix", end));
11272             heap_segment_used (seg) = end;
11273         }
11274
11275         dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
11276                      used, alloc_start, end, size_to_clear));
11277     }
11278     else
11279     {
11280         dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
11281     }
11282
11283 #ifdef VERIFY_HEAP
11284     // since we filled in 0xcc for free object when we verify heap,
11285     // we need to make sure we clear those bytes.
11286     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
11287     {
11288         if (size_to_clear < saved_size_to_clear)
11289         {
11290             size_to_clear = saved_size_to_clear;
11291         }
11292     }
11293 #endif //VERIFY_HEAP
11294     
11295     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
11296     add_saved_spinlock_info (me_release, mt_clr_large_mem);
11297     leave_spin_lock (&more_space_lock);
11298     memclr (alloc_start + size_to_skip, size_to_clear);
11299
11300     bgc_alloc_lock->loh_alloc_set (alloc_start);
11301
11302     acontext->alloc_ptr = alloc_start;
11303     acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
11304
11305     // need to clear the rest of the object before we hand it out.
11306     clear_unused_array(alloc_start, size);
11307 }
11308 #endif //BACKGROUND_GC
11309
11310 BOOL gc_heap::a_fit_free_list_large_p (size_t size, 
11311                                        alloc_context* acontext,
11312                                        int align_const)
11313 {
11314 #ifdef BACKGROUND_GC
11315     wait_for_background_planning (awr_loh_alloc_during_plan);
11316 #endif //BACKGROUND_GC
11317
11318     BOOL can_fit = FALSE;
11319     int gen_number = max_generation + 1;
11320     generation* gen = generation_of (gen_number);
11321     allocator* loh_allocator = generation_allocator (gen); 
11322
11323 #ifdef FEATURE_LOH_COMPACTION
11324     size_t loh_pad = Align (loh_padding_obj_size, align_const);
11325 #endif //FEATURE_LOH_COMPACTION
11326
11327 #ifdef BACKGROUND_GC
11328     int cookie = -1;
11329 #endif //BACKGROUND_GC
11330     size_t sz_list = loh_allocator->first_bucket_size();
11331     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
11332     {
11333         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
11334         {
11335             BYTE* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
11336             BYTE* prev_free_item = 0; 
11337             while (free_list != 0)
11338             {
11339                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11340
11341                 size_t free_list_size = unused_array_size(free_list);
11342
11343 #ifdef FEATURE_LOH_COMPACTION
11344                 if ((size + loh_pad) <= free_list_size)
11345 #else
11346                 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
11347                     (size == free_list_size))
11348 #endif //FEATURE_LOH_COMPACTION
11349                 {
11350 #ifdef BACKGROUND_GC
11351                     cookie = bgc_alloc_lock->loh_alloc_set (free_list);
11352 #endif //BACKGROUND_GC
11353
11354                     //unlink the free_item
11355                     loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11356
11357                     // Substract min obj size because limit_from_size adds it. Not needed for LOH
11358                     size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size, 
11359                                                     gen_number, align_const);
11360
11361 #ifdef FEATURE_LOH_COMPACTION
11362                     make_unused_array (free_list, loh_pad);
11363                     limit -= loh_pad;
11364                     free_list += loh_pad;
11365                     free_list_size -= loh_pad;
11366 #endif //FEATURE_LOH_COMPACTION
11367
11368                     BYTE*  remain = (free_list + limit);
11369                     size_t remain_size = (free_list_size - limit);
11370                     if (remain_size != 0)
11371                     {
11372                         assert (remain_size >= Align (min_obj_size, align_const));
11373                         make_unused_array (remain, remain_size);
11374                     }
11375                     if (remain_size >= Align(min_free_list, align_const))
11376                     {
11377                         loh_thread_gap_front (remain, remain_size, gen);
11378                         assert (remain_size >= Align (min_obj_size, align_const));
11379                     }
11380                     else
11381                     {
11382                         generation_free_obj_space (gen) += remain_size;
11383                     }
11384                     generation_free_list_space (gen) -= free_list_size;
11385                     dprintf (3, ("found fit on loh at %Ix", free_list));
11386 #ifdef BACKGROUND_GC
11387                     if (cookie != -1)
11388                     {
11389                         bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
11390                     }
11391                     else
11392 #endif //BACKGROUND_GC
11393                     {
11394                         adjust_limit_clr (free_list, limit, acontext, 0, align_const);
11395                     }
11396
11397                     //fix the limit to compensate for adjust_limit_clr making it too short 
11398                     acontext->alloc_limit += Align (min_obj_size, align_const);
11399                     can_fit = TRUE;
11400                     goto exit;
11401                 }
11402                 prev_free_item = free_list;
11403                 free_list = free_list_slot (free_list); 
11404             }
11405         }
11406         sz_list = sz_list * 2;
11407     }
11408 exit:
11409     return can_fit;
11410 }
11411
11412 #ifdef _MSC_VER
11413 #pragma warning(default:4706)
11414 #endif // _MSC_VER
11415
11416 BOOL gc_heap::a_fit_segment_end_p (int gen_number,
11417                                    heap_segment* seg,
11418                                    size_t size, 
11419                                    alloc_context* acontext,
11420                                    int align_const,
11421                                    BOOL* commit_failed_p)
11422 {
11423     *commit_failed_p = FALSE;
11424     size_t limit = 0;
11425 #ifdef BACKGROUND_GC
11426     int cookie = -1;
11427 #endif //BACKGROUND_GC
11428
11429     BYTE*& allocated = ((gen_number == 0) ? 
11430                         alloc_allocated : 
11431                         heap_segment_allocated(seg));
11432
11433     size_t pad = Align (min_obj_size, align_const);
11434
11435 #ifdef FEATURE_LOH_COMPACTION
11436     if (gen_number == (max_generation + 1))
11437     {
11438         pad += Align (loh_padding_obj_size, align_const);
11439     }
11440 #endif //FEATURE_LOH_COMPACTION
11441
11442     BYTE* end = heap_segment_committed (seg) - pad;
11443
11444     if (a_size_fit_p (size, allocated, end, align_const))
11445     {
11446         limit = limit_from_size (size, 
11447                                  (end - allocated), 
11448                                  gen_number, align_const);
11449         goto found_fit;
11450     }
11451
11452     end = heap_segment_reserved (seg) - pad;
11453
11454     if (a_size_fit_p (size, allocated, end, align_const))
11455     {
11456         limit = limit_from_size (size, 
11457                                  (end - allocated), 
11458                                  gen_number, align_const);
11459         if (grow_heap_segment (seg, allocated + limit))
11460         {
11461             goto found_fit;
11462         }
11463         else
11464         {
11465             dprintf (2, ("can't grow segment, doing a full gc"));
11466             *commit_failed_p = TRUE;
11467         }
11468     }
11469     goto found_no_fit;
11470
11471 found_fit:
11472
11473 #ifdef BACKGROUND_GC
11474     if (gen_number != 0)
11475     {
11476         cookie = bgc_alloc_lock->loh_alloc_set (allocated);
11477     }
11478 #endif //BACKGROUND_GC
11479
11480     BYTE* old_alloc;
11481     old_alloc = allocated;
11482 #ifdef FEATURE_LOH_COMPACTION
11483     if (gen_number == (max_generation + 1))
11484     {
11485         size_t loh_pad = Align (loh_padding_obj_size, align_const);
11486         make_unused_array (old_alloc, loh_pad);
11487         old_alloc += loh_pad;
11488         allocated += loh_pad;
11489         limit -= loh_pad;
11490     }
11491 #endif //FEATURE_LOH_COMPACTION
11492
11493 #if defined (VERIFY_HEAP) && defined (_DEBUG)
11494         ((void**) allocated)[-1] = 0;     //clear the sync block
11495 #endif //VERIFY_HEAP && _DEBUG
11496     allocated += limit;
11497
11498     dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
11499
11500 #ifdef BACKGROUND_GC
11501     if (cookie != -1)
11502     {
11503         bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
11504     }
11505     else
11506 #endif //BACKGROUND_GC
11507     {
11508         adjust_limit_clr (old_alloc, limit, acontext, seg, align_const);
11509     }
11510
11511     return TRUE;
11512
11513 found_no_fit:
11514
11515     return FALSE;
11516 }
11517
11518 BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
11519                                        size_t size, 
11520                                        alloc_context* acontext,
11521                                        int align_const,
11522                                        BOOL* commit_failed_p,
11523                                        oom_reason* oom_r)
11524 {
11525     *commit_failed_p = FALSE;
11526     heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
11527     BOOL can_allocate_p = FALSE;
11528
11529     while (seg)
11530     {
11531         if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)), 
11532                                  acontext, align_const, commit_failed_p))
11533         {
11534             acontext->alloc_limit += Align (min_obj_size, align_const);
11535             can_allocate_p = TRUE;
11536             break;
11537         }
11538         else
11539         {
11540             if (*commit_failed_p)
11541             {
11542                 *oom_r = oom_cant_commit;
11543                 break;
11544             }
11545             else
11546             {
11547                 seg = heap_segment_next_rw (seg);
11548             }
11549         }
11550     }
11551
11552     return can_allocate_p;
11553 }
11554
11555 #ifdef BACKGROUND_GC
11556 inline
11557 void gc_heap::wait_for_background (alloc_wait_reason awr)
11558 {
11559     dprintf (2, ("BGC is already in progress, waiting for it to finish"));
11560     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc done", heap_number));
11561     add_saved_spinlock_info (me_release, mt_wait_bgc);
11562     leave_spin_lock (&more_space_lock);
11563     background_gc_wait (awr);
11564     enter_spin_lock (&more_space_lock);
11565     add_saved_spinlock_info (me_acquire, mt_wait_bgc);
11566     dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc done", heap_number));
11567 }
11568
11569 void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr)
11570 {
11571     if (recursive_gc_sync::background_running_p())
11572     {
11573         MEMORYSTATUSEX ms;
11574         memset (&ms, 0, sizeof(ms));
11575         GetProcessMemoryLoad(&ms);
11576         if (ms.dwMemoryLoad >= 95)
11577         {
11578             dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
11579             wait_for_background (awr);
11580         }
11581     }
11582 }
11583
11584 #endif //BACKGROUND_GC
11585
11586 // We request to trigger an ephemeral GC but we may get a full compacting GC.
11587 // return TRUE if that's the case.
11588 BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
11589 {
11590 #ifdef BACKGROUND_GC
11591     wait_for_bgc_high_memory (awr_loh_oos_bgc);
11592 #endif //BACKGROUND_GC
11593
11594     BOOL did_full_compact_gc = FALSE;
11595
11596     dprintf (2, ("triggering a gen1 GC"));
11597     size_t last_full_compact_gc_count = get_full_compact_gc_count();
11598     vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
11599
11600 #ifdef MULTIPLE_HEAPS
11601     enter_spin_lock (&more_space_lock);
11602     add_saved_spinlock_info (me_acquire, mt_t_eph_gc);
11603     dprintf (SPINLOCK_LOG, ("[%d]Emsl after a GC", heap_number));
11604 #endif //MULTIPLE_HEAPS
11605
11606     size_t current_full_compact_gc_count = get_full_compact_gc_count();
11607
11608     if (current_full_compact_gc_count > last_full_compact_gc_count)
11609     {
11610         dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
11611         did_full_compact_gc = TRUE;
11612     }
11613
11614     return did_full_compact_gc;
11615 }
11616
11617 BOOL gc_heap::soh_try_fit (int gen_number,
11618                            size_t size, 
11619                            alloc_context* acontext,
11620                            int align_const,
11621                            BOOL* commit_failed_p,
11622                            BOOL* short_seg_end_p)
11623 {
11624     BOOL can_allocate = TRUE;
11625     if (short_seg_end_p)
11626     {
11627         *short_seg_end_p = FALSE;
11628     }
11629
11630     can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
11631     if (!can_allocate)
11632     {
11633         if (short_seg_end_p)
11634         {
11635             *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
11636         }
11637         // If the caller doesn't care, we always try to fit at the end of seg;
11638         // otherwise we would only try if we are actually not short at end of seg.
11639         if (!short_seg_end_p || !(*short_seg_end_p))
11640         {
11641             can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size, 
11642                                                 acontext, align_const, commit_failed_p);
11643         }
11644     }
11645
11646     return can_allocate;
11647 }
11648
11649 BOOL gc_heap::allocate_small (int gen_number,
11650                               size_t size, 
11651                               alloc_context* acontext,
11652                               int align_const)
11653 {
11654 #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
11655     if (recursive_gc_sync::background_running_p())
11656     {
11657         background_soh_alloc_count++;
11658         if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
11659         {
11660             Thread* current_thread = GetThread();
11661             add_saved_spinlock_info (me_release, mt_alloc_small);
11662             dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl", heap_number));
11663             leave_spin_lock (&more_space_lock);
11664             BOOL cooperative_mode = enable_preemptive (current_thread);
11665             __SwitchToThread (bgc_alloc_spin, CALLER_LIMITS_SPINNING);
11666             disable_preemptive (current_thread, cooperative_mode);
11667             enter_spin_lock (&more_space_lock);
11668             add_saved_spinlock_info (me_acquire, mt_alloc_small);
11669             dprintf (SPINLOCK_LOG, ("[%d]spin Emsl", heap_number));
11670         }
11671         else
11672         {
11673             //__SwitchToThread (0, CALLER_LIMITS_SPINNING);
11674         }
11675     }
11676 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
11677
11678     gc_reason gr = reason_oos_soh;
11679     oom_reason oom_r = oom_no_failure;
11680
11681     // No variable values should be "carried over" from one state to the other. 
11682     // That's why there are local variable for each state
11683
11684     allocation_state soh_alloc_state = a_state_start;
11685
11686     // If we can get a new seg it means allocation will succeed.
11687     while (1)
11688     {
11689         dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
11690         switch (soh_alloc_state)
11691         {
11692             case a_state_can_allocate:
11693             case a_state_cant_allocate:
11694             {
11695                 goto exit;
11696             }
11697             case a_state_start:
11698             {
11699                 soh_alloc_state = a_state_try_fit;
11700                 break;
11701             }
11702             case a_state_try_fit:
11703             {
11704                 BOOL commit_failed_p = FALSE;
11705                 BOOL can_use_existing_p = FALSE;
11706
11707                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11708                                                   align_const, &commit_failed_p,
11709                                                   NULL);
11710                 soh_alloc_state = (can_use_existing_p ?
11711                                         a_state_can_allocate : 
11712                                         (commit_failed_p ? 
11713                                             a_state_trigger_full_compact_gc :
11714                                             a_state_trigger_ephemeral_gc));
11715                 break;
11716             }
11717             case a_state_try_fit_after_bgc:
11718             {
11719                 BOOL commit_failed_p = FALSE;
11720                 BOOL can_use_existing_p = FALSE;
11721                 BOOL short_seg_end_p = FALSE;
11722
11723                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11724                                                   align_const, &commit_failed_p,
11725                                                   &short_seg_end_p);
11726                 soh_alloc_state = (can_use_existing_p ? 
11727                                         a_state_can_allocate : 
11728                                         (short_seg_end_p ? 
11729                                             a_state_trigger_2nd_ephemeral_gc : 
11730                                             a_state_trigger_full_compact_gc));
11731                 break;
11732             }
11733             case a_state_try_fit_after_cg:
11734             {
11735                 BOOL commit_failed_p = FALSE;
11736                 BOOL can_use_existing_p = FALSE;
11737                 BOOL short_seg_end_p = FALSE;
11738
11739                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11740                                                   align_const, &commit_failed_p,
11741                                                   &short_seg_end_p);
11742                 if (short_seg_end_p)
11743                 {
11744                     soh_alloc_state = a_state_cant_allocate;
11745                     oom_r = oom_budget;
11746                 }
11747                 else
11748                 {
11749                     if (can_use_existing_p)
11750                     {
11751                         soh_alloc_state = a_state_can_allocate;
11752                     }
11753                     else
11754                     {
11755 #ifdef MULTIPLE_HEAPS
11756                         if (!commit_failed_p)
11757                         {
11758                             // some other threads already grabbed the more space lock and allocated
11759                             // so we should attemp an ephemeral GC again.
11760                             assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
11761                             soh_alloc_state = a_state_trigger_ephemeral_gc; 
11762                         }
11763                         else
11764 #endif //MULTIPLE_HEAPS
11765                         {
11766                             assert (commit_failed_p);
11767                             soh_alloc_state = a_state_cant_allocate;
11768                             oom_r = oom_cant_commit;
11769                         }
11770                     }
11771                 }
11772                 break;
11773             }
11774             case a_state_check_and_wait_for_bgc:
11775             {
11776                 BOOL bgc_in_progress_p = FALSE;
11777                 BOOL did_full_compacting_gc = FALSE;
11778
11779                 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc);
11780                 soh_alloc_state = (did_full_compacting_gc ? 
11781                                         a_state_try_fit_after_cg : 
11782                                         a_state_try_fit_after_bgc);
11783                 break;
11784             }
11785             case a_state_trigger_ephemeral_gc:
11786             {
11787                 BOOL commit_failed_p = FALSE;
11788                 BOOL can_use_existing_p = FALSE;
11789                 BOOL short_seg_end_p = FALSE;
11790                 BOOL bgc_in_progress_p = FALSE;
11791                 BOOL did_full_compacting_gc = FALSE;
11792
11793                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
11794                 if (did_full_compacting_gc)
11795                 {
11796                     soh_alloc_state = a_state_try_fit_after_cg;
11797                 }
11798                 else
11799                 {
11800                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11801                                                       align_const, &commit_failed_p,
11802                                                       &short_seg_end_p);
11803 #ifdef BACKGROUND_GC
11804                     bgc_in_progress_p = recursive_gc_sync::background_running_p();
11805 #endif //BACKGROUND_GC
11806
11807                     if (short_seg_end_p)
11808                     {
11809                         soh_alloc_state = (bgc_in_progress_p ? 
11810                                                 a_state_check_and_wait_for_bgc : 
11811                                                 a_state_trigger_full_compact_gc);
11812
11813                         if (fgn_maxgen_percent)
11814                         {
11815                             dprintf (2, ("FGN: doing last GC before we throw OOM"));
11816                             send_full_gc_notification (max_generation, FALSE);
11817                         }
11818                     }
11819                     else
11820                     {
11821                         if (can_use_existing_p)
11822                         {
11823                             soh_alloc_state = a_state_can_allocate;
11824                         }
11825                         else
11826                         {
11827 #ifdef MULTIPLE_HEAPS
11828                             if (!commit_failed_p)
11829                             {
11830                                 // some other threads already grabbed the more space lock and allocated
11831                                 // so we should attemp an ephemeral GC again.
11832                                 assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
11833                                 soh_alloc_state = a_state_trigger_ephemeral_gc;
11834                             }
11835                             else
11836 #endif //MULTIPLE_HEAPS
11837                             {
11838                                 soh_alloc_state = a_state_trigger_full_compact_gc;
11839                                 if (fgn_maxgen_percent)
11840                                 {
11841                                     dprintf (2, ("FGN: failed to commit, doing full compacting GC"));
11842                                     send_full_gc_notification (max_generation, FALSE);
11843                                 }
11844                             }
11845                         }
11846                     }
11847                 }
11848                 break;
11849             }
11850             case a_state_trigger_2nd_ephemeral_gc:
11851             {
11852                 BOOL commit_failed_p = FALSE;
11853                 BOOL can_use_existing_p = FALSE;
11854                 BOOL short_seg_end_p = FALSE;
11855                 BOOL did_full_compacting_gc = FALSE;
11856
11857
11858                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
11859                 
11860                 if (did_full_compacting_gc)
11861                 {
11862                     soh_alloc_state = a_state_try_fit_after_cg;
11863                 }
11864                 else
11865                 {
11866                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
11867                                                       align_const, &commit_failed_p,
11868                                                       &short_seg_end_p);
11869                     if (short_seg_end_p || commit_failed_p)
11870                     {
11871                         soh_alloc_state = a_state_trigger_full_compact_gc;
11872                     }
11873                     else
11874                     {
11875                         assert (can_use_existing_p);
11876                         soh_alloc_state = a_state_can_allocate;
11877                     }
11878                 }
11879                 break;
11880             }
11881             case a_state_trigger_full_compact_gc:
11882             {
11883                 BOOL got_full_compacting_gc = FALSE;
11884
11885                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
11886                 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
11887                 break;
11888             }
11889             default:
11890             {
11891                 assert (!"Invalid state!");
11892                 break;
11893             }
11894         }
11895     }
11896
11897 exit:
11898     if (soh_alloc_state == a_state_cant_allocate)
11899     {
11900         assert (oom_r != oom_no_failure);
11901         handle_oom (heap_number, 
11902                     oom_r, 
11903                     size,
11904                     heap_segment_allocated (ephemeral_heap_segment),
11905                     heap_segment_reserved (ephemeral_heap_segment));
11906
11907         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for oom", heap_number));
11908         add_saved_spinlock_info (me_release, mt_alloc_small_cant);
11909         leave_spin_lock (&more_space_lock);
11910     }
11911
11912     return (soh_alloc_state == a_state_can_allocate);
11913 }
11914
11915 #ifdef BACKGROUND_GC
11916 inline
11917 void gc_heap::wait_for_background_planning (alloc_wait_reason awr)
11918 {
11919     while (current_c_gc_state == c_gc_state_planning)
11920     {
11921         dprintf (3, ("lh state planning, cannot allocate"));
11922
11923         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc plan", heap_number));
11924         add_saved_spinlock_info (me_release, mt_wait_bgc_plan);
11925         leave_spin_lock (&more_space_lock);
11926         background_gc_wait_lh (awr);
11927         enter_spin_lock (&more_space_lock);
11928         add_saved_spinlock_info (me_acquire, mt_wait_bgc_plan);
11929         dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc plan", heap_number));
11930     }
11931     assert ((current_c_gc_state == c_gc_state_free) ||
11932             (current_c_gc_state == c_gc_state_marking));
11933 }
11934
11935 BOOL gc_heap::bgc_loh_should_allocate()
11936 {
11937     size_t min_gc_size = dd_min_gc_size(dynamic_data_of (max_generation + 1));
11938
11939     if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
11940     {
11941         return TRUE;
11942     }
11943
11944     if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
11945     {
11946         if ((bgc_begin_loh_size / end_loh_size) > 2)
11947         {
11948             dprintf (3, ("alloc-ed too much before bgc started"));
11949         }
11950         else
11951         {
11952             dprintf (3, ("alloc-ed too much after bgc started"));
11953         }
11954         return FALSE;
11955     }
11956     else
11957     {
11958         bgc_alloc_spin_loh = (DWORD)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
11959         return TRUE;
11960     }
11961 }
11962 #endif //BACKGROUND_GC
11963
11964 size_t gc_heap::get_large_seg_size (size_t size)
11965 {
11966     size_t default_seg_size = get_valid_segment_size(TRUE);
11967 #ifdef SEG_MAPPING_TABLE
11968     size_t align_size =  default_seg_size;
11969 #else //SEG_MAPPING_TABLE
11970     size_t align_size =  default_seg_size / 2;
11971 #endif //SEG_MAPPING_TABLE
11972     int align_const = get_alignment_constant (FALSE);
11973     size_t large_seg_size = align_on_page (
11974         max (default_seg_size,
11975             ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE + 
11976             align_size) / align_size * align_size)));
11977     return large_seg_size;
11978 }
11979
11980 BOOL gc_heap::loh_get_new_seg (generation* gen,
11981                                size_t size,
11982                                int align_const,
11983                                BOOL* did_full_compact_gc,
11984                                oom_reason* oom_r)
11985 {
11986     *did_full_compact_gc = FALSE;
11987
11988     size_t seg_size = get_large_seg_size (size);
11989
11990     heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
11991
11992     if (new_seg)
11993     {
11994         loh_alloc_since_cg += seg_size;
11995     }
11996     else
11997     {
11998         *oom_r = oom_loh;
11999     }
12000
12001     return (new_seg != 0);
12002 }
12003
12004 BOOL gc_heap::retry_full_compact_gc (size_t size)
12005 {
12006     size_t seg_size = get_large_seg_size (size);
12007
12008     if (loh_alloc_since_cg >= (2 * (unsigned __int64)seg_size))
12009     {
12010         return TRUE;
12011     }
12012
12013 #ifdef MULTIPLE_HEAPS
12014     unsigned __int64 total_alloc_size = 0;
12015     for (int i = 0; i < n_heaps; i++)
12016     {
12017         total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12018     }
12019
12020     if (total_alloc_size >= (2 * (unsigned __int64)seg_size))
12021     {
12022         return TRUE;
12023     }
12024 #endif //MULTIPLE_HEAPS
12025
12026     return FALSE;
12027 }
12028
12029 BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12030                                       BOOL* did_full_compact_gc)
12031 {
12032     BOOL bgc_in_progress = FALSE;
12033     *did_full_compact_gc = FALSE;
12034 #ifdef BACKGROUND_GC
12035     if (recursive_gc_sync::background_running_p())
12036     {
12037         bgc_in_progress = TRUE;
12038         size_t last_full_compact_gc_count = get_full_compact_gc_count();
12039         wait_for_background (awr_loh_oos_bgc);
12040         size_t current_full_compact_gc_count = get_full_compact_gc_count();
12041         if (current_full_compact_gc_count > last_full_compact_gc_count)
12042         {
12043             *did_full_compact_gc = TRUE;
12044         }
12045     }
12046 #endif //BACKGROUND_GC
12047
12048     return bgc_in_progress;
12049 }
12050
12051 BOOL gc_heap::loh_try_fit (int gen_number,
12052                            size_t size, 
12053                            alloc_context* acontext,
12054                            int align_const,
12055                            BOOL* commit_failed_p,
12056                            oom_reason* oom_r)
12057 {
12058     BOOL can_allocate = TRUE;
12059
12060     if (!a_fit_free_list_large_p (size, acontext, align_const))
12061     {
12062         can_allocate = loh_a_fit_segment_end_p (gen_number, size, 
12063                                                 acontext, align_const, 
12064                                                 commit_failed_p, oom_r);
12065
12066 #ifdef BACKGROUND_GC
12067         if (can_allocate && recursive_gc_sync::background_running_p())
12068         {
12069             bgc_loh_size_increased += size;
12070         }
12071 #endif //BACKGROUND_GC
12072     }
12073 #ifdef BACKGROUND_GC
12074     else
12075     {
12076         if (recursive_gc_sync::background_running_p())
12077         {
12078             bgc_loh_allocated_in_free += size;
12079         }
12080     }
12081 #endif //BACKGROUND_GC
12082
12083     return can_allocate;
12084 }
12085
12086 BOOL gc_heap::trigger_full_compact_gc (gc_reason gr, 
12087                                        oom_reason* oom_r)
12088 {
12089     BOOL did_full_compact_gc = FALSE;
12090
12091     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12092
12093     // Set this so the next GC will be a full compacting GC.
12094     if (!last_gc_before_oom)
12095     {
12096         last_gc_before_oom = TRUE;
12097     }
12098
12099 #ifdef BACKGROUND_GC
12100     if (recursive_gc_sync::background_running_p())
12101     {
12102         wait_for_background (awr_loh_oos_bgc);
12103         dprintf (2, ("waited for BGC - done"));
12104     }
12105 #endif //BACKGROUND_GC
12106
12107     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12108     if (current_full_compact_gc_count > last_full_compact_gc_count)
12109     {
12110         dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
12111         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12112         did_full_compact_gc = TRUE;
12113         goto exit;
12114     }
12115
12116     dprintf (3, ("h%d full GC", heap_number));
12117     vm_heap->GarbageCollectGeneration(max_generation, gr);
12118
12119 #ifdef MULTIPLE_HEAPS
12120     enter_spin_lock (&more_space_lock);
12121     dprintf (SPINLOCK_LOG, ("[%d]Emsl after full gc", heap_number));
12122     add_saved_spinlock_info (me_acquire, mt_t_full_gc);
12123 #endif //MULTIPLE_HEAPS
12124
12125     current_full_compact_gc_count = get_full_compact_gc_count();
12126
12127     if (current_full_compact_gc_count == last_full_compact_gc_count)
12128     {
12129         dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
12130         // We requested a full GC but didn't get because of the elevation logic
12131         // which means we should fail.
12132         *oom_r = oom_unproductive_full_gc;
12133     }
12134     else
12135     {
12136         dprintf (3, ("h%d: T full compacting GC (%d->%d)", 
12137             heap_number, 
12138             last_full_compact_gc_count, 
12139             current_full_compact_gc_count));
12140
12141         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12142         did_full_compact_gc = TRUE;
12143     }
12144
12145 exit:
12146     return did_full_compact_gc;
12147 }
12148
12149 #ifdef RECORD_LOH_STATE
12150 void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, DWORD thread_id)
12151 {
12152     // When the state is can_allocate we already have released the more
12153     // space lock. So we are not logging states here since this code
12154     // is not thread safe.
12155     if (loh_state_to_save != a_state_can_allocate)
12156     {
12157         last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
12158         last_loh_states[loh_state_index].thread_id = thread_id;
12159         loh_state_index++;
12160
12161         if (loh_state_index == max_saved_loh_states)
12162         {
12163             loh_state_index = 0;
12164         }
12165
12166         assert (loh_state_index < max_saved_loh_states);
12167     }
12168 }
12169 #endif //RECORD_LOH_STATE
12170
12171 BOOL gc_heap::allocate_large (int gen_number,
12172                               size_t size, 
12173                               alloc_context* acontext,
12174                               int align_const)
12175 {
12176 #ifdef BACKGROUND_GC
12177     if (recursive_gc_sync::background_running_p() && (current_c_gc_state != c_gc_state_planning))
12178     {
12179         background_loh_alloc_count++;
12180         //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
12181         {
12182             if (bgc_loh_should_allocate())
12183             {
12184                 if (!bgc_alloc_spin_loh)
12185                 {
12186                     Thread* current_thread = GetThread();
12187                     add_saved_spinlock_info (me_release, mt_alloc_large);
12188                     dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl loh", heap_number));
12189                     leave_spin_lock (&more_space_lock);
12190                     BOOL cooperative_mode = enable_preemptive (current_thread);
12191                     __SwitchToThread (bgc_alloc_spin_loh, CALLER_LIMITS_SPINNING);
12192                     disable_preemptive (current_thread, cooperative_mode);
12193                     enter_spin_lock (&more_space_lock);
12194                     add_saved_spinlock_info (me_acquire, mt_alloc_large);
12195                     dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
12196                 }
12197             }
12198             else
12199             {
12200                 wait_for_background (awr_loh_alloc_during_bgc);
12201             }
12202         }
12203     }
12204 #endif //BACKGROUND_GC
12205
12206     gc_reason gr = reason_oos_loh;
12207     generation* gen = generation_of (gen_number);
12208     oom_reason oom_r = oom_no_failure;
12209     size_t current_full_compact_gc_count = 0;
12210
12211     // No variable values should be "carried over" from one state to the other. 
12212     // That's why there are local variable for each state
12213     allocation_state loh_alloc_state = a_state_start;
12214 #ifdef RECORD_LOH_STATE
12215     DWORD current_thread_id = GetCurrentThreadId();
12216 #endif //RECORD_LOH_STATE
12217
12218     // If we can get a new seg it means allocation will succeed.
12219     while (1)
12220     {
12221         dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
12222
12223 #ifdef RECORD_LOH_STATE
12224         add_saved_loh_state (loh_alloc_state, current_thread_id);
12225 #endif //RECORD_LOH_STATE
12226         switch (loh_alloc_state)
12227         {
12228             case a_state_can_allocate:
12229             case a_state_cant_allocate:
12230             {
12231                 goto exit;
12232             }
12233             case a_state_start:
12234             {
12235                 loh_alloc_state = a_state_try_fit;
12236                 break;
12237             }
12238             case a_state_try_fit:
12239             {
12240                 BOOL commit_failed_p = FALSE;
12241                 BOOL can_use_existing_p = FALSE;
12242
12243                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12244                                                   align_const, &commit_failed_p, &oom_r);
12245                 loh_alloc_state = (can_use_existing_p ?
12246                                         a_state_can_allocate : 
12247                                         (commit_failed_p ? 
12248                                             a_state_trigger_full_compact_gc :
12249                                             a_state_acquire_seg));
12250                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12251                 break;
12252             }
12253             case a_state_try_fit_new_seg:
12254             {
12255                 BOOL commit_failed_p = FALSE;
12256                 BOOL can_use_existing_p = FALSE;
12257
12258                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12259                                                   align_const, &commit_failed_p, &oom_r);
12260                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12261                 // another LOH allocating thread could have beat us to acquire the msl so 
12262                 // we need to try again.
12263                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
12264                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12265                 break;
12266             }
12267             case a_state_try_fit_new_seg_after_cg:
12268             {
12269                 BOOL commit_failed_p = FALSE;
12270                 BOOL can_use_existing_p = FALSE;
12271
12272                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12273                                                   align_const, &commit_failed_p, &oom_r);
12274                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12275                 // another LOH allocating thread could have beat us to acquire the msl so 
12276                 // we need to try again. However, if we failed to commit, which means we 
12277                 // did have space on the seg, we bail right away 'cause we already did a 
12278                 // full compacting GC.
12279                 loh_alloc_state = (can_use_existing_p ? 
12280                                         a_state_can_allocate : 
12281                                         (commit_failed_p ? 
12282                                             a_state_cant_allocate :
12283                                             a_state_acquire_seg_after_cg));
12284                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12285                 break;
12286             }
12287             case a_state_try_fit_no_seg:
12288             {
12289                 BOOL commit_failed_p = FALSE;
12290                 BOOL can_use_existing_p = FALSE;
12291
12292                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12293                                                   align_const, &commit_failed_p, &oom_r);
12294                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_cant_allocate);
12295                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12296                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12297                 break;
12298             }
12299             case a_state_try_fit_after_cg:
12300             {
12301                 BOOL commit_failed_p = FALSE;
12302                 BOOL can_use_existing_p = FALSE;
12303
12304                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12305                                                   align_const, &commit_failed_p, &oom_r);
12306                 loh_alloc_state = (can_use_existing_p ?
12307                                         a_state_can_allocate : 
12308                                         (commit_failed_p ? 
12309                                             a_state_cant_allocate :
12310                                             a_state_acquire_seg_after_cg));
12311                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12312                 break;
12313             }
12314             case a_state_try_fit_after_bgc:
12315             {
12316                 BOOL commit_failed_p = FALSE;
12317                 BOOL can_use_existing_p = FALSE;
12318
12319                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
12320                                                   align_const, &commit_failed_p, &oom_r);
12321                 loh_alloc_state = (can_use_existing_p ?
12322                                         a_state_can_allocate : 
12323                                         (commit_failed_p ? 
12324                                             a_state_trigger_full_compact_gc :
12325                                             a_state_acquire_seg_after_bgc));
12326                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12327                 break;
12328             }
12329             case a_state_acquire_seg:
12330             {
12331                 BOOL can_get_new_seg_p = FALSE;
12332                 BOOL did_full_compacting_gc = FALSE;
12333
12334                 current_full_compact_gc_count = get_full_compact_gc_count();
12335
12336                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12337                 loh_alloc_state = (can_get_new_seg_p ? 
12338                                         a_state_try_fit_new_seg : 
12339                                         (did_full_compacting_gc ? 
12340                                             a_state_check_retry_seg :
12341                                             a_state_check_and_wait_for_bgc));
12342                 break;
12343             }
12344             case a_state_acquire_seg_after_cg:
12345             {
12346                 BOOL can_get_new_seg_p = FALSE;
12347                 BOOL did_full_compacting_gc = FALSE;
12348
12349                 current_full_compact_gc_count = get_full_compact_gc_count();
12350
12351                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12352                 // Since we release the msl before we try to allocate a seg, other
12353                 // threads could have allocated a bunch of segments before us so
12354                 // we might need to retry.
12355                 loh_alloc_state = (can_get_new_seg_p ? 
12356                                         a_state_try_fit_new_seg_after_cg : 
12357                                         a_state_check_retry_seg);
12358                 break;
12359             }
12360             case a_state_acquire_seg_after_bgc:
12361             {
12362                 BOOL can_get_new_seg_p = FALSE;
12363                 BOOL did_full_compacting_gc = FALSE;
12364              
12365                 current_full_compact_gc_count = get_full_compact_gc_count();
12366
12367                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); 
12368                 loh_alloc_state = (can_get_new_seg_p ? 
12369                                         a_state_try_fit_new_seg : 
12370                                         (did_full_compacting_gc ? 
12371                                             a_state_check_retry_seg :
12372                                             a_state_trigger_full_compact_gc));
12373                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12374                 break;
12375             }
12376             case a_state_check_and_wait_for_bgc:
12377             {
12378                 BOOL bgc_in_progress_p = FALSE;
12379                 BOOL did_full_compacting_gc = FALSE;
12380
12381                 if (fgn_maxgen_percent)
12382                 {
12383                     dprintf (2, ("FGN: failed to acquire seg, may need to do a full blocking GC"));
12384                     send_full_gc_notification (max_generation, FALSE);
12385                 }
12386
12387                 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc);
12388                 loh_alloc_state = (!bgc_in_progress_p ?
12389                                         a_state_trigger_full_compact_gc : 
12390                                         (did_full_compacting_gc ? 
12391                                             a_state_try_fit_after_cg :
12392                                             a_state_try_fit_after_bgc));
12393                 break;
12394             }
12395             case a_state_trigger_full_compact_gc:
12396             {
12397                 BOOL got_full_compacting_gc = FALSE;
12398
12399                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
12400                 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12401                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12402                 break;
12403             }
12404             case a_state_check_retry_seg:
12405             {
12406                 BOOL should_retry_gc = retry_full_compact_gc (size);
12407                 BOOL should_retry_get_seg = FALSE;
12408                 if (!should_retry_gc)
12409                 {
12410                     size_t last_full_compact_gc_count = current_full_compact_gc_count;
12411                     current_full_compact_gc_count = get_full_compact_gc_count();
12412
12413                     if (current_full_compact_gc_count > (last_full_compact_gc_count + 1))
12414                     {
12415                         should_retry_get_seg = TRUE;
12416                     }
12417                 }
12418     
12419                 loh_alloc_state = (should_retry_gc ? 
12420                                         a_state_trigger_full_compact_gc : 
12421                                         (should_retry_get_seg ?
12422                                             a_state_acquire_seg_after_cg :
12423                                             a_state_cant_allocate));
12424                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12425                 break;
12426             }
12427             default:
12428             {
12429                 assert (!"Invalid state!");
12430                 break;
12431             }
12432         }
12433     }
12434
12435 exit:
12436     if (loh_alloc_state == a_state_cant_allocate)
12437     {
12438         assert (oom_r != oom_no_failure);
12439         handle_oom (heap_number, 
12440                     oom_r, 
12441                     size,
12442                     0,
12443                     0);
12444
12445         add_saved_spinlock_info (me_release, mt_alloc_large_cant);
12446         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for loh oom", heap_number));
12447         leave_spin_lock (&more_space_lock);
12448     }
12449
12450     return (loh_alloc_state == a_state_can_allocate);
12451 }
12452
12453 int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
12454                                    int gen_number)
12455 {
12456     if (gc_heap::gc_started)
12457     {
12458         wait_for_gc_done();
12459         return -1;
12460     }
12461
12462 #ifdef SYNCHRONIZATION_STATS
12463     unsigned int msl_acquire_start = GetCycleCount32();
12464 #endif //SYNCHRONIZATION_STATS
12465     enter_spin_lock (&more_space_lock);
12466     add_saved_spinlock_info (me_acquire, mt_try_alloc);
12467     dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
12468 #ifdef SYNCHRONIZATION_STATS
12469     unsigned int msl_acquire = GetCycleCount32() - msl_acquire_start;
12470     total_msl_acquire += msl_acquire;
12471     num_msl_acquired++;
12472     if (msl_acquire > 200)
12473     {
12474         num_high_msl_acquire++;
12475     }
12476     else
12477     {
12478         num_low_msl_acquire++;
12479     }
12480 #endif //SYNCHRONIZATION_STATS
12481
12482     /*
12483     // We are commenting this out 'cause we don't see the point - we already
12484     // have checked gc_started when we were acquiring the msl - no need to check
12485     // again. This complicates the logic in bgc_suspend_EE 'cause that one would
12486     // need to release msl which causes all sorts of trouble.
12487     if (gc_heap::gc_started)
12488     {
12489 #ifdef SYNCHRONIZATION_STATS
12490         good_suspension++;
12491 #endif //SYNCHRONIZATION_STATS
12492         BOOL fStress = (g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_TRANSITION) != 0;
12493         if (!fStress)
12494         {
12495             //Rendez vous early (MP scaling issue)
12496             //dprintf (1, ("[%d]waiting for gc", heap_number));
12497             wait_for_gc_done();
12498 #ifdef MULTIPLE_HEAPS
12499             return -1;
12500 #endif //MULTIPLE_HEAPS
12501         }
12502     }
12503     */
12504
12505     dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
12506
12507     int align_const = get_alignment_constant (gen_number != (max_generation+1));
12508
12509     if (fgn_maxgen_percent)
12510     {
12511         check_for_full_gc (gen_number, size);
12512     }
12513
12514     if (!(new_allocation_allowed (gen_number)))
12515     {
12516         if (fgn_maxgen_percent && (gen_number == 0))
12517         {
12518             // We only check gen0 every so often, so take this opportunity to check again.
12519             check_for_full_gc (gen_number, size);
12520         }
12521
12522 #ifdef BACKGROUND_GC
12523         wait_for_bgc_high_memory (awr_gen0_alloc);
12524 #endif //BACKGROUND_GC
12525
12526 #ifdef SYNCHRONIZATION_STATS
12527         bad_suspension++;
12528 #endif //SYNCHRONIZATION_STATS
12529         dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
12530
12531         if (!settings.concurrent || (gen_number == 0))
12532         {
12533             vm_heap->GarbageCollectGeneration (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh));
12534 #ifdef MULTIPLE_HEAPS
12535             enter_spin_lock (&more_space_lock);
12536             add_saved_spinlock_info (me_acquire, mt_try_budget);
12537             dprintf (SPINLOCK_LOG, ("[%d]Emsl out budget", heap_number));
12538 #endif //MULTIPLE_HEAPS
12539         }
12540     }
12541
12542     BOOL can_allocate = ((gen_number == 0) ?
12543         allocate_small (gen_number, size, acontext, align_const) :
12544         allocate_large (gen_number, size, acontext, align_const));
12545    
12546     if (can_allocate)
12547     {
12548         //ETW trace for allocation tick
12549         size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
12550         int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
12551
12552         etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
12553
12554         if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
12555         {
12556 #ifdef FEATURE_REDHAWK
12557             FireEtwGCAllocationTick_V1((ULONG)etw_allocation_running_amount[etw_allocation_index], 
12558                                     ((gen_number == 0) ? ETW::GCLog::ETW_GC_INFO::AllocationSmall : ETW::GCLog::ETW_GC_INFO::AllocationLarge), 
12559                                     GetClrInstanceId());
12560 #else
12561             // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
12562             // The ones that do are much less efficient.
12563 #if defined(FEATURE_EVENT_TRACE)
12564             if (EventEnabledGCAllocationTick_V2())
12565             {
12566                 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
12567             }
12568 #endif //FEATURE_EVENT_TRACE
12569 #endif //FEATURE_REDHAWK
12570             etw_allocation_running_amount[etw_allocation_index] = 0;
12571         }
12572     }
12573
12574     return (int)can_allocate;
12575 }
12576
12577 #ifdef MULTIPLE_HEAPS
12578 void gc_heap::balance_heaps (alloc_context* acontext)
12579 {
12580
12581     if (acontext->alloc_count < 4)
12582     {
12583         if (acontext->alloc_count == 0)
12584         {
12585             acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, 0) );
12586             gc_heap* hp = acontext->home_heap->pGenGCHeap;
12587             dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
12588             acontext->alloc_heap = acontext->home_heap;
12589             hp->alloc_context_count++;
12590         }
12591     }
12592     else
12593     {
12594         BOOL set_home_heap = FALSE;
12595         int hint = 0;
12596
12597         if (heap_select::can_find_heap_fast())
12598         {
12599             if (acontext->home_heap != NULL)
12600                 hint = acontext->home_heap->pGenGCHeap->heap_number;
12601             if (acontext->home_heap != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
12602             {
12603                 set_home_heap = TRUE;
12604             }
12605         }
12606         else
12607         {
12608             // can't use gdt
12609             if ((acontext->alloc_count & 3) == 0)
12610                 set_home_heap = TRUE;
12611         }
12612
12613         if (set_home_heap)
12614         {
12615 /*
12616             // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
12617             if (n_heaps > MAX_SUPPORTED_CPUS)
12618             {
12619                 // on machines with many processors cache affinity is really king, so don't even try
12620                 // to balance on these.
12621                 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
12622                 acontext->alloc_heap = acontext->home_heap;
12623             }
12624             else
12625 */
12626             {
12627                 gc_heap* org_hp = acontext->alloc_heap->pGenGCHeap;
12628
12629                 dynamic_data* dd = org_hp->dynamic_data_of (0);
12630                 ptrdiff_t org_size = dd_new_allocation (dd);
12631                 int org_alloc_context_count;
12632                 int max_alloc_context_count;
12633                 gc_heap* max_hp;
12634                 ptrdiff_t max_size;
12635                 size_t delta = dd_min_size (dd)/4;
12636
12637                 int start, end, finish;
12638                 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
12639                 finish = start + n_heaps;
12640
12641 try_again:
12642                 do
12643                 {
12644                     max_hp = org_hp;
12645                     max_size = org_size + delta;
12646                     acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
12647
12648                     if (org_hp == acontext->home_heap->pGenGCHeap)
12649                         max_size = max_size + delta;
12650
12651                     org_alloc_context_count = org_hp->alloc_context_count;
12652                     max_alloc_context_count = org_alloc_context_count;
12653                     if (max_alloc_context_count > 1)
12654                         max_size /= max_alloc_context_count;
12655
12656                     for (int i = start; i < end; i++)
12657                     {
12658                         gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
12659                         dd = hp->dynamic_data_of (0);
12660                         ptrdiff_t size = dd_new_allocation (dd);
12661                         if (hp == acontext->home_heap->pGenGCHeap)
12662                             size = size + delta;
12663                         int hp_alloc_context_count = hp->alloc_context_count;
12664                         if (hp_alloc_context_count > 0)
12665                             size /= (hp_alloc_context_count + 1);
12666                         if (size > max_size)
12667                         {
12668                             max_hp = hp;
12669                             max_size = size;
12670                             max_alloc_context_count = hp_alloc_context_count;
12671                         }
12672                     }
12673                 }
12674                 while (org_alloc_context_count != org_hp->alloc_context_count ||
12675                        max_alloc_context_count != max_hp->alloc_context_count);
12676
12677                 if ((max_hp == org_hp) && (end < finish))
12678                 {   
12679                     start = end; end = finish; 
12680                     delta = dd_min_size(dd)/4; //Use the same threshold as tier 1 for now. Tune it later
12681                     goto try_again;
12682                 }
12683
12684                 if (max_hp != org_hp)
12685                 {
12686                     org_hp->alloc_context_count--;
12687                     max_hp->alloc_context_count++;
12688                     acontext->alloc_heap = GCHeap::GetHeap(max_hp->heap_number);
12689 #if !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL)
12690                     if (CPUGroupInfo::CanEnableGCCPUGroups())
12691                     {   //only set ideal processor when max_hp and org_hp are in the same cpu
12692                         //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
12693                         BYTE org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
12694                         BYTE max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
12695                         if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
12696                         {   
12697                             BYTE group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
12698
12699 #if !defined(FEATURE_CORESYSTEM)
12700                             SetThreadIdealProcessor(GetCurrentThread(), (DWORD)group_proc_no);
12701 #else
12702                             PROCESSOR_NUMBER proc;
12703                             proc.Group = org_gn;
12704                             proc.Number = group_proc_no;
12705                             proc.Reserved = 0;
12706                             
12707                             if(!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL))
12708                             {
12709                                 dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
12710                                             org_hp->heap_number));
12711                             }
12712 #endif
12713                         }
12714                     }
12715                     else 
12716                     {
12717                         BYTE proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
12718
12719 #if !defined(FEATURE_CORESYSTEM)
12720                         SetThreadIdealProcessor(GetCurrentThread(), (DWORD)proc_no);
12721 #else
12722                         PROCESSOR_NUMBER proc;
12723                         if(GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
12724                         {
12725                             proc.Number = proc_no;
12726                             BOOL result;
12727                             if(!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL))
12728                             {
12729                                 dprintf (3, ("Failed to set the ideal processor for heap %d.",
12730                                             org_hp->heap_number));
12731                             }
12732                         }
12733 #endif
12734                     }
12735 #endif // !FEATURE_REDHAWK && !FEATURE_PAL
12736                     dprintf (3, ("Switching context %p (home heap %d) ", 
12737                                  acontext,
12738                         acontext->home_heap->pGenGCHeap->heap_number));
12739                     dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ", 
12740                                  org_hp->heap_number,
12741                                  org_size,
12742                                  org_alloc_context_count));
12743                     dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n", 
12744                                  max_hp->heap_number,
12745                                  dd_new_allocation(max_hp->dynamic_data_of(0)),
12746                                                    max_alloc_context_count));
12747                 }
12748             }
12749         }
12750     }
12751     acontext->alloc_count++;
12752 }
12753
12754 gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t size)
12755 {
12756     gc_heap* org_hp = acontext->alloc_heap->pGenGCHeap;
12757     //dprintf (1, ("LA: %Id", size));
12758
12759     //if (size > 128*1024)
12760     if (1)
12761     {
12762         dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
12763
12764         ptrdiff_t org_size = dd_new_allocation (dd);
12765         gc_heap* max_hp;
12766         ptrdiff_t max_size;
12767         size_t delta = dd_min_size (dd) * 4;
12768
12769         int start, end, finish;
12770         heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
12771         finish = start + n_heaps;
12772
12773 try_again:
12774         {
12775             max_hp = org_hp;
12776             max_size = org_size + delta;
12777             dprintf (3, ("orig hp: %d, max size: %d",
12778                 org_hp->heap_number,
12779                 max_size));
12780
12781             for (int i = start; i < end; i++)
12782             {
12783                 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
12784                 dd = hp->dynamic_data_of (max_generation + 1);
12785                 ptrdiff_t size = dd_new_allocation (dd);
12786                 dprintf (3, ("hp: %d, size: %d",
12787                     hp->heap_number,
12788                     size));
12789                 if (size > max_size)
12790                 {
12791                     max_hp = hp;
12792                     max_size = size;
12793                     dprintf (3, ("max hp: %d, max size: %d",
12794                         max_hp->heap_number,
12795                         max_size));
12796                 }
12797             }
12798         }
12799
12800         if ((max_hp == org_hp) && (end < finish))
12801         {
12802             start = end; end = finish;
12803             delta = dd_min_size(dd) * 4;   // Need to tuning delta
12804             goto try_again;
12805         }
12806
12807         if (max_hp != org_hp)
12808         {
12809             dprintf (3, ("loh: %d(%Id)->%d(%Id)", 
12810                 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
12811                 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
12812         }
12813
12814         return max_hp;
12815     }
12816     else
12817     {
12818         return org_hp;
12819     }
12820 }
12821 #endif //MULTIPLE_HEAPS
12822
12823 BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
12824                                   int alloc_generation_number)
12825 {
12826     int status;
12827     do
12828     { 
12829 #ifdef MULTIPLE_HEAPS
12830         if (alloc_generation_number == 0)
12831         {
12832             balance_heaps (acontext);
12833             status = acontext->alloc_heap->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
12834         }
12835         else
12836         {
12837             gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
12838             status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
12839         }
12840 #else
12841         status = try_allocate_more_space (acontext, size, alloc_generation_number);
12842 #endif //MULTIPLE_HEAPS
12843     }
12844     while (status == -1);
12845     
12846     return (status != 0);
12847 }
12848
12849 inline
12850 CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
12851 {
12852     size_t size = Align (jsize);
12853     assert (size >= Align (min_obj_size));
12854     {
12855     retry:
12856         BYTE*  result = acontext->alloc_ptr;
12857         acontext->alloc_ptr+=size;
12858         if (acontext->alloc_ptr <= acontext->alloc_limit)
12859         {
12860             CObjectHeader* obj = (CObjectHeader*)result;
12861             assert (obj != 0);
12862             return obj;
12863         }
12864         else
12865         {
12866             acontext->alloc_ptr -= size;
12867
12868 #ifdef _MSC_VER
12869 #pragma inline_depth(0)
12870 #endif //_MSC_VER
12871
12872             if (! allocate_more_space (acontext, size, 0))
12873                 return 0;
12874
12875 #ifdef _MSC_VER
12876 #pragma inline_depth(20)
12877 #endif //_MSC_VER
12878
12879             goto retry;
12880         }
12881     }
12882 }
12883
12884 inline
12885 CObjectHeader* gc_heap::try_fast_alloc (size_t jsize)
12886 {
12887     size_t size = Align (jsize);
12888     assert (size >= Align (min_obj_size));
12889     generation* gen = generation_of (0);
12890     BYTE*  result = generation_allocation_pointer (gen);
12891     generation_allocation_pointer (gen) += size;
12892     if (generation_allocation_pointer (gen) <=
12893         generation_allocation_limit (gen))
12894     {
12895         return (CObjectHeader*)result;
12896     }
12897     else
12898     {
12899         generation_allocation_pointer (gen) -= size;
12900         return 0;
12901     }
12902 }
12903 void  gc_heap::leave_allocation_segment (generation* gen)
12904 {
12905     adjust_limit (0, 0, gen, max_generation);
12906 }
12907
12908 void gc_heap::init_free_and_plug()
12909 {
12910 #ifdef FREE_USAGE_STATS
12911     for (int i = 0; i <= settings.condemned_generation; i++)
12912     {
12913         generation* gen = generation_of (i);
12914         memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
12915         memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
12916         memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
12917     }
12918
12919     if (settings.condemned_generation != max_generation)
12920     {
12921         for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
12922         {
12923             generation* gen = generation_of (i);
12924             memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
12925         }
12926     }
12927 #endif //FREE_USAGE_STATS
12928 }
12929
12930 void gc_heap::print_free_and_plug (const char* msg)
12931 {
12932 #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
12933     int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
12934     for (int i = 0; i <= older_gen; i++)
12935     {
12936         generation* gen = generation_of (i);
12937         for (int j = 0; j < NUM_GEN_POWER2; j++)
12938         {
12939             if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
12940             {
12941                 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id", 
12942                     msg, 
12943                     heap_number, 
12944                     (settings.concurrent ? "BGC" : "GC"),
12945                     settings.gc_index,
12946                     i,
12947                     (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
12948             }
12949         }
12950     }
12951 #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
12952 }
12953
12954 void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
12955 {
12956 #ifdef FREE_USAGE_STATS
12957     dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
12958     generation* gen = generation_of (gen_number);
12959     size_t sz = BASE_GEN_SIZE;
12960     int i = 0;
12961
12962     for (; i < NUM_GEN_POWER2; i++)
12963     {
12964         if (plug_size < sz)
12965         {
12966             break;
12967         }
12968         sz = sz * 2;
12969     }
12970     
12971     (gen->gen_plugs[i])++;
12972 #endif //FREE_USAGE_STATS
12973 }
12974
12975 void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
12976 {
12977 #ifdef FREE_USAGE_STATS
12978     generation* gen = generation_of (gen_number);
12979     size_t sz = BASE_GEN_SIZE;
12980     int i = 0;
12981
12982     for (; i < NUM_GEN_POWER2; i++)
12983     {
12984         if (free_size < sz)
12985         {
12986             break;
12987         }
12988         sz = sz * 2;
12989     }
12990     
12991     (gen->gen_current_pinned_free_spaces[i])++;
12992     generation_pinned_free_obj_space (gen) += free_size;
12993     dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)", 
12994         free_size, (i + 10), gen_number, 
12995         generation_pinned_free_obj_space (gen),
12996         gen->gen_current_pinned_free_spaces[i]));
12997 #endif //FREE_USAGE_STATS
12998 }
12999
13000 void gc_heap::add_gen_free (int gen_number, size_t free_size)
13001 {
13002 #ifdef FREE_USAGE_STATS
13003     dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13004     generation* gen = generation_of (gen_number);
13005     size_t sz = BASE_GEN_SIZE;
13006     int i = 0;
13007
13008     for (; i < NUM_GEN_POWER2; i++)
13009     {
13010         if (free_size < sz)
13011         {
13012             break;
13013         }
13014         sz = sz * 2;
13015     }
13016     
13017     (gen->gen_free_spaces[i])++;
13018 #endif //FREE_USAGE_STATS
13019 }
13020
13021 void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13022 {
13023 #ifdef FREE_USAGE_STATS
13024     dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13025     generation* gen = generation_of (gen_number);
13026     size_t sz = BASE_GEN_SIZE;
13027     int i = 0;
13028
13029     for (; i < NUM_GEN_POWER2; i++)
13030     {
13031         if (free_size < sz)
13032         {
13033             break;
13034         }
13035         sz = sz * 2;
13036     }
13037     
13038     (gen->gen_free_spaces[i])--;
13039 #endif //FREE_USAGE_STATS
13040 }
13041
13042 BYTE* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13043                                              int from_gen_number,
13044                                              BYTE* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13045 {
13046     size = Align (size);
13047     assert (size >= Align (min_obj_size));
13048     assert (from_gen_number < max_generation);
13049     assert (from_gen_number >= 0);
13050     assert (generation_of (from_gen_number + 1) == gen);
13051
13052     allocator* gen_allocator = generation_allocator (gen);
13053     BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13054     int pad_in_front = (old_loc != 0)? USE_PADDING_FRONT : 0;
13055     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13056                        generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13057     {
13058         size_t sz_list = gen_allocator->first_bucket_size();
13059         for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13060         {
13061             if ((size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13062             {
13063                 BYTE* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13064                 BYTE* prev_free_item = 0; 
13065                 while (free_list != 0)
13066                 {
13067                     dprintf (3, ("considering free list %Ix", (size_t)free_list));
13068
13069                     size_t free_list_size = unused_array_size (free_list);
13070                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
13071                                     old_loc, USE_PADDING_TAIL | pad_in_front))
13072                     {
13073                         dprintf (4, ("F:%Ix-%Id",
13074                                      (size_t)free_list, free_list_size));
13075                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
13076                         generation_free_list_space (gen) -= free_list_size;
13077                         remove_gen_free (gen->gen_num, free_list_size);
13078
13079                         adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
13080                         goto finished;
13081                     }
13082                     else if (discard_p)
13083                     {
13084                         dprintf (3, ("couldn't use this free area, discarding"));
13085                         generation_free_obj_space (gen) += free_list_size;
13086
13087                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
13088                         generation_free_list_space (gen) -= free_list_size;
13089                         remove_gen_free (gen->gen_num, free_list_size);
13090                     }
13091                     else
13092                     {
13093                         prev_free_item = free_list;
13094                     }
13095                     free_list = free_list_slot (free_list); 
13096                 }
13097             }
13098             sz_list = sz_list * 2;
13099         }
13100         //go back to the beginning of the segment list 
13101         generation_allocate_end_seg_p (gen) = TRUE;
13102         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
13103         if (seg != generation_allocation_segment (gen))
13104         {
13105             leave_allocation_segment (gen);
13106             generation_allocation_segment (gen) = seg;
13107         }
13108         while (seg != ephemeral_heap_segment)
13109         {
13110             if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13111                            heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
13112             {
13113                 dprintf (3, ("using what's left in committed"));
13114                 adjust_limit (heap_segment_plan_allocated (seg),
13115                               heap_segment_committed (seg) -
13116                               heap_segment_plan_allocated (seg),
13117                               gen, from_gen_number+1);
13118                 // dformat (t, 3, "Expanding segment allocation");
13119                 heap_segment_plan_allocated (seg) =
13120                     heap_segment_committed (seg);
13121                 goto finished;
13122             }
13123             else
13124             {
13125                 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13126                                 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13127                     grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
13128                 {
13129                     dprintf (3, ("using what's left in reserved"));
13130                     adjust_limit (heap_segment_plan_allocated (seg),
13131                                   heap_segment_committed (seg) -
13132                                   heap_segment_plan_allocated (seg),
13133                                   gen, from_gen_number+1);
13134                     heap_segment_plan_allocated (seg) =
13135                         heap_segment_committed (seg);
13136
13137                     goto finished;
13138                 }
13139                 else
13140                 {
13141                     leave_allocation_segment (gen);
13142                     heap_segment*   next_seg = heap_segment_next_rw (seg);
13143                     if (next_seg)
13144                     {
13145                         dprintf (3, ("getting next segment"));
13146                         generation_allocation_segment (gen) = next_seg;
13147                         generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13148                         generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13149                     }
13150                     else
13151                     {
13152                         size = 0;
13153                         goto finished;
13154                     }
13155                 }
13156             }
13157             seg = generation_allocation_segment (gen);
13158         }
13159         //No need to fix the last region. Will be done later
13160         size = 0;
13161         goto finished;
13162     }
13163     finished:
13164     if (0 == size)
13165     {
13166         return 0;
13167     }
13168     else
13169     {
13170         BYTE*  result = generation_allocation_pointer (gen);
13171         size_t pad = 0;
13172
13173 #ifdef SHORT_PLUGS
13174         if ((pad_in_front & USE_PADDING_FRONT) &&
13175             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13176              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13177         {
13178             pad = Align (min_obj_size);
13179             set_plug_padded (old_loc);
13180         }
13181 #endif //SHORT_PLUGS
13182
13183 #ifdef FEATURE_STRUCTALIGN
13184         _ASSERTE(!old_loc || alignmentOffset != 0);
13185         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13186         if (old_loc != 0)
13187         {
13188             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13189             set_node_aligninfo (old_loc, requiredAlignment, pad1);
13190             pad += pad1;
13191         }
13192 #else // FEATURE_STRUCTALIGN
13193         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13194         {
13195             pad += switch_alignment_size (is_plug_padded (old_loc));
13196             set_node_realigned (old_loc);
13197             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13198                          (size_t)old_loc, (size_t)(result+pad)));
13199             assert (same_large_alignment_p (result + pad, old_loc));
13200         }
13201 #endif // FEATURE_STRUCTALIGN
13202         dprintf (3, ("Allocate %Id bytes", size));
13203
13204         if ((old_loc == 0) || (pad != 0))
13205         {
13206             //allocating a non plug or a gap, so reset the start region
13207             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13208         }
13209
13210         generation_allocation_pointer (gen) += size + pad;
13211         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13212         if (generation_allocate_end_seg_p (gen))
13213         {
13214             generation_end_seg_allocated (gen) += size;
13215         }
13216         else
13217         {
13218             generation_free_list_allocated (gen) += size;
13219         }
13220         generation_allocation_size (gen) += size;
13221
13222         dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix", 
13223             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13224             generation_allocation_context_start_region (gen)));
13225
13226         return result + pad;;
13227     }
13228 }
13229
13230 void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
13231 {
13232     //make sure that every generation has a planned allocation start
13233     int  gen_number = max_generation - 1;
13234     while (gen_number>= 0)
13235     {
13236         generation* gen = generation_of (gen_number);
13237         if (0 == generation_plan_allocation_start (gen))
13238         {
13239             realloc_plan_generation_start (gen, consing_gen);
13240
13241             assert (generation_plan_allocation_start (gen));
13242         }
13243         gen_number--;
13244     }
13245
13246     // now we know the planned allocation size
13247     size_t  size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
13248     heap_segment* seg = generation_allocation_segment (consing_gen);
13249     if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
13250     {
13251         if (size != 0)
13252         {
13253             heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
13254         }
13255     }
13256     else
13257     {
13258         assert (settings.condemned_generation == max_generation);
13259         BYTE* first_address = generation_allocation_limit (consing_gen);
13260         //look through the pinned plugs for relevant ones.
13261         //Look for the right pinned plug to start from.
13262         size_t mi = 0;
13263         mark* m = 0;
13264         while (mi != mark_stack_tos)
13265         {
13266             m = pinned_plug_of (mi);
13267             if ((pinned_plug (m) == first_address))
13268                 break;
13269             else
13270                 mi++;
13271         }
13272         assert (mi != mark_stack_tos);
13273         pinned_len (m) = size;
13274     }
13275 }
13276
13277 //tododefrag optimize for new segment (plan_allocated == mem)
13278 BYTE* gc_heap::allocate_in_expanded_heap (generation* gen, 
13279                                           size_t size,
13280                                           BOOL& adjacentp,
13281                                           BYTE* old_loc,
13282 #ifdef SHORT_PLUGS
13283                                           BOOL set_padding_on_saved_p,
13284                                           mark* pinned_plug_entry,
13285 #endif //SHORT_PLUGS
13286                                           BOOL consider_bestfit,
13287                                           int active_new_gen_number
13288                                           REQD_ALIGN_AND_OFFSET_DCL)
13289 {
13290     dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
13291
13292     size = Align (size);
13293     assert (size >= Align (min_obj_size));
13294     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
13295
13296     if (consider_bestfit && use_bestfit)
13297     {
13298         assert (bestfit_seg);
13299         dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id", 
13300                     old_loc, size));
13301         return bestfit_seg->fit (old_loc, 
13302 #ifdef SHORT_PLUGS
13303                                  set_padding_on_saved_p,
13304                                  pinned_plug_entry,
13305 #endif //SHORT_PLUGS
13306                                  size REQD_ALIGN_AND_OFFSET_ARG);
13307     }
13308
13309     heap_segment* seg = generation_allocation_segment (gen);
13310
13311     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13312                        generation_allocation_limit (gen), old_loc,
13313                        ((generation_allocation_limit (gen) !=
13314                           heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
13315     {
13316         dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
13317             generation_allocation_limit (gen)));
13318
13319         adjacentp = FALSE;
13320         BYTE* first_address = (generation_allocation_limit (gen) ?
13321                                generation_allocation_limit (gen) :
13322                                heap_segment_mem (seg));
13323         assert (in_range_for_segment (first_address, seg));
13324
13325         BYTE* end_address   = heap_segment_reserved (seg);
13326
13327         dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
13328             first_address, generation_allocation_limit (gen), end_address));
13329
13330         size_t mi = 0;
13331         mark* m = 0;
13332
13333         if (heap_segment_allocated (seg) != heap_segment_mem (seg))
13334         {
13335             assert (settings.condemned_generation == max_generation);
13336             //look through the pinned plugs for relevant ones.
13337             //Look for the right pinned plug to start from.
13338             while (mi != mark_stack_tos)
13339             {
13340                 m = pinned_plug_of (mi);
13341                 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
13342                 {
13343                     dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
13344                     break;
13345                 }
13346                 else
13347                     mi++;
13348             }
13349             if (mi != mark_stack_tos)
13350             {
13351                 //fix old free list.
13352                 size_t  hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
13353                 {
13354                     dprintf(3,("gc filling up hole"));
13355                     ptrdiff_t mi1 = (ptrdiff_t)mi;
13356                     while ((mi1 >= 0) &&
13357                            (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
13358                     {
13359                         dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
13360                         mi1--;
13361                     }
13362                     if (mi1 >= 0)
13363                     {
13364                         size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
13365                         pinned_len (pinned_plug_of(mi1)) = hsize;
13366                         dprintf (3, ("changing %Ix len %Ix->%Ix", 
13367                             pinned_plug (pinned_plug_of(mi1)), 
13368                             saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
13369                     }
13370                 }
13371             }
13372         }
13373         else
13374         {
13375             assert (generation_allocation_limit (gen) ==
13376                     generation_allocation_pointer (gen));
13377             mi = mark_stack_tos;
13378         }
13379
13380         while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
13381         {
13382             size_t len = pinned_len (m);
13383             BYTE*  free_list = (pinned_plug (m) - len);
13384             dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)", 
13385                 free_list, (free_list + len), len));
13386             if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
13387             {
13388                 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
13389                             (size_t)free_list, len));
13390                 {
13391                     generation_allocation_pointer (gen) = free_list;
13392                     generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13393                     generation_allocation_limit (gen) = (free_list + len);
13394                 }
13395                 goto allocate_in_free;
13396             }
13397             mi++;
13398             m = pinned_plug_of (mi);
13399         }
13400
13401         //switch to the end of the segment.
13402         generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
13403         generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13404         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
13405         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13406         dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)", 
13407             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13408             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
13409
13410         if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13411                          generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
13412         {
13413             dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
13414                 generation_allocation_limit (gen)));
13415             assert (!"Can't allocate if no free space");
13416             return 0;
13417         }
13418     }
13419     else
13420     {
13421         adjacentp = TRUE;
13422     }
13423
13424 allocate_in_free:
13425     {
13426         BYTE*  result = generation_allocation_pointer (gen);
13427         size_t pad = 0;
13428
13429 #ifdef SHORT_PLUGS
13430         if ((pad_in_front & USE_PADDING_FRONT) &&
13431             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13432              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13433
13434         {
13435             pad = Align (min_obj_size);
13436             set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
13437         }
13438 #endif //SHORT_PLUGS
13439
13440 #ifdef FEATURE_STRUCTALIGN
13441         _ASSERTE(!old_loc || alignmentOffset != 0);
13442         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13443         if (old_loc != 0)
13444         {
13445             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13446             set_node_aligninfo (old_loc, requiredAlignment, pad1);
13447             pad += pad1;
13448             adjacentp = FALSE;
13449         }
13450 #else // FEATURE_STRUCTALIGN
13451         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13452         {
13453             pad += switch_alignment_size (is_plug_padded (old_loc));
13454             set_node_realigned (old_loc);
13455             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13456                          (size_t)old_loc, (size_t)(result+pad)));
13457             assert (same_large_alignment_p (result + pad, old_loc));
13458             adjacentp = FALSE;
13459         }
13460 #endif // FEATURE_STRUCTALIGN
13461
13462         if ((old_loc == 0) || (pad != 0))
13463         {
13464             //allocating a non plug or a gap, so reset the start region
13465             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13466         }
13467
13468         generation_allocation_pointer (gen) += size + pad;
13469         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13470         dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
13471
13472         dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix", 
13473             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13474             generation_allocation_context_start_region (gen)));
13475
13476         return result + pad;
13477     }
13478 }
13479
13480 generation*  gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
13481 {
13482     heap_segment* seg = generation_allocation_segment (consing_gen);
13483     if (seg != ephemeral_heap_segment)
13484     {
13485         assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
13486         assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
13487
13488         //fix the allocated size of the segment.
13489         heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
13490
13491         generation* new_consing_gen = generation_of (max_generation - 1);
13492         generation_allocation_pointer (new_consing_gen) =
13493                 heap_segment_mem (ephemeral_heap_segment);
13494         generation_allocation_limit (new_consing_gen) =
13495             generation_allocation_pointer (new_consing_gen);
13496         generation_allocation_context_start_region (new_consing_gen) = 
13497             generation_allocation_pointer (new_consing_gen);
13498         generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
13499
13500         return new_consing_gen;
13501     }
13502     else
13503         return consing_gen;
13504 }
13505
13506 BYTE* gc_heap::allocate_in_condemned_generations (generation* gen,
13507                                                   size_t size,
13508                                                   int from_gen_number,
13509 #ifdef SHORT_PLUGS
13510                                                   BYTE* next_pinned_plug,
13511                                                   heap_segment* current_seg,
13512 #endif //SHORT_PLUGS
13513                                                   BYTE* old_loc
13514                                                   REQD_ALIGN_AND_OFFSET_DCL)
13515 {
13516     // Make sure that the youngest generation gap hasn't been allocated
13517     if (settings.promotion)
13518     {
13519         assert (generation_plan_allocation_start (youngest_generation) == 0);
13520     }
13521
13522     size = Align (size);
13523     assert (size >= Align (min_obj_size));
13524     int to_gen_number = from_gen_number;
13525     if (from_gen_number != (int)max_generation)
13526     {
13527         to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
13528     }
13529
13530     dprintf (3, ("aic gen%d: s: %Id", gen->gen_num, size));
13531
13532     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
13533
13534     if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
13535     {
13536         generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
13537         generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
13538     }
13539 retry:
13540     {
13541         heap_segment* seg = generation_allocation_segment (gen);
13542         if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13543                            generation_allocation_limit (gen), old_loc,
13544                            ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
13545         {
13546             if ((! (pinned_plug_que_empty_p()) &&
13547                  (generation_allocation_limit (gen) ==
13548                   pinned_plug (oldest_pin()))))
13549             {
13550                 size_t entry = deque_pinned_plug();
13551                 mark* pinned_plug_entry = pinned_plug_of (entry);
13552                 size_t len = pinned_len (pinned_plug_entry);
13553                 BYTE* plug = pinned_plug (pinned_plug_entry);
13554                 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
13555
13556 #ifdef FREE_USAGE_STATS
13557                 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
13558                 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id", 
13559                     generation_allocated_since_last_pin (gen), 
13560                     plug,
13561                     generation_allocated_in_pinned_free (gen)));
13562                 generation_allocated_since_last_pin (gen) = 0;
13563
13564                 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
13565 #endif //FREE_USAGE_STATS
13566
13567                 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix", 
13568                     mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
13569
13570                 assert(mark_stack_array[entry].len == 0 ||
13571                        mark_stack_array[entry].len >= Align(min_obj_size));
13572                 generation_allocation_pointer (gen) = plug + len;
13573                 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13574                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13575                 set_allocator_next_pin (gen);
13576
13577                 //Add the size of the pinned plug to the right pinned allocations
13578                 //find out which gen this pinned plug came from 
13579                 int frgn = object_gennum (plug);
13580                 if ((frgn != (int)max_generation) && settings.promotion)
13581                 {
13582                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
13583                     int togn = object_gennum_plan (plug);
13584                     if (frgn < togn)
13585                     {
13586                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
13587                     }
13588                 }
13589                 goto retry;
13590             }
13591             
13592             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
13593             {
13594                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13595                 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
13596             }
13597             else
13598             {
13599                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
13600                 {
13601                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
13602                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13603                     dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
13604                 }
13605                 else
13606                 {
13607 #ifndef RESPECT_LARGE_ALIGNMENT
13608                     assert (gen != youngest_generation);
13609 #endif //RESPECT_LARGE_ALIGNMENT
13610
13611                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13612                                     heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13613                         (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
13614                                             size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
13615                     {
13616                         dprintf (3, ("Expanded segment allocation by committing more memory"));
13617                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
13618                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13619                     }
13620                     else
13621                     {
13622                         heap_segment*   next_seg = heap_segment_next (seg);
13623                         assert (generation_allocation_pointer (gen)>=
13624                                 heap_segment_mem (seg));
13625                         // Verify that all pinned plugs for this segment are consumed
13626                         if (!pinned_plug_que_empty_p() &&
13627                             ((pinned_plug (oldest_pin()) <
13628                               heap_segment_allocated (seg)) &&
13629                              (pinned_plug (oldest_pin()) >=
13630                               generation_allocation_pointer (gen))))
13631                         {
13632                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
13633                                          pinned_plug (oldest_pin())));
13634                             FATAL_GC_ERROR();
13635                         }
13636                         assert (generation_allocation_pointer (gen)>=
13637                                 heap_segment_mem (seg));
13638                         assert (generation_allocation_pointer (gen)<=
13639                                 heap_segment_committed (seg));
13640                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
13641
13642                         if (next_seg)
13643                         {
13644                             generation_allocation_segment (gen) = next_seg;
13645                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13646                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13647                             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13648                         }
13649                         else
13650                         {
13651                             return 0; //should only happen during allocation of generation 0 gap
13652                             // in that case we are going to grow the heap anyway
13653                         }
13654                     }
13655                 }
13656             }
13657             set_allocator_next_pin (gen);
13658
13659             goto retry;
13660         }
13661     }
13662
13663     {
13664         assert (generation_allocation_pointer (gen)>=
13665                 heap_segment_mem (generation_allocation_segment (gen)));
13666         BYTE* result = generation_allocation_pointer (gen);
13667         size_t pad = 0;
13668 #ifdef SHORT_PLUGS
13669         if ((pad_in_front & USE_PADDING_FRONT) &&
13670             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13671              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13672         {
13673             SSIZE_T dist = old_loc - result;
13674             if (dist == 0)
13675             {
13676                 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
13677                 pad = 0;
13678             }
13679             else
13680             {
13681                 if ((dist > 0) && (dist < (SSIZE_T)Align (min_obj_size)))
13682                 {
13683                     dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
13684                     FATAL_GC_ERROR();
13685                 }
13686
13687                 pad = Align (min_obj_size);
13688                 set_plug_padded (old_loc);
13689             }
13690         }
13691 #endif //SHORT_PLUGS
13692 #ifdef FEATURE_STRUCTALIGN
13693         _ASSERTE(!old_loc || alignmentOffset != 0);
13694         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13695         if ((old_loc != 0))
13696         {
13697             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13698             set_node_aligninfo (old_loc, requiredAlignment, pad1);
13699             pad += pad1;
13700         }
13701 #else // FEATURE_STRUCTALIGN
13702         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13703         {
13704             pad += switch_alignment_size (is_plug_padded (old_loc));
13705             set_node_realigned(old_loc);
13706             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13707                          (size_t)old_loc, (size_t)(result+pad)));
13708             assert (same_large_alignment_p (result + pad, old_loc));
13709         }
13710 #endif // FEATURE_STRUCTALIGN
13711
13712 #ifdef SHORT_PLUGS
13713         if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
13714         {
13715             assert (old_loc != 0);
13716             ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
13717             assert (dist_to_next_pin >= 0);
13718
13719             if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
13720             {
13721                 clear_plug_padded (old_loc);
13722                 pad = 0;
13723             }
13724         }
13725 #endif //SHORT_PLUGS
13726
13727         if ((old_loc == 0) || (pad != 0))
13728         {
13729             //allocating a non plug or a gap, so reset the start region
13730             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13731         }
13732
13733         generation_allocation_pointer (gen) += size + pad;
13734         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13735
13736 #ifdef FREE_USAGE_STATS
13737         generation_allocated_since_last_pin (gen) += size;
13738 #endif //FREE_USAGE_STATS
13739
13740         dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix", 
13741             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13742             generation_allocation_context_start_region (gen)));
13743
13744         assert (result + pad);
13745         return result + pad;
13746     }
13747 }
13748
13749 inline int power (int x, int y)
13750 {
13751     int z = 1;
13752     for (int i = 0; i < y; i++)
13753     {
13754         z = z*x;
13755     }
13756     return z;
13757 }
13758
13759 inline
13760 int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation, 
13761                                            int n_initial,
13762                                            BOOL* blocking_collection_p
13763                                            STRESS_HEAP_ARG(int n_original))
13764 {
13765     int n = n_initial;
13766 #ifdef MULTIPLE_HEAPS
13767     BOOL blocking_p = *blocking_collection_p;
13768     if (!blocking_p)
13769     {
13770         for (int i = 0; i < n_heaps; i++)
13771         {
13772             if (g_heaps[i]->last_gc_before_oom)
13773             {
13774                 dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
13775                 *blocking_collection_p = TRUE;
13776                 break;
13777             }
13778         }
13779     }
13780 #endif //MULTIPLE_HEAPS
13781
13782     if (should_evaluate_elevation && (n == max_generation))
13783     {
13784         dprintf (GTC_LOG, ("lock: %d(%d)", 
13785             (settings.should_lock_elevation ? 1 : 0), 
13786             settings.elevation_locked_count));
13787
13788         if (settings.should_lock_elevation)
13789         {
13790             settings.elevation_locked_count++;
13791             if (settings.elevation_locked_count == 6)
13792             {
13793                 settings.elevation_locked_count = 0;
13794             }
13795             else
13796             {
13797                 n = max_generation - 1;
13798             }
13799         }
13800         else
13801         {
13802             settings.elevation_locked_count = 0;
13803         }
13804     }
13805     else
13806     {
13807         settings.should_lock_elevation = FALSE;
13808         settings.elevation_locked_count = 0;
13809     }
13810
13811 #ifdef STRESS_HEAP
13812     // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
13813     // generations to be collected,
13814
13815     if (n_original != max_generation &&
13816         g_pConfig->GetGCStressLevel() && g_pConfig->GetGCconcurrent())
13817     {
13818 #ifndef FEATURE_REDHAWK
13819         // for the GC stress mix mode throttle down gen2 collections
13820         if (g_pConfig->IsGCStressMix())
13821         {
13822             size_t current_gc_count = 0;
13823
13824 #ifdef MULTIPLE_HEAPS
13825             current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
13826 #else
13827             current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
13828 #endif //MULTIPLE_HEAPS
13829             // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
13830             if ((current_gc_count % 10) == 0)
13831             {
13832                 n = max_generation;
13833             }
13834         }
13835         // for traditional GC stress
13836         else
13837 #endif // !FEATURE_REDHAWK
13838         if (*blocking_collection_p)
13839         {
13840             // We call StressHeap() a lot for Concurrent GC Stress. However,
13841             // if we can not do a concurrent collection, no need to stress anymore.
13842             // @TODO: Enable stress when the memory pressure goes down again
13843             GCStressPolicy::GlobalDisable();
13844         }
13845         else
13846         {
13847             n = max_generation;
13848         }
13849     }
13850 #endif //STRESS_HEAP
13851
13852     return n;
13853 }
13854
13855 inline
13856 size_t get_survived_size (gc_history_per_heap* hist)
13857 {
13858     size_t surv_size = 0;
13859     gc_generation_data* gen_data;
13860
13861     for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
13862     {
13863         gen_data = &(hist->gen_data[gen_number]); 
13864         surv_size += (gen_data->size_after - 
13865                       gen_data->free_list_space_after - 
13866                       gen_data->free_obj_space_after);
13867     }
13868
13869     return surv_size;
13870 }
13871
13872 size_t gc_heap::get_total_survived_size()
13873 {
13874     size_t total_surv_size = 0;
13875 #ifdef MULTIPLE_HEAPS
13876     for (int i = 0; i < gc_heap::n_heaps; i++)
13877     {
13878         gc_heap* hp = gc_heap::g_heaps[i];
13879         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
13880         total_surv_size += get_survived_size (current_gc_data_per_heap);
13881     }
13882 #else
13883     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
13884     total_surv_size = get_survived_size (current_gc_data_per_heap);
13885 #endif //MULTIPLE_HEAPS
13886     return total_surv_size;
13887 }
13888
13889 // Gets what's allocated on both SOH and LOH that hasn't been collected.
13890 size_t gc_heap::get_current_allocated()
13891 {
13892     dynamic_data* dd = dynamic_data_of (0);
13893     size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
13894     dd = dynamic_data_of (max_generation + 1);
13895     current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
13896
13897     return current_alloc;
13898 }
13899
13900 size_t gc_heap::get_total_allocated()
13901 {
13902     size_t total_current_allocated = 0;
13903 #ifdef MULTIPLE_HEAPS
13904     for (int i = 0; i < gc_heap::n_heaps; i++)
13905     {
13906         gc_heap* hp = gc_heap::g_heaps[i];
13907         total_current_allocated += hp->get_current_allocated();
13908     }
13909 #else
13910     total_current_allocated = get_current_allocated();
13911 #endif //MULTIPLE_HEAPS
13912     return total_current_allocated;
13913 }
13914
13915 size_t gc_heap::current_generation_size (int gen_number)
13916 {
13917     dynamic_data* dd = dynamic_data_of (gen_number);
13918     size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
13919                         - dd_new_allocation (dd));
13920
13921     return gen_size;
13922 }
13923
13924 #ifdef _PREFAST_
13925 #pragma warning(push)
13926 #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
13927 #endif //_PREFAST_
13928
13929 /*
13930     This is called by when we are actually doing a GC, or when we are just checking whether
13931     we would do a full blocking GC, in which case check_only_p is TRUE.
13932
13933     The difference between calling this with check_only_p TRUE and FALSE is that when it's
13934     TRUE: 
13935             settings.reason is ignored
13936             budgets are not checked (since they are checked before this is called)
13937             it doesn't change anything non local like generation_skip_ratio
13938 */
13939 int gc_heap::generation_to_condemn (int n_initial, 
13940                                     BOOL* blocking_collection_p, 
13941                                     BOOL* elevation_requested_p,
13942                                     BOOL check_only_p)
13943 {
13944     gc_mechanisms temp_settings = settings;
13945     gen_to_condemn_tuning temp_condemn_reasons;
13946     gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
13947     gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
13948     if (!check_only_p)
13949     {
13950         if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
13951         {
13952             assert (n_initial >= 1);
13953         }
13954
13955         assert (settings.reason != reason_empty);
13956     }
13957
13958     local_condemn_reasons->init();
13959
13960     int n = n_initial;
13961     int n_alloc = n;
13962     if (heap_number == 0)
13963     {
13964         dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
13965     }
13966     int i = 0;
13967     int temp_gen = 0;
13968     MEMORYSTATUSEX ms;
13969     memset (&ms, 0, sizeof(ms));
13970     BOOL low_memory_detected = g_low_memory_status;
13971     BOOL check_memory = FALSE;
13972     BOOL high_fragmentation  = FALSE;
13973     BOOL v_high_memory_load  = FALSE;
13974     BOOL high_memory_load    = FALSE;
13975     BOOL low_ephemeral_space = FALSE;
13976     BOOL evaluate_elevation  = TRUE;
13977     *elevation_requested_p   = FALSE;
13978     *blocking_collection_p   = FALSE;
13979
13980     BOOL check_max_gen_alloc = TRUE;
13981
13982 #ifdef STRESS_HEAP
13983     int orig_gen = n;
13984 #endif //STRESS_HEAP
13985
13986     if (!check_only_p)
13987     {
13988         dd_fragmentation (dynamic_data_of (0)) = 
13989             generation_free_list_space (youngest_generation) + 
13990             generation_free_obj_space (youngest_generation);
13991
13992         dd_fragmentation (dynamic_data_of (max_generation + 1)) = 
13993             generation_free_list_space (large_object_generation) + 
13994             generation_free_obj_space (large_object_generation);
13995
13996         //save new_allocation
13997         for (i = 0; i <= max_generation+1; i++)
13998         {
13999             dynamic_data* dd = dynamic_data_of (i);
14000             dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)", 
14001                             heap_number, i,
14002                             dd_new_allocation (dd),
14003                             dd_desired_allocation (dd)));
14004             dd_gc_new_allocation (dd) = dd_new_allocation (dd);
14005         }
14006
14007         local_condemn_reasons->set_gen (gen_initial, n);
14008         temp_gen = n;
14009
14010 #ifdef BACKGROUND_GC
14011         if (recursive_gc_sync::background_running_p())
14012         {
14013             dprintf (GTC_LOG, ("bgc in prog, 1"));
14014             check_max_gen_alloc = FALSE;
14015         }
14016 #endif //BACKGROUND_GC
14017
14018         if (check_max_gen_alloc)
14019         {
14020             //figure out if large objects need to be collected.
14021             if (get_new_allocation (max_generation+1) <= 0)
14022             {
14023                 n = max_generation;
14024                 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14025             }
14026         }
14027
14028         //figure out which generation ran out of allocation
14029         for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
14030         {
14031             if (get_new_allocation (i) <= 0)
14032             {
14033                 n = i;
14034             }
14035             else
14036                 break;
14037         }
14038     }
14039
14040     if (n > temp_gen)
14041     {
14042         local_condemn_reasons->set_gen (gen_alloc_budget, n);
14043     }
14044
14045     dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
14046
14047     n_alloc = n;
14048
14049 #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
14050     //time based tuning
14051     // if enough time has elapsed since the last gc
14052     // and the number of gc is too low (1/10 of lower gen) then collect
14053     // This should also be enabled if we have memory concerns
14054     int n_time_max = max_generation;
14055
14056     if (!check_only_p)
14057     {
14058         if (recursive_gc_sync::background_running_p())
14059         {
14060             n_time_max = max_generation - 1;
14061         }
14062     }
14063
14064     if ((local_settings->pause_mode == pause_interactive) ||
14065         (local_settings->pause_mode == pause_sustained_low_latency))
14066     {
14067         dynamic_data* dd0 = dynamic_data_of (0);
14068         LARGE_INTEGER ts;
14069         if (!QueryPerformanceCounter(&ts))
14070             FATAL_GC_ERROR();
14071         
14072         size_t now = (size_t) (ts.QuadPart/(qpf.QuadPart/1000));
14073         temp_gen = n;
14074         for (i = (temp_gen+1); i <= n_time_max; i++)
14075         {
14076             dynamic_data* dd = dynamic_data_of (i);
14077             if ((now > dd_time_clock(dd) + power (10, i)*1000) &&
14078                 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + (power (10, i)))) &&
14079                 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
14080             {
14081                 n = min (i, n_time_max);
14082                 dprintf (GTC_LOG, ("time %d", n));
14083             }
14084         }
14085         if (n > temp_gen)
14086         {
14087             local_condemn_reasons->set_gen (gen_time_tuning, n);
14088         }
14089     }
14090
14091     if (n != n_alloc)
14092     {
14093         dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
14094     }
14095 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
14096
14097     if (n < (max_generation - 1))
14098     {
14099         if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
14100         {
14101             n = max (n, max_generation - 1);
14102             local_settings->promotion = TRUE;
14103             dprintf (GTC_LOG, ("h%d: skip %d, c %d",
14104                         heap_number, generation_skip_ratio, n));
14105             local_condemn_reasons->set_condition (gen_low_card_p);
14106         }
14107     }
14108
14109     if (!check_only_p)
14110     {
14111         generation_skip_ratio = 100;
14112     }
14113
14114     if (dt_low_ephemeral_space_p (check_only_p ? 
14115                                   tuning_deciding_full_gc : 
14116                                   tuning_deciding_condemned_gen))
14117     {
14118         low_ephemeral_space = TRUE;
14119
14120         n = max (n, max_generation - 1);
14121         local_condemn_reasons->set_condition (gen_low_ephemeral_p);
14122         dprintf (GTC_LOG, ("h%d: low eph", heap_number));
14123
14124 #ifdef BACKGROUND_GC
14125         if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
14126 #endif //BACKGROUND_GC
14127         {
14128             //It is better to defragment first if we are running out of space for
14129             //the ephemeral generation but we have enough fragmentation to make up for it
14130             //in the non ephemeral generation. Essentially we are trading a gen2 for 
14131             // having to expand heap in ephemeral collections.
14132             if (dt_high_frag_p (tuning_deciding_condemned_gen, 
14133                                 max_generation - 1, 
14134                                 TRUE))
14135             {
14136                 high_fragmentation = TRUE;
14137                 local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
14138                 dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
14139             }
14140         }
14141     }
14142
14143     //figure out which ephemeral generation is too fragramented
14144     temp_gen = n;
14145     for (i = n+1; i < max_generation; i++)
14146     {
14147         if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
14148         {
14149             dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
14150             n = i;
14151         }
14152         else
14153             break;
14154     }
14155
14156     if (low_ephemeral_space)
14157     {
14158         //enable promotion
14159         local_settings->promotion = TRUE;
14160     }
14161
14162     if (n > temp_gen)
14163     {
14164         local_condemn_reasons->set_condition (gen_eph_high_frag_p);
14165     }
14166
14167     if (!check_only_p)
14168     {
14169         if (settings.pause_mode == pause_low_latency)
14170         {
14171             if (!is_induced (settings.reason))
14172             {
14173                 n = min (n, max_generation - 1);
14174                 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
14175                 evaluate_elevation = FALSE;
14176                 goto exit;
14177             }
14178         }
14179     }
14180
14181     // It's hard to catch when we get to the point that the memory load is so high
14182     // we get an induced GC from the finalizer thread so we are checking the memory load
14183     // for every gen0 GC.
14184     check_memory = (check_only_p ? 
14185                     (n >= 0) : 
14186                     ((n >= 1) || low_memory_detected));
14187
14188     if (check_memory)
14189     {
14190         //find out if we are short on memory
14191         GetProcessMemoryLoad(&ms);
14192         if (heap_number == 0)
14193         {
14194             dprintf (GTC_LOG, ("ml: %d", ms.dwMemoryLoad));
14195         }
14196
14197 #ifdef _WIN64
14198         if (heap_number == 0)
14199         {
14200             available_physical_mem = ms.ullAvailPhys;
14201             local_settings->entry_memory_load = ms.dwMemoryLoad;
14202         }
14203 #endif //_WIN64
14204         
14205         // @TODO: Force compaction more often under GCSTRESS
14206         if (ms.dwMemoryLoad >= 90 || low_memory_detected)
14207         {
14208 #ifdef SIMPLE_DPRINTF
14209             // stress log can't handle any parameter that's bigger than a void*.
14210             if (heap_number == 0)
14211             {
14212                 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d, tp: %I64d, ap: %I64d", 
14213                     ms.ullTotalPhys, ms.ullAvailPhys, ms.ullTotalPageFile, ms.ullAvailPageFile));
14214             }
14215 #endif //SIMPLE_DPRINTF
14216
14217             high_memory_load = TRUE;
14218
14219             if (ms.dwMemoryLoad >= 97 || low_memory_detected)
14220             {
14221                 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
14222                 // gen1/gen0 may take a lot more memory than gen2.
14223                 if (!high_fragmentation)
14224                 {
14225                     high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation, ms.ullTotalPhys);
14226                 }
14227                 v_high_memory_load = TRUE;
14228             }
14229             else
14230             {
14231                 if (!high_fragmentation)
14232                 {
14233                     high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, ms.ullAvailPhys);
14234                 }
14235             }
14236
14237             if (high_fragmentation)
14238             {
14239                 if (high_memory_load)
14240                 {
14241                     local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
14242                 }
14243                 else if (v_high_memory_load)
14244                 {
14245                     local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
14246                 }
14247             }
14248         }
14249     }
14250
14251     dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
14252                  heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
14253                  high_fragmentation));
14254
14255     if (should_expand_in_full_gc)
14256     {
14257         dprintf (GTC_LOG, ("h%d: expand_in_full", heap_number));
14258         *blocking_collection_p = TRUE;
14259         if (!check_only_p)
14260         {
14261             should_expand_in_full_gc = FALSE;
14262         }
14263         evaluate_elevation = FALSE;
14264         n = max_generation;
14265         local_condemn_reasons->set_condition (gen_expand_fullgc_p);
14266     }
14267
14268     if (last_gc_before_oom)
14269     {
14270         dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
14271         n = max_generation;
14272         *blocking_collection_p = TRUE;
14273
14274         local_condemn_reasons->set_condition (gen_before_oom);
14275     }
14276
14277     if (!check_only_p)
14278     {
14279         if (is_induced_blocking (settings.reason) && 
14280             n_initial == max_generation
14281             IN_STRESS_HEAP( && !settings.stress_induced ))
14282         {
14283             if (heap_number == 0)
14284             {
14285                 dprintf (GTC_LOG, ("induced - BLOCK"));
14286             }
14287
14288             *blocking_collection_p = TRUE;
14289             local_condemn_reasons->set_condition (gen_induced_fullgc_p);
14290             evaluate_elevation = FALSE;
14291         }
14292
14293         if (settings.reason == reason_induced_noforce)
14294         {
14295             local_condemn_reasons->set_condition (gen_induced_noforce_p);
14296             evaluate_elevation = FALSE;
14297         }
14298     }
14299
14300     if (evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
14301     {
14302         *elevation_requested_p = TRUE;
14303 #ifdef _WIN64
14304         // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
14305         if (high_memory_load || v_high_memory_load)
14306         {
14307             dynamic_data* dd_max = dynamic_data_of (max_generation);
14308             if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
14309             {
14310                 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)", 
14311                     dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
14312                 n = max_generation;
14313             }
14314         }
14315
14316         if (n <= max_generation)
14317         {
14318 #endif //_WIN64
14319             if (high_fragmentation)
14320             {
14321                 //elevate to max_generation
14322                 n = max_generation;
14323                 dprintf (GTC_LOG, ("h%d: f full", heap_number));
14324
14325 #ifdef BACKGROUND_GC
14326                 if (high_memory_load || v_high_memory_load)
14327                 {
14328                     // For background GC we want to do blocking collections more eagerly because we don't
14329                     // want to get into the situation where the memory load becomes high while we are in
14330                     // a background GC and we'd have to wait for the background GC to finish to start
14331                     // a blocking collection (right now the implemenation doesn't handle converting 
14332                     // a background GC to a blocking collection midway.
14333                     dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
14334                     *blocking_collection_p = TRUE;
14335                 }
14336 #else
14337                 if (v_high_memory_load)
14338                 {
14339                     dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
14340                     *blocking_collection_p = TRUE;
14341                 }
14342 #endif //BACKGROUND_GC
14343             }
14344             else
14345             {
14346                 n = max (n, max_generation - 1);
14347                 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
14348             }
14349 #ifdef _WIN64
14350         }
14351 #endif //_WIN64
14352     }
14353
14354     if ((n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
14355     {
14356         dprintf (GTC_LOG, ("h%d: budget %d, check 2",
14357                       heap_number, n_alloc));
14358         if (get_new_allocation (max_generation) <= 0)
14359         {
14360             dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
14361             n = max_generation;
14362             local_condemn_reasons->set_condition (gen_max_gen1);
14363         }
14364     }
14365
14366     //figure out if max_generation is too fragmented -> blocking collection
14367     if (n == max_generation)
14368     {
14369         if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
14370         {
14371             dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
14372             local_condemn_reasons->set_condition (gen_max_high_frag_p);
14373             if (local_settings->pause_mode != pause_sustained_low_latency)
14374             {
14375                 *blocking_collection_p = TRUE;
14376             }
14377         }
14378     }
14379
14380 #ifdef BACKGROUND_GC
14381     if (n == max_generation)
14382     {
14383         if (heap_number == 0)
14384         {
14385             BOOL bgc_heap_too_small = TRUE;
14386             size_t gen2size = 0;
14387             size_t gen3size = 0;
14388 #ifdef MULTIPLE_HEAPS
14389             for (int i = 0; i < n_heaps; i++)
14390             {
14391                 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) || 
14392                     ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
14393                 {
14394                     bgc_heap_too_small = FALSE;
14395                     break;
14396                 }
14397             }
14398 #else //MULTIPLE_HEAPS
14399             if ((current_generation_size (max_generation) > bgc_min_per_heap) || 
14400                 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
14401             {
14402                 bgc_heap_too_small = FALSE;
14403             }            
14404 #endif //MULTIPLE_HEAPS
14405
14406             if (bgc_heap_too_small)
14407             {
14408                 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
14409
14410 #ifdef STRESS_HEAP
14411                 // do not turn stress-induced collections into blocking GCs
14412                 if (!settings.stress_induced)
14413 #endif //STRESS_HEAP
14414                 {
14415                     *blocking_collection_p = TRUE;
14416                 }
14417
14418                 local_condemn_reasons->set_condition (gen_gen2_too_small);
14419             }
14420         }
14421     }
14422 #endif //BACKGROUND_GC
14423
14424 exit:
14425     if (!check_only_p)
14426     {
14427 #ifdef STRESS_HEAP
14428         // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14429         // generations to be collected,
14430
14431         if (orig_gen != max_generation &&
14432             g_pConfig->GetGCStressLevel() && g_pConfig->GetGCconcurrent())
14433         {
14434             *elevation_requested_p = FALSE;
14435         }
14436 #endif //STRESS_HEAP
14437
14438         if (check_memory)
14439         {
14440             gc_data_per_heap.mem_pressure = ms.dwMemoryLoad;
14441             fgm_result.available_pagefile_mb = (size_t)(ms.ullAvailPageFile / (1024 * 1024));
14442         }
14443
14444         local_condemn_reasons->set_gen (gen_final_per_heap, n);
14445         gc_data_per_heap.gen_to_condemn_reasons.init (local_condemn_reasons);
14446
14447 #ifdef DT_LOG
14448         local_condemn_reasons->print (heap_number);
14449 #endif //DT_LOG
14450
14451         if ((local_settings->reason == reason_oos_soh) || 
14452             (local_settings->reason == reason_oos_loh))
14453         {
14454             assert (n >= 1);
14455         }
14456     }
14457
14458 #ifndef FEATURE_REDHAWK
14459     if (n == max_generation)
14460     {
14461         if (SystemDomain::System()->RequireAppDomainCleanup())
14462         {
14463 #ifdef BACKGROUND_GC
14464             // do not turn stress-induced collections into blocking GCs, unless there
14465             // have already been more full BGCs than full NGCs
14466 #if 0
14467             // This exposes DevDiv 94129, so we'll leave this out for now
14468             if (!settings.stress_induced || 
14469                 full_gc_counts[gc_type_blocking] <= full_gc_counts[gc_type_background])
14470 #endif // 0
14471 #endif // BACKGROUND_GC
14472             {
14473                 *blocking_collection_p = TRUE;
14474             }
14475         }
14476     }
14477 #endif //!FEATURE_REDHAWK
14478
14479     return n;
14480 }
14481
14482 #ifdef _PREFAST_
14483 #pragma warning(pop)
14484 #endif //_PREFAST_
14485
14486 inline
14487 size_t gc_heap::min_reclaim_fragmentation_threshold(ULONGLONG total_mem, DWORD num_heaps)
14488 {
14489      return min ((size_t)((float)total_mem * 0.03), (100*1024*1024)) / num_heaps;
14490 }
14491
14492 inline
14493 ULONGLONG gc_heap::min_high_fragmentation_threshold(ULONGLONG available_mem, DWORD num_heaps)
14494 {
14495     return min (available_mem, (256*1024*1024)) / num_heaps;
14496 }
14497
14498 enum {
14499 CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
14500 };
14501
14502
14503 #ifdef BACKGROUND_GC
14504 void gc_heap::init_background_gc ()
14505 {
14506     //reset the allocation so foreground gc can allocate into older (max_generation) generation
14507     generation* gen = generation_of (max_generation);
14508     generation_allocation_pointer (gen)= 0;
14509     generation_allocation_limit (gen) = 0;
14510     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
14511
14512     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
14513
14514     //reset the plan allocation for each segment
14515     for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
14516         seg = heap_segment_next_rw (seg))
14517     {
14518         heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
14519     }
14520
14521     if (heap_number == 0)
14522     {
14523         dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix", 
14524             heap_number,
14525             background_saved_lowest_address, 
14526             background_saved_highest_address));
14527     }
14528
14529     gc_lh_block_event.Reset();
14530 }
14531
14532 #endif //BACKGROUND_GC
14533
14534 #define fire_bgc_event(x) { FireEtw##x(GetClrInstanceId()); }
14535
14536 inline
14537 void fire_drain_mark_list_event (size_t mark_list_objects)
14538 {
14539     FireEtwBGCDrainMark (mark_list_objects, GetClrInstanceId());
14540 }
14541
14542 inline
14543 void fire_revisit_event (size_t dirtied_pages, 
14544                          size_t marked_objects,
14545                          BOOL large_objects_p)
14546 {
14547     FireEtwBGCRevisit (dirtied_pages, marked_objects, large_objects_p, GetClrInstanceId());
14548 }
14549
14550 inline
14551 void fire_overflow_event (BYTE* overflow_min,
14552                           BYTE* overflow_max,
14553                           size_t marked_objects, 
14554                           int large_objects_p)
14555 {
14556     FireEtwBGCOverflow ((UINT64)overflow_min, (UINT64)overflow_max, 
14557                         marked_objects, large_objects_p, 
14558                         GetClrInstanceId());
14559 }
14560
14561 void gc_heap::concurrent_print_time_delta (const char* msg)
14562 {
14563 #ifdef TRACE_GC
14564     LARGE_INTEGER ts;
14565     QueryPerformanceCounter (&ts);
14566     
14567     size_t current_time = (size_t) (ts.QuadPart/(qpf.QuadPart/1000));
14568     size_t elapsed_time = current_time - time_bgc_last;
14569     time_bgc_last = current_time;
14570
14571     dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
14572 #endif //TRACE_GC
14573 }
14574
14575 void gc_heap::free_list_info (int gen_num, const char* msg)
14576 {
14577 #if defined (BACKGROUND_GC) && defined (TRACE_GC)
14578     dprintf (3, ("h%d: %s", heap_number, msg));
14579     for (int i = 0; i <= (max_generation + 1); i++)
14580     {
14581         generation* gen = generation_of (i);
14582         if ((generation_allocation_size (gen) == 0) && 
14583             (generation_free_list_space (gen) == 0) && 
14584             (generation_free_obj_space (gen) == 0))
14585         {
14586             // don't print if everything is 0.
14587         }
14588         else
14589         {
14590             dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
14591                 heap_number, i, 
14592                 generation_allocation_size (gen), 
14593                 generation_free_list_space (gen), 
14594                 generation_free_obj_space (gen)));
14595         }
14596     }
14597 #endif // BACKGROUND_GC && TRACE_GC
14598 }
14599
14600 //internal part of gc used by the serial and concurrent version
14601 void gc_heap::gc1()
14602 {
14603 #ifdef BACKGROUND_GC
14604     assert (settings.concurrent == (DWORD)(GetCurrentThreadId() == bgc_thread_id));
14605 #endif //BACKGROUND_GC
14606
14607 #ifdef TIME_GC
14608     mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
14609 #endif //TIME_GC
14610
14611     verify_soh_segment_list();
14612
14613     int n = settings.condemned_generation;
14614
14615     update_collection_counts ();
14616
14617 #ifdef BACKGROUND_GC
14618     bgc_alloc_lock->check();
14619 #endif //BACKGROUND_GC
14620
14621     free_list_info (max_generation, "beginning");
14622
14623     vm_heap->GcCondemnedGeneration = settings.condemned_generation;
14624
14625     assert (g_card_table == card_table);
14626
14627     {
14628         if (n == max_generation)
14629         {
14630             gc_low = lowest_address;
14631             gc_high = highest_address;
14632         }
14633         else
14634         {
14635             gc_low = generation_allocation_start (generation_of (n));
14636             gc_high = heap_segment_reserved (ephemeral_heap_segment);
14637         }   
14638 #ifdef BACKGROUND_GC
14639         if (settings.concurrent)
14640         {
14641 #ifdef TRACE_GC
14642             LARGE_INTEGER ts;
14643             QueryPerformanceCounter (&ts);
14644
14645             time_bgc_last = (size_t)(ts.QuadPart/(qpf.QuadPart/1000));
14646 #endif //TRACE_GC
14647
14648             fire_bgc_event (BGCBegin);
14649
14650             concurrent_print_time_delta ("BGC");
14651
14652 //#ifdef WRITE_WATCH
14653             //reset_write_watch (FALSE);
14654 //#endif //WRITE_WATCH
14655
14656             concurrent_print_time_delta ("RW");
14657             background_mark_phase();
14658             free_list_info (max_generation, "after mark phase");
14659             
14660             background_sweep();
14661             free_list_info (max_generation, "after sweep phase");
14662         }
14663         else
14664 #endif //BACKGROUND_GC
14665         {
14666             mark_phase (n, FALSE);
14667
14668             CNameSpace::GcRuntimeStructuresValid (FALSE);
14669             plan_phase (n);
14670             CNameSpace::GcRuntimeStructuresValid (TRUE);
14671         }
14672     }
14673
14674     LARGE_INTEGER ts;
14675     if (!QueryPerformanceCounter(&ts))
14676         FATAL_GC_ERROR();
14677     
14678     size_t end_gc_time = (size_t) (ts.QuadPart/(qpf.QuadPart/1000));
14679 //    printf ("generation: %d, elapsed time: %Id\n", n,  end_gc_time - dd_time_clock (dynamic_data_of (0)));
14680
14681     //adjust the allocation size from the pinned quantities. 
14682     for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
14683     {
14684         generation* gn = generation_of (gen_number);
14685         if (settings.compaction)
14686         {
14687             generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
14688             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
14689         }
14690         else
14691         {
14692             generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
14693             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
14694         }
14695         generation_pinned_allocation_sweep_size (gn) = 0;
14696         generation_pinned_allocation_compact_size (gn) = 0;
14697     }
14698
14699 #ifdef BACKGROUND_GC
14700     if (settings.concurrent)
14701     {
14702         dynamic_data* dd = dynamic_data_of (n);
14703         dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
14704
14705         free_list_info (max_generation, "after computing new dynamic data");
14706
14707         gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14708
14709         for (int gen_number = 0; gen_number < max_generation; gen_number++)
14710         {
14711             dprintf (2, ("end of BGC: gen%d new_alloc: %Id", 
14712                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
14713             current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
14714             current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
14715             current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
14716         }
14717     }
14718     else
14719 #endif //BACKGROUND_GC
14720     {
14721         free_list_info (max_generation, "end");
14722         for (int gen_number = 0; gen_number <= n; gen_number++)
14723         {
14724             dynamic_data* dd = dynamic_data_of (gen_number);
14725             dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
14726             compute_new_dynamic_data (gen_number);
14727         }
14728
14729         if (n != max_generation)
14730         {
14731             for (int gen_number = (n+1); gen_number <= (max_generation+1); gen_number++)
14732             {
14733                 gc_data_per_heap.gen_data[gen_number].size_after = generation_size (gen_number);
14734                 gc_data_per_heap.gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
14735                 gc_data_per_heap.gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
14736             }
14737         }
14738
14739         free_list_info (max_generation, "after computing new dynamic data");
14740         
14741         if (heap_number == 0)
14742         {
14743             dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms", 
14744                 dd_collection_count (dynamic_data_of (0)), 
14745                 settings.condemned_generation,
14746                 dd_gc_elapsed_time (dynamic_data_of (0))));
14747        }
14748
14749         for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14750         {
14751             dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id", 
14752                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
14753         }
14754     }
14755
14756     if (n < max_generation)
14757     {
14758         compute_promoted_allocation (1 + n);
14759         dynamic_data* dd = dynamic_data_of (1 + n);
14760         size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) + 
14761                                    generation_free_obj_space (generation_of (1 + n));
14762
14763 #ifdef BACKGROUND_GC
14764         if (current_c_gc_state != c_gc_state_planning)
14765 #endif //BACKGROUND_GC
14766         {
14767             if (settings.promotion)
14768             {
14769                 dd_fragmentation (dd) = new_fragmentation;
14770             }
14771             else
14772             {
14773                 //assert (dd_fragmentation (dd) == new_fragmentation);
14774             }
14775         }
14776     }
14777
14778 #ifdef BACKGROUND_GC
14779     if (!settings.concurrent)
14780 #endif //BACKGROUND_GC
14781     {
14782         adjust_ephemeral_limits();
14783     }
14784
14785 #ifdef BACKGROUND_GC
14786     assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
14787     assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
14788 #endif //BACKGROUND_GC
14789
14790     int bottom_gen = 0;
14791 #ifdef BACKGROUND_GC
14792     if (settings.concurrent) 
14793     {
14794         bottom_gen = max_generation;
14795     }
14796 #endif //BACKGROUND_GC
14797     {
14798         for (int gen_number = bottom_gen; gen_number <= max_generation+1; gen_number++)
14799         {
14800             dynamic_data* dd = dynamic_data_of (gen_number);
14801             dd_new_allocation(dd) = dd_gc_new_allocation (dd);
14802         }
14803     }
14804
14805     if (fgn_maxgen_percent)
14806     {
14807         if (settings.condemned_generation == (max_generation - 1))
14808         {
14809             check_for_full_gc (max_generation - 1, 0);
14810         }
14811         else if (settings.condemned_generation == max_generation)
14812         {
14813             if (full_gc_approach_event_set 
14814 #ifdef MULTIPLE_HEAPS
14815                 && (heap_number == 0)
14816 #endif //MULTIPLE_HEAPS
14817                 )
14818             {
14819                 dprintf (2, ("FGN-GC: setting gen2 end event"));
14820
14821                 full_gc_approach_event.Reset();
14822 #ifdef BACKGROUND_GC
14823                 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
14824                 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
14825 #endif //BACKGROUND_GC
14826                 full_gc_end_event.Set();
14827                 full_gc_approach_event_set = false;            
14828             }
14829         }
14830     }
14831
14832 #ifdef BACKGROUND_GC
14833     if (!settings.concurrent)
14834 #endif //BACKGROUND_GC
14835     {
14836         //decide on the next allocation quantum
14837         if (alloc_contexts_used >= 1)
14838         {
14839             allocation_quantum = Align (min ((size_t)CLR_SIZE,
14840                                             (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
14841                                             get_alignment_constant(FALSE));
14842             dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
14843         }
14844     }
14845 #ifdef NO_WRITE_BARRIER
14846     reset_write_watch(FALSE);
14847 #endif //NO_WRITE_BARRIER
14848
14849     descr_generations (FALSE);
14850     descr_card_table();
14851
14852     verify_soh_segment_list();
14853
14854 #ifdef BACKGROUND_GC
14855     add_to_history_per_heap();
14856     if (heap_number == 0)
14857     {
14858         add_to_history();
14859     }
14860 #endif // BACKGROUND_GC
14861
14862 #ifdef GC_STATS
14863     if (GCStatistics::Enabled() && heap_number == 0)
14864         g_GCStatistics.AddGCStats(settings, 
14865             dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
14866 #endif // GC_STATS
14867
14868 #ifdef TIME_GC
14869     fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
14870              n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
14871 #endif //TIME_GC
14872
14873 #ifdef BACKGROUND_GC
14874     assert (settings.concurrent == (DWORD)(GetCurrentThreadId() == bgc_thread_id));
14875 #endif //BACKGROUND_GC
14876
14877 #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
14878     if (FALSE 
14879 #ifdef VERIFY_HEAP
14880         || (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
14881 #endif
14882 #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
14883         || (ETW::GCLog::ShouldTrackMovementForEtw() && settings.concurrent)
14884 #endif
14885         )
14886     {
14887 #ifdef BACKGROUND_GC
14888         Thread* current_thread = GetThread();
14889         BOOL cooperative_mode = TRUE;
14890
14891         if (settings.concurrent)
14892         {
14893             cooperative_mode = enable_preemptive (current_thread);
14894
14895 #ifdef MULTIPLE_HEAPS
14896             bgc_t_join.join(this, gc_join_suspend_ee_verify);
14897             if (bgc_t_join.joined())
14898             {
14899                 bgc_threads_sync_event.Reset();
14900
14901                 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
14902                 bgc_t_join.restart();
14903             }
14904             if (heap_number == 0)
14905             {
14906                 suspend_EE();
14907                 bgc_threads_sync_event.Set();
14908             }
14909             else
14910             {
14911                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
14912                 dprintf (2, ("bgc_threads_sync_event is signalled"));
14913             }
14914 #else
14915             suspend_EE();
14916 #endif //MULTIPLE_HEAPS
14917
14918             //fix the allocation area so verify_heap can proceed.
14919             fix_allocation_contexts (FALSE);
14920         }
14921 #endif //BACKGROUND_GC
14922
14923 #ifdef BACKGROUND_GC
14924         assert (settings.concurrent == (DWORD)(GetCurrentThreadId() == bgc_thread_id));
14925 #ifdef FEATURE_EVENT_TRACE
14926         if (ETW::GCLog::ShouldTrackMovementForEtw() && settings.concurrent)
14927         {
14928             make_free_lists_for_profiler_for_bgc();
14929         }
14930 #endif // FEATURE_EVENT_TRACE
14931 #endif //BACKGROUND_GC
14932
14933 #ifdef VERIFY_HEAP
14934         if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
14935             verify_heap (FALSE);
14936 #endif // VERIFY_HEAP
14937
14938 #ifdef BACKGROUND_GC
14939         if (settings.concurrent)
14940         {
14941             repair_allocation_contexts (TRUE);
14942
14943 #ifdef MULTIPLE_HEAPS
14944             bgc_t_join.join(this, gc_join_restart_ee_verify);
14945             if (bgc_t_join.joined())
14946             {
14947                 bgc_threads_sync_event.Reset();
14948
14949                 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
14950                 bgc_t_join.restart();
14951             }
14952             if (heap_number == 0)
14953             {
14954                 restart_EE();
14955                 bgc_threads_sync_event.Set();
14956             }
14957             else
14958             {
14959                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
14960                 dprintf (2, ("bgc_threads_sync_event is signalled"));
14961             }
14962 #else
14963             restart_EE();
14964 #endif //MULTIPLE_HEAPS
14965
14966             disable_preemptive (current_thread, cooperative_mode);
14967         }
14968 #endif //BACKGROUND_GC
14969     }
14970 #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
14971
14972 #ifdef MULTIPLE_HEAPS
14973     if (!settings.concurrent)
14974     {
14975         gc_t_join.join(this, gc_join_done);
14976         if (gc_t_join.joined ())
14977         {
14978             gc_heap::internal_gc_done = false;
14979
14980             //equalize the new desired size of the generations
14981             int limit = settings.condemned_generation;
14982             if (limit == max_generation)
14983             {
14984                 limit = max_generation+1;
14985             }
14986             for (int gen = 0; gen <= limit; gen++)
14987             {
14988                 size_t total_desired = 0;
14989
14990                 for (int i = 0; i < gc_heap::n_heaps; i++)
14991                 {
14992                     gc_heap* hp = gc_heap::g_heaps[i];
14993                     dynamic_data* dd = hp->dynamic_data_of (gen);
14994                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
14995                     if (temp_total_desired < total_desired)
14996                     {
14997                         // we overflowed.
14998                         total_desired = (size_t)MAX_PTR;
14999                         break;
15000                     }
15001                     total_desired = temp_total_desired;
15002                 }
15003
15004                 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
15005                                                     get_alignment_constant ((gen != (max_generation+1))));
15006
15007                 if (gen == 0)
15008                 {
15009 #if 1 //subsumed by the linear allocation model 
15010                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15011                     // apply some smoothing.
15012                     static size_t smoothed_desired_per_heap = 0;
15013                     size_t smoothing = 3; // exponential smoothing factor
15014                     if (smoothing  > VolatileLoad(&settings.gc_index))
15015                         smoothing  = VolatileLoad(&settings.gc_index);
15016                     smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
15017                     dprintf (1, ("sn = %Id  n = %Id", smoothed_desired_per_heap, desired_per_heap));
15018                     desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
15019 #endif //0
15020
15021                     // if desired_per_heap is close to min_gc_size, trim it
15022                     // down to min_gc_size to stay in the cache
15023                     gc_heap* hp = gc_heap::g_heaps[0];
15024                     dynamic_data* dd = hp->dynamic_data_of (gen);
15025                     size_t min_gc_size = dd_min_gc_size(dd);
15026                     // if min GC size larger than true on die cache, then don't bother
15027                     // limiting the desired size
15028                     if ((min_gc_size <= GetLargestOnDieCacheSize(TRUE) / GetLogicalCpuCount()) &&
15029                         desired_per_heap <= 2*min_gc_size)
15030                     {
15031                         desired_per_heap = min_gc_size;
15032                     }
15033 #ifdef _WIN64
15034                     desired_per_heap = joined_youngest_desired (desired_per_heap);
15035                     dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
15036 #endif //_WIN64
15037
15038                     gc_data_global.final_youngest_desired = desired_per_heap;
15039                 }
15040 #if 1 //subsumed by the linear allocation model 
15041                 if (gen == (max_generation + 1))
15042                 {
15043                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15044                     // apply some smoothing.
15045                     static size_t smoothed_desired_per_heap_loh = 0;
15046                     size_t smoothing = 3; // exponential smoothing factor
15047                     size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
15048                     if (smoothing  > loh_count)
15049                         smoothing  = loh_count;
15050                     smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
15051                     dprintf( 2, ("smoothed_desired_per_heap_loh  = %Id  desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
15052                     desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
15053                 }
15054 #endif //0
15055                 for (int i = 0; i < gc_heap::n_heaps; i++)
15056                 {
15057                     gc_heap* hp = gc_heap::g_heaps[i];
15058                     dynamic_data* dd = hp->dynamic_data_of (gen);
15059                     dd_desired_allocation (dd) = desired_per_heap;
15060                     dd_gc_new_allocation (dd) = desired_per_heap;
15061                     dd_new_allocation (dd) = desired_per_heap;
15062
15063                     if (gen == 0)
15064                     {
15065                         hp->fgn_last_alloc = desired_per_heap;
15066                     }
15067                 }
15068             }
15069
15070 #ifdef FEATURE_LOH_COMPACTION
15071             BOOL all_heaps_compacted_p = TRUE;
15072 #endif //FEATURE_LOH_COMPACTION
15073             for (int i = 0; i < gc_heap::n_heaps; i++)
15074             {
15075                 gc_heap* hp = gc_heap::g_heaps[i];
15076                 hp->decommit_ephemeral_segment_pages();
15077                 hp->rearrange_large_heap_segments();
15078 #ifdef FEATURE_LOH_COMPACTION
15079                 all_heaps_compacted_p &= hp->loh_compacted_p;
15080 #endif //FEATURE_LOH_COMPACTION
15081             }
15082
15083 #ifdef FEATURE_LOH_COMPACTION
15084             check_loh_compact_mode (all_heaps_compacted_p);
15085 #endif //FEATURE_LOH_COMPACTION
15086
15087             fire_pevents();
15088
15089             gc_t_join.restart();
15090         }
15091         alloc_context_count = 0;
15092         heap_select::mark_heap (heap_number);
15093     }
15094
15095 #else
15096     gc_data_global.final_youngest_desired = 
15097         dd_desired_allocation (dynamic_data_of (0));
15098
15099     check_loh_compact_mode (loh_compacted_p);
15100
15101     decommit_ephemeral_segment_pages();
15102     fire_pevents();
15103
15104     if (!(settings.concurrent))
15105     {
15106         rearrange_large_heap_segments();
15107         do_post_gc();
15108     }
15109
15110 #ifdef BACKGROUND_GC
15111     recover_bgc_settings();
15112 #endif //BACKGROUND_GC
15113 #endif //MULTIPLE_HEAPS
15114 }
15115
15116 //update counters
15117 void gc_heap::update_collection_counts ()
15118 {
15119     dynamic_data* dd0 = dynamic_data_of (0);
15120     dd_gc_clock (dd0) += 1;
15121
15122     LARGE_INTEGER ts;
15123     if (!QueryPerformanceCounter (&ts))
15124         FATAL_GC_ERROR();
15125     
15126     size_t now = (size_t)(ts.QuadPart/(qpf.QuadPart/1000));
15127
15128     for (int i = 0; i <= settings.condemned_generation;i++)
15129     {
15130         dynamic_data* dd = dynamic_data_of (i);
15131         dd_collection_count (dd)++;
15132         //this is needed by the linear allocation model
15133         if (i == max_generation)
15134             dd_collection_count (dynamic_data_of (max_generation+1))++;
15135         dd_gc_clock (dd) = dd_gc_clock (dd0);
15136         dd_time_clock (dd) = now;
15137     }
15138 }
15139
15140 #ifdef HEAP_ANALYZE
15141 inline
15142 BOOL AnalyzeSurvivorsRequested(int condemnedGeneration)
15143 {
15144     // Is the list active?
15145     GcNotifications gn(g_pGcNotificationTable);
15146     if (gn.IsActive())
15147     {
15148         GcEvtArgs gea = { GC_MARK_END, { (1<<condemnedGeneration) } };
15149         if (gn.GetNotification(gea) != 0)
15150         {
15151             return TRUE;
15152         }
15153     }
15154     return FALSE;
15155 }
15156
15157 void DACNotifyGcMarkEnd(int condemnedGeneration)
15158 {
15159     // Is the list active?
15160     GcNotifications gn(g_pGcNotificationTable);
15161     if (gn.IsActive())
15162     {
15163         GcEvtArgs gea = { GC_MARK_END, { (1<<condemnedGeneration) } };
15164         if (gn.GetNotification(gea) != 0)
15165         {
15166             DACNotify::DoGCNotification(gea);
15167         }
15168     }
15169 }
15170 #endif // HEAP_ANALYZE
15171
15172 int gc_heap::garbage_collect (int n)
15173 {
15174     //TODO BACKGROUND_GC remove these when ready
15175 #ifndef NO_CATCH_HANDLERS
15176     PAL_TRY
15177 #endif //NO_CATCH_HANDLERS
15178     {
15179         //reset the number of alloc contexts
15180         alloc_contexts_used = 0;
15181
15182         fix_allocation_contexts (TRUE);
15183 #ifdef MULTIPLE_HEAPS
15184         clear_gen0_bricks();
15185 #endif //MULTIPLE_HEAPS
15186
15187 #ifdef BACKGROUND_GC
15188         if (recursive_gc_sync::background_running_p())
15189         {
15190             save_bgc_data_per_heap();
15191         }
15192 #endif //BACKGROUND_GC
15193
15194         memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
15195         gc_data_per_heap.heap_index = heap_number;
15196         memset (&fgm_result, 0, sizeof (fgm_result));
15197         settings.reason = gc_trigger_reason;
15198         verify_pinned_queue_p = FALSE;
15199
15200 #ifdef STRESS_HEAP
15201         if (settings.reason == reason_gcstress)
15202         {
15203             settings.reason = reason_induced;
15204             settings.stress_induced = TRUE;
15205         }
15206 #endif // STRESS_HEAP
15207
15208 #ifdef MULTIPLE_HEAPS
15209         //align all heaps on the max generation to condemn
15210         dprintf (3, ("Joining for max generation to condemn"));
15211         condemned_generation_num = generation_to_condemn (n, 
15212                                                           &blocking_collection, 
15213                                                           &elevation_requested, 
15214                                                           FALSE);
15215         gc_t_join.join(this, gc_join_generation_determined);
15216         if (gc_t_join.joined())
15217 #endif //MULTIPLE_HEAPS
15218         {
15219 #ifdef TRACE_GC
15220             int gc_count = (int)dd_collection_count (dynamic_data_of (0));
15221             if (gc_count >= g_pConfig->GetGCtraceStart())
15222                 trace_gc = 1;
15223             if (gc_count >=  g_pConfig->GetGCtraceEnd())
15224                 trace_gc = 0;
15225 #endif //TRACE_GC
15226
15227 #ifdef MULTIPLE_HEAPS
15228 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
15229             //delete old slots from the segment table
15230             seg_table->delete_old_slots();
15231 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
15232             for (int i = 0; i < n_heaps; i++)
15233             {
15234                 //copy the card and brick tables
15235                 if (g_card_table != g_heaps[i]->card_table)
15236                 {
15237                     g_heaps[i]->copy_brick_card_table (FALSE);
15238                 }
15239
15240                 g_heaps[i]->rearrange_large_heap_segments();
15241                 if (!recursive_gc_sync::background_running_p())
15242                 {
15243                     g_heaps[i]->rearrange_small_heap_segments();
15244                 }
15245             }
15246 #else //MULTIPLE_HEAPS
15247 #ifdef BACKGROUND_GC
15248                 //delete old slots from the segment table
15249 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
15250                 seg_table->delete_old_slots();
15251 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
15252                 rearrange_large_heap_segments();
15253                 if (!recursive_gc_sync::background_running_p())
15254                 {
15255                     rearrange_small_heap_segments();
15256                 }
15257 #endif //BACKGROUND_GC
15258             // check for card table growth
15259             if (g_card_table != card_table)
15260                 copy_brick_card_table (FALSE);
15261
15262 #endif //MULTIPLE_HEAPS
15263
15264         BOOL should_evaluate_elevation = FALSE;
15265         BOOL should_do_blocking_collection = FALSE;
15266
15267 #ifdef MULTIPLE_HEAPS
15268         int gen_max = condemned_generation_num;
15269         for (int i = 0; i < n_heaps; i++)
15270         {
15271             if (gen_max < g_heaps[i]->condemned_generation_num)
15272                 gen_max = g_heaps[i]->condemned_generation_num;
15273             if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested))
15274                 should_evaluate_elevation = TRUE;
15275             if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
15276                 should_do_blocking_collection = TRUE;
15277         }
15278
15279         settings.condemned_generation = gen_max;
15280 //logically continues after GC_PROFILING.
15281 #else //MULTIPLE_HEAPS
15282         settings.condemned_generation = generation_to_condemn (n, 
15283                                                                &blocking_collection, 
15284                                                                &elevation_requested, 
15285                                                                FALSE);
15286         should_evaluate_elevation = elevation_requested;
15287         should_do_blocking_collection = blocking_collection;
15288 #endif //MULTIPLE_HEAPS
15289
15290         settings.condemned_generation = joined_generation_to_condemn (
15291                                             should_evaluate_elevation, 
15292                                             settings.condemned_generation,
15293                                             &should_do_blocking_collection
15294                                             STRESS_HEAP_ARG(n)
15295                                             );
15296
15297         STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, 
15298                 "condemned generation num: %d\n", settings.condemned_generation);
15299
15300         if (settings.condemned_generation > 1)
15301             settings.promotion = TRUE;
15302
15303 #ifdef HEAP_ANALYZE
15304         // At this point we've decided what generation is condemned
15305         // See if we've been requested to analyze survivors after the mark phase
15306         if (AnalyzeSurvivorsRequested(settings.condemned_generation))
15307         {
15308             heap_analyze_enabled = TRUE;
15309         }
15310 #endif // HEAP_ANALYZE
15311
15312 #ifdef GC_PROFILING
15313
15314             // If we're tracking GCs, then we need to walk the first generation
15315             // before collection to track how many items of each class has been
15316             // allocated.
15317             UpdateGenerationBounds();
15318             GarbageCollectionStartedCallback(settings.condemned_generation, settings.reason == reason_induced);
15319             {
15320                 BEGIN_PIN_PROFILER(CORProfilerTrackGC());
15321                 size_t profiling_context = 0;
15322
15323 #ifdef MULTIPLE_HEAPS
15324                 int hn = 0;
15325                 for (hn = 0; hn < gc_heap::n_heaps; hn++)
15326                 {
15327                     gc_heap* hp = gc_heap::g_heaps [hn];
15328
15329                     // When we're walking objects allocated by class, then we don't want to walk the large
15330                     // object heap because then it would count things that may have been around for a while.
15331                     hp->walk_heap (&AllocByClassHelper, (void *)&profiling_context, 0, FALSE);
15332                 }
15333 #else
15334                 // When we're walking objects allocated by class, then we don't want to walk the large
15335                 // object heap because then it would count things that may have been around for a while.
15336                 gc_heap::walk_heap (&AllocByClassHelper, (void *)&profiling_context, 0, FALSE);
15337 #endif //MULTIPLE_HEAPS
15338
15339                 // Notify that we've reached the end of the Gen 0 scan
15340                 g_profControlBlock.pProfInterface->EndAllocByClass(&profiling_context);
15341                 END_PIN_PROFILER();
15342             }
15343
15344 #endif // GC_PROFILING
15345
15346 #ifdef BACKGROUND_GC
15347             if ((settings.condemned_generation == max_generation) &&
15348                 (recursive_gc_sync::background_running_p()))
15349             {
15350                 //TODO BACKGROUND_GC If we just wait for the end of gc, it won't woork
15351                 // because we have to collect 0 and 1 properly
15352                 // in particular, the allocation contexts are gone.
15353                 // For now, it is simpler to collect max_generation-1
15354                 settings.condemned_generation = max_generation - 1;
15355                 dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
15356             }
15357
15358             if ((settings.condemned_generation == max_generation) &&
15359                 (should_do_blocking_collection == FALSE) &&
15360                 gc_can_use_concurrent &&
15361                 !temp_disable_concurrent_p &&                 
15362                 ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
15363             {
15364                 keep_bgc_threads_p = TRUE;
15365                 c_write (settings.concurrent,  TRUE);
15366             }
15367 #endif //BACKGROUND_GC
15368
15369             settings.gc_index = (DWORD)dd_collection_count (dynamic_data_of (0)) + 1;
15370
15371             // Call the EE for start of GC work
15372             // just one thread for MP GC
15373             GCToEEInterface::GcStartWork (settings.condemned_generation,
15374                                      max_generation);            
15375
15376             // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
15377             // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
15378             // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
15379             // fired in gc1.
15380             do_pre_gc();
15381
15382 #ifdef MULTIPLE_HEAPS
15383             gc_start_event.Reset();
15384             //start all threads on the roots.
15385             dprintf(3, ("Starting all gc threads for gc"));
15386             gc_t_join.restart();
15387 #endif //MULTIPLE_HEAPS
15388         }
15389
15390         for (int i = 0; i <= (max_generation+1); i++)
15391         {
15392             gc_data_per_heap.gen_data[i].size_before = generation_size (i);
15393             generation* gen = generation_of (i);
15394             gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
15395             gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
15396         }
15397
15398         descr_generations (TRUE);
15399 //    descr_card_table();
15400
15401 #ifdef NO_WRITE_BARRIER
15402         fix_card_table();
15403 #endif //NO_WRITE_BARRIER
15404
15405 #ifdef VERIFY_HEAP
15406         if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC) &&
15407            !(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_POST_GC_ONLY))
15408         {
15409             verify_heap (TRUE);
15410         }
15411         if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)
15412             checkGCWriteBarrier();
15413
15414 #endif // VERIFY_HEAP
15415
15416 #ifdef BACKGROUND_GC
15417         if (settings.concurrent)
15418         {
15419             // We need to save the settings because we'll need to restore it after each FGC.
15420             assert (settings.condemned_generation == max_generation);
15421             saved_bgc_settings = settings;
15422             bgc_data_saved_p = FALSE;
15423
15424 #ifdef MULTIPLE_HEAPS
15425             if (heap_number == 0)
15426             {
15427                 for (int i = 0; i < n_heaps; i++)
15428                 {
15429                     prepare_bgc_thread (g_heaps[i]);
15430                 }
15431                 dprintf (2, ("setting bgc_threads_sync_event"));
15432                 bgc_threads_sync_event.Set();
15433             }
15434             else
15435             {
15436                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15437                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15438             }
15439 #else
15440             prepare_bgc_thread(0);
15441 #endif //MULTIPLE_HEAPS
15442
15443 #ifdef MULTIPLE_HEAPS
15444             gc_t_join.join(this, gc_join_start_bgc);
15445             if (gc_t_join.joined())
15446 #endif //MULTIPLE_HEAPS
15447             {
15448                 do_concurrent_p = TRUE;
15449                 do_ephemeral_gc_p = FALSE;
15450 #ifdef MULTIPLE_HEAPS
15451                 dprintf(2, ("Joined to perform a background GC"));
15452
15453                 for (int i = 0; i < n_heaps; i++)
15454                 {
15455                     gc_heap* hp = g_heaps[i];
15456                     if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
15457                     {
15458                         do_concurrent_p = FALSE;
15459                         break;
15460                     }
15461                     else
15462                     {
15463                         hp->background_saved_lowest_address = hp->lowest_address;
15464                         hp->background_saved_highest_address = hp->highest_address;
15465                     }
15466                 }
15467 #else
15468                 do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
15469                 if (do_concurrent_p)
15470                 {
15471                     background_saved_lowest_address = lowest_address;
15472                     background_saved_highest_address = highest_address;
15473                 }
15474 #endif //MULTIPLE_HEAPS
15475
15476                 if (do_concurrent_p)
15477                 {
15478 #ifdef MULTIPLE_HEAPS
15479                     for (int i = 0; i < n_heaps; i++)
15480                         g_heaps[i]->current_bgc_state = bgc_initialized;
15481 #else
15482                     current_bgc_state = bgc_initialized;
15483 #endif //MULTIPLE_HEAPS
15484
15485                     int gen = check_for_ephemeral_alloc();
15486                     // always do a gen1 GC before we start BGC. 
15487                     // This is temporary for testing purpose.
15488                     //int gen = max_generation - 1;
15489                     dont_restart_ee_p = TRUE;
15490                     if (gen == -1)
15491                     {
15492                         // If we decide to not do a GC before the BGC we need to 
15493                         // restore the gen0 alloc context.
15494 #ifdef MULTIPLE_HEAPS
15495                         for (int i = 0; i < n_heaps; i++)
15496                         {
15497                             generation_allocation_pointer (g_heaps[i]->generation_of (0)) =  0;
15498                             generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
15499                         }
15500 #else
15501                         generation_allocation_pointer (youngest_generation) =  0;
15502                         generation_allocation_limit (youngest_generation) = 0;
15503 #endif //MULTIPLE_HEAPS
15504                     }
15505                     else
15506                     {
15507                         do_ephemeral_gc_p = TRUE;
15508
15509                         settings.init_mechanisms();
15510                         settings.condemned_generation = gen;
15511                         settings.gc_index = (SIZE_T)dd_collection_count (dynamic_data_of (0)) + 2;
15512                         do_pre_gc();
15513
15514                         // TODO BACKGROUND_GC need to add the profiling stuff here.
15515                         dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
15516                     }
15517
15518                     //clear the cards so they don't bleed in gen 1 during collection
15519                     // shouldn't this always be done at the beginning of any GC?
15520                     //clear_card_for_addresses (
15521                     //    generation_allocation_start (generation_of (0)),
15522                     //    heap_segment_allocated (ephemeral_heap_segment));
15523
15524                     if (!do_ephemeral_gc_p)
15525                     {
15526                         do_background_gc();
15527                     }
15528                 }
15529                 else
15530                 {
15531                     c_write (settings.concurrent, FALSE);
15532                 }
15533
15534 #ifdef MULTIPLE_HEAPS
15535                 gc_t_join.restart();
15536 #endif //MULTIPLE_HEAPS
15537             }
15538
15539             if (do_concurrent_p)
15540             {
15541                 if (do_ephemeral_gc_p)
15542                 {
15543                     dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
15544                     save_bgc_data_per_heap();
15545
15546                     gen_to_condemn_reasons.init();
15547                     gen_to_condemn_reasons.set_condition (gen_before_bgc);
15548                     gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
15549                     gc1();
15550 #ifdef MULTIPLE_HEAPS
15551                     gc_t_join.join(this, gc_join_bgc_after_ephemeral);
15552                     if (gc_t_join.joined())
15553 #endif //MULTIPLE_HEAPS
15554                     {
15555 #ifdef MULTIPLE_HEAPS
15556                         do_post_gc();
15557 #endif //MULTIPLE_HEAPS
15558                         settings = saved_bgc_settings;
15559                         assert (settings.concurrent);
15560
15561                         do_background_gc();
15562
15563 #ifdef MULTIPLE_HEAPS
15564                         gc_t_join.restart();
15565 #endif //MULTIPLE_HEAPS
15566                     }
15567                 }
15568             }
15569             else
15570             {
15571                 dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
15572                 gc1();
15573             }
15574         }
15575         else
15576 #endif //BACKGROUND_GC
15577         {
15578             gc1();
15579         }
15580 #ifndef MULTIPLE_HEAPS
15581         allocation_running_time = (size_t)GetTickCount();
15582         allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
15583         fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
15584 #endif //MULTIPLE_HEAPS
15585     }
15586
15587     //TODO BACKGROUND_GC remove these when ready
15588 #ifndef NO_CATCH_HANDLERS
15589
15590     PAL_EXCEPT_FILTER(CheckException, NULL)
15591     {
15592         _ASSERTE(!"Exception during garbage_collect()");
15593         EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
15594     }
15595     PAL_ENDTRY
15596 #endif //NO_CATCH_HANDLERS
15597
15598     int gn = settings.condemned_generation;
15599     return gn;
15600 }
15601
15602 #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
15603
15604 inline
15605 size_t& gc_heap::promoted_bytes(int thread)
15606 {
15607 #ifdef MULTIPLE_HEAPS
15608     return g_promoted [thread*16];
15609 #else //MULTIPLE_HEAPS
15610     thread = thread;
15611     return g_promoted;
15612 #endif //MULTIPLE_HEAPS
15613 }
15614
15615 #ifdef INTERIOR_POINTERS
15616 heap_segment* gc_heap::find_segment (BYTE* interior, BOOL small_segment_only_p)
15617 {
15618 #ifdef SEG_MAPPING_TABLE
15619     heap_segment* seg = seg_mapping_table_segment_of (interior);
15620     if (seg)
15621     {
15622         if (small_segment_only_p && heap_segment_loh_p (seg))
15623             return 0;
15624     }
15625     return seg;
15626 #else //SEG_MAPPING_TABLE
15627 #ifdef MULTIPLE_HEAPS
15628     for (int i = 0; i < gc_heap::n_heaps; i++)
15629     {
15630         gc_heap* h = gc_heap::g_heaps [i];
15631         hs = h->find_segment_per_heap (o, small_segment_only_p);
15632         if (hs)
15633         {
15634             break;
15635         }        
15636     }
15637 #else
15638     {
15639         gc_heap* h = pGenGCHeap;
15640         hs = h->find_segment_per_heap (o, small_segment_only_p);
15641     }
15642 #endif //MULTIPLE_HEAPS
15643 #endif //SEG_MAPPING_TABLE
15644 }
15645
15646 heap_segment* gc_heap::find_segment_per_heap (BYTE* interior, BOOL small_segment_only_p)
15647 {
15648 #ifdef SEG_MAPPING_TABLE
15649     return find_segment (interior, small_segment_only_p);
15650 #else //SEG_MAPPING_TABLE
15651     if (in_range_for_segment (interior, ephemeral_heap_segment))
15652     {
15653         return ephemeral_heap_segment;
15654     }
15655     else
15656     {
15657         heap_segment* found_seg = 0;
15658
15659         {
15660             heap_segment* seg = generation_start_segment (generation_of (max_generation));
15661             do
15662             {
15663                 if (in_range_for_segment (interior, seg))
15664                 {
15665                     found_seg = seg;
15666                     goto end_find_segment;
15667                 }
15668
15669             } while ((seg = heap_segment_next (seg)) != 0);
15670         }
15671         if (!small_segment_only_p)
15672         {
15673 #ifdef BACKGROUND_GC
15674             {
15675                 ptrdiff_t delta = 0;
15676                 heap_segment* seg = segment_of (interior, delta);
15677                 if (seg && in_range_for_segment (interior, seg))
15678                 {
15679                     found_seg = seg;
15680                 }
15681                 goto end_find_segment;
15682             }
15683 #else //BACKGROUND_GC
15684             heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
15685             do
15686             {
15687                 if (in_range_for_segment(interior, seg))
15688                 {
15689                     found_seg = seg;
15690                     goto end_find_segment;
15691                 }
15692
15693             } while ((seg = heap_segment_next (seg)) != 0);
15694 #endif //BACKGROUND_GC
15695         }
15696 end_find_segment:
15697
15698         return found_seg;
15699     }
15700 #endif //SEG_MAPPING_TABLE
15701 }
15702 #endif //INTERIOR_POINTERS
15703
15704 #if !defined(_DEBUG) && !defined(__GNUC__)
15705 inline // This causes link errors if global optimization is off
15706 #endif //!_DEBUG && !__GNUC__
15707 gc_heap* gc_heap::heap_of (BYTE* o)
15708 {
15709 #ifdef MULTIPLE_HEAPS
15710     if (o == 0)
15711         return g_heaps [0];
15712 #ifdef SEG_MAPPING_TABLE
15713     gc_heap* hp = seg_mapping_table_heap_of (o);
15714     return (hp ? hp : g_heaps[0]);
15715 #else //SEG_MAPPING_TABLE
15716     ptrdiff_t delta = 0;
15717     heap_segment* seg = segment_of (o, delta);
15718     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
15719 #endif //SEG_MAPPING_TABLE
15720 #else //MULTIPLE_HEAPS
15721     return __this;
15722 #endif //MULTIPLE_HEAPS
15723 }
15724
15725 inline
15726 gc_heap* gc_heap::heap_of_gc (BYTE* o)
15727 {
15728 #ifdef MULTIPLE_HEAPS
15729     if (o == 0)
15730         return g_heaps [0];
15731 #ifdef SEG_MAPPING_TABLE
15732     gc_heap* hp = seg_mapping_table_heap_of_gc (o);
15733     return (hp ? hp : g_heaps[0]);
15734 #else //SEG_MAPPING_TABLE
15735     ptrdiff_t delta = 0;
15736     heap_segment* seg = segment_of (o, delta);
15737     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
15738 #endif //SEG_MAPPING_TABLE
15739 #else //MULTIPLE_HEAPS
15740     return __this;
15741 #endif //MULTIPLE_HEAPS
15742 }
15743
15744 #ifdef INTERIOR_POINTERS
15745 // will find all heap objects (large and small)
15746 BYTE* gc_heap::find_object (BYTE* interior, BYTE* low)
15747 {
15748     if (!gen0_bricks_cleared)
15749     {
15750 #ifdef MULTIPLE_HEAPS
15751         assert (!"Should have already been done in server GC");
15752 #endif //MULTIPLE_HEAPS
15753         gen0_bricks_cleared = TRUE;
15754         //initialize brick table for gen 0
15755         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
15756              b < brick_of (align_on_brick
15757                            (heap_segment_allocated (ephemeral_heap_segment)));
15758              b++)
15759         {
15760             set_brick (b, -1);
15761         }
15762     }
15763 #ifdef FFIND_OBJECT
15764     //indicate that in the future this needs to be done during allocation
15765 #ifdef MULTIPLE_HEAPS
15766     gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
15767 #else
15768     gen0_must_clear_bricks = FFIND_DECAY;
15769 #endif //MULTIPLE_HEAPS
15770 #endif //FFIND_OBJECT
15771
15772     int brick_entry = brick_table [brick_of (interior)];
15773     if (brick_entry == 0)
15774     {
15775         // this is a pointer to a large object
15776         heap_segment* seg = find_segment_per_heap (interior, FALSE);
15777         if (seg
15778 #ifdef FEATURE_CONSERVATIVE_GC
15779             && (!g_pConfig->GetGCConservative() || interior <= heap_segment_allocated(seg))
15780 #endif
15781             )
15782         {
15783             // If interior falls within the first free object at the beginning of a generation,
15784             // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
15785             int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
15786 #ifdef FEATURE_CONSERVATIVE_GC
15787                                                        || (g_pConfig->GetGCConservative() && !heap_segment_loh_p (seg))
15788 #endif
15789                                                       );
15790             //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
15791             assert (interior < heap_segment_allocated (seg));
15792
15793             BYTE* o = heap_segment_mem (seg);
15794             while (o < heap_segment_allocated (seg))
15795             {
15796                 BYTE* next_o = o + Align (size (o), align_const);
15797                 assert (next_o > o);
15798                 if ((o <= interior) && (interior < next_o))
15799                 return o;
15800                 o = next_o;
15801             }
15802             return 0;
15803         }
15804         else
15805         {
15806             return 0;
15807         }
15808     }
15809     else if (interior >= low)
15810     {
15811         heap_segment* seg = find_segment_per_heap (interior, TRUE);
15812         if (seg)
15813         {
15814 #ifdef FEATURE_CONSERVATIVE_GC
15815             if (interior >= heap_segment_allocated (seg))
15816                 return 0;
15817 #else
15818             assert (interior < heap_segment_allocated (seg));
15819 #endif
15820             BYTE* o = find_first_object (interior, heap_segment_mem (seg));
15821             return o;
15822         }
15823         else
15824             return 0;
15825     }
15826     else
15827         return 0;
15828 }
15829
15830 BYTE*
15831 gc_heap::find_object_for_relocation (BYTE* interior, BYTE* low, BYTE* high)
15832 {
15833     BYTE* old_address = interior;
15834     if (!((old_address >= low) && (old_address < high)))
15835         return 0;
15836     BYTE* plug = 0;
15837     size_t  brick = brick_of (old_address);
15838     int    brick_entry =  brick_table [ brick ];
15839     if (brick_entry != 0)
15840     {
15841     retry:
15842         {
15843             while (brick_entry < 0)
15844             {
15845                 brick = (brick + brick_entry);
15846                 brick_entry =  brick_table [ brick ];
15847             }
15848             BYTE* old_loc = old_address;
15849             BYTE* node = tree_search ((brick_address (brick) + brick_entry-1),
15850                                       old_loc);
15851             if (node <= old_loc)
15852                 plug = node;
15853             else
15854             {
15855                 brick = brick - 1;
15856                 brick_entry =  brick_table [ brick ];
15857                 goto retry;
15858             }
15859
15860         }
15861         assert (plug);
15862         //find the object by going along the plug
15863         BYTE* o = plug;
15864         while (o <= interior)
15865         {
15866             BYTE* next_o = o + Align (size (o));
15867             assert (next_o > o);
15868             if (next_o > interior)
15869             {
15870                 break;
15871             }
15872             o = next_o;
15873         }
15874         assert ((o <= interior) && ((o + Align (size (o))) > interior));
15875         return o;
15876     }
15877     else
15878     {
15879         // this is a pointer to a large object
15880         heap_segment* seg = find_segment_per_heap (interior, FALSE);
15881         if (seg)
15882         {
15883             assert (interior < heap_segment_allocated (seg));
15884
15885             BYTE* o = heap_segment_mem (seg);
15886             while (o < heap_segment_allocated (seg))
15887             {
15888                 BYTE* next_o = o + Align (size (o));
15889                 assert (next_o > o);
15890                 if ((o < interior) && (interior < next_o))
15891                 return o;
15892                 o = next_o;
15893             }
15894             return 0;
15895         }
15896         else
15897             {
15898             return 0;
15899         }
15900     }
15901 }
15902 #else //INTERIOR_POINTERS
15903 inline
15904 BYTE* gc_heap::find_object (BYTE* o, BYTE* low)
15905 {
15906     return o;
15907 }
15908 #endif //INTERIOR_POINTERS
15909
15910 #ifdef MARK_LIST
15911 #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;}
15912 #else //MARK_LIST
15913 #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
15914 #endif //MARK_LIST
15915
15916 #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
15917
15918 #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
15919
15920 inline
15921 BOOL gc_heap::gc_mark1 (BYTE* o)
15922 {
15923     BOOL marked = !marked (o);
15924     set_marked (o);
15925     dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
15926     return marked;
15927 }
15928
15929 inline
15930 BOOL gc_heap::gc_mark (BYTE* o, BYTE* low, BYTE* high)
15931 {
15932     BOOL marked = FALSE;
15933     if ((o >= low) && (o < high))
15934         marked = gc_mark1 (o);
15935 #ifdef MULTIPLE_HEAPS
15936     else if (o)
15937     {
15938         //find the heap
15939         gc_heap* hp = heap_of_gc (o);
15940         assert (hp);
15941         if ((o >= hp->gc_low) && (o < hp->gc_high))
15942             marked = gc_mark1 (o);
15943     }
15944 #ifdef SNOOP_STATS
15945     snoop_stat.objects_checked_count++;
15946
15947     if (marked)
15948     {
15949         snoop_stat.objects_marked_count++;
15950     }
15951     if (!o)
15952     {
15953         snoop_stat.zero_ref_count++;
15954     }
15955
15956 #endif //SNOOP_STATS
15957 #endif //MULTIPLE_HEAPS
15958     return marked;
15959 }
15960
15961 #ifdef BACKGROUND_GC
15962
15963 inline
15964 BOOL gc_heap::background_marked (BYTE* o)
15965 {
15966     return mark_array_marked (o);
15967 }
15968 inline
15969 BOOL gc_heap::background_mark1 (BYTE* o)
15970 {
15971     BOOL to_mark = !mark_array_marked (o);
15972
15973     dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
15974     if (to_mark)
15975     {
15976         mark_array_set_marked (o);
15977         dprintf (4, ("n*%Ix*n", (size_t)o));
15978         return TRUE;
15979     }
15980     else
15981         return FALSE;
15982 }
15983
15984 // TODO: we could consider filtering out NULL's here instead of going to 
15985 // look for it on other heaps
15986 inline
15987 BOOL gc_heap::background_mark (BYTE* o, BYTE* low, BYTE* high)
15988 {
15989     BOOL marked = FALSE;
15990     if ((o >= low) && (o < high))
15991         marked = background_mark1 (o);
15992 #ifdef MULTIPLE_HEAPS
15993     else if (o)
15994     {
15995         //find the heap
15996         gc_heap* hp = heap_of (o);
15997         assert (hp);
15998         if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
15999             marked = background_mark1 (o);
16000     }
16001 #endif //MULTIPLE_HEAPS
16002     return marked;
16003 }
16004
16005 #endif //BACKGROUND_GC
16006
16007 inline
16008 BYTE* gc_heap::next_end (heap_segment* seg, BYTE* f)
16009 {
16010     if (seg == ephemeral_heap_segment)
16011         return  f;
16012     else
16013         return  heap_segment_allocated (seg);
16014 }
16015
16016 #define new_start() {if (ppstop <= start) {break;} else {parm = start}}
16017 #define ignore_start 0
16018 #define use_start 1
16019
16020 #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp)      \
16021 {                                                                           \
16022     CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt));           \
16023     CGCDescSeries* cur = map->GetHighestSeries();                           \
16024     SSIZE_T cnt = (SSIZE_T) map->GetNumSeries();                            \
16025                                                                             \
16026     if (cnt >= 0)                                                           \
16027     {                                                                       \
16028         CGCDescSeries* last = map->GetLowestSeries();                       \
16029         BYTE** parm = 0;                                                    \
16030         do                                                                  \
16031         {                                                                   \
16032             assert (parm <= (BYTE**)((o) + cur->GetSeriesOffset()));        \
16033             parm = (BYTE**)((o) + cur->GetSeriesOffset());                  \
16034             BYTE** ppstop =                                                 \
16035                 (BYTE**)((BYTE*)parm + cur->GetSeriesSize() + (size));      \
16036             if (!start_useful || (BYTE*)ppstop > (start))                   \
16037             {                                                               \
16038                 if (start_useful && (BYTE*)parm < (start)) parm = (BYTE**)(start);\
16039                 while (parm < ppstop)                                       \
16040                 {                                                           \
16041                    {exp}                                                    \
16042                    parm++;                                                  \
16043                 }                                                           \
16044             }                                                               \
16045             cur--;                                                          \
16046                                                                             \
16047         } while (cur >= last);                                              \
16048     }                                                                       \
16049     else                                                                    \
16050     {                                                                       \
16051         /* Handle the repeating case - array of valuetypes */               \
16052         BYTE** parm = (BYTE**)((o) + cur->startoffset);                     \
16053         if (start_useful && start > (BYTE*)parm)                            \
16054         {                                                                   \
16055             SSIZE_T cs = mt->RawGetComponentSize();                         \
16056             parm = (BYTE**)((BYTE*)parm + (((start) - (BYTE*)parm)/cs)*cs); \
16057         }                                                                   \
16058         while ((BYTE*)parm < ((o)+(size)-plug_skew))                        \
16059         {                                                                   \
16060             for (SSIZE_T __i = 0; __i > cnt; __i--)                         \
16061             {                                                               \
16062                 HALF_SIZE_T skip =  cur->val_serie[__i].skip;               \
16063                 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs;              \
16064                 BYTE** ppstop = parm + nptrs;                               \
16065                 if (!start_useful || (BYTE*)ppstop > (start))               \
16066                 {                                                           \
16067                     if (start_useful && (BYTE*)parm < (start)) parm = (BYTE**)(start);      \
16068                     do                                                      \
16069                     {                                                       \
16070                        {exp}                                                \
16071                        parm++;                                              \
16072                     } while (parm < ppstop);                                \
16073                 }                                                           \
16074                 parm = (BYTE**)((BYTE*)ppstop + skip);                      \
16075             }                                                               \
16076         }                                                                   \
16077     }                                                                       \
16078 }
16079
16080 #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
16081
16082 // 1 thing to note about this macro:
16083 // 1) you can use *parm safely but in general you don't want to use parm 
16084 // because for the collectible types it's not an address on the managed heap.
16085 #ifndef COLLECTIBLE_CLASS
16086 #define go_through_object_cl(mt,o,size,parm,exp)                            \
16087 {                                                                           \
16088     if (header(o)->ContainsPointers())                                      \
16089     {                                                                       \
16090         go_through_object_nostart(mt,o,size,parm,exp);                      \
16091     }                                                                       \
16092 }
16093 #else //COLLECTIBLE_CLASS
16094 #define go_through_object_cl(mt,o,size,parm,exp)                            \
16095 {                                                                           \
16096     if (header(o)->Collectible())                                           \
16097     {                                                                       \
16098         BYTE* class_obj = get_class_object (o);                             \
16099         BYTE** parm = &class_obj;                                           \
16100         do {exp} while (false);                                             \
16101     }                                                                       \
16102     if (header(o)->ContainsPointers())                                      \
16103     {                                                                       \
16104         go_through_object_nostart(mt,o,size,parm,exp);                      \
16105     }                                                                       \
16106 }
16107 #endif //COLLECTIBLE_CLASS
16108
16109 // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
16110 void gc_heap::enque_pinned_plug (BYTE* plug, 
16111                                  BOOL save_pre_plug_info_p, 
16112                                  BYTE* last_object_in_last_plug)
16113 {
16114     if (mark_stack_array_length <= mark_stack_tos)
16115     {
16116         if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
16117         {
16118             // we don't want to continue here due to security
16119             // risks. This happens very rarely and fixing it in the
16120             // way so that we can continue is a bit involved and will
16121             // not be done in Dev10.
16122             EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
16123         }
16124     }
16125
16126     dprintf (3, ("enquing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d", 
16127         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)));
16128     mark& m = mark_stack_array[mark_stack_tos];
16129     m.first = plug;
16130     // Must be set now because if we have a short object we'll need the value of saved_pre_p.
16131     m.saved_pre_p = save_pre_plug_info_p;
16132
16133     if (save_pre_plug_info_p)
16134     {
16135         memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
16136         memcpy (&(m.saved_pre_plug_reloc), &(m.saved_pre_plug), sizeof (gap_reloc_pair));
16137
16138         // If the last object in the last plug is too short, it requires special handling.
16139         size_t last_obj_size = plug - last_object_in_last_plug;
16140         if (last_obj_size < min_pre_pin_obj_size)
16141         {
16142             dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!", 
16143                                  last_object_in_last_plug, plug));
16144             // Need to set the short bit regardless of having refs or not because we need to 
16145             // indicate that this object is not walkable.
16146             m.set_pre_short();
16147
16148 #ifdef COLLECTIBLE_CLASS
16149             if (is_collectible (last_object_in_last_plug))
16150             {
16151                 m.set_pre_short_collectible();
16152             }
16153 #endif //COLLECTIBLE_CLASS
16154
16155             if (contain_pointers (last_object_in_last_plug))
16156             {
16157                 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
16158
16159                 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
16160                     {
16161                         size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (BYTE*);
16162                         dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (BYTE*)pval, *pval, gap_offset));
16163                         m.set_pre_short_bit (gap_offset);
16164                     }
16165                 );
16166             }
16167         }
16168     }
16169
16170     m.saved_post_p = FALSE;
16171 }
16172
16173 void gc_heap::save_post_plug_info (BYTE* last_pinned_plug, BYTE* last_object_in_last_plug, BYTE* post_plug)
16174 {
16175     mark& m = mark_stack_array[mark_stack_tos - 1];
16176     assert (last_pinned_plug == m.first);
16177     m.saved_post_plug_info_start = (BYTE*)&(((plug_and_gap*)post_plug)[-1]);
16178     memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
16179     memcpy (&(m.saved_post_plug_reloc), &(m.saved_post_plug), sizeof (gap_reloc_pair));
16180
16181     // This is important - we need to clear all bits here except the last one.
16182     m.saved_post_p = TRUE;
16183
16184 #ifdef _DEBUG
16185     m.saved_post_plug_debug.gap = 1;
16186 #endif //_DEBUG
16187
16188     dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
16189
16190     size_t last_obj_size = post_plug - last_object_in_last_plug;
16191     if (last_obj_size < min_pre_pin_obj_size)
16192     {
16193         dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
16194         m.set_post_short();
16195         verify_pinned_queue_p = TRUE;
16196
16197 #ifdef COLLECTIBLE_CLASS
16198         if (is_collectible (last_object_in_last_plug))
16199         {
16200             m.set_post_short_collectible();
16201         }
16202 #endif //COLLECTIBLE_CLASS
16203
16204         if (contain_pointers (last_object_in_last_plug))
16205         {
16206             dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
16207
16208             // TODO: since we won't be able to walk this object in relocation, we still need to
16209             // take care of collectible assemblies here.
16210             go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
16211                 {
16212                     size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (BYTE*);
16213                     dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (BYTE*)pval, *pval, gap_offset));
16214                     m.set_post_short_bit (gap_offset);
16215                 }
16216             );
16217         }
16218     }
16219 }
16220
16221 //#define PREFETCH
16222 #ifdef PREFETCH
16223 __declspec(naked) void __fastcall Prefetch(void* addr)
16224 {
16225    __asm {
16226        PREFETCHT0 [ECX]
16227         ret
16228     };
16229 }
16230 #else //PREFETCH
16231 inline void Prefetch (void* addr)
16232 {
16233     addr = addr;
16234 }
16235 #endif //PREFETCH
16236 #ifdef MH_SC_MARK
16237 inline
16238 VOLATILE(BYTE*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
16239 {
16240     return ((VOLATILE(BYTE*)*)(hp->mark_stack_array))[index];
16241 }
16242
16243 #endif //MH_SC_MARK
16244
16245 #define stolen 2
16246 #define partial 1
16247 #define partial_object 3
16248 inline 
16249 BYTE* ref_from_slot (BYTE* r)
16250 {
16251     return (BYTE*)((size_t)r & ~(stolen | partial));
16252 }
16253 inline
16254 BOOL stolen_p (BYTE* r)
16255 {
16256     return (((size_t)r&2) && !((size_t)r&1));
16257 }
16258 inline 
16259 BOOL ready_p (BYTE* r)
16260 {
16261     return ((size_t)r != 1);
16262 }
16263 inline
16264 BOOL partial_p (BYTE* r)
16265 {
16266     return (((size_t)r&1) && !((size_t)r&2));
16267 }
16268 inline 
16269 BOOL straight_ref_p (BYTE* r)
16270 {
16271     return (!stolen_p (r) && !partial_p (r));
16272 }
16273 inline 
16274 BOOL partial_object_p (BYTE* r)
16275 {
16276     return (((size_t)r & partial_object) == partial_object);
16277 }
16278 inline
16279 BOOL ref_p (BYTE* r)
16280 {
16281     return (straight_ref_p (r) || partial_object_p (r));
16282 }
16283
16284 void gc_heap::mark_object_simple1 (BYTE* oo, BYTE* start THREAD_NUMBER_DCL)
16285 {
16286     SERVER_SC_MARK_VOLATILE(BYTE*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(BYTE*)*)mark_stack_array;
16287     SERVER_SC_MARK_VOLATILE(BYTE*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(BYTE*)*)&mark_stack_array[mark_stack_array_length];
16288     SERVER_SC_MARK_VOLATILE(BYTE*)* mark_stack_base = mark_stack_tos;
16289 #ifdef SORT_MARK_STACK
16290     SERVER_SC_MARK_VOLATILE(BYTE*)* sorted_tos = mark_stack_base;
16291 #endif //SORT_MARK_STACK
16292
16293     // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't 
16294     // update mark list.
16295     BOOL  full_p = (settings.condemned_generation == max_generation);
16296
16297     assert ((start >= oo) && (start < oo+size(oo)));
16298
16299 #ifndef MH_SC_MARK
16300     *mark_stack_tos = oo;
16301 #endif //!MH_SC_MARK
16302
16303     while (1)
16304     {
16305 #ifdef MULTIPLE_HEAPS
16306 #else  //MULTIPLE_HEAPS
16307         const int thread = 0;
16308 #endif //MULTIPLE_HEAPS
16309
16310         if (oo && ((size_t)oo != 4))
16311         {
16312             size_t s = 0; 
16313             if (stolen_p (oo))
16314             {
16315                 --mark_stack_tos;
16316                 goto next_level;
16317             }
16318             else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (BYTE*))))
16319             {
16320                 BOOL overflow_p = FALSE;
16321
16322                 if (mark_stack_tos + (s) /sizeof (BYTE*) >= (mark_stack_limit  - 1))
16323                 {
16324                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
16325                     if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
16326                     {
16327                         overflow_p = TRUE;
16328                     }
16329                 }
16330                 
16331                 if (overflow_p == FALSE)
16332                 {
16333                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
16334
16335                     go_through_object_cl (method_table(oo), oo, s, ppslot,
16336                                           {
16337                                               BYTE* o = *ppslot;
16338                                               Prefetch(o);
16339                                               if (gc_mark (o, gc_low, gc_high))
16340                                               {
16341                                                   if (full_p)
16342                                                   {
16343                                                       m_boundary_fullgc (o);
16344                                                   }
16345                                                   else
16346                                                   {
16347                                                       m_boundary (o);
16348                                                   }
16349                                                   size_t obj_size = size (o);
16350                                                   promoted_bytes (thread) += obj_size;
16351                                                   if (contain_pointers_or_collectible (o))
16352                                                   {
16353                                                       *(mark_stack_tos++) = o;
16354                                                   }
16355                                               }
16356                                           }
16357                         );
16358                 }
16359                 else
16360                 {
16361                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
16362                     min_overflow_address = min (min_overflow_address, oo);
16363                     max_overflow_address = max (max_overflow_address, oo);
16364                 }
16365             }
16366             else
16367             {
16368                 if (partial_p (oo))
16369                 {
16370                     start = ref_from_slot (oo);
16371                     oo = ref_from_slot (*(--mark_stack_tos));
16372                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
16373                     assert ((oo < start) && (start < (oo + size (oo))));
16374                 }
16375 #ifdef COLLECTIBLE_CLASS
16376                 else
16377                 {
16378                     // If there's a class object, push it now. We are guaranteed to have the slot since
16379                     // we just popped one object off.
16380                     if (is_collectible (oo))
16381                     {
16382                         BYTE* class_obj = get_class_object (oo);
16383                         if (gc_mark (class_obj, gc_low, gc_high))
16384                         {
16385                             if (full_p)
16386                             {
16387                                 m_boundary_fullgc (class_obj);
16388                             }
16389                             else
16390                             {
16391                                 m_boundary (class_obj);
16392                             }
16393
16394                             size_t obj_size = size (class_obj);
16395                             promoted_bytes (thread) += obj_size;
16396                             *(mark_stack_tos++) = class_obj;
16397                         }
16398                     }
16399                 }
16400 #endif //COLLECTIBLE_CLASS
16401
16402                 s = size (oo);
16403                 
16404                 BOOL overflow_p = FALSE;
16405             
16406                 if (mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
16407                 {
16408                     overflow_p = TRUE;
16409                 }
16410                 if (overflow_p == FALSE)
16411                 {
16412                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
16413
16414                     //push the object and its current 
16415                     SERVER_SC_MARK_VOLATILE(BYTE*)* place = ++mark_stack_tos;
16416                     mark_stack_tos++;
16417 #ifdef MH_SC_MARK
16418                     *(place-1) = 0;
16419                     *(place) = (BYTE*)partial;
16420 #endif //MH_SC_MARK
16421                     int i = num_partial_refs; 
16422                     BYTE* ref_to_continue = 0;
16423
16424                     go_through_object (method_table(oo), oo, s, ppslot,
16425                                        start, use_start, (oo + s),
16426                                        {
16427                                            BYTE* o = *ppslot;
16428                                            Prefetch(o);
16429                                            if (gc_mark (o, gc_low, gc_high))
16430                                            {
16431                                                 if (full_p)
16432                                                 {
16433                                                     m_boundary_fullgc (o);
16434                                                 }
16435                                                 else
16436                                                 {
16437                                                     m_boundary (o);
16438                                                 }
16439                                                 size_t obj_size = size (o);
16440                                                 promoted_bytes (thread) += obj_size;
16441                                                 if (contain_pointers_or_collectible (o))
16442                                                 {
16443                                                     *(mark_stack_tos++) = o;
16444                                                     if (--i == 0)
16445                                                     {
16446                                                         ref_to_continue = (BYTE*)((size_t)(ppslot+1) | partial);
16447                                                         goto more_to_do;
16448                                                     }
16449
16450                                                 }
16451                                            }
16452
16453                                        }
16454                         );
16455                     //we are finished with this object
16456                     assert (ref_to_continue == 0);
16457 #ifdef MH_SC_MARK
16458                     assert ((*(place-1)) == (BYTE*)0);
16459 #else //MH_SC_MARK
16460                     *(place-1) = 0;
16461 #endif //MH_SC_MARK
16462                     *place = 0; 
16463                     // shouldn't we decrease tos by 2 here??
16464
16465 more_to_do:
16466                     if (ref_to_continue)
16467                     {
16468                         //update the start
16469 #ifdef MH_SC_MARK
16470                         assert ((*(place-1)) == (BYTE*)0);
16471                         *(place-1) = (BYTE*)((size_t)oo | partial_object);
16472                         assert (((*place) == (BYTE*)1) || ((*place) == (BYTE*)2));
16473 #endif //MH_SC_MARK
16474                         *place = ref_to_continue;
16475                     }
16476                 }
16477                 else
16478                 {
16479                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
16480                     min_overflow_address = min (min_overflow_address, oo);
16481                     max_overflow_address = max (max_overflow_address, oo);
16482                 }
16483             }
16484 #ifdef SORT_MARK_STACK
16485             if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
16486             {
16487                 rqsort1 (sorted_tos, mark_stack_tos-1);
16488                 sorted_tos = mark_stack_tos-1;
16489             }
16490 #endif //SORT_MARK_STACK
16491         }
16492     next_level:
16493         if (!(mark_stack_empty_p()))
16494         {
16495             oo = *(--mark_stack_tos);
16496             start = oo;
16497
16498 #ifdef SORT_MARK_STACK
16499             sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
16500 #endif //SORT_MARK_STACK
16501         }
16502         else
16503             break;
16504     }
16505 }
16506
16507 #ifdef MH_SC_MARK
16508 BOOL same_numa_node_p (int hn1, int hn2)
16509 {
16510     return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
16511 }
16512
16513 int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
16514 {
16515     int hn = (current_buddy+1)%n_heaps;
16516     while (hn != current_buddy)
16517     {
16518         if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
16519             return hn;
16520         hn = (hn+1)%n_heaps;
16521     }
16522     return current_buddy;
16523 }
16524
16525 void 
16526 gc_heap::mark_steal()
16527 {
16528     mark_stack_busy() = 0;
16529     //clear the mark stack in the snooping range
16530     for (int i = 0; i < max_snoop_level; i++)
16531     {
16532         ((VOLATILE(BYTE*)*)(mark_stack_array))[i] = 0;
16533     }
16534
16535     //pick the next heap as our buddy
16536     int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
16537
16538 #ifdef SNOOP_STATS
16539         dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
16540         DWORD begin_tick = GetTickCount();
16541 #endif //SNOOP_STATS
16542
16543     int idle_loop_count = 0; 
16544     int first_not_ready_level = 0;
16545
16546     while (1)
16547     {
16548         gc_heap* hp = g_heaps [thpn];
16549         int level = first_not_ready_level;
16550         first_not_ready_level = 0; 
16551
16552         while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
16553         {
16554             idle_loop_count = 0; 
16555 #ifdef SNOOP_STATS
16556             snoop_stat.busy_count++;
16557             dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix", 
16558                                  heap_number, level, (int)((BYTE**)(hp->mark_stack_array))[level]));
16559 #endif //SNOOP_STATS
16560
16561             BYTE* o = ref_mark_stack (hp, level);
16562
16563             BYTE* start = o;
16564             if (ref_p (o))
16565             {
16566                 mark_stack_busy() = 1;
16567
16568                 BOOL success = TRUE;
16569                 BYTE* next = (ref_mark_stack (hp, level+1));
16570                 if (ref_p (next))
16571                 {
16572                     if (((size_t)o > 4) && !partial_object_p (o))
16573                     {
16574                         //this is a normal object, not a partial mark tuple
16575                         //success = (FastInterlockCompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
16576                         success = (FastInterlockCompareExchangePointer (&ref_mark_stack (hp, level), 4, o)==o);
16577 #ifdef SNOOP_STATS
16578                         snoop_stat.interlocked_count++;
16579                         if (success)
16580                             snoop_stat.normal_count++;
16581 #endif //SNOOP_STATS
16582                     }
16583                     else
16584                     {
16585                         //it is a stolen entry, or beginning/ending of a partial mark
16586                         level++;
16587 #ifdef SNOOP_STATS
16588                         snoop_stat.stolen_or_pm_count++;
16589 #endif //SNOOP_STATS
16590                         success = FALSE;
16591                     }
16592                 }
16593                 else if (stolen_p (next))
16594                 {
16595                     //ignore the stolen guy and go to the next level
16596                     success = FALSE;
16597                     level+=2;
16598 #ifdef SNOOP_STATS
16599                     snoop_stat.stolen_entry_count++;
16600 #endif //SNOOP_STATS
16601                 }
16602                 else
16603                 {
16604                     assert (partial_p (next));
16605                     start = ref_from_slot (next);
16606                     //re-read the object
16607                     o = ref_from_slot (ref_mark_stack (hp, level));
16608                     if (o && start)
16609                     {
16610                         //steal the object
16611                         success = (FastInterlockCompareExchangePointer (&ref_mark_stack (hp, level+1), stolen, next)==next);
16612 #ifdef SNOOP_STATS
16613                         snoop_stat.interlocked_count++;
16614                         if (success)
16615                         {
16616                             snoop_stat.partial_mark_parent_count++;                    
16617                         }
16618 #endif //SNOOP_STATS
16619                     }
16620                     else
16621                     {
16622                         // stack is not ready, or o is completely different from the last time we read from this stack level.
16623                         // go up 2 levels to steal children or totally unrelated objects.
16624                         success = FALSE;
16625                         if (first_not_ready_level == 0)
16626                         {
16627                             first_not_ready_level = level;
16628                         }
16629                         level+=2;
16630 #ifdef SNOOP_STATS
16631                         snoop_stat.pm_not_ready_count++;
16632 #endif //SNOOP_STATS                        
16633                     }
16634                 }
16635                 if (success)
16636                 {
16637
16638 #ifdef SNOOP_STATS
16639                     dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
16640                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
16641                             (GetTickCount()-begin_tick)));
16642                     DWORD start_tick = GetTickCount();
16643 #endif //SNOOP_STATS
16644
16645                     mark_object_simple1 (o, start, heap_number);
16646
16647 #ifdef SNOOP_STATS
16648                     dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
16649                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
16650                             (GetTickCount()-start_tick),(GetTickCount()-begin_tick)));
16651 #endif //SNOOP_STATS
16652
16653                     mark_stack_busy() = 0;
16654
16655                     //clear the mark stack in snooping range
16656                     for (int i = 0; i < max_snoop_level; i++)
16657                     {
16658                         if (((BYTE**)mark_stack_array)[i] != 0)
16659                         {
16660                             ((VOLATILE(BYTE*)*)(mark_stack_array))[i] = 0;
16661 #ifdef SNOOP_STATS
16662                             snoop_stat.stack_bottom_clear_count++;
16663 #endif //SNOOP_STATS
16664                         }
16665                     }
16666
16667                     level = 0; 
16668                 }
16669                 mark_stack_busy() = 0;
16670             }
16671             else
16672             {
16673                 //slot is either partial or stolen
16674                 level++;
16675             }
16676         }
16677         if ((first_not_ready_level != 0) && hp->mark_stack_busy())
16678         {
16679             continue;
16680         } 
16681         if (!hp->mark_stack_busy())
16682         {
16683             first_not_ready_level = 0; 
16684             idle_loop_count++;
16685
16686             if ((idle_loop_count % (6) )==1)
16687             {
16688 #ifdef SNOOP_STATS
16689                 snoop_stat.switch_to_thread_count++;
16690 #endif //SNOOP_STATS
16691                 __SwitchToThread(1,0);
16692             }
16693             int free_count = 1;
16694 #ifdef SNOOP_STATS
16695             snoop_stat.stack_idle_count++;
16696             //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
16697 #endif //SNOOP_STATS
16698             for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
16699             {
16700                 if (!((g_heaps [hpn])->mark_stack_busy()))
16701                 {
16702                     free_count++;
16703 #ifdef SNOOP_STATS
16704                 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
16705 #endif //SNOOP_STATS
16706                 }
16707                 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
16708                 {
16709                     thpn = hpn;
16710                     break;
16711                 }
16712                 hpn = (hpn+1)%n_heaps;
16713                 YieldProcessor();
16714             }
16715             if (free_count == n_heaps)
16716             {
16717                 break;
16718             }
16719         }
16720     }
16721 }
16722
16723 inline
16724 BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
16725 {
16726 #ifdef SNOOP_STATS
16727     snoop_stat.check_level_count++;
16728 #endif //SNOOP_STATS
16729     return (next_heap->mark_stack_busy()>=1);
16730 }
16731 #endif //MH_SC_MARK
16732
16733 #ifdef SNOOP_STATS
16734 void gc_heap::print_snoop_stat()
16735 {
16736     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
16737         "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
16738     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
16739         snoop_stat.heap_index,
16740         snoop_stat.objects_checked_count,
16741         snoop_stat.zero_ref_count,
16742         snoop_stat.objects_marked_count,
16743         snoop_stat.stolen_stack_count,
16744         snoop_stat.partial_stack_count,
16745         snoop_stat.normal_stack_count,
16746         snoop_stat.non_stack_count));
16747     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
16748         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
16749     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
16750         snoop_stat.heap_index,
16751         snoop_stat.check_level_count,
16752         snoop_stat.busy_count,
16753         snoop_stat.interlocked_count,
16754         snoop_stat.partial_mark_parent_count,
16755         snoop_stat.stolen_or_pm_count,
16756         snoop_stat.stolen_entry_count,
16757         snoop_stat.pm_not_ready_count,
16758         snoop_stat.normal_count,
16759         snoop_stat.stack_bottom_clear_count));
16760
16761     printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n", 
16762         "heap", "check", "zero", "mark", "idle", "switch");
16763     printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
16764         snoop_stat.heap_index,
16765         snoop_stat.objects_checked_count,
16766         snoop_stat.zero_ref_count,
16767         snoop_stat.objects_marked_count,
16768         snoop_stat.stack_idle_count,
16769         snoop_stat.switch_to_thread_count);
16770     printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
16771         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
16772     printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
16773         snoop_stat.heap_index,
16774         snoop_stat.check_level_count,
16775         snoop_stat.busy_count,
16776         snoop_stat.interlocked_count,
16777         snoop_stat.partial_mark_parent_count,
16778         snoop_stat.stolen_or_pm_count,
16779         snoop_stat.stolen_entry_count,
16780         snoop_stat.pm_not_ready_count,
16781         snoop_stat.normal_count,
16782         snoop_stat.stack_bottom_clear_count);
16783 }
16784 #endif //SNOOP_STATS
16785
16786 #ifdef HEAP_ANALYZE
16787 void
16788 gc_heap::ha_mark_object_simple (BYTE** po THREAD_NUMBER_DCL)
16789 {
16790     if (!internal_root_array)
16791     {
16792         internal_root_array = new (nothrow) (BYTE* [internal_root_array_length]);
16793         if (!internal_root_array)
16794         {
16795             heap_analyze_success = FALSE;
16796         }
16797     }
16798
16799     if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
16800     {
16801         size_t new_size = 2*internal_root_array_length;
16802
16803         MEMORYSTATUSEX statex;
16804         GetProcessMemoryLoad(&statex);
16805         if (new_size > (size_t)(statex.ullAvailPhys / 10))
16806         {
16807             heap_analyze_success = FALSE;
16808         }
16809         else
16810         {
16811             BYTE** tmp = new (nothrow) (BYTE* [new_size]);
16812             if (tmp)
16813             {
16814                 memcpy (tmp, internal_root_array,
16815                         internal_root_array_length*sizeof (BYTE*));
16816                 delete[] internal_root_array;
16817                 internal_root_array = tmp;
16818                 internal_root_array_length = new_size;
16819             }
16820             else
16821             {
16822                 heap_analyze_success = FALSE;
16823             }
16824         }
16825     }
16826
16827     if (heap_analyze_success)
16828     {
16829         PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
16830
16831         BYTE* ref = (BYTE*)po;
16832         if (!current_obj || 
16833             !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
16834         {
16835             gc_heap* hp = gc_heap::heap_of (ref);
16836             current_obj = hp->find_object (ref, hp->lowest_address);
16837             current_obj_size = size (current_obj);
16838
16839             internal_root_array[internal_root_array_index] = current_obj;
16840             internal_root_array_index++;
16841         }
16842     }
16843
16844     mark_object_simple (po THREAD_NUMBER_ARG);
16845 }
16846 #endif //HEAP_ANALYZE
16847
16848 //this method assumes that *po is in the [low. high[ range
16849 void
16850 gc_heap::mark_object_simple (BYTE** po THREAD_NUMBER_DCL)
16851 {
16852     BYTE* o = *po;
16853 #ifdef MULTIPLE_HEAPS
16854 #else  //MULTIPLE_HEAPS
16855     const int thread = 0;
16856 #endif //MULTIPLE_HEAPS
16857     {
16858 #ifdef SNOOP_STATS
16859         snoop_stat.objects_checked_count++;
16860 #endif //SNOOP_STATS
16861
16862         if (gc_mark1 (o))
16863         {
16864             m_boundary (o);
16865             size_t s = size (o);
16866             promoted_bytes (thread) += s;
16867             {
16868                 go_through_object_cl (method_table(o), o, s, poo,
16869                                         {
16870                                             BYTE* oo = *poo;
16871                                             if (gc_mark (oo, gc_low, gc_high))
16872                                             {
16873                                                 m_boundary (oo);
16874                                                 size_t obj_size = size (oo);
16875                                                 promoted_bytes (thread) += obj_size;
16876
16877                                                 if (contain_pointers_or_collectible (oo))
16878                                                     mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
16879                                             }
16880                                         }
16881                     );
16882             }
16883         }
16884     }
16885 }
16886
16887 inline
16888 BYTE* gc_heap::mark_object (BYTE* o THREAD_NUMBER_DCL)
16889 {
16890     if ((o >= gc_low) && (o < gc_high))
16891         mark_object_simple (&o THREAD_NUMBER_ARG);
16892 #ifdef MULTIPLE_HEAPS
16893     else if (o)
16894     {
16895         //find the heap
16896         gc_heap* hp = heap_of (o);
16897         assert (hp);
16898         if ((o >= hp->gc_low) && (o < hp->gc_high))
16899             mark_object_simple (&o THREAD_NUMBER_ARG);
16900     }
16901 #endif //MULTIPLE_HEAPS
16902
16903     return o;
16904 }
16905
16906 #ifdef BACKGROUND_GC
16907
16908 void gc_heap::background_mark_simple1 (BYTE* oo THREAD_NUMBER_DCL)
16909 {
16910     BYTE** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
16911
16912 #ifdef SORT_MARK_STACK
16913     BYTE** sorted_tos = background_mark_stack_array;
16914 #endif //SORT_MARK_STACK
16915
16916     background_mark_stack_tos = background_mark_stack_array;
16917
16918     while (1)
16919     {
16920 #ifdef MULTIPLE_HEAPS
16921 #else  //MULTIPLE_HEAPS
16922         const int thread = 0;
16923 #endif //MULTIPLE_HEAPS
16924         if (oo)
16925         {
16926             size_t s = 0; 
16927             if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (BYTE*))))
16928             {
16929                 BOOL overflow_p = FALSE;
16930             
16931                 if (background_mark_stack_tos + (s) /sizeof (BYTE*) >= (mark_stack_limit - 1))
16932                 {
16933                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
16934                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
16935                     if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
16936                     {
16937                         dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs", 
16938                             heap_number,
16939                             (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
16940                             method_table(oo), 
16941                             num_pointers));
16942
16943                         bgc_overflow_count++;
16944                         overflow_p = TRUE;
16945                     }
16946                 }
16947             
16948                 if (overflow_p == FALSE)
16949                 {
16950                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
16951
16952                     go_through_object_cl (method_table(oo), oo, s, ppslot,
16953                     {
16954                         BYTE* o = *ppslot;
16955                         Prefetch(o);
16956                         if (background_mark (o, 
16957                                              background_saved_lowest_address, 
16958                                              background_saved_highest_address))
16959                         {
16960                             //m_boundary (o);
16961                             size_t obj_size = size (o);
16962                             bpromoted_bytes (thread) += obj_size;
16963                             if (contain_pointers_or_collectible (o))
16964                             {
16965                                 *(background_mark_stack_tos++) = o;
16966
16967                             }
16968                         }
16969                     }
16970                         );
16971                 }
16972                 else
16973                 {
16974                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
16975                     background_min_overflow_address = min (background_min_overflow_address, oo);
16976                     background_max_overflow_address = max (background_max_overflow_address, oo);
16977                 }
16978             }
16979             else 
16980             {
16981                 BYTE* start = oo;
16982                 if ((size_t)oo & 1)
16983                 {
16984                     oo = (BYTE*)((size_t)oo & ~1);
16985                     start = *(--background_mark_stack_tos);
16986                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
16987                 }
16988 #ifdef COLLECTIBLE_CLASS
16989                 else
16990                 {
16991                     // If there's a class object, push it now. We are guaranteed to have the slot since
16992                     // we just popped one object off.
16993                     if (is_collectible (oo))
16994                     {
16995                         BYTE* class_obj = get_class_object (oo);
16996                         if (background_mark (class_obj, 
16997                                             background_saved_lowest_address, 
16998                                             background_saved_highest_address))
16999                         {
17000                             size_t obj_size = size (class_obj);
17001                             bpromoted_bytes (thread) += obj_size;
17002
17003                             *(background_mark_stack_tos++) = class_obj;
17004                         }
17005                     }
17006                 }
17007 #endif //COLLECTIBLE_CLASS
17008
17009                 s = size (oo);
17010                 
17011                 BOOL overflow_p = FALSE;
17012             
17013                 if (background_mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
17014                 {
17015                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
17016                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
17017
17018                     dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id", 
17019                         heap_number,
17020                         (size_t)(mark_stack_limit - background_mark_stack_tos),
17021                         oo,
17022                         method_table(oo), 
17023                         start,
17024                         num_pointers));
17025
17026                     bgc_overflow_count++;
17027                     overflow_p = TRUE;
17028                 }
17029                 if (overflow_p == FALSE)
17030                 {
17031                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17032
17033                     //push the object and its current 
17034                     BYTE** place = background_mark_stack_tos++;
17035                     *(place) = start;
17036                     *(background_mark_stack_tos++) = (BYTE*)((size_t)oo | 1);
17037
17038                     int i = num_partial_refs; 
17039
17040                     go_through_object (method_table(oo), oo, s, ppslot,
17041                                        start, use_start, (oo + s),
17042                     {
17043                         BYTE* o = *ppslot;
17044                         Prefetch(o);
17045
17046                         if (background_mark (o, 
17047                                             background_saved_lowest_address, 
17048                                             background_saved_highest_address))
17049                         {
17050                             //m_boundary (o);
17051                             size_t obj_size = size (o);
17052                             bpromoted_bytes (thread) += obj_size;
17053                             if (contain_pointers_or_collectible (o))
17054                             {
17055                                 *(background_mark_stack_tos++) = o;
17056                                 if (--i == 0)
17057                                 {
17058                                     //update the start
17059                                     *place = (BYTE*)(ppslot+1);
17060                                     goto more_to_do;
17061                                 }
17062
17063                             }
17064                         }
17065
17066                     }
17067                         );
17068                     //we are finished with this object
17069                     *place = 0; 
17070                     *(place+1) = 0;
17071
17072                 more_to_do:;
17073                 }
17074                 else
17075                 {
17076                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
17077                     background_min_overflow_address = min (background_min_overflow_address, oo);
17078                     background_max_overflow_address = max (background_max_overflow_address, oo);
17079                 }
17080             }
17081         }
17082 #ifdef SORT_MARK_STACK
17083         if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
17084         {
17085             rqsort1 (sorted_tos, background_mark_stack_tos-1);
17086             sorted_tos = background_mark_stack_tos-1;
17087         }
17088 #endif //SORT_MARK_STACK
17089
17090         allow_fgc();
17091
17092         if (!(background_mark_stack_tos == background_mark_stack_array))
17093         {
17094             oo = *(--background_mark_stack_tos);
17095
17096 #ifdef SORT_MARK_STACK
17097             sorted_tos = (BYTE**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
17098 #endif //SORT_MARK_STACK
17099         }
17100         else
17101             break;
17102     }
17103
17104     assert (background_mark_stack_tos == background_mark_stack_array);
17105
17106
17107 }
17108
17109 //this version is different than the foreground GC because
17110 //it can't keep pointers to the inside of an object
17111 //while calling background_mark_simple1. The object could be moved
17112 //by an intervening foreground gc.
17113 //this method assumes that *po is in the [low. high[ range
17114 void
17115 gc_heap::background_mark_simple (BYTE* o THREAD_NUMBER_DCL)
17116 {
17117 #ifdef MULTIPLE_HEAPS
17118 #else  //MULTIPLE_HEAPS
17119     const int thread = 0;
17120 #endif //MULTIPLE_HEAPS
17121     {
17122         dprintf (3, ("bmarking %Ix", o));
17123         
17124         if (background_mark1 (o))
17125         {
17126             //m_boundary (o);
17127             size_t s = size (o);
17128             bpromoted_bytes (thread) += s;
17129
17130             if (contain_pointers_or_collectible (o))
17131             {
17132                 background_mark_simple1 (o THREAD_NUMBER_ARG);
17133             }
17134         }
17135     }
17136 }
17137
17138 inline
17139 BYTE* gc_heap::background_mark_object (BYTE* o THREAD_NUMBER_DCL)
17140 {
17141     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
17142     {
17143         background_mark_simple (o THREAD_NUMBER_ARG);
17144     }
17145     else
17146     {
17147         if (o)
17148         {
17149             dprintf (3, ("or-%Ix", o));
17150         }
17151     }
17152     return o;
17153 }
17154
17155 void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, DWORD flags)
17156 {
17157     assert (settings.concurrent);
17158     BYTE* o = (BYTE*)object;
17159
17160     gc_heap* hp = gc_heap::heap_of (o);
17161 #ifdef INTERIOR_POINTERS
17162     if (flags & GC_CALL_INTERIOR)
17163     {
17164         o = hp->find_object (o, background_saved_lowest_address);
17165     }
17166 #endif //INTERIOR_POINTERS
17167
17168     if (!background_object_marked (o, FALSE))
17169     {
17170         FATAL_GC_ERROR();
17171     }
17172 }
17173
17174 void gc_heap::background_promote (Object** ppObject, ScanContext* sc, DWORD flags)
17175 {
17176     sc;
17177     //in order to save space on the array, mark the object,
17178     //knowing that it will be visited later
17179     assert (settings.concurrent);
17180
17181     THREAD_NUMBER_FROM_CONTEXT;
17182 #ifndef MULTIPLE_HEAPS
17183     const int thread = 0;
17184 #endif //!MULTIPLE_HEAPS
17185
17186     BYTE* o = (BYTE*)*ppObject;
17187
17188     if (o == 0)
17189         return;
17190
17191 #ifdef DEBUG_DestroyedHandleValue
17192     // we can race with destroy handle during concurrent scan
17193     if (o == (BYTE*)DEBUG_DestroyedHandleValue)
17194         return;
17195 #endif //DEBUG_DestroyedHandleValue
17196
17197     HEAP_FROM_THREAD;
17198
17199     gc_heap* hp = gc_heap::heap_of (o);
17200
17201     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
17202     {
17203         return;
17204     }
17205
17206 #ifdef INTERIOR_POINTERS
17207     if (flags & GC_CALL_INTERIOR)
17208     {
17209         o = hp->find_object (o, hp->background_saved_lowest_address);
17210         if (o == 0)
17211             return;
17212     }
17213 #endif //INTERIOR_POINTERS
17214
17215 #ifdef FEATURE_CONSERVATIVE_GC
17216     // For conservative GC, a value on stack may point to middle of a free object.
17217     // In this case, we don't need to promote the pointer.
17218     if (g_pConfig->GetGCConservative() && ((CObjectHeader*)o)->IsFree())
17219     {
17220         return;
17221     }
17222 #endif //FEATURE_CONSERVATIVE_GC
17223
17224 #ifdef _DEBUG
17225     ((CObjectHeader*)o)->Validate();
17226 #endif //_DEBUG
17227
17228     dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
17229
17230     //needs to be called before the marking because it is possible for a foreground
17231     //gc to take place during the mark and move the object
17232     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetMethodTable() : NULL);
17233
17234     hpt->background_mark_simple (o THREAD_NUMBER_ARG);
17235 }
17236
17237 //used by the ephemeral collection to scan the local background structures
17238 //containing references.
17239 void
17240 gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
17241 {
17242     ScanContext sc;
17243     if (pSC == 0)
17244         pSC = &sc;
17245
17246     pSC->thread_number = hn;
17247
17248 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
17249     pSC->pCurrentDomain = 0;
17250 #endif
17251
17252     BOOL relocate_p = (fn == &GCHeap::Relocate);
17253
17254     dprintf (3, ("Scanning background mark list"));
17255
17256     //scan mark_list
17257     size_t mark_list_finger = 0;
17258     while (mark_list_finger < c_mark_list_index)
17259     {
17260         BYTE** o = &c_mark_list [mark_list_finger];
17261         if (!relocate_p)
17262         {
17263             // We may not be able to calculate the size during relocate as POPO
17264             // may have written over the object.
17265             size_t s = size (*o);
17266             assert (Align (s) >= Align (min_obj_size));
17267             dprintf(3,("background root %Ix", (size_t)*o));
17268         }
17269         (*fn) ((Object**)o, pSC, 0);
17270         mark_list_finger++;
17271     }
17272
17273     //scan the mark stack
17274     dprintf (3, ("Scanning background mark stack"));
17275
17276     BYTE** finger = background_mark_stack_array;
17277     while (finger < background_mark_stack_tos)
17278     {
17279         if ((finger + 1) < background_mark_stack_tos)
17280         {
17281             // We need to check for the partial mark case here.
17282             BYTE* parent_obj = *(finger + 1);
17283             if ((size_t)parent_obj & 1)
17284             {
17285                 BYTE* place = *finger;
17286                 size_t place_offset = 0;
17287                 BYTE* real_parent_obj = (BYTE*)((size_t)parent_obj & ~1);
17288
17289                 if (relocate_p)
17290                 {
17291                     *(finger + 1) = real_parent_obj;
17292                     place_offset = place - real_parent_obj;
17293                     dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
17294                     (*fn) ((Object**)(finger + 1), pSC, 0);
17295                     real_parent_obj = *(finger + 1);
17296                     *finger = real_parent_obj + place_offset;
17297                     *(finger + 1) = (BYTE*)((size_t)real_parent_obj | 1);
17298                     dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
17299                 }
17300                 else
17301                 {
17302                     BYTE** temp = &real_parent_obj;
17303                     dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
17304                     (*fn) ((Object**)temp, pSC, 0);
17305                 }
17306
17307                 finger += 2;
17308                 continue;
17309             }
17310         }
17311         dprintf(3,("background root %Ix", (size_t)*finger));
17312         (*fn) ((Object**)finger, pSC, 0);
17313         finger++;
17314     }
17315 }
17316
17317 #endif //BACKGROUND_GC
17318
17319
17320 void gc_heap::fix_card_table ()
17321 {
17322 #ifdef WRITE_WATCH
17323     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
17324
17325     PREFIX_ASSUME(seg != NULL);
17326
17327     DWORD granularity;
17328 #ifdef BACKGROUND_GC
17329     DWORD mode = settings.concurrent ? 1 : 0;
17330 #else //BACKGROUND_GC
17331     DWORD mode = 0;
17332 #endif //BACKGROUND_GC
17333     BOOL small_object_segments = TRUE;
17334     while (1)
17335     {
17336         if (seg == 0)
17337         {
17338             if (small_object_segments)
17339             {
17340                 small_object_segments = FALSE;
17341                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
17342
17343                 PREFIX_ASSUME(seg != NULL);
17344
17345                 continue;
17346             }
17347             else
17348     {
17349                 break;
17350             }
17351         }
17352         BYTE* base_address = align_lower_page (heap_segment_mem (seg));
17353         BYTE* high_address =  align_on_page (
17354             (seg != ephemeral_heap_segment) ?
17355             heap_segment_allocated (seg) :
17356             generation_allocation_start (generation_of (0))
17357             );
17358         ULONG_PTR bcount = array_size;
17359         do
17360         {
17361             if(high_address <= base_address)
17362                 break;
17363
17364             size_t region_size = high_address - base_address;
17365             assert (region_size > 0);
17366             dprintf (3,("Probing pages [%Ix, %Ix[", (size_t)base_address, (size_t)high_address));
17367
17368 #ifdef TIME_WRITE_WATCH
17369             unsigned int time_start = GetCycleCount32();
17370 #endif //TIME_WRITE_WATCH
17371             UINT status = GetWriteWatch (mode, base_address, region_size,
17372                                           (PVOID*)g_addresses,
17373                                           &bcount, &granularity);
17374             assert (status == 0);
17375
17376 #ifdef TIME_WRITE_WATCH
17377             unsigned int time_stop = GetCycleCount32();
17378             tot_cycles += time_stop - time_start;
17379             printf ("GetWriteWatch Duration: %d, total: %d\n",
17380                     time_stop - time_start, tot_cycles);
17381 #endif //TIME_WRITE_WATCH
17382
17383             assert( ((card_size * card_word_width)&(OS_PAGE_SIZE-1))==0 );
17384             assert (granularity == OS_PAGE_SIZE);
17385             //printf ("%Ix written into\n", bcount);
17386             dprintf (3,("Found %Id pages written", bcount));
17387             for (unsigned  i = 0; i < bcount; i++)
17388             {
17389                 for (unsigned j = 0; j< (card_size*card_word_width)/OS_PAGE_SIZE; j++)
17390                 {
17391                     card_table [card_word (card_of (g_addresses [i]))+j] = ~0u;
17392                 }
17393                 dprintf (2,("Set Cards [%Ix:%Ix, %Ix:%Ix[",
17394                       card_of (g_addresses [i]), (size_t)g_addresses [i],
17395                       card_of (g_addresses [i]+OS_PAGE_SIZE), (size_t)g_addresses [i]+OS_PAGE_SIZE));
17396             }
17397             if (bcount >= array_size){
17398                 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
17399                 bcount = array_size;
17400             }
17401         } while (bcount >= array_size);
17402         seg = heap_segment_next_rw (seg);
17403     }
17404 #ifdef BACKGROUND_GC
17405     if (settings.concurrent)
17406     {
17407         //reset the ephemeral page allocated by generation_of (0)
17408         BYTE* base_address =
17409             align_on_page (generation_allocation_start (generation_of (0)));
17410         size_t region_size =
17411             heap_segment_allocated (ephemeral_heap_segment) - base_address;
17412         ResetWriteWatch (base_address, region_size);
17413     }
17414 #endif //BACKGROUND_GC
17415 #endif //WRITE_WATCH
17416 }
17417
17418 #ifdef BACKGROUND_GC
17419 inline
17420 void gc_heap::background_mark_through_object (BYTE* oo THREAD_NUMBER_DCL)
17421 {
17422     if (contain_pointers (oo))
17423     {
17424         size_t total_refs = 0;
17425         size_t s = size (oo);
17426         go_through_object_nostart (method_table(oo), oo, s, po,
17427                           {
17428                             BYTE* o = *po;
17429                             total_refs++;
17430                             background_mark_object (o THREAD_NUMBER_ARG);
17431                           }
17432             );
17433
17434         dprintf (3,("Background marking through %Ix went through %Id refs", 
17435                           (size_t)oo,
17436                            total_refs));
17437     }
17438 }
17439
17440 BYTE* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
17441 {
17442     if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
17443     {
17444         // for now we stop at where gen1 started when we started processing 
17445         return background_min_soh_overflow_address;
17446     }
17447     else
17448     {
17449         return heap_segment_allocated (seg);
17450     }
17451 }
17452
17453 BYTE* gc_heap::background_first_overflow (BYTE* min_add,
17454                                           heap_segment* seg,
17455                                           BOOL concurrent_p, 
17456                                           BOOL small_object_p)
17457 {
17458     BYTE* o = 0;
17459
17460     if (small_object_p)
17461     {
17462         if (in_range_for_segment (min_add, seg))
17463         {
17464             // min_add was the beginning of gen1 when we did the concurrent
17465             // overflow. Now we could be in a situation where min_add is
17466             // actually the same as allocated for that segment (because
17467             // we expanded heap), in which case we can not call 
17468             // find first on this address or we will AV.
17469             if (min_add >= heap_segment_allocated (seg))
17470             {
17471                 return min_add;
17472             }
17473             else
17474             {
17475                 if (concurrent_p && 
17476                     ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
17477                 {
17478                     return background_min_soh_overflow_address;
17479                 }
17480                 else
17481                 {
17482                     o = find_first_object (min_add, heap_segment_mem (seg));
17483                     return o;
17484                 }
17485             }
17486         }
17487     }
17488
17489     o = max (heap_segment_mem (seg), min_add);
17490     return o;
17491 }
17492
17493 void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
17494                                                          BYTE* min_add, BYTE* max_add,
17495                                                          BOOL concurrent_p)
17496 {
17497     if (concurrent_p)
17498     {
17499         current_bgc_state = bgc_overflow_soh;
17500     }
17501
17502     size_t total_marked_objects = 0;
17503
17504 #ifdef MULTIPLE_HEAPS
17505     int thread = heap_number;
17506 #endif //MULTIPLE_HEAPS
17507
17508     exclusive_sync* loh_alloc_lock = 0;
17509
17510     dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
17511 #ifdef MULTIPLE_HEAPS
17512     // We don't have each heap scan all heaps concurrently because we are worried about
17513     // multiple threads calling things like find_first_object.
17514     int h_start = (concurrent_p ? heap_number : 0);
17515     int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
17516     for (int hi = h_start; hi < h_end; hi++)
17517     {
17518         gc_heap*  hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
17519
17520 #else
17521     {
17522         gc_heap*  hp = 0;
17523
17524 #endif //MULTIPLE_HEAPS
17525         BOOL small_object_segments = TRUE;
17526         int align_const = get_alignment_constant (small_object_segments);
17527         generation* gen = hp->generation_of (condemned_gen_number);
17528         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
17529         PREFIX_ASSUME(seg != NULL);
17530         loh_alloc_lock = hp->bgc_alloc_lock;
17531
17532         BYTE*  o = hp->background_first_overflow (min_add, 
17533                                                   seg, 
17534                                                   concurrent_p, 
17535                                                   small_object_segments);
17536
17537         while (1)
17538         {
17539             while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
17540             {
17541                 dprintf (3, ("considering %Ix", (size_t)o));
17542
17543                 size_t s;
17544
17545                 if (concurrent_p && !small_object_segments)
17546                 {
17547                     loh_alloc_lock->bgc_mark_set (o);
17548
17549                     if (((CObjectHeader*)o)->IsFree())
17550                     {
17551                         s = unused_array_size (o);
17552                     }
17553                     else
17554                     {
17555                         s = size (o);
17556                     }
17557                 }
17558                 else
17559                 {
17560                     s = size (o);
17561                 }
17562
17563                 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
17564                 {
17565                     total_marked_objects++;
17566                     go_through_object_cl (method_table(o), o, s, poo,
17567                                           BYTE* oo = *poo;
17568                                           background_mark_object (oo THREAD_NUMBER_ARG);
17569                                          );
17570                 }
17571
17572                 if (concurrent_p && !small_object_segments)
17573                 {
17574                     loh_alloc_lock->bgc_mark_done ();
17575                 }
17576
17577                 o = o + Align (s, align_const);
17578
17579                 if (concurrent_p)
17580                 {
17581                     allow_fgc();
17582                 }
17583             }
17584
17585             dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)", 
17586                 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
17587
17588             if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
17589                 (seg = heap_segment_next_in_range (seg)) == 0)
17590             {
17591                 if (small_object_segments)
17592                 {
17593                     if (concurrent_p)
17594                     {
17595                         current_bgc_state = bgc_overflow_loh;
17596                     }
17597
17598                     dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
17599                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
17600                     concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
17601                     total_marked_objects = 0;
17602                     small_object_segments = FALSE;
17603                     align_const = get_alignment_constant (small_object_segments);
17604                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
17605
17606                     PREFIX_ASSUME(seg != NULL);
17607
17608                     o = max (heap_segment_mem (seg), min_add);
17609                     continue;
17610                 }
17611                 else
17612                 {
17613                     dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
17614                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
17615                     break;
17616                 }
17617             } 
17618             else
17619             {
17620                 o = hp->background_first_overflow (min_add, 
17621                                                    seg, 
17622                                                    concurrent_p, 
17623                                                    small_object_segments);
17624                 continue;
17625             }
17626         }
17627     }
17628 }
17629
17630 BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
17631 {
17632     BOOL grow_mark_array_p = TRUE;
17633
17634     if (concurrent_p)
17635     {
17636         assert (!processed_soh_overflow_p);
17637
17638         if ((background_max_overflow_address != 0) &&
17639             (background_min_overflow_address != MAX_PTR))
17640         {
17641             // We have overflow to process but we know we can't process the ephemeral generations
17642             // now (we actually could process till the current gen1 start but since we are going to 
17643             // make overflow per segment, for now I'll just stop at the saved gen1 start.
17644             saved_overflow_ephemeral_seg = ephemeral_heap_segment;
17645             background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
17646             background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
17647         }
17648     }
17649     else
17650     {
17651         assert ((saved_overflow_ephemeral_seg == 0) || 
17652                 ((background_max_soh_overflow_address != 0) &&
17653                  (background_min_soh_overflow_address != MAX_PTR)));
17654         
17655         if (!processed_soh_overflow_p)
17656         {
17657             // if there was no more overflow we just need to process what we didn't process 
17658             // on the saved ephemeral segment.
17659             if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
17660             {
17661                 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
17662                 grow_mark_array_p = FALSE;
17663             }
17664
17665             background_min_overflow_address = min (background_min_overflow_address, 
17666                                                 background_min_soh_overflow_address);
17667             background_max_overflow_address = max (background_max_overflow_address,
17668                                                 background_max_soh_overflow_address);
17669             processed_soh_overflow_p = TRUE;
17670         }
17671     }
17672
17673     BOOL  overflow_p = FALSE;
17674 recheck:
17675     if ((! ((background_max_overflow_address == 0)) ||
17676          ! ((background_min_overflow_address == MAX_PTR))))
17677     {
17678         overflow_p = TRUE;
17679
17680         if (grow_mark_array_p)
17681         {
17682             // Try to grow the array.
17683             size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
17684
17685             if ((new_size * sizeof(mark)) > 100*1024)
17686             {
17687                 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
17688
17689                 new_size = min(new_max_size, new_size);
17690             }
17691
17692             if ((background_mark_stack_array_length < new_size) && 
17693                 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
17694             {
17695                 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
17696
17697                 BYTE** tmp = new (nothrow) (BYTE* [new_size]);
17698                 if (tmp)
17699                 {
17700                     delete background_mark_stack_array;
17701                     background_mark_stack_array = tmp;
17702                     background_mark_stack_array_length = new_size;
17703                     background_mark_stack_tos = background_mark_stack_array;
17704                 }
17705             }
17706         }
17707         else
17708         {
17709             grow_mark_array_p = TRUE;
17710         }
17711
17712         BYTE*  min_add = background_min_overflow_address;
17713         BYTE*  max_add = background_max_overflow_address;
17714
17715         background_max_overflow_address = 0;
17716         background_min_overflow_address = MAX_PTR;
17717
17718         background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
17719         if (!concurrent_p)
17720         {        
17721             goto recheck;
17722         }
17723     }
17724
17725     return overflow_p;
17726 }
17727
17728 #endif //BACKGROUND_GC
17729
17730 inline
17731 void gc_heap::mark_through_object (BYTE* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
17732 {
17733 #ifndef COLLECTIBLE_CLASS
17734     BOOL to_mark_class_object = FALSE;
17735 #else //COLLECTIBLE_CLASS
17736     BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
17737 #endif //COLLECTIBLE_CLASS
17738     if (contain_pointers (oo) || to_mark_class_object)
17739     {
17740         dprintf(3,( "Marking through %Ix", (size_t)oo));
17741         size_t s = size (oo);
17742
17743 #ifdef COLLECTIBLE_CLASS
17744         if (to_mark_class_object)
17745         {
17746             BYTE* class_obj = get_class_object (oo);
17747             mark_object (class_obj THREAD_NUMBER_ARG);
17748         }
17749 #endif //COLLECTIBLE_CLASS
17750
17751         if (contain_pointers (oo))
17752         {
17753             go_through_object_nostart (method_table(oo), oo, s, po,
17754                                 BYTE* o = *po;
17755                                 mark_object (o THREAD_NUMBER_ARG);
17756                                 );
17757         }
17758     }
17759 }
17760
17761 size_t gc_heap::get_total_heap_size()
17762 {
17763     size_t total_heap_size = 0;
17764
17765 #ifdef MULTIPLE_HEAPS
17766     int hn = 0;
17767
17768     for (hn = 0; hn < gc_heap::n_heaps; hn++)
17769     {
17770         gc_heap* hp2 = gc_heap::g_heaps [hn];
17771         total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
17772     }
17773 #else
17774     total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
17775 #endif //MULTIPLE_HEAPS
17776
17777     return total_heap_size;
17778 }
17779
17780 //returns TRUE is an overflow happened.
17781 BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
17782 {
17783     BOOL  overflow_p = FALSE;
17784 recheck:
17785     if ((! (max_overflow_address == 0) ||
17786          ! (min_overflow_address == MAX_PTR)))
17787     {
17788         overflow_p = TRUE;
17789         // Try to grow the array.
17790         size_t new_size =
17791             max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
17792
17793         if ((new_size * sizeof(mark)) > 100*1024)
17794         {
17795             size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
17796
17797             new_size = min(new_max_size, new_size);
17798         }
17799
17800         if ((mark_stack_array_length < new_size) && 
17801             ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
17802         {
17803             mark* tmp = new (nothrow) (mark [new_size]);
17804             if (tmp)
17805             {
17806                 delete mark_stack_array;
17807                 mark_stack_array = tmp;
17808                 mark_stack_array_length = new_size;
17809             }
17810         }
17811
17812         BYTE*  min_add = min_overflow_address;
17813         BYTE*  max_add = max_overflow_address;
17814         max_overflow_address = 0;
17815         min_overflow_address = MAX_PTR;
17816         process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
17817         goto recheck;
17818     }
17819
17820     return overflow_p;
17821 }
17822
17823 void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
17824                                               BYTE* min_add, BYTE* max_add)
17825 {
17826 #ifdef MULTIPLE_HEAPS
17827     int thread = heap_number;
17828 #endif //MULTIPLE_HEAPS
17829     BOOL  full_p = (condemned_gen_number == max_generation);
17830
17831         dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
17832 #ifdef MULTIPLE_HEAPS
17833             for (int hi = 0; hi < n_heaps; hi++)
17834             {
17835                 gc_heap*  hp = g_heaps [(heap_number + hi) % n_heaps];
17836
17837 #else
17838             {
17839                 gc_heap*  hp = 0;
17840
17841 #endif //MULTIPLE_HEAPS
17842         BOOL small_object_segments = TRUE;
17843         int align_const = get_alignment_constant (small_object_segments);
17844         generation* gen = hp->generation_of (condemned_gen_number);
17845         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
17846         
17847         PREFIX_ASSUME(seg != NULL);
17848         BYTE*  o = max (heap_segment_mem (seg), min_add);
17849         while (1)
17850         {
17851             BYTE*  end = heap_segment_allocated (seg);
17852
17853             while ((o < end) && (o <= max_add))
17854             {
17855                 assert ((min_add <= o) && (max_add >= o));
17856                 dprintf (3, ("considering %Ix", (size_t)o));
17857                 if (marked (o))
17858                 {
17859                     mark_through_object (o, TRUE THREAD_NUMBER_ARG);
17860                 }
17861
17862                 o = o + Align (size (o), align_const);
17863             }
17864
17865             if (( seg = heap_segment_next_in_range (seg)) == 0)
17866             {
17867                 if (small_object_segments && full_p)
17868                 {
17869                     small_object_segments = FALSE;
17870                     align_const = get_alignment_constant (small_object_segments);
17871                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
17872
17873                     PREFIX_ASSUME(seg != NULL);
17874
17875                     o = max (heap_segment_mem (seg), min_add);
17876                     continue;
17877                 }
17878                 else
17879                 {
17880                     break;
17881                 } 
17882             } 
17883             else
17884             {
17885                 o = max (heap_segment_mem (seg), min_add);
17886                 continue;
17887             }
17888         }
17889     }
17890 }
17891
17892 inline 
17893 void fire_mark_event (int heap_num, int mark_num)
17894 {
17895     switch(mark_num)
17896     {
17897         case ETW_TYPE_GC_MARK_1:
17898             FireEtwGCMarkStackRoots(heap_num, GetClrInstanceId());
17899             FireEtwPrvGCMarkStackRoots_V1(heap_num, GetClrInstanceId());
17900             break;
17901
17902         case ETW_TYPE_GC_MARK_2:
17903             FireEtwGCMarkFinalizeQueueRoots(heap_num, GetClrInstanceId());
17904             FireEtwPrvGCMarkFinalizeQueueRoots_V1(heap_num, GetClrInstanceId());
17905             break;
17906
17907         case ETW_TYPE_GC_MARK_3:
17908             FireEtwGCMarkHandles(heap_num, GetClrInstanceId());
17909             FireEtwPrvGCMarkHandles_V1(heap_num, GetClrInstanceId());
17910             break;
17911
17912         case ETW_TYPE_GC_MARK_4:
17913             FireEtwGCMarkOlderGenerationRoots(heap_num, GetClrInstanceId());
17914             FireEtwPrvGCMarkCards_V1(heap_num, GetClrInstanceId());
17915             break;
17916
17917         default:
17918             _ASSERTE(mark_num==ETW_TYPE_GC_MARK_1 || mark_num==ETW_TYPE_GC_MARK_2 || mark_num==ETW_TYPE_GC_MARK_3 || mark_num==ETW_TYPE_GC_MARK_4);
17919             break;
17920     }
17921 }
17922
17923 // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
17924 // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
17925 // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
17926 // promotion scan multiple times.
17927 // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
17928 // also has the effect of processing any mark stack overflow.
17929
17930 #ifdef MULTIPLE_HEAPS
17931 // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
17932 // worker threads synchronized. The algorithms are sufficiently divergent that we have different
17933 // implementations based on whether MULTIPLE_HEAPS is defined or not.
17934 //
17935 // Define some static variables used for synchronization in the method below. These should really be defined
17936 // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
17937 //
17938 // A note about the synchronization used within this method. Communication between the worker threads is
17939 // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
17940 // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
17941 // protection of a join.
17942 static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
17943 static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
17944 static VOLATILE(BOOL) s_fScanRequired;
17945 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
17946 {
17947     // Whenever we call this method there may have been preceding object promotions. So set
17948     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
17949     // based on the how the scanning proceeded).
17950     s_fUnscannedPromotions = TRUE;
17951
17952     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
17953     // the state of this thread's portion of the dependent handle table. That's because promotions on other
17954     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
17955     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
17956     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
17957     // as all the others or they'll get out of step).
17958     while (true)
17959     {
17960         // The various worker threads are all currently racing in this code. We need to work out if at least
17961         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
17962         // dependent handle table when both of the following conditions apply:
17963         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
17964         //     object happens to correspond to a primary in one of our handles we might potentially have to
17965         //     promote the associated secondary).
17966         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
17967         //
17968         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
17969         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
17970         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
17971         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
17972         // follows below. Note that we can't read this outside of the join since on any iteration apart from
17973         // the first threads will be racing between reading this value and completing their previous
17974         // iteration's table scan.
17975         //
17976         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
17977         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
17978         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
17979         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
17980         // we're safely joined.
17981         if (CNameSpace::GcDhUnpromotedHandlesExist(sc))
17982             s_fUnpromotedHandles = TRUE;
17983
17984         // Synchronize all the threads so we can read our state variables safely. The shared variable
17985         // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
17986         // a single thread inside the join.
17987         gc_t_join.join(this, gc_join_scan_dependent_handles);
17988         if (gc_t_join.joined())
17989         {
17990             // We're synchronized so it's safe to read our shared state variables. We update another shared
17991             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
17992             // the loop. We scan if there has been at least one object promotion since last time and at least
17993             // one thread has a dependent handle table with a potential handle promotion possible.
17994             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
17995
17996             // Reset our shared state variables (ready to be set again on this scan or with a good initial
17997             // value for the next call if we're terminating the loop).
17998             s_fUnscannedPromotions = FALSE;
17999             s_fUnpromotedHandles = FALSE;
18000
18001             if (!s_fScanRequired)
18002             {
18003                 // We're terminating the loop. Perform any last operations that require single threaded access.
18004                 if (!initial_scan_p)
18005                 {
18006                     // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
18007                     // load balance if some of the heaps have an abnormally large workload.
18008                     BYTE* all_heaps_max = 0;
18009                     BYTE* all_heaps_min = MAX_PTR;
18010                     int i;
18011                     for (i = 0; i < n_heaps; i++)
18012                     {
18013                         if (all_heaps_max < g_heaps[i]->max_overflow_address)
18014                             all_heaps_max = g_heaps[i]->max_overflow_address;
18015                         if (all_heaps_min > g_heaps[i]->min_overflow_address)
18016                             all_heaps_min = g_heaps[i]->min_overflow_address;
18017                     }
18018                     for (i = 0; i < n_heaps; i++)
18019                     {
18020                         g_heaps[i]->max_overflow_address = all_heaps_max;
18021                         g_heaps[i]->min_overflow_address = all_heaps_min;
18022                     }
18023                 }
18024             }
18025
18026             // Restart all the workers.
18027             dprintf(3, ("Starting all gc thread mark stack overflow processing"));
18028             gc_t_join.restart();
18029         }
18030
18031         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
18032         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
18033         // global flag indicating that at least one object promotion may have occurred (the usual comment
18034         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
18035         // exit the method since we unconditionally set this variable on method entry anyway).
18036         if (process_mark_overflow(condemned_gen_number))
18037             s_fUnscannedPromotions = TRUE;
18038
18039         // If we decided that no scan was required we can terminate the loop now.
18040         if (!s_fScanRequired)
18041             break;
18042
18043         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
18044         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
18045         // could miss noting the promotion of some primary objects).
18046         gc_t_join.join(this, gc_join_rescan_dependent_handles);
18047         if (gc_t_join.joined())
18048         {
18049             // Restart all the workers.
18050             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
18051             gc_t_join.restart();
18052         }
18053
18054         // If the portion of the dependent handle table managed by this worker has handles that could still be
18055         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
18056         // could require a rescan of handles on this or other workers.
18057         if (CNameSpace::GcDhUnpromotedHandlesExist(sc))
18058             if (CNameSpace::GcDhReScan(sc))
18059                 s_fUnscannedPromotions = TRUE;
18060     }
18061 }
18062 #else //MULTIPLE_HEAPS
18063 // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
18064 // threads synchronized.
18065 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
18066 {
18067     // Whenever we call this method there may have been preceding object promotions. So set
18068     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
18069     // based on the how the scanning proceeded).
18070     bool fUnscannedPromotions = true;
18071
18072     // Loop until there are either no more dependent handles that can have their secondary promoted or we've
18073     // managed to perform a scan without promoting anything new.
18074     while (CNameSpace::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
18075     {
18076         // On each iteration of the loop start with the assumption that no further objects have been promoted.
18077         fUnscannedPromotions = false;
18078
18079         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
18080         // being visible. If there was an overflow (process_mark_overflow returned true) then additional
18081         // objects now appear to be promoted and we should set the flag.
18082         if (process_mark_overflow(condemned_gen_number))
18083             fUnscannedPromotions = true;
18084
18085         // Perform the scan and set the flag if any promotions resulted.
18086         if (CNameSpace::GcDhReScan(sc))
18087             fUnscannedPromotions = true;
18088     }
18089
18090     // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
18091     // scan any handles at all this is the processing of overflows that may have occured prior to this method
18092     // invocation).
18093     process_mark_overflow(condemned_gen_number);
18094 }
18095 #endif //MULTIPLE_HEAPS
18096
18097 void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
18098 {
18099     assert (settings.concurrent == FALSE);
18100
18101     ScanContext sc;
18102     sc.thread_number = heap_number;
18103     sc.promotion = TRUE;
18104     sc.concurrent = FALSE;
18105
18106     dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
18107     BOOL  full_p = (condemned_gen_number == max_generation);
18108
18109 #ifdef TIME_GC
18110     unsigned start;
18111     unsigned finish;
18112     start = GetCycleCount32();
18113 #endif //TIME_GC
18114
18115     int gen_to_init = condemned_gen_number;
18116     if (condemned_gen_number == max_generation)
18117     {
18118         gen_to_init = max_generation + 1;
18119     }
18120     for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
18121     {
18122         dynamic_data* dd = dynamic_data_of (gen_idx);
18123         dd_begin_data_size (dd) = generation_size (gen_idx) - 
18124                                    dd_fragmentation (dd) -
18125                                    Align (size (generation_allocation_start (generation_of (gen_idx))));
18126         dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
18127         dd_survived_size (dd) = 0;
18128         dd_pinned_survived_size (dd) = 0;
18129         dd_artificial_pinned_survived_size (dd) = 0;
18130         dd_added_pinned_size (dd) = 0;
18131 #ifdef SHORT_PLUGS
18132         dd_padding_size (dd) = 0;
18133 #endif //SHORT_PLUGS
18134 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
18135         dd_num_npinned_plugs (dd) = 0;
18136 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
18137     }
18138
18139 #ifdef FFIND_OBJECT
18140     if (gen0_must_clear_bricks > 0)
18141         gen0_must_clear_bricks--;
18142 #endif //FFIND_OBJECT
18143
18144     promoted_bytes (heap_number) = 0;
18145     reset_mark_stack();
18146
18147 #ifdef SNOOP_STATS
18148     memset (&snoop_stat, 0, sizeof(snoop_stat));
18149     snoop_stat.heap_index = heap_number;
18150 #endif //SNOOP_STATS
18151
18152 #ifdef MH_SC_MARK
18153     if (full_p)
18154     {
18155         //initialize the mark stack
18156         for (int i = 0; i < max_snoop_level; i++)
18157         {
18158             ((BYTE**)(mark_stack_array))[i] = 0;
18159         }
18160
18161         mark_stack_busy() = 1;
18162     }
18163 #endif //MH_SC_MARK
18164
18165     static DWORD num_sizedrefs = 0;
18166
18167 #ifdef MH_SC_MARK
18168     static BOOL do_mark_steal_p = FALSE;
18169 #endif //MH_SC_MARK
18170
18171 #ifdef MULTIPLE_HEAPS
18172     gc_t_join.join(this, gc_join_begin_mark_phase);
18173     if (gc_t_join.joined())
18174     {
18175 #endif //MULTIPLE_HEAPS
18176
18177         num_sizedrefs = SystemDomain::System()->GetTotalNumSizedRefHandles();
18178
18179 #ifdef MULTIPLE_HEAPS
18180
18181 #ifdef MH_SC_MARK
18182         if (full_p)
18183         {
18184             size_t total_heap_size = get_total_heap_size();
18185
18186             if (total_heap_size > (100 * 1024 * 1024))
18187             {
18188                 do_mark_steal_p = TRUE;
18189             }
18190             else
18191             {
18192                 do_mark_steal_p = FALSE;
18193             }
18194         }
18195         else
18196         {
18197             do_mark_steal_p = FALSE;
18198         }
18199 #endif //MH_SC_MARK
18200
18201         gc_t_join.restart();
18202     }
18203 #endif //MULTIPLE_HEAPS
18204
18205     {
18206
18207 #ifdef MARK_LIST
18208         //set up the mark lists from g_mark_list
18209         assert (g_mark_list);
18210 #ifdef MULTIPLE_HEAPS
18211         mark_list = &g_mark_list [heap_number*mark_list_size];
18212 #else
18213         mark_list = g_mark_list;
18214 #endif //MULTIPLE_HEAPS
18215         //dont use the mark list for full gc
18216         //because multiple segments are more complex to handle and the list
18217         //is likely to overflow
18218         if (condemned_gen_number != max_generation)
18219             mark_list_end = &mark_list [mark_list_size-1];
18220         else
18221             mark_list_end = &mark_list [0];
18222         mark_list_index = &mark_list [0];
18223 #endif //MARK_LIST
18224
18225         shigh = (BYTE*) 0;
18226         slow  = MAX_PTR;
18227
18228         //%type%  category = quote (mark);
18229
18230         if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
18231         {
18232             CNameSpace::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
18233
18234 #ifdef MULTIPLE_HEAPS
18235             gc_t_join.join(this, gc_join_scan_sizedref_done);
18236             if (gc_t_join.joined())
18237             {
18238                 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
18239                 gc_t_join.restart();
18240             }
18241 #endif //MULTIPLE_HEAPS
18242         }
18243     
18244         dprintf(3,("Marking Roots"));
18245
18246         CNameSpace::GcScanRoots(GCHeap::Promote,
18247                                 condemned_gen_number, max_generation,
18248                                 &sc);
18249
18250         fire_mark_event (heap_number, ETW_TYPE_GC_MARK_1);
18251
18252 #ifdef BACKGROUND_GC
18253         if (recursive_gc_sync::background_running_p())
18254         {
18255             scan_background_roots (GCHeap::Promote, heap_number, &sc);
18256         }
18257 #endif //BACKGROUND_GC
18258
18259 #ifdef FEATURE_PREMORTEM_FINALIZATION
18260         dprintf(3, ("Marking finalization data"));
18261         finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
18262 #endif // FEATURE_PREMORTEM_FINALIZATION
18263
18264         fire_mark_event (heap_number, ETW_TYPE_GC_MARK_2);
18265
18266 // MTHTS
18267         {
18268
18269             dprintf(3,("Marking handle table"));
18270             CNameSpace::GcScanHandles(GCHeap::Promote,
18271                                       condemned_gen_number, max_generation,
18272                                       &sc);
18273             fire_mark_event (heap_number, ETW_TYPE_GC_MARK_3);
18274         }
18275
18276 #ifdef TRACE_GC
18277         size_t promoted_before_cards = promoted_bytes (heap_number);
18278 #endif //TRACE_GC
18279
18280         dprintf (3, ("before cards: %Id", promoted_before_cards));
18281         if (!full_p)
18282         {
18283 #ifdef CARD_BUNDLE
18284 #ifdef MULTIPLE_HEAPS
18285             if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
18286             {
18287 #endif //MULTIPLE_HEAPS
18288
18289                 update_card_table_bundle ();
18290
18291 #ifdef MULTIPLE_HEAPS
18292                 gc_t_join.r_restart();
18293             }
18294 #endif //MULTIPLE_HEAPS
18295 #endif //CARD_BUNDLE
18296
18297             card_fn mark_object_fn = &gc_heap::mark_object_simple;
18298 #ifdef HEAP_ANALYZE
18299             heap_analyze_success = TRUE;
18300             if (heap_analyze_enabled)
18301             {
18302                 internal_root_array_index = 0;
18303                 current_obj = 0;
18304                 current_obj_size = 0;
18305                 mark_object_fn = &gc_heap::ha_mark_object_simple;
18306             }
18307 #endif //HEAP_ANALYZE
18308
18309             dprintf(3,("Marking cross generation pointers"));
18310             mark_through_cards_for_segments (mark_object_fn, FALSE);
18311
18312             dprintf(3,("Marking cross generation pointers for large objects"));
18313             mark_through_cards_for_large_objects (mark_object_fn, FALSE);
18314
18315             dprintf (3, ("marked by cards: %Id", 
18316                 (promoted_bytes (heap_number) - promoted_before_cards)));
18317             fire_mark_event (heap_number, ETW_TYPE_GC_MARK_4);
18318         }
18319     }
18320
18321 #ifdef MH_SC_MARK
18322     if (do_mark_steal_p)
18323     {
18324         mark_steal();
18325     }
18326 #endif //MH_SC_MARK
18327
18328     // Dependent handles need to be scanned with a special algorithm (see the header comment on
18329     // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
18330     // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
18331     // but in a common case (where there are no dependent handles that are due to be collected) it allows us
18332     // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
18333     // iterations if required and will also perform processing of any mark stack overflow once the dependent
18334     // handle table has been fully promoted.
18335     CNameSpace::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
18336     scan_dependent_handles(condemned_gen_number, &sc, true);
18337
18338 #ifdef MULTIPLE_HEAPS
18339     dprintf(3, ("Joining for short weak handle scan"));
18340     gc_t_join.join(this, gc_join_null_dead_short_weak);
18341     if (gc_t_join.joined())
18342 #endif //MULTIPLE_HEAPS
18343     {
18344 #ifdef HEAP_ANALYZE
18345         heap_analyze_enabled = FALSE;
18346         DACNotifyGcMarkEnd(condemned_gen_number);
18347 #endif // HEAP_ANALYZE
18348         GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
18349
18350 #ifdef MULTIPLE_HEAPS
18351         if (!full_p)
18352         {
18353             // we used r_join and need to reinitialize states for it here.
18354             gc_t_join.r_init();
18355         }
18356
18357         //start all threads on the roots.
18358         dprintf(3, ("Starting all gc thread for short weak handle scan"));
18359         gc_t_join.restart();
18360 #endif //MULTIPLE_HEAPS
18361
18362     }
18363
18364     // null out the target of short weakref that were not promoted.
18365     CNameSpace::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
18366
18367 // MTHTS: keep by single thread
18368 #ifdef MULTIPLE_HEAPS
18369     dprintf(3, ("Joining for finalization"));
18370     gc_t_join.join(this, gc_join_scan_finalization);
18371     if (gc_t_join.joined())
18372 #endif //MULTIPLE_HEAPS
18373
18374     {
18375 #ifdef MULTIPLE_HEAPS
18376         //start all threads on the roots.
18377         dprintf(3, ("Starting all gc thread for Finalization"));
18378         gc_t_join.restart();
18379 #endif //MULTIPLE_HEAPS
18380     }
18381
18382     //Handle finalization.
18383     size_t promoted_bytes_live = promoted_bytes (heap_number);
18384
18385 #ifdef FEATURE_PREMORTEM_FINALIZATION
18386     dprintf (3, ("Finalize marking"));
18387     finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
18388
18389 #ifdef GC_PROFILING
18390     if (CORProfilerTrackGC())
18391     {
18392         finalize_queue->WalkFReachableObjects (__this);
18393     }
18394 #endif //GC_PROFILING
18395 #endif // FEATURE_PREMORTEM_FINALIZATION
18396
18397     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
18398     // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
18399     scan_dependent_handles(condemned_gen_number, &sc, false);
18400
18401 #ifdef MULTIPLE_HEAPS
18402     dprintf(3, ("Joining for weak pointer deletion"));
18403     gc_t_join.join(this, gc_join_null_dead_long_weak);
18404     if (gc_t_join.joined())
18405     {
18406         //start all threads on the roots.
18407         dprintf(3, ("Starting all gc thread for weak pointer deletion"));
18408         gc_t_join.restart();
18409     }
18410 #endif //MULTIPLE_HEAPS
18411
18412     // null out the target of long weakref that were not promoted.
18413     CNameSpace::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
18414
18415 // MTHTS: keep by single thread
18416 #ifdef MULTIPLE_HEAPS
18417 #ifdef MARK_LIST
18418 #ifdef PARALLEL_MARK_LIST_SORT
18419 //    unsigned long start = GetCycleCount32();
18420     sort_mark_list();
18421 //    printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
18422 #endif //PARALLEL_MARK_LIST_SORT
18423 #endif //MARK_LIST
18424
18425     dprintf (3, ("Joining for sync block cache entry scanning"));
18426     gc_t_join.join(this, gc_join_null_dead_syncblk);
18427     if (gc_t_join.joined())
18428 #endif //MULTIPLE_HEAPS
18429     {
18430         // scan for deleted entries in the syncblk cache
18431         CNameSpace::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
18432
18433 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
18434         if (g_fEnableARM)
18435         {
18436             size_t promoted_all_heaps = 0;
18437 #ifdef MULTIPLE_HEAPS
18438             for (int i = 0; i < n_heaps; i++)
18439             {
18440                 promoted_all_heaps += promoted_bytes (i);
18441             }
18442 #else
18443             promoted_all_heaps = promoted_bytes (heap_number);
18444 #endif //MULTIPLE_HEAPS
18445             SystemDomain::RecordTotalSurvivedBytes (promoted_all_heaps);
18446         }
18447 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
18448
18449 #ifdef MULTIPLE_HEAPS
18450
18451 #ifdef MARK_LIST
18452 #ifndef PARALLEL_MARK_LIST_SORT
18453         //compact g_mark_list and sort it.
18454         combine_mark_lists();
18455 #endif //PARALLEL_MARK_LIST_SORT
18456 #endif //MARK_LIST
18457
18458         //decide on promotion
18459         if (settings.promotion != TRUE)
18460         {
18461             size_t m = 0;
18462             for (int n = 0; n <= condemned_gen_number;n++)
18463             {
18464                 m +=  (size_t)(dd_min_gc_size (dynamic_data_of (n))*(n+1)*0.1);
18465             }
18466
18467             for (int i = 0; i < n_heaps; i++)
18468             {
18469                 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
18470                                                                      max_generation));
18471                 size_t older_gen_size = (dd_current_size (dd) +
18472                                          (dd_desired_allocation (dd) -
18473                                          dd_new_allocation (dd)));
18474
18475                 if ((m > (older_gen_size)) ||
18476                     (promoted_bytes (i) > m))
18477                 {
18478                     settings.promotion = TRUE;
18479                 }
18480             }
18481         }
18482
18483 #ifdef SNOOP_STATS
18484         if (do_mark_steal_p)
18485         {
18486             size_t objects_checked_count = 0;
18487             size_t zero_ref_count = 0;
18488             size_t objects_marked_count = 0;
18489             size_t check_level_count = 0;
18490             size_t busy_count = 0;
18491             size_t interlocked_count = 0;
18492             size_t partial_mark_parent_count = 0;
18493             size_t stolen_or_pm_count = 0; 
18494             size_t stolen_entry_count = 0; 
18495             size_t pm_not_ready_count = 0; 
18496             size_t normal_count = 0;
18497             size_t stack_bottom_clear_count = 0;
18498
18499             for (int i = 0; i < n_heaps; i++)
18500             {
18501                 gc_heap* hp = g_heaps[i];
18502                 hp->print_snoop_stat();
18503                 objects_checked_count += hp->snoop_stat.objects_checked_count;
18504                 zero_ref_count += hp->snoop_stat.zero_ref_count;
18505                 objects_marked_count += hp->snoop_stat.objects_marked_count;
18506                 check_level_count += hp->snoop_stat.check_level_count;
18507                 busy_count += hp->snoop_stat.busy_count;
18508                 interlocked_count += hp->snoop_stat.interlocked_count;
18509                 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
18510                 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
18511                 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
18512                 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
18513                 normal_count += hp->snoop_stat.normal_count;
18514                 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
18515             }
18516
18517             fflush (stdout);
18518
18519             printf ("-------total stats-------\n");
18520             printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
18521                 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18522             printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18523                 objects_checked_count,
18524                 zero_ref_count,
18525                 objects_marked_count,
18526                 check_level_count,
18527                 busy_count,
18528                 interlocked_count,
18529                 partial_mark_parent_count,
18530                 stolen_or_pm_count,
18531                 stolen_entry_count,
18532                 pm_not_ready_count,
18533                 normal_count,
18534                 stack_bottom_clear_count);
18535         }
18536 #endif //SNOOP_STATS
18537
18538         //start all threads.
18539         dprintf(3, ("Starting all threads for end of mark phase"));
18540         gc_t_join.restart();
18541 #else //MULTIPLE_HEAPS
18542
18543         //decide on promotion
18544         if (!settings.promotion)
18545         {
18546             size_t m = 0;
18547             for (int n = 0; n <= condemned_gen_number;n++)
18548             {
18549                 m +=  (size_t)(dd_min_gc_size (dynamic_data_of (n))*(n+1)*0.06);
18550             }
18551             dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
18552                                                      max_generation));
18553             size_t older_gen_size = (dd_current_size (dd) +
18554                                      (dd_desired_allocation (dd) -
18555                                      dd_new_allocation (dd)));
18556
18557             dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
18558                          m, promoted_bytes (heap_number), older_gen_size));
18559
18560             if ((m > older_gen_size) ||
18561                     (promoted_bytes (heap_number) > m))
18562             {
18563                 settings.promotion = TRUE;
18564             }
18565         }
18566
18567 #endif //MULTIPLE_HEAPS
18568     }
18569
18570 #ifdef MULTIPLE_HEAPS
18571 #ifdef MARK_LIST
18572 #ifdef PARALLEL_MARK_LIST_SORT
18573 //    start = GetCycleCount32();
18574     merge_mark_lists();
18575 //    printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
18576 #endif //PARALLEL_MARK_LIST_SORT
18577 #endif //MARK_LIST
18578 #endif //MULTIPLE_HEAPS
18579
18580 #ifdef BACKGROUND_GC
18581     total_promoted_bytes = promoted_bytes (heap_number);
18582 #endif //BACKGROUND_GC
18583
18584     promoted_bytes (heap_number) -= promoted_bytes_live;
18585
18586 #ifdef TIME_GC
18587         finish = GetCycleCount32();
18588         mark_time = finish - start;
18589 #endif //TIME_GC
18590
18591     dprintf(2,("---- End of mark phase ----"));
18592 }
18593
18594 inline
18595 void gc_heap::pin_object (BYTE* o, BYTE** ppObject, BYTE* low, BYTE* high)
18596 {
18597     dprintf (3, ("Pinning %Ix", (size_t)o));
18598     if ((o >= low) && (o < high))
18599     {
18600         dprintf(3,("^%Ix^", (size_t)o));
18601         set_pinned (o);
18602
18603 #ifdef FEATURE_EVENT_TRACE        
18604         if(EventEnabledPinObjectAtGCTime())
18605         {
18606             fire_etw_pin_object_event(o, ppObject);
18607         }
18608 #endif // FEATURE_EVENT_TRACE        
18609         COUNTER_ONLY(GetPerfCounters().m_GC.cPinnedObj ++);
18610     }
18611 }
18612
18613 void gc_heap::reset_mark_stack ()
18614 {
18615     reset_pinned_queue();
18616     max_overflow_address = 0;
18617     min_overflow_address = MAX_PTR;
18618 }
18619
18620 #ifdef FEATURE_STRUCTALIGN
18621 //
18622 // The word with left child, right child, and align info is laid out as follows:
18623 //
18624 //      |   upper short word   |   lower short word   |
18625 //      |<------------> <----->|<------------> <----->|
18626 //      |  left child   info hi| right child   info lo|
18627 // x86: |    10 bits     6 bits|   10 bits      6 bits|
18628 //
18629 // where left/right child are signed values and concat(info hi, info lo) is unsigned.
18630 //
18631 // The "align info" encodes two numbers: the required alignment (a power of two)
18632 // and the misalignment (the number of machine words the destination address needs
18633 // to be adjusted by to provide alignment - so this number is always smaller than
18634 // the required alignment).  Thus, the two can be represented as the "logical or"
18635 // of the two numbers.  Note that the actual pad is computed from the misalignment
18636 // by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
18637 //
18638
18639 // The number of bits in a brick.
18640 #if defined (_TARGET_AMD64_)
18641 #define brick_bits (12)
18642 #else
18643 #define brick_bits (11)
18644 #endif //_TARGET_AMD64_
18645 C_ASSERT(brick_size == (1 << brick_bits));
18646
18647 // The number of bits needed to represent the offset to a child node.
18648 // "brick_bits + 1" allows us to represent a signed offset within a brick.
18649 #define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
18650
18651 // The number of bits in each of the pad hi, pad lo fields.
18652 #define pad_bits (sizeof(short) * 8 - child_bits)
18653
18654 #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
18655 #define pad_mask ((1 << pad_bits) - 1)
18656 #define pad_from_short(w) ((size_t)(w) & pad_mask)
18657 #else // FEATURE_STRUCTALIGN
18658 #define child_from_short(w) (w)
18659 #endif // FEATURE_STRUCTALIGN
18660
18661 inline
18662 short node_left_child(BYTE* node)
18663 {
18664     return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
18665 }
18666
18667 inline
18668 void set_node_left_child(BYTE* node, ptrdiff_t val)
18669 {
18670     assert (val > -(ptrdiff_t)brick_size);
18671     assert (val < (ptrdiff_t)brick_size);
18672     assert (Aligned (val));
18673 #ifdef FEATURE_STRUCTALIGN
18674     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
18675     ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
18676 #else // FEATURE_STRUCTALIGN
18677     ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
18678 #endif // FEATURE_STRUCTALIGN
18679     assert (node_left_child (node) == val);
18680 }
18681
18682 inline
18683 short node_right_child(BYTE* node)
18684 {
18685     return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
18686 }
18687
18688 inline
18689 void set_node_right_child(BYTE* node, ptrdiff_t val)
18690 {
18691     assert (val > -(ptrdiff_t)brick_size);
18692     assert (val < (ptrdiff_t)brick_size);
18693     assert (Aligned (val));
18694 #ifdef FEATURE_STRUCTALIGN
18695     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
18696     ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
18697 #else // FEATURE_STRUCTALIGN
18698     ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
18699 #endif // FEATURE_STRUCTALIGN
18700     assert (node_right_child (node) == val);
18701 }
18702
18703 #ifdef FEATURE_STRUCTALIGN
18704 void node_aligninfo (BYTE* node, int& requiredAlignment, ptrdiff_t& pad)
18705 {
18706     // Extract the single-number aligninfo from the fields.
18707     short left = ((plug_and_pair*)node)[-1].m_pair.left;
18708     short right = ((plug_and_pair*)node)[-1].m_pair.right;
18709     ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
18710     ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
18711
18712     // Replicate the topmost bit into all lower bits.
18713     ptrdiff_t x = aligninfo;
18714     x |= x >> 8;
18715     x |= x >> 4;
18716     x |= x >> 2;
18717     x |= x >> 1;
18718
18719     // Clear all bits but the highest.
18720     requiredAlignment = (int)(x ^ (x >> 1));
18721     pad = aligninfo - requiredAlignment;
18722     pad += AdjustmentForMinPadSize(pad, requiredAlignment);
18723 }
18724
18725 inline
18726 ptrdiff_t node_alignpad (BYTE* node)
18727 {
18728     int requiredAlignment;
18729     ptrdiff_t alignpad;
18730     node_aligninfo (node, requiredAlignment, alignpad);
18731     return alignpad;
18732 }
18733
18734 void clear_node_aligninfo (BYTE* node)
18735 {
18736     ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
18737     ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
18738 }
18739
18740 void set_node_aligninfo (BYTE* node, int requiredAlignment, ptrdiff_t pad)
18741 {
18742     // Encode the alignment requirement and alignment offset as a single number
18743     // as described above.
18744     ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
18745     assert (Aligned (aligninfo));
18746     ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
18747     assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
18748
18749     ptrdiff_t hi = aligninfo_shifted >> pad_bits;
18750     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
18751     ((plug_and_pair*)node)[-1].m_pair.left |= hi;
18752
18753     ptrdiff_t lo = aligninfo_shifted & pad_mask;
18754     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
18755     ((plug_and_pair*)node)[-1].m_pair.right |= lo;
18756
18757 #ifdef _DEBUG
18758     int requiredAlignment2;
18759     ptrdiff_t pad2;
18760     node_aligninfo (node, requiredAlignment2, pad2);
18761     assert (requiredAlignment == requiredAlignment2);
18762     assert (pad == pad2);
18763 #endif // _DEBUG
18764 }
18765 #endif // FEATURE_STRUCTALIGN
18766
18767 inline
18768 void loh_set_node_relocation_distance(BYTE* node, ptrdiff_t val)
18769 {
18770     ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
18771     *place = val;
18772 }
18773
18774 inline
18775 ptrdiff_t loh_node_relocation_distance(BYTE* node)
18776 {
18777     return (((loh_obj_and_pad*)node)[-1].reloc);
18778 }
18779
18780 inline
18781 ptrdiff_t node_relocation_distance (BYTE* node)
18782 {
18783     return (((plug_and_reloc*)(node))[-1].reloc & ~3);
18784 }
18785
18786 inline
18787 void set_node_relocation_distance(BYTE* node, ptrdiff_t val)
18788 {
18789     assert (val == (val & ~3));
18790     ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
18791     //clear the left bit and the relocation field
18792     *place &= 1;
18793     // store the value
18794     *place |= val;
18795 }
18796
18797 #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
18798
18799 #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
18800
18801 #ifndef FEATURE_STRUCTALIGN
18802 #define node_realigned(node)    (((plug_and_reloc*)(node))[-1].reloc & 1)
18803
18804 void set_node_realigned(BYTE* node)
18805 {
18806     ((plug_and_reloc*)(node))[-1].reloc |= 1;
18807 }
18808
18809 void clear_node_realigned(BYTE* node)
18810 {
18811 #ifdef RESPECT_LARGE_ALIGNMENT
18812     ((plug_and_reloc*)(node))[-1].reloc &= ~1;
18813 #endif //RESPECT_LARGE_ALIGNMENT
18814 }
18815 #endif // FEATURE_STRUCTALIGN
18816
18817 inline
18818 size_t  node_gap_size (BYTE* node)
18819 {
18820     return ((plug_and_gap *)node)[-1].gap;
18821 }
18822
18823 void set_gap_size (BYTE* node, size_t size)
18824 {
18825     assert (Aligned (size));
18826
18827     // clear the 2 DWORD used by the node.
18828     ((plug_and_gap *)node)[-1].reloc = 0;
18829     ((plug_and_gap *)node)[-1].lr =0;
18830     ((plug_and_gap *)node)[-1].gap = size;
18831
18832     assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
18833
18834 }
18835
18836 BYTE* gc_heap::insert_node (BYTE* new_node, size_t sequence_number,
18837                    BYTE* tree, BYTE* last_node)
18838 {
18839     dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
18840                  (size_t)new_node, brick_of(new_node), 
18841                  (size_t)tree, brick_of(tree), 
18842                  (size_t)last_node, brick_of(last_node),
18843                  sequence_number));
18844     if (power_of_two_p (sequence_number))
18845     {
18846         set_node_left_child (new_node, (tree - new_node));
18847         dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
18848         tree = new_node;
18849     }
18850     else
18851     {
18852         if (oddp (sequence_number))
18853         {
18854             set_node_right_child (last_node, (new_node - last_node));
18855             dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
18856         }
18857         else
18858         {
18859             BYTE*  earlier_node = tree;
18860             size_t imax = logcount(sequence_number) - 2;
18861             for (size_t i = 0; i != imax; i++)
18862             {
18863                 earlier_node = earlier_node + node_right_child (earlier_node);
18864             }
18865             int tmp_offset = node_right_child (earlier_node);
18866             assert (tmp_offset); // should never be empty
18867             set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
18868             set_node_right_child (earlier_node, (new_node - earlier_node));
18869
18870             dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix", 
18871                 new_node, ((earlier_node + tmp_offset ) - new_node),
18872                 earlier_node, (new_node - earlier_node)));
18873         }
18874     }
18875     return tree;
18876 }
18877
18878 size_t gc_heap::update_brick_table (BYTE* tree, size_t current_brick,
18879                                     BYTE* x, BYTE* plug_end)
18880 {
18881     dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
18882         tree, current_brick, x, plug_end));
18883
18884     if (tree > 0)
18885     {
18886         dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix", 
18887             current_brick, (size_t)(tree - brick_address (current_brick)), tree));
18888         set_brick (current_brick, (tree - brick_address (current_brick)));
18889     }
18890     else
18891     {
18892         dprintf (3, ("b- %Ix->-1", current_brick));
18893         set_brick (current_brick, -1);
18894     }
18895     size_t  b = 1 + current_brick;
18896     ptrdiff_t  offset = 0;
18897     size_t last_br = brick_of (plug_end-1);
18898     current_brick = brick_of (x-1);
18899     dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
18900     while (b <= current_brick)
18901     {
18902         if (b <= last_br)
18903         {
18904             set_brick (b, --offset);
18905         }
18906         else
18907         {
18908             set_brick (b,-1);
18909         }
18910         b++;
18911     }
18912     return brick_of (x);
18913 }
18914
18915 void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, BYTE* next_plug_to_allocate)
18916 {
18917 #ifdef _WIN64
18918     // We should never demote big plugs to ephemeral generations.
18919     if (gen == youngest_generation)
18920     {
18921         heap_segment* seg = ephemeral_heap_segment;
18922         size_t mark_stack_large_bos = mark_stack_bos;
18923         size_t large_plug_pos = 0;
18924         while (mark_stack_large_bos < mark_stack_tos)
18925         {
18926             if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
18927             {
18928                 while (mark_stack_bos <= mark_stack_large_bos)
18929                 {
18930                     size_t entry = deque_pinned_plug();
18931                     size_t len = pinned_len (pinned_plug_of (entry));
18932                     BYTE* plug = pinned_plug (pinned_plug_of(entry));
18933                     if (len > demotion_plug_len_th)
18934                     {
18935                         dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
18936                     }
18937                     pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
18938                     assert(mark_stack_array[entry].len == 0 ||
18939                             mark_stack_array[entry].len >= Align(min_obj_size));
18940                     generation_allocation_pointer (consing_gen) = plug + len;
18941                     generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
18942                     set_allocator_next_pin (consing_gen);
18943                 }
18944             }
18945
18946             mark_stack_large_bos++;
18947         }
18948     }
18949 #endif //_WIN64
18950
18951     generation_plan_allocation_start (gen) =
18952         allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
18953     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
18954     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
18955 #ifdef RESPECT_LARGE_ALIGNMENT
18956     if (next_plug_to_allocate)
18957     {
18958         size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
18959         if (allocation_left > dist_to_next_plug)
18960         {
18961             allocation_left = dist_to_next_plug;
18962         }
18963     }
18964 #endif //RESPECT_LARGE_ALIGNMENT
18965     if (allocation_left < Align (min_obj_size))
18966     {
18967         generation_plan_allocation_start_size (gen) += allocation_left;
18968         generation_allocation_pointer (consing_gen) += allocation_left;
18969     }
18970
18971     dprintf (1, ("plan alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num, 
18972         generation_plan_allocation_start (gen),
18973         generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen))); 
18974 }
18975
18976 void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
18977 {
18978     BOOL adjacentp = FALSE;
18979
18980     generation_plan_allocation_start (gen) =  
18981         allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0, 
18982 #ifdef SHORT_PLUGS
18983                                    FALSE, NULL, 
18984 #endif //SHORT_PLUGS
18985                                    FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
18986
18987     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
18988     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
18989     if ((allocation_left < Align (min_obj_size)) && 
18990          (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
18991     {
18992         generation_plan_allocation_start_size (gen) += allocation_left;
18993         generation_allocation_pointer (consing_gen) += allocation_left;
18994     }
18995
18996     dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num, 
18997         generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen))); 
18998 }
18999
19000 void gc_heap::plan_generation_starts (generation*& consing_gen)
19001 {
19002     //make sure that every generation has a planned allocation start
19003     int  gen_number = settings.condemned_generation;
19004     while (gen_number >= 0)
19005     {
19006         if (gen_number < max_generation)
19007         {
19008             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
19009         }
19010         generation* gen = generation_of (gen_number);
19011         if (0 == generation_plan_allocation_start (gen))
19012         {
19013             plan_generation_start (gen, consing_gen, 0);
19014             assert (generation_plan_allocation_start (gen));
19015         }
19016         gen_number--;
19017     }
19018     // now we know the planned allocation size
19019     heap_segment_plan_allocated (ephemeral_heap_segment) =
19020         generation_allocation_pointer (consing_gen);
19021 }
19022
19023 void gc_heap::advance_pins_for_demotion (generation* gen)
19024 {
19025     BYTE* original_youngest_start = generation_allocation_start (youngest_generation);
19026     heap_segment* seg = ephemeral_heap_segment;
19027
19028     if ((!(pinned_plug_que_empty_p())))
19029     {
19030         size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
19031         size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
19032         size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
19033         float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
19034         float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
19035         if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
19036         {
19037             while (!pinned_plug_que_empty_p() &&
19038                     (pinned_plug (oldest_pin()) < original_youngest_start))
19039             {
19040                 size_t entry = deque_pinned_plug();
19041                 size_t len = pinned_len (pinned_plug_of (entry));
19042                 BYTE* plug = pinned_plug (pinned_plug_of(entry));
19043                 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
19044                 assert(mark_stack_array[entry].len == 0 ||
19045                         mark_stack_array[entry].len >= Align(min_obj_size));
19046                 generation_allocation_pointer (gen) = plug + len;
19047                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19048                 set_allocator_next_pin (gen);
19049
19050                 //Add the size of the pinned plug to the right pinned allocations
19051                 //find out which gen this pinned plug came from 
19052                 int frgn = object_gennum (plug);
19053                 if ((frgn != (int)max_generation) && settings.promotion)
19054                 {
19055                     int togn = object_gennum_plan (plug);
19056                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
19057                     if (frgn < togn)
19058                     {
19059                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
19060                     }
19061                 }
19062
19063                 dprintf (2, ("skipping gap %d, pin %Ix (%Id)", 
19064                     pinned_len (pinned_plug_of (entry)), plug, len));
19065             }
19066         }
19067         dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d", 
19068             gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
19069     }
19070 }
19071
19072 void gc_heap::process_ephemeral_boundaries (BYTE* x,
19073                                             int& active_new_gen_number,
19074                                             int& active_old_gen_number,
19075                                             generation*& consing_gen,
19076                                             BOOL& allocate_in_condemned)
19077 {
19078 retry:
19079     if ((active_old_gen_number > 0) &&
19080         (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
19081     {
19082         dprintf (1, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
19083
19084         if (!pinned_plug_que_empty_p())
19085         {
19086             dprintf (1, ("oldest pin: %Ix(%Id)",
19087                 pinned_plug (oldest_pin()), 
19088                 (x - pinned_plug (oldest_pin()))));
19089         }
19090
19091         if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
19092         {
19093             active_new_gen_number--;
19094         }
19095
19096         active_old_gen_number--;
19097         assert ((!settings.promotion) || (active_new_gen_number>0));
19098
19099         if (active_new_gen_number == (max_generation - 1))
19100         {
19101 #ifdef FREE_USAGE_STATS
19102             if (settings.condemned_generation == max_generation)
19103             {
19104                 // We need to do this before we skip the rest of the pinned plugs.
19105                 generation* gen_2 = generation_of (max_generation);
19106                 generation* gen_1 = generation_of (max_generation - 1);
19107
19108                 size_t total_num_pinned_free_spaces_left = 0;
19109
19110                 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
19111                 for (int j = 0; j < NUM_GEN_POWER2; j++)
19112                 {
19113                     dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)", 
19114                         heap_number, 
19115                         settings.gc_index,
19116                         (j + 10), 
19117                         gen_2->gen_current_pinned_free_spaces[j],
19118                         gen_2->gen_plugs[j], gen_1->gen_plugs[j],
19119                         (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
19120
19121                     total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
19122                 }
19123
19124                 float pinned_free_list_efficiency = 0;
19125                 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
19126                 if (total_pinned_free_space != 0)
19127                 {
19128                     pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
19129                 }
19130
19131                 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
19132                             heap_number,
19133                             generation_allocated_in_pinned_free (gen_2),
19134                             total_pinned_free_space, 
19135                             (int)(pinned_free_list_efficiency * 100),
19136                             generation_pinned_free_obj_space (gen_2),
19137                             total_num_pinned_free_spaces_left));
19138             }
19139 #endif //FREE_USAGE_STATS
19140
19141             //Go past all of the pinned plugs for this generation.
19142             while (!pinned_plug_que_empty_p() &&
19143                    (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
19144             {
19145                 size_t  entry = deque_pinned_plug();
19146                 mark*  m = pinned_plug_of (entry);
19147                 BYTE*  plug = pinned_plug (m);
19148                 size_t  len = pinned_len (m);
19149                 // detect pinned block in different segment (later) than
19150                 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
19151                 // adjust the allocation segment along the way (at the end it will
19152                 // be the ephemeral segment.
19153                 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
19154
19155                 PREFIX_ASSUME(nseg != NULL);
19156
19157                 while (!((plug >= generation_allocation_pointer (consing_gen))&&
19158                         (plug < heap_segment_allocated (nseg))))
19159                 {
19160                     //adjust the end of the segment to be the end of the plug
19161                     assert (generation_allocation_pointer (consing_gen)>=
19162                             heap_segment_mem (nseg));
19163                     assert (generation_allocation_pointer (consing_gen)<=
19164                             heap_segment_committed (nseg));
19165
19166                     heap_segment_plan_allocated (nseg) =
19167                         generation_allocation_pointer (consing_gen);
19168                     //switch allocation segment
19169                     nseg = heap_segment_next_rw (nseg);
19170                     generation_allocation_segment (consing_gen) = nseg;
19171                     //reset the allocation pointer and limits
19172                     generation_allocation_pointer (consing_gen) =
19173                         heap_segment_mem (nseg);
19174                 }
19175                 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
19176                 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
19177                 generation_allocation_pointer (consing_gen) = plug + len;
19178                 generation_allocation_limit (consing_gen) =
19179                     generation_allocation_pointer (consing_gen);
19180             }
19181             allocate_in_condemned = TRUE;
19182             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
19183         }
19184
19185         if (active_new_gen_number != max_generation)
19186         {
19187             if ((active_new_gen_number == (max_generation - 1)) && !demote_gen1_p)
19188             {
19189                 advance_pins_for_demotion (consing_gen);
19190             }
19191
19192             plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
19193                 
19194             dprintf (1, ("process eph: allocated gen%d start at %Ix", 
19195                 active_new_gen_number,
19196                 generation_plan_allocation_start (generation_of (active_new_gen_number))));
19197
19198             if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
19199             {
19200                 BYTE* pplug = pinned_plug (oldest_pin());
19201                 if (object_gennum (pplug) > 0)
19202                 {
19203                     demotion_low = pplug;
19204                     dprintf (3, ("process eph: dlow->%Ix", demotion_low));
19205                 }
19206             }
19207
19208             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
19209         }
19210
19211         goto retry;
19212     }
19213 }
19214
19215 inline
19216 void gc_heap::seg_clear_mark_bits (heap_segment* seg)
19217 {
19218     BYTE* o = heap_segment_mem (seg);
19219     while (o < heap_segment_allocated (seg))
19220     {
19221         if (marked (o))
19222         {
19223             clear_marked (o);
19224         }
19225         o = o  + Align (size (o));
19226     }
19227 }
19228
19229 void gc_heap::sweep_ro_segments (heap_segment* start_seg)
19230 {
19231
19232 #if 0
19233     //go through all of the segment in range and reset the mark bit
19234     //TODO works only on small object segments
19235
19236     heap_segment* seg = start_seg;
19237
19238     while (seg)
19239     {
19240         if (heap_segment_read_only_p (seg) &&
19241             heap_segment_in_range_p (seg))
19242         {
19243 #ifdef BACKGROUND_GC
19244             if (settings.concurrent)
19245             {
19246                 seg_clear_mark_array_bits_soh (seg);
19247             }
19248             else
19249             {
19250                 seg_clear_mark_bits (seg);
19251             }
19252 #else //BACKGROUND_GC
19253
19254 #ifdef MARK_ARRAY
19255             if(gc_can_use_concurrent)
19256             {
19257                 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
19258                               min (heap_segment_allocated (seg), highest_address),
19259                               false); // read_only segments need the mark clear
19260             }
19261 #else //MARK_ARRAY
19262             seg_clear_mark_bits (seg);
19263 #endif //MARK_ARRAY
19264
19265 #endif //BACKGROUND_GC
19266         }
19267         seg = heap_segment_next (seg);
19268     }
19269 #endif //0 
19270 }
19271
19272 #ifdef FEATURE_LOH_COMPACTION
19273 inline
19274 BOOL gc_heap::loh_pinned_plug_que_empty_p()
19275 {
19276     return (loh_pinned_queue_bos == loh_pinned_queue_tos);
19277 }
19278
19279 void gc_heap::loh_set_allocator_next_pin()
19280 {
19281     if (!(loh_pinned_plug_que_empty_p()))
19282     {
19283         mark*  oldest_entry = loh_oldest_pin();
19284         BYTE* plug = pinned_plug (oldest_entry);
19285         generation* gen = large_object_generation;
19286         if ((plug >= generation_allocation_pointer (gen)) &&
19287             (plug <  generation_allocation_limit (gen)))
19288         {
19289             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
19290         }
19291         else
19292             assert (!((plug < generation_allocation_pointer (gen)) &&
19293                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
19294     }
19295 }
19296
19297 size_t gc_heap::loh_deque_pinned_plug ()
19298 {
19299     size_t m = loh_pinned_queue_bos;
19300     loh_pinned_queue_bos++;
19301     return m;
19302 }
19303
19304 inline
19305 mark* gc_heap::loh_pinned_plug_of (size_t bos)
19306 {
19307     return &loh_pinned_queue[bos];
19308 }
19309
19310 inline
19311 mark* gc_heap::loh_oldest_pin()
19312 {
19313     return loh_pinned_plug_of (loh_pinned_queue_bos);
19314 }
19315
19316 // If we can't grow the queue, then don't compact.
19317 BOOL gc_heap::loh_enque_pinned_plug (BYTE* plug, size_t len)
19318 {
19319     assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
19320
19321     if (loh_pinned_queue_length <= loh_pinned_queue_tos)
19322     {
19323         if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
19324         {
19325             return FALSE;
19326         }
19327     }
19328     dprintf (3, (" P: %Ix(%Id)", plug, len));
19329     mark& m = loh_pinned_queue[loh_pinned_queue_tos];
19330     m.first = plug;
19331     m.len = len;
19332     loh_pinned_queue_tos++;
19333     loh_set_allocator_next_pin();
19334     return TRUE;
19335 }
19336
19337 inline
19338 BOOL gc_heap::loh_size_fit_p (size_t size, BYTE* alloc_pointer, BYTE* alloc_limit)
19339 {
19340     dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)", 
19341         size, 
19342         (2* AlignQword (loh_padding_obj_size) +  size),
19343         alloc_pointer,
19344         alloc_limit,
19345         (alloc_limit - alloc_pointer)));
19346
19347     return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) +  size) <= alloc_limit);
19348 }
19349
19350 BYTE* gc_heap::loh_allocate_in_condemned (BYTE* old_loc, size_t size)
19351 {
19352     generation* gen = large_object_generation;
19353     dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id", 
19354         generation_allocation_pointer (gen),
19355         generation_allocation_limit (gen),
19356         size));
19357
19358 retry:
19359     {
19360         heap_segment* seg = generation_allocation_segment (gen);
19361         if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
19362         {
19363             if ((!(loh_pinned_plug_que_empty_p()) &&
19364                  (generation_allocation_limit (gen) ==
19365                   pinned_plug (loh_oldest_pin()))))
19366             {
19367                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
19368                 size_t len = pinned_len (m);
19369                 BYTE* plug = pinned_plug (m);
19370                 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
19371                 pinned_len (m) = plug - generation_allocation_pointer (gen);
19372                 generation_allocation_pointer (gen) = plug + len;
19373                 
19374                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19375                 loh_set_allocator_next_pin();
19376                 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)", 
19377                     generation_allocation_pointer (gen), 
19378                     generation_allocation_limit (gen),
19379                     (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19380
19381                 goto retry;
19382             }
19383
19384             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
19385             {
19386                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19387                 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
19388             }
19389             else
19390             {
19391                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
19392                 {
19393                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
19394                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19395                     dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
19396                 }
19397                 else
19398                 {
19399                     if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
19400                         (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
19401                     {
19402                         dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
19403                                          (generation_allocation_pointer (gen) + size)));
19404
19405                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
19406                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
19407
19408                         dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)", 
19409                             generation_allocation_pointer (gen), 
19410                             generation_allocation_limit (gen),
19411                             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19412                     }
19413                     else
19414                     {
19415                         heap_segment* next_seg = heap_segment_next (seg);
19416                         assert (generation_allocation_pointer (gen)>=
19417                                 heap_segment_mem (seg));
19418                         // Verify that all pinned plugs for this segment are consumed
19419                         if (!loh_pinned_plug_que_empty_p() &&
19420                             ((pinned_plug (loh_oldest_pin()) <
19421                               heap_segment_allocated (seg)) &&
19422                              (pinned_plug (loh_oldest_pin()) >=
19423                               generation_allocation_pointer (gen))))
19424                         {
19425                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
19426                                          pinned_plug (loh_oldest_pin())));
19427                             dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
19428                             FATAL_GC_ERROR();
19429                         }
19430                         assert (generation_allocation_pointer (gen)>=
19431                                 heap_segment_mem (seg));
19432                         assert (generation_allocation_pointer (gen)<=
19433                                 heap_segment_committed (seg));
19434                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
19435
19436                         if (next_seg)
19437                         {
19438                             // for LOH do we want to try starting from the first LOH every time though?
19439                             generation_allocation_segment (gen) = next_seg;
19440                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
19441                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
19442
19443                             dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)", 
19444                                 generation_allocation_pointer (gen), 
19445                                 generation_allocation_limit (gen),
19446                                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19447                         }
19448                         else
19449                         {
19450                             dprintf (1, ("We ran out of space compacting, shouldn't happen"));
19451                             FATAL_GC_ERROR();
19452                         }
19453                     }
19454                 }
19455             }
19456             loh_set_allocator_next_pin();
19457
19458             dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)", 
19459                 generation_allocation_pointer (gen), 
19460                 generation_allocation_limit (gen),
19461                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19462
19463             goto retry;
19464         }
19465     }
19466
19467     {
19468         assert (generation_allocation_pointer (gen)>=
19469                 heap_segment_mem (generation_allocation_segment (gen)));
19470         BYTE* result = generation_allocation_pointer (gen);
19471         size_t loh_pad = AlignQword (loh_padding_obj_size);
19472
19473         generation_allocation_pointer (gen) += size + loh_pad;
19474         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
19475
19476         dprintf (1235, ("p: %Ix, l: %Ix (%Id)", 
19477             generation_allocation_pointer (gen), 
19478             generation_allocation_limit (gen),
19479             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
19480
19481         assert (result + loh_pad);
19482         return result + loh_pad;
19483     }
19484 }
19485
19486 BOOL gc_heap::should_compact_loh()
19487 {
19488     return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
19489 }
19490
19491 inline
19492 void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
19493 {
19494     if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
19495     {
19496         if (all_heaps_compacted_p)
19497         {
19498             // If the compaction mode says to compact once and we are going to compact LOH, 
19499             // we need to revert it back to no compaction.
19500             loh_compaction_mode = loh_compaction_default;
19501         }
19502     }
19503 }
19504
19505 BOOL gc_heap::plan_loh()
19506 {
19507     if (!loh_pinned_queue)
19508     {
19509         loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
19510         if (!loh_pinned_queue)
19511         {
19512             dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction", 
19513                          LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
19514             return FALSE;
19515         }
19516
19517         loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
19518     }
19519
19520     if (heap_number == 0)
19521         loh_pinned_queue_decay = LOH_PIN_DECAY;
19522
19523     loh_pinned_queue_tos = 0;
19524     loh_pinned_queue_bos = 0;
19525     
19526     generation* gen        = large_object_generation;
19527     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
19528     PREFIX_ASSUME(start_seg != NULL);
19529     heap_segment* seg      = start_seg;
19530     BYTE* o                = generation_allocation_start (gen);
19531
19532     dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n", 
19533         generation_size (max_generation + 1), 
19534         generation_free_list_space (gen),
19535         generation_free_obj_space (gen)));
19536
19537     while (seg)
19538     {
19539         heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
19540         seg = heap_segment_next (seg);
19541     }
19542
19543     seg = start_seg;
19544
19545     //Skip the generation gap object
19546     o = o + AlignQword (size (o));
19547     // We don't need to ever realloc gen3 start so don't touch it.
19548     heap_segment_plan_allocated (seg) = o;
19549     generation_allocation_pointer (gen) = o;
19550     generation_allocation_limit (gen) = generation_allocation_pointer (gen);
19551     generation_allocation_segment (gen) = start_seg;
19552
19553     BYTE* free_space_start = o;
19554     BYTE* free_space_end = o;
19555     BYTE* new_address = 0;
19556
19557     while (1)
19558     {
19559         if (o >= heap_segment_allocated (seg))
19560         {
19561             seg = heap_segment_next (seg);
19562             if (seg == 0)
19563             {
19564                 break;
19565             }
19566
19567             o = heap_segment_mem (seg);
19568         }
19569
19570         if (marked (o))
19571         {
19572             free_space_end = o;
19573             size_t size = AlignQword (size (o));
19574             dprintf (1235, ("%Ix(%Id) M", o, size));
19575
19576             if (pinned (o))
19577             {
19578                 // We don't clear the pinned bit yet so we can check in 
19579                 // compact phase how big a free object we should allocate
19580                 // in front of the pinned object. We use the reloc address
19581                 // field to store this.
19582                 if (!loh_enque_pinned_plug (o, size))
19583                 {
19584                     return FALSE;
19585                 }
19586                 new_address = o;
19587             }
19588             else
19589             {
19590                 new_address = loh_allocate_in_condemned (o, size);
19591             }
19592
19593             loh_set_node_relocation_distance (o, (new_address - o));
19594             dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
19595
19596             o = o + size;
19597             free_space_start = o;
19598             if (o < heap_segment_allocated (seg))
19599             {
19600                 assert (!marked (o));
19601             }
19602         }
19603         else
19604         {
19605             while (o < heap_segment_allocated (seg) && !marked (o))
19606             {
19607                 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_pFreeObjectMethodTable) ? 1 : 0)));
19608                 o = o + AlignQword (size (o));
19609             }
19610         }
19611     }
19612
19613     while (!loh_pinned_plug_que_empty_p())
19614     {
19615         mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
19616         size_t len = pinned_len (m);
19617         BYTE* plug = pinned_plug (m);
19618
19619         // detect pinned block in different segment (later) than
19620         // allocation segment
19621         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
19622
19623         while ((plug < generation_allocation_pointer (gen)) ||
19624                (plug >= heap_segment_allocated (nseg)))
19625         {
19626             assert ((plug < heap_segment_mem (nseg)) ||
19627                     (plug > heap_segment_reserved (nseg)));
19628             //adjust the end of the segment to be the end of the plug
19629             assert (generation_allocation_pointer (gen)>=
19630                     heap_segment_mem (nseg));
19631             assert (generation_allocation_pointer (gen)<=
19632                     heap_segment_committed (nseg));
19633
19634             heap_segment_plan_allocated (nseg) =
19635                 generation_allocation_pointer (gen);
19636             //switch allocation segment
19637             nseg = heap_segment_next_rw (nseg);
19638             generation_allocation_segment (gen) = nseg;
19639             //reset the allocation pointer and limits
19640             generation_allocation_pointer (gen) =
19641                 heap_segment_mem (nseg);
19642         }
19643
19644         dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
19645         pinned_len (m) = plug - generation_allocation_pointer (gen);
19646         generation_allocation_pointer (gen) = plug + len;
19647     }
19648
19649     heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
19650     generation_allocation_pointer (gen) = 0;
19651     generation_allocation_limit (gen) = 0;
19652
19653     return TRUE;
19654 }
19655
19656 void gc_heap::compact_loh()
19657 {
19658     assert (should_compact_loh());
19659
19660     generation* gen        = large_object_generation;
19661     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
19662     PREFIX_ASSUME(start_seg != NULL);
19663     heap_segment* seg      = start_seg;
19664     heap_segment* prev_seg = 0;
19665     BYTE* o                = generation_allocation_start (gen);
19666
19667     //Skip the generation gap object
19668     o = o + AlignQword (size (o));
19669     // We don't need to ever realloc gen3 start so don't touch it.
19670     BYTE* free_space_start = o;
19671     BYTE* free_space_end = o;
19672     generation_allocator (gen)->clear();
19673     generation_free_list_space (gen) = 0;
19674     generation_free_obj_space (gen) = 0;
19675
19676     loh_pinned_queue_bos = 0;
19677
19678     while (1)
19679     {
19680         if (o >= heap_segment_allocated (seg))
19681         {
19682             heap_segment* next_seg = heap_segment_next (seg);
19683
19684             if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
19685                 (seg != start_seg) && !heap_segment_read_only_p (seg))
19686             {
19687                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
19688                 assert (prev_seg);
19689                 heap_segment_next (prev_seg) = next_seg;
19690                 heap_segment_next (seg) = freeable_large_heap_segment;
19691                 freeable_large_heap_segment = seg;
19692             }
19693             else
19694             {
19695                 if (!heap_segment_read_only_p (seg))
19696                 {
19697                     // We grew the segment to accommondate allocations.
19698                     if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
19699                     {
19700                         if ((heap_segment_plan_allocated (seg) - plug_skew)  > heap_segment_used (seg))
19701                         {
19702                             heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
19703                         }
19704                     }
19705
19706                     heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
19707                     dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
19708                     decommit_heap_segment_pages (seg, 0);
19709                     dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
19710                         seg, 
19711                         heap_segment_allocated (seg),
19712                         heap_segment_used (seg),
19713                         heap_segment_committed (seg)));
19714                     //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
19715                     dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
19716                 }
19717                 prev_seg = seg;
19718             }
19719
19720             seg = next_seg;
19721             if (seg == 0)
19722                 break;
19723             else
19724             {
19725                 o = heap_segment_mem (seg);
19726             }
19727         }
19728
19729         if (marked (o))
19730         {
19731             free_space_end = o;
19732             size_t size = AlignQword (size (o));
19733
19734             size_t loh_pad;
19735             BYTE* reloc = o;
19736             clear_marked (o);
19737
19738             if (pinned (o))
19739             {
19740                 // We are relying on the fact the pinned objects are always looked at in the same order 
19741                 // in plan phase and in compact phase.
19742                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
19743                 BYTE* plug = pinned_plug (m);
19744                 assert (plug == o);
19745
19746                 loh_pad = pinned_len (m);
19747                 clear_pinned (o);
19748             }
19749             else
19750             {
19751                 loh_pad = AlignQword (loh_padding_obj_size);
19752
19753                 reloc += loh_node_relocation_distance (o);
19754                 gcmemcopy (reloc, o, size, TRUE);
19755             }
19756
19757             thread_gap ((reloc - loh_pad), loh_pad, gen);
19758
19759             o = o + size;
19760             free_space_start = o;
19761             if (o < heap_segment_allocated (seg))
19762             {
19763                 assert (!marked (o));
19764             }
19765         }
19766         else
19767         {
19768             while (o < heap_segment_allocated (seg) && !marked (o))
19769             {
19770                 o = o + AlignQword (size (o));
19771             }
19772         }
19773     }
19774
19775     assert (loh_pinned_plug_que_empty_p());
19776
19777     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
19778         generation_size (max_generation + 1), 
19779         generation_free_list_space (gen),
19780         generation_free_obj_space (gen)));
19781 }
19782
19783 void gc_heap::relocate_in_loh_compact()
19784 {
19785     generation* gen        = large_object_generation;
19786     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
19787     BYTE* o                = generation_allocation_start (gen);
19788
19789     //Skip the generation gap object
19790     o = o + AlignQword (size (o));
19791
19792     relocate_args args;
19793     args.low = gc_low;
19794     args.high = gc_high;
19795     args.last_plug = 0;
19796
19797     while (1)
19798     {
19799         if (o >= heap_segment_allocated (seg))
19800         {
19801             seg = heap_segment_next (seg);
19802             if (seg == 0)
19803             {
19804                 break;
19805             }
19806
19807             o = heap_segment_mem (seg);
19808         }
19809
19810         if (marked (o))
19811         {
19812             size_t size = AlignQword (size (o));
19813
19814             check_class_object_demotion (o);
19815             if (contain_pointers (o))
19816             {
19817                 go_through_object_nostart (method_table (o), o, size(o), pval,
19818                 {
19819                     reloc_survivor_helper (pval);
19820                 });
19821             }
19822
19823             o = o + size;
19824             if (o < heap_segment_allocated (seg))
19825             {
19826                 assert (!marked (o));
19827             }
19828         }
19829         else
19830         {
19831             while (o < heap_segment_allocated (seg) && !marked (o))
19832             {
19833                 o = o + AlignQword (size (o));
19834             }
19835         }
19836     }
19837
19838     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
19839         generation_size (max_generation + 1), 
19840         generation_free_list_space (gen),
19841         generation_free_obj_space (gen)));
19842 }
19843
19844 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
19845 void gc_heap::walk_relocation_loh (size_t profiling_context)
19846 {
19847     generation* gen        = large_object_generation;
19848     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
19849     BYTE* o                = generation_allocation_start (gen);
19850
19851     //Skip the generation gap object
19852     o = o + AlignQword (size (o));
19853
19854     while (1)
19855     {
19856         if (o >= heap_segment_allocated (seg))
19857         {
19858             seg = heap_segment_next (seg);
19859             if (seg == 0)
19860             {
19861                 break;
19862             }
19863
19864             o = heap_segment_mem (seg);
19865         }
19866
19867         if (marked (o))
19868         {
19869             size_t size = AlignQword (size (o));
19870
19871             ptrdiff_t reloc = loh_node_relocation_distance (o);
19872
19873             STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
19874
19875             {
19876                 ETW::GCLog::MovedReference(
19877                                     o,
19878                                    (o + size),
19879                                    reloc,
19880                                    profiling_context,
19881                                    settings.compaction);
19882             }
19883
19884             o = o + size;
19885             if (o < heap_segment_allocated (seg))
19886             {
19887                 assert (!marked (o));
19888             }
19889         }
19890         else
19891         {
19892             while (o < heap_segment_allocated (seg) && !marked (o))
19893             {
19894                 o = o + AlignQword (size (o));
19895             }
19896         }
19897     }
19898 }
19899 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
19900
19901 BOOL gc_heap::loh_object_p (BYTE* o)
19902 {
19903 #ifdef MULTIPLE_HEAPS
19904     gc_heap* hp = gc_heap::g_heaps [0];
19905     int brick_entry = hp->brick_table[hp->brick_of (o)];
19906 #else //MULTIPLE_HEAPS
19907     int brick_entry = brick_table[brick_of (o)];
19908 #endif //MULTIPLE_HEAPS
19909
19910     return (brick_entry == 0);
19911 }
19912 #endif //FEATURE_LOH_COMPACTION
19913
19914 // Because we have the artifical pinning, we can't gaurantee that pinned and npinned
19915 // plugs are always interleaved.
19916 void gc_heap::store_plug_gap_info (BYTE* plug_start,
19917                                    BYTE* plug_end,
19918                                    BOOL& last_npinned_plug_p, 
19919                                    BOOL& last_pinned_plug_p, 
19920                                    BYTE*& last_pinned_plug,
19921                                    BOOL& pinned_plug_p,
19922                                    BYTE* last_object_in_last_plug,
19923                                    BOOL& merge_with_last_pin_p,
19924                                    // this is only for verification purpose
19925                                    size_t last_plug_len)
19926 {
19927     if (!last_npinned_plug_p && !last_pinned_plug_p)
19928     {
19929         //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
19930         dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
19931         assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
19932         set_gap_size (plug_start, plug_start - plug_end);
19933     }
19934
19935     if (pinned (plug_start))
19936     {
19937         BOOL save_pre_plug_info_p = FALSE;
19938
19939         if (last_npinned_plug_p || last_pinned_plug_p)
19940         {
19941             //if (last_plug_len == Align (min_obj_size))
19942             //{
19943             //    dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
19944             //    DebugBreak();
19945             //}
19946             save_pre_plug_info_p = TRUE;
19947         }
19948
19949         pinned_plug_p = TRUE;
19950         last_npinned_plug_p = FALSE;
19951
19952         if (last_pinned_plug_p)
19953         {
19954             dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
19955             merge_with_last_pin_p = TRUE;
19956         }
19957         else
19958         {
19959             last_pinned_plug_p = TRUE;
19960             last_pinned_plug = plug_start;
19961                 
19962             enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
19963
19964             if (save_pre_plug_info_p)
19965             {
19966                 set_gap_size (plug_start, sizeof (gap_reloc_pair));
19967             }
19968         }
19969     }
19970     else
19971     {
19972         if (last_pinned_plug_p)
19973         {
19974             //if (Align (last_plug_len) < min_pre_pin_obj_size)
19975             //{
19976             //    dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
19977             //    DebugBreak();
19978             //}
19979
19980             save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
19981             set_gap_size (plug_start, sizeof (gap_reloc_pair));
19982
19983             verify_pins_with_post_plug_info("after saving post plug info");
19984         }
19985         last_npinned_plug_p = TRUE;
19986         last_pinned_plug_p = FALSE;
19987     }
19988 }
19989
19990 #ifdef _PREFAST_
19991 #pragma warning(push)
19992 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
19993 #endif //_PREFAST_
19994 void gc_heap::plan_phase (int condemned_gen_number)
19995 {
19996     size_t old_gen2_allocated = 0;
19997     size_t old_gen2_size = 0;
19998
19999     if (condemned_gen_number == (max_generation - 1))
20000     {
20001         old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
20002         old_gen2_size = generation_size (max_generation);
20003     }
20004
20005     assert (settings.concurrent == FALSE);
20006
20007     // %type%  category = quote (plan);
20008 #ifdef TIME_GC
20009     unsigned start;
20010     unsigned finish;
20011     start = GetCycleCount32();
20012 #endif //TIME_GC
20013
20014     dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
20015                 condemned_gen_number, settings.promotion ? 1 : 0));
20016
20017     generation*  condemned_gen1 = generation_of (condemned_gen_number);
20018
20019 #ifdef MARK_LIST
20020     BOOL use_mark_list = FALSE;
20021     BYTE** mark_list_next = &mark_list[0];
20022     dprintf (3, ("mark_list length: %Id",
20023                  mark_list_index - &mark_list[0]));
20024
20025     if ((condemned_gen_number < max_generation) &&
20026         (mark_list_index <= mark_list_end) 
20027 #ifdef BACKGROUND_GC        
20028         && (!recursive_gc_sync::background_running_p())
20029 #endif //BACKGROUND_GC
20030         )
20031     {
20032 #ifndef MULTIPLE_HEAPS
20033         _sort (&mark_list[0], mark_list_index-1, 0);
20034         //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
20035         //verify_qsort_array (&mark_list[0], mark_list_index-1);
20036 #endif //!MULTIPLE_HEAPS
20037         use_mark_list = TRUE;
20038     }
20039     else
20040     {
20041         dprintf (3, ("mark_list not used"));
20042     }
20043
20044 #endif //MARK_LIST
20045
20046     if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
20047         ro_segments_in_range)
20048     {
20049         sweep_ro_segments (generation_start_segment (condemned_gen1));
20050     }
20051
20052 #ifndef MULTIPLE_HEAPS
20053     if (shigh != (BYTE*)0)
20054     {
20055         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
20056
20057         PREFIX_ASSUME(seg != NULL);
20058
20059         heap_segment* fseg = seg;
20060         do
20061         {
20062             if (slow > heap_segment_mem (seg) &&
20063                 slow < heap_segment_reserved (seg))
20064             {
20065                 if (seg == fseg)
20066                 {
20067                     BYTE* o = generation_allocation_start (condemned_gen1) +
20068                         Align (size (generation_allocation_start (condemned_gen1)));
20069                     if (slow > o)
20070                     {
20071                         assert ((slow - o) >= (int)Align (min_obj_size));
20072 #ifdef BACKGROUND_GC
20073                         if (current_c_gc_state == c_gc_state_marking)
20074                         {
20075                             bgc_clear_batch_mark_array_bits (o, slow);
20076                         }
20077 #endif //BACKGROUND_GC
20078                         make_unused_array (o, slow - o);
20079                     }
20080                 } 
20081                 else
20082                 {
20083                     assert (condemned_gen_number == max_generation);
20084                     make_unused_array (heap_segment_mem (seg),
20085                                        slow - heap_segment_mem (seg));
20086                 }
20087             }
20088             if (in_range_for_segment (shigh, seg))
20089             {
20090 #ifdef BACKGROUND_GC
20091                 if (current_c_gc_state == c_gc_state_marking)
20092                 {
20093                     bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
20094                 }
20095 #endif //BACKGROUND_GC
20096                 heap_segment_allocated (seg) = shigh + Align (size (shigh));
20097             }
20098             // test if the segment is in the range of [slow, shigh]
20099             if (!((heap_segment_reserved (seg) >= slow) &&
20100                   (heap_segment_mem (seg) <= shigh)))
20101             {
20102                 // shorten it to minimum
20103                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
20104             }
20105             seg = heap_segment_next_rw (seg);
20106         } while (seg);
20107     }
20108     else
20109     {
20110         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
20111
20112         PREFIX_ASSUME(seg != NULL);
20113
20114         heap_segment* sseg = seg;
20115         do
20116         {
20117             // shorten it to minimum
20118             if (seg == sseg)
20119             {
20120                 // no survivors make all generations look empty
20121                 BYTE* o = generation_allocation_start (condemned_gen1) +
20122                     Align (size (generation_allocation_start (condemned_gen1)));
20123 #ifdef BACKGROUND_GC
20124                 if (current_c_gc_state == c_gc_state_marking)
20125                 {
20126                     bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
20127                 }
20128 #endif //BACKGROUND_GC
20129                 heap_segment_allocated (seg) = o;
20130             }
20131             else
20132             {
20133                 assert (condemned_gen_number == max_generation);
20134 #ifdef BACKGROUND_GC
20135                 if (current_c_gc_state == c_gc_state_marking)
20136                 {
20137                     bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
20138                 }
20139 #endif //BACKGROUND_GC
20140                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
20141             }
20142             seg = heap_segment_next_rw (seg);
20143         } while (seg);
20144     }
20145
20146 #endif //MULTIPLE_HEAPS
20147
20148     heap_segment*  seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
20149
20150     PREFIX_ASSUME(seg1 != NULL);
20151
20152     BYTE*  end = heap_segment_allocated (seg1);
20153     BYTE*  first_condemned_address = generation_allocation_start (condemned_gen1);
20154     BYTE*  x = first_condemned_address;
20155
20156     assert (!marked (x));
20157     BYTE*  plug_end = x;
20158     BYTE*  tree = 0;
20159     size_t  sequence_number = 0;
20160     BYTE*  last_node = 0;
20161     size_t  current_brick = brick_of (x);
20162     BOOL  allocate_in_condemned = ((condemned_gen_number == max_generation)||
20163                                    (settings.promotion == FALSE));
20164     int  active_old_gen_number = condemned_gen_number;
20165     int  active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
20166                                   (1 + condemned_gen_number));
20167     generation*  older_gen = 0;
20168     generation* consing_gen = condemned_gen1;
20169     alloc_list  r_free_list [MAX_BUCKET_COUNT];
20170
20171     size_t r_free_list_space = 0;
20172     size_t r_free_obj_space = 0;
20173     size_t r_older_gen_free_list_allocated = 0;
20174     size_t r_older_gen_condemned_allocated = 0;
20175     size_t r_older_gen_end_seg_allocated = 0;
20176     BYTE*  r_allocation_pointer = 0;
20177     BYTE*  r_allocation_limit = 0;
20178     BYTE* r_allocation_start_region = 0;
20179     heap_segment*  r_allocation_segment = 0;
20180 #ifdef FREE_USAGE_STATS
20181     size_t r_older_gen_free_space[NUM_GEN_POWER2];
20182 #endif //FREE_USAGE_STATS
20183
20184     if ((condemned_gen_number < max_generation))
20185     {
20186         older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
20187         generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
20188
20189         r_free_list_space = generation_free_list_space (older_gen);
20190         r_free_obj_space = generation_free_obj_space (older_gen);
20191 #ifdef FREE_USAGE_STATS
20192         memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
20193 #endif //FREE_USAGE_STATS
20194         generation_allocate_end_seg_p (older_gen) = FALSE;
20195         r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
20196         r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
20197         r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
20198         r_allocation_limit = generation_allocation_limit (older_gen);
20199         r_allocation_pointer = generation_allocation_pointer (older_gen);
20200         r_allocation_start_region = generation_allocation_context_start_region (older_gen);
20201         r_allocation_segment = generation_allocation_segment (older_gen);
20202         heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
20203
20204         PREFIX_ASSUME(start_seg != NULL);
20205
20206         if (start_seg != ephemeral_heap_segment)
20207         {
20208             assert (condemned_gen_number == (max_generation - 1));
20209             while (start_seg && (start_seg != ephemeral_heap_segment))
20210             {
20211                 assert (heap_segment_allocated (start_seg) >=
20212                         heap_segment_mem (start_seg));
20213                 assert (heap_segment_allocated (start_seg) <=
20214                         heap_segment_reserved (start_seg));
20215                 heap_segment_plan_allocated (start_seg) =
20216                     heap_segment_allocated (start_seg);
20217                 start_seg = heap_segment_next_rw (start_seg);
20218             }
20219         }
20220     }
20221
20222     //reset all of the segment allocated sizes
20223     {
20224         heap_segment*  seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
20225
20226         PREFIX_ASSUME(seg2 != NULL);
20227
20228         while (seg2)
20229         {
20230             heap_segment_plan_allocated (seg2) =
20231                 heap_segment_mem (seg2);
20232             seg2 = heap_segment_next_rw (seg2);
20233         }
20234     }
20235     int  condemned_gn = condemned_gen_number;
20236
20237     int bottom_gen = 0;
20238     init_free_and_plug();
20239
20240     while (condemned_gn >= bottom_gen)
20241     {
20242         generation*  condemned_gen2 = generation_of (condemned_gn);
20243         generation_allocator (condemned_gen2)->clear();
20244         generation_free_list_space (condemned_gen2) = 0;
20245         generation_free_obj_space (condemned_gen2) = 0;
20246         generation_allocation_size (condemned_gen2) = 0;
20247         generation_condemned_allocated (condemned_gen2) = 0; 
20248         generation_pinned_allocated (condemned_gen2) = 0; 
20249         generation_free_list_allocated(condemned_gen2) = 0; 
20250         generation_end_seg_allocated (condemned_gen2) = 0; 
20251         generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
20252         generation_pinned_allocation_compact_size (condemned_gen2) = 0;
20253 #ifdef FREE_USAGE_STATS
20254         generation_pinned_free_obj_space (condemned_gen2) = 0;
20255         generation_allocated_in_pinned_free (condemned_gen2) = 0;
20256         generation_allocated_since_last_pin (condemned_gen2) = 0;
20257 #endif //FREE_USAGE_STATS
20258         generation_plan_allocation_start (condemned_gen2) = 0;
20259         generation_allocation_segment (condemned_gen2) =
20260             heap_segment_rw (generation_start_segment (condemned_gen2));
20261
20262         PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
20263
20264         if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
20265         {
20266             generation_allocation_pointer (condemned_gen2) =
20267                 heap_segment_mem (generation_allocation_segment (condemned_gen2));
20268         }
20269         else
20270         {
20271             generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
20272         }
20273
20274         generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
20275         generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
20276
20277         condemned_gn--;
20278     }
20279
20280     BOOL allocate_first_generation_start = FALSE;
20281     
20282     if (allocate_in_condemned)
20283     {
20284         allocate_first_generation_start = TRUE;
20285     }
20286
20287     dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
20288
20289     demotion_low = MAX_PTR;
20290     demotion_high = heap_segment_allocated (ephemeral_heap_segment);
20291
20292     // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
20293     // from gen1. They should get promoted to gen2.
20294     demote_gen1_p = !(settings.promotion && 
20295                       (settings.condemned_generation == (max_generation - 1)) && 
20296                       gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
20297
20298     total_ephemeral_size = 0;
20299
20300     print_free_and_plug ("BP");
20301
20302     for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
20303     {
20304         generation* temp_gen = generation_of (gen_idx);
20305
20306         dprintf (2, ("gen%d start %Ix, plan start %Ix",
20307             gen_idx, 
20308             generation_allocation_start (temp_gen),
20309             generation_plan_allocation_start (temp_gen)));
20310     }
20311
20312     BOOL fire_pinned_plug_events_p = ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PinPlugAtGCTime);
20313     size_t last_plug_len = 0;
20314
20315     while (1)
20316     {
20317         if (x >= end)
20318         {
20319             assert (x == end);
20320             assert (heap_segment_allocated (seg1) == end);
20321             heap_segment_allocated (seg1) = plug_end;
20322
20323             current_brick = update_brick_table (tree, current_brick, x, plug_end);
20324             dprintf (3, ("end of seg: new tree, sequence# 0"));
20325             sequence_number = 0;
20326             tree = 0;
20327
20328             if (heap_segment_next_rw (seg1))
20329             {
20330                 seg1 = heap_segment_next_rw (seg1);
20331                 end = heap_segment_allocated (seg1);
20332                 plug_end = x = heap_segment_mem (seg1);
20333                 current_brick = brick_of (x);
20334                 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
20335                 continue;
20336             }
20337             else
20338             {
20339                 break;
20340             }
20341         }
20342
20343         BOOL last_npinned_plug_p = FALSE;
20344         BOOL last_pinned_plug_p = FALSE;
20345
20346         // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
20347         // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
20348         // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
20349         BYTE* last_pinned_plug = 0;
20350         size_t num_pinned_plugs_in_plug = 0;
20351
20352         BYTE* last_object_in_plug = 0;
20353
20354         while ((x < end) && marked (x))
20355         {
20356             BYTE*  plug_start = x;
20357             BYTE*  saved_plug_end = plug_end;
20358             BOOL   pinned_plug_p = FALSE;
20359             BOOL   npin_before_pin_p = FALSE;
20360             BOOL   saved_last_npinned_plug_p = last_npinned_plug_p;
20361             BYTE*  saved_last_object_in_plug = last_object_in_plug;
20362             BOOL   merge_with_last_pin_p = FALSE;
20363
20364             size_t added_pinning_size = 0;
20365             size_t artificial_pinned_size = 0;
20366
20367             store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p, 
20368                                  last_pinned_plug, pinned_plug_p, last_object_in_plug, 
20369                                  merge_with_last_pin_p, last_plug_len);
20370
20371 #ifdef FEATURE_STRUCTALIGN
20372             int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
20373             size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
20374 #endif // FEATURE_STRUCTALIGN
20375
20376             {
20377                 BYTE* xl = x;
20378                 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
20379                 {
20380                     assert (xl < end);
20381                     if (pinned(xl))
20382                     {
20383                         clear_pinned (xl);
20384                     }
20385 #ifdef FEATURE_STRUCTALIGN
20386                     else
20387                     {
20388                         int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
20389                         if (obj_requiredAlignment > requiredAlignment)
20390                         {
20391                             requiredAlignment = obj_requiredAlignment;
20392                             alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
20393                         }
20394                     }
20395 #endif // FEATURE_STRUCTALIGN
20396
20397                     clear_marked (xl);
20398
20399                     dprintf(4, ("+%Ix+", (size_t)xl));
20400                     assert ((size (xl) > 0));
20401                     assert ((size (xl) <= LARGE_OBJECT_SIZE));
20402
20403                     last_object_in_plug = xl;
20404
20405                     xl = xl + Align (size (xl));
20406                     Prefetch (xl);
20407                 }
20408
20409                 BOOL next_object_marked_p = ((xl < end) && marked (xl));
20410
20411                 if (pinned_plug_p)
20412                 {
20413                     // If it is pinned we need to extend to the next marked object as we can't use part of
20414                     // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
20415                     // references but for now I am just using the next non pinned object for that).
20416                     if (next_object_marked_p) 
20417                     {
20418                         clear_marked (xl);
20419                         last_object_in_plug = xl;
20420                         size_t extra_size = Align (size (xl));
20421                         xl = xl + extra_size;
20422                         added_pinning_size = extra_size;
20423                     }
20424                 }
20425                 else
20426                 {
20427                     if (next_object_marked_p)
20428                         npin_before_pin_p = TRUE;
20429                 }
20430
20431                 assert (xl <= end);
20432                 x = xl;
20433             }
20434             dprintf (3, ( "%Ix[", (size_t)x));
20435             plug_end = x;
20436             size_t ps = plug_end - plug_start;
20437             last_plug_len = ps;
20438             dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
20439             BYTE*  new_address = 0;
20440
20441             if (!pinned_plug_p)
20442             {
20443                 if (allocate_in_condemned &&
20444                     (settings.condemned_generation == max_generation) &&
20445                     (ps > (OS_PAGE_SIZE)))
20446                 {
20447                     ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
20448                     //reloc should >=0 except when we relocate
20449                     //across segments and the dest seg is higher then the src
20450
20451                     if ((ps > (8*OS_PAGE_SIZE)) &&
20452                         (reloc > 0) &&
20453                         ((size_t)reloc < (ps/16)))
20454                     {
20455                         dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
20456                                      (size_t)plug_start, reloc));
20457                         // The last plug couldn't have been a npinned plug or it would have
20458                         // included this plug.
20459                         assert (!saved_last_npinned_plug_p);
20460
20461                         if (last_pinned_plug)
20462                         {
20463                             dprintf (3, ("artificially pinned plug merged with last pinned plug"));
20464                             merge_with_last_pin_p = TRUE;
20465                         }
20466                         else
20467                         {
20468                             enque_pinned_plug (plug_start, FALSE, 0);
20469                             last_pinned_plug = plug_start;
20470                         }
20471
20472                         last_pinned_plug_p = TRUE;
20473                         last_npinned_plug_p = FALSE;
20474                         pinned_plug_p = TRUE;
20475                         artificial_pinned_size = ps;
20476                     }
20477                 }
20478             }
20479
20480             if (pinned_plug_p)
20481             {
20482                 if (fire_pinned_plug_events_p)
20483                     FireEtwPinPlugAtGCTime(plug_start, plug_end, 
20484                                            (merge_with_last_pin_p ? 0 : (BYTE*)node_gap_size (plug_start)), 
20485                                            GetClrInstanceId());
20486
20487                 if (merge_with_last_pin_p)
20488                 {
20489                     merge_with_last_pinned_plug (last_pinned_plug, ps);
20490                 }
20491                 else
20492                 {
20493                     assert (last_pinned_plug == plug_start);
20494                     set_pinned_info (plug_start, ps, consing_gen);
20495                 }
20496
20497                 new_address = plug_start;
20498             }
20499
20500             if (allocate_first_generation_start)
20501             {
20502                 allocate_first_generation_start = FALSE;
20503                 plan_generation_start (condemned_gen1, consing_gen, plug_start);
20504                 assert (generation_plan_allocation_start (condemned_gen1));
20505             }
20506
20507             if (seg1 == ephemeral_heap_segment)
20508             {
20509                 process_ephemeral_boundaries (plug_start, active_new_gen_number,
20510                                               active_old_gen_number,
20511                                               consing_gen,
20512                                               allocate_in_condemned);
20513             }
20514
20515             dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
20516
20517             dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
20518             dd_survived_size (dd_active_old) += ps;
20519
20520             if (!pinned_plug_p)
20521             {
20522 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
20523                 dd_num_npinned_plugs (dd_active_old)++;
20524 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
20525
20526                 add_gen_plug (active_old_gen_number, ps);
20527
20528                 if (allocate_in_condemned)
20529                 {
20530                     verify_pins_with_post_plug_info("before aic");
20531
20532                     new_address =
20533                         allocate_in_condemned_generations (consing_gen,
20534                                                            ps,
20535                                                            active_old_gen_number,
20536 #ifdef SHORT_PLUGS
20537                                                            (npin_before_pin_p ? plug_end : 0),
20538                                                            seg1,
20539 #endif //SHORT_PLUGS
20540                                                            plug_start REQD_ALIGN_AND_OFFSET_ARG);
20541                     verify_pins_with_post_plug_info("after aic");
20542                 }
20543                 else
20544                 {
20545                     new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
20546
20547                     if (new_address != 0)
20548                     {
20549                         if (settings.condemned_generation == (max_generation - 1))
20550                         {
20551                             dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
20552                                 plug_start, plug_end,
20553                                 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
20554                                 (size_t)(plug_end - plug_start)));
20555                         }
20556                     }
20557                     else
20558                     {
20559                         allocate_in_condemned = TRUE;
20560                         new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number, 
20561 #ifdef SHORT_PLUGS
20562                                                                          (npin_before_pin_p ? plug_end : 0),
20563                                                                          seg1,
20564 #endif //SHORT_PLUGS
20565                                                                          plug_start REQD_ALIGN_AND_OFFSET_ARG);
20566                     }
20567                 }
20568
20569                 if (!new_address)
20570                 {
20571                     //verify that we are at then end of the ephemeral segment
20572                     assert (generation_allocation_segment (consing_gen) ==
20573                             ephemeral_heap_segment);
20574                     //verify that we are near the end
20575                     assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
20576                             heap_segment_allocated (ephemeral_heap_segment));
20577                     assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
20578                             (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
20579                 }
20580                 else
20581                 {
20582 #ifdef SIMPLE_DPRINTF
20583                     dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix",
20584                         (size_t)(node_gap_size (plug_start)), 
20585                         plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
20586                             (size_t)new_address + ps, ps));
20587 #endif //SIMPLE_DPRINTF
20588 #ifdef SHORT_PLUGS
20589                     if (is_plug_padded (plug_start))
20590                     {
20591                         dprintf (3, ("%Ix was padded", plug_start));
20592                         dd_padding_size (dd_active_old) += Align (min_obj_size);
20593                     }
20594 #endif //SHORT_PLUGS
20595                 }
20596             }
20597             else
20598             {
20599                 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix]",
20600                             (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
20601                             (size_t)plug_end, ps));
20602                 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
20603                 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
20604                 dd_added_pinned_size (dd_active_old) += added_pinning_size;
20605                 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
20606
20607                 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
20608                 {
20609                     last_gen1_pin_end = plug_end;
20610                 }
20611             }
20612
20613 #ifdef _DEBUG
20614             // detect forward allocation in the same segment
20615             assert (!((new_address > plug_start) &&
20616                 (new_address < heap_segment_reserved (seg1))));
20617 #endif //_DEBUG
20618
20619             if (!merge_with_last_pin_p)
20620             {
20621                 if (current_brick != brick_of (plug_start))
20622                 {
20623                     current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
20624                     sequence_number = 0;
20625                     tree = 0;
20626                 }
20627
20628                 set_node_relocation_distance (plug_start, (new_address - plug_start));
20629                 if (last_node && (node_relocation_distance (last_node) ==
20630                                   (node_relocation_distance (plug_start) +
20631                                    (int)node_gap_size (plug_start))))
20632                 {
20633                     //dprintf(3,( " Lb"));
20634                     dprintf (3, ("%Ix Lb", plug_start));
20635                     set_node_left (plug_start);
20636                 }
20637                 if (0 == sequence_number)
20638                 {
20639                     dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
20640                     tree = plug_start;
20641                 }
20642
20643                 verify_pins_with_post_plug_info("before insert node");
20644
20645                 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
20646                 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
20647                 last_node = plug_start;
20648
20649 #ifdef _DEBUG
20650                 // If we detect if the last plug is pinned plug right before us, we should save this gap info
20651                 if (!pinned_plug_p)
20652                 {
20653                     if (mark_stack_tos > 0)
20654                     {
20655                         mark& m = mark_stack_array[mark_stack_tos - 1];
20656                         if (m.has_post_plug_info())
20657                         {
20658                             BYTE* post_plug_info_start = m.saved_post_plug_info_start;
20659                             size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
20660                             if ((BYTE*)current_plug_gap_start == post_plug_info_start)
20661                             {
20662                                 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
20663                                     *current_plug_gap_start, *(current_plug_gap_start + 1),
20664                                     *(current_plug_gap_start + 2)));
20665                                 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
20666                             }
20667                         }
20668                     }
20669                 }
20670 #endif //_DEBUG
20671
20672                 verify_pins_with_post_plug_info("after insert node");
20673             }
20674         }
20675         
20676         if (num_pinned_plugs_in_plug > 1)
20677         {
20678             dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
20679         }
20680
20681         {
20682 #ifdef MARK_LIST
20683             if (use_mark_list)
20684             {
20685                while ((mark_list_next < mark_list_index) &&
20686                       (*mark_list_next <= x))
20687                {
20688                    mark_list_next++;
20689                }
20690                if ((mark_list_next < mark_list_index)
20691 #ifdef MULTIPLE_HEAPS
20692                    && (*mark_list_next < end) //for multiple segments
20693 #endif //MULTIPLE_HEAPS
20694                    )
20695                    x = *mark_list_next;
20696                else
20697                    x = end;
20698             }
20699             else
20700 #endif //MARK_LIST
20701             {
20702                 BYTE* xl = x;
20703 #ifdef BACKGROUND_GC
20704                 if (current_c_gc_state == c_gc_state_marking)
20705                 {
20706                     assert (recursive_gc_sync::background_running_p());
20707                     while ((xl < end) && !marked (xl))
20708                     {
20709                         dprintf (4, ("-%Ix-", (size_t)xl));
20710                         assert ((size (xl) > 0));
20711                         background_object_marked (xl, TRUE);
20712                         xl = xl + Align (size (xl));
20713                         Prefetch (xl);
20714                     }
20715                 }
20716                 else
20717 #endif //BACKGROUND_GC
20718                 {
20719                     while ((xl < end) && !marked (xl))
20720                     {
20721                         dprintf (4, ("-%Ix-", (size_t)xl));
20722                         assert ((size (xl) > 0));
20723                         xl = xl + Align (size (xl));
20724                         Prefetch (xl);
20725                     }
20726                 }
20727                 assert (xl <= end);
20728                 x = xl;
20729             }
20730         }
20731     }
20732
20733     while (!pinned_plug_que_empty_p())
20734     {
20735         if (settings.promotion)
20736         {
20737             BYTE* pplug = pinned_plug (oldest_pin());
20738             if (in_range_for_segment (pplug, ephemeral_heap_segment))
20739             {
20740                 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20741                 //allocate all of the generation gaps
20742                 while (active_new_gen_number > 0)
20743                 {
20744                     active_new_gen_number--;
20745
20746                     if ((active_new_gen_number == (max_generation - 1)) && !demote_gen1_p)
20747                     {
20748                         advance_pins_for_demotion (consing_gen);
20749                     }
20750
20751                     generation* gen = generation_of (active_new_gen_number);
20752                     plan_generation_start (gen, consing_gen, 0);
20753
20754                     if ((demotion_low == MAX_PTR))
20755                     {
20756                         demotion_low = pplug;
20757                         dprintf (3, ("end plan: dlow->%Ix", demotion_low));
20758                     }
20759
20760                     dprintf (2, ("(%d)gen%d plan start: %Ix", 
20761                                   heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
20762                     assert (generation_plan_allocation_start (gen));
20763                 }
20764             }
20765         }
20766
20767         if (pinned_plug_que_empty_p())
20768             break;
20769
20770         size_t  entry = deque_pinned_plug();
20771         mark*  m = pinned_plug_of (entry);
20772         BYTE*  plug = pinned_plug (m);
20773         size_t  len = pinned_len (m);
20774
20775         // detect pinned block in different segment (later) than
20776         // allocation segment
20777         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
20778
20779         while ((plug < generation_allocation_pointer (consing_gen)) ||
20780                (plug >= heap_segment_allocated (nseg)))
20781         {
20782             assert ((plug < heap_segment_mem (nseg)) ||
20783                     (plug > heap_segment_reserved (nseg)));
20784             //adjust the end of the segment to be the end of the plug
20785             assert (generation_allocation_pointer (consing_gen)>=
20786                     heap_segment_mem (nseg));
20787             assert (generation_allocation_pointer (consing_gen)<=
20788                     heap_segment_committed (nseg));
20789
20790             heap_segment_plan_allocated (nseg) =
20791                 generation_allocation_pointer (consing_gen);
20792             //switch allocation segment
20793             nseg = heap_segment_next_rw (nseg);
20794             generation_allocation_segment (consing_gen) = nseg;
20795             //reset the allocation pointer and limits
20796             generation_allocation_pointer (consing_gen) =
20797                 heap_segment_mem (nseg);
20798         }
20799
20800         set_new_pin_info (m, generation_allocation_pointer (consing_gen));
20801         dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
20802             (size_t)(brick_table[brick_of (plug)])));
20803
20804         generation_allocation_pointer (consing_gen) = plug + len;
20805         generation_allocation_limit (consing_gen) =
20806             generation_allocation_pointer (consing_gen);
20807         //Add the size of the pinned plug to the right pinned allocations
20808         //find out which gen this pinned plug came from 
20809         int frgn = object_gennum (plug);
20810         if ((frgn != (int)max_generation) && settings.promotion)
20811         {
20812             generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20813         }
20814
20815     }
20816
20817     plan_generation_starts (consing_gen);
20818     print_free_and_plug ("AP");
20819
20820     {
20821 #ifdef SIMPLE_DPRINTF
20822         for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
20823         {
20824             generation* temp_gen = generation_of (gen_idx);
20825             dynamic_data* temp_dd = dynamic_data_of (gen_idx);
20826
20827             int added_pinning_ratio = 0;
20828             int artificial_pinned_ratio = 0;
20829
20830             if (dd_pinned_survived_size (temp_dd) != 0)
20831             {
20832                 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
20833                 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
20834             }
20835
20836             size_t padding_size = 
20837 #ifdef SHORT_PLUGS
20838                 dd_padding_size (temp_dd);
20839 #else
20840                 0;
20841 #endif //SHORT_PLUGS
20842             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",
20843                 gen_idx, 
20844                 generation_allocation_start (temp_gen),
20845                 generation_plan_allocation_start (temp_gen),
20846                 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
20847                 generation_allocation_size (temp_gen),
20848                 generation_pinned_allocation_compact_size (temp_gen),
20849                 generation_pinned_allocation_sweep_size (temp_gen),
20850                 dd_survived_size (temp_dd),
20851                 dd_pinned_survived_size (temp_dd),
20852                 added_pinning_ratio,
20853                 artificial_pinned_ratio,
20854                 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
20855                 padding_size));
20856         }
20857 #endif //SIMPLE_DPRINTF
20858     }
20859
20860 #ifdef FREE_USAGE_STATS
20861     if (settings.condemned_generation == (max_generation - 1 ))
20862     {
20863         size_t plan_gen2_size = generation_plan_size (max_generation);
20864         size_t growth = plan_gen2_size - old_gen2_size;
20865
20866         dprintf (1, ("gen2's FL effi: %d", (int)(generation_allocator_efficiency (generation_of (max_generation)) * 100)));
20867
20868         if (growth > 0)
20869         {
20870             dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
20871                 growth, generation_end_seg_allocated (generation_of (max_generation)), 
20872                 generation_condemned_allocated (generation_of (max_generation - 1))));
20873         }
20874         else
20875         {
20876             dprintf (1, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id", 
20877                 (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)), 
20878                 generation_condemned_allocated (generation_of (max_generation - 1))));
20879         }
20880
20881         generation*  older_gen = generation_of (settings.condemned_generation + 1);
20882         size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
20883         size_t free_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
20884
20885         dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
20886                     r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
20887                     r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen), 
20888                     r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
20889
20890         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", 
20891             free_allocated,
20892             rejected_free_space,
20893             (generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated),
20894             (generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated),
20895              generation_condemned_allocated (generation_of (settings.condemned_generation))));
20896
20897         float running_free_list_efficiency = 0;
20898         if ((free_allocated + rejected_free_space) != 0)
20899         {
20900             running_free_list_efficiency = (float) (free_allocated) / (float)(free_allocated + rejected_free_space);
20901         }
20902
20903         float free_list_efficiency = 0;
20904         if ((generation_free_list_allocated (older_gen) + generation_free_obj_space (older_gen)) != 0)
20905         {
20906             free_list_efficiency =
20907             (float) (generation_free_list_allocated (older_gen)) / (float)(generation_free_list_allocated (older_gen) + generation_free_obj_space (older_gen));
20908         }
20909
20910         dprintf (1, ("gen%d running free list alloc effi: %d%%(%d%%), current effi: %d%%",
20911                     older_gen->gen_num,
20912                     (int)(running_free_list_efficiency*100), 
20913                     (int)(free_list_efficiency*100),
20914                     (int)(generation_allocator_efficiency(older_gen)*100)));
20915
20916         dprintf (1, ("gen2 free list change"));
20917         for (int j = 0; j < NUM_GEN_POWER2; j++)
20918         {
20919             dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id", 
20920                 heap_number, 
20921                 settings.gc_index,
20922                 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j], 
20923                 (SSIZE_T)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
20924                 (generation_of(max_generation - 1))->gen_plugs[j]));
20925         }
20926     }
20927 #endif //FREE_USAGE_STATS
20928
20929     size_t fragmentation =
20930         generation_fragmentation (generation_of (condemned_gen_number),
20931                                   consing_gen,
20932                                   heap_segment_allocated (ephemeral_heap_segment));
20933
20934     dprintf (2,("Fragmentation: %Id", fragmentation));
20935     dprintf (2,("---- End of Plan phase ----"));
20936
20937 #ifdef TIME_GC
20938     finish = GetCycleCount32();
20939     plan_time = finish - start;
20940 #endif //TIME_GC
20941
20942     // We may update write barrier code.  We assume here EE has been suspended if we are on a GC thread.
20943     assert(GCHeap::IsGCInProgress());
20944
20945     BOOL should_expand = FALSE;
20946     BOOL should_compact= FALSE;
20947     ephemeral_promotion = FALSE;
20948
20949 #ifdef _WIN64
20950     if ((!settings.concurrent) &&
20951         ((condemned_gen_number < max_generation) && 
20952          ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
20953     {
20954         dprintf (2, ("gen0 reduction count is %d, condemning %d, mem load %d",
20955                      settings.gen0_reduction_count,
20956                      condemned_gen_number,
20957                      settings.entry_memory_load));
20958         should_compact = TRUE;
20959
20960         if ((condemned_gen_number >= (max_generation - 1)) && 
20961             dt_low_ephemeral_space_p (tuning_deciding_expansion))
20962         {
20963             dprintf(2,("Not enough space for all ephemeral generations with compaction"));
20964             should_expand = TRUE;
20965         }
20966     }
20967     else
20968     {
20969 #endif //_WIN64
20970         should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
20971 #ifdef _WIN64
20972     }
20973 #endif //_WIN64
20974
20975 #ifdef FEATURE_LOH_COMPACTION
20976     loh_compacted_p = FALSE;
20977 #endif //FEATURE_LOH_COMPACTION
20978
20979     if (condemned_gen_number == max_generation)
20980     {
20981 #ifdef FEATURE_LOH_COMPACTION
20982         if (settings.loh_compaction)
20983         {
20984             if (plan_loh())
20985             {
20986                 should_compact = TRUE;
20987                 gc_data_per_heap.set_mechanism (gc_compact, compact_loh_forced);
20988                 loh_compacted_p = TRUE;
20989             }
20990         }
20991         else
20992         {
20993             if ((heap_number == 0) && (loh_pinned_queue))
20994             {
20995                 loh_pinned_queue_decay--;
20996
20997                 if (!loh_pinned_queue_decay)
20998                 {
20999                     delete loh_pinned_queue;
21000                     loh_pinned_queue = 0;
21001                 }
21002             }
21003         }
21004
21005         if (!loh_compacted_p)
21006 #endif //FEATURE_LOH_COMPACTION
21007         {
21008 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21009             if (ShouldTrackMovementForProfilerOrEtw())
21010                 notify_profiler_of_surviving_large_objects();
21011 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21012             sweep_large_objects();
21013         }
21014     }
21015     else
21016     {
21017         settings.loh_compaction = FALSE;
21018     }
21019
21020 #ifdef MULTIPLE_HEAPS
21021
21022     new_heap_segment = NULL;
21023
21024     if (should_compact && should_expand)
21025         gc_policy = policy_expand;
21026     else if (should_compact)
21027         gc_policy = policy_compact;
21028     else
21029         gc_policy = policy_sweep;
21030
21031     //vote for result of should_compact
21032     dprintf (3, ("Joining for compaction decision"));
21033     gc_t_join.join(this, gc_join_decide_on_compaction);
21034     if (gc_t_join.joined())
21035     {
21036         //safe place to delete large heap segments
21037         if (condemned_gen_number == max_generation)
21038         {
21039             for (int i = 0; i < n_heaps; i++)
21040             {
21041                 g_heaps [i]->rearrange_large_heap_segments ();
21042             }
21043         }
21044
21045         settings.demotion = FALSE;
21046         int pol_max = policy_sweep;
21047         int i;
21048         for (i = 0; i < n_heaps; i++)
21049         {
21050             if (pol_max < g_heaps[i]->gc_policy)
21051                 pol_max = policy_compact;
21052             // set the demotion flag is any of the heap has demotion
21053             if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
21054                 settings.demotion = TRUE;
21055         }
21056
21057         for (i = 0; i < n_heaps; i++)
21058         {
21059             if (pol_max > g_heaps[i]->gc_policy)
21060                 g_heaps[i]->gc_policy = pol_max;
21061             //get the segment while we are serialized
21062             if (g_heaps[i]->gc_policy == policy_expand)
21063             {
21064                 g_heaps[i]->new_heap_segment =
21065                      g_heaps[i]->soh_get_segment_to_expand();
21066                 if (!g_heaps[i]->new_heap_segment)
21067                 {
21068                     set_expand_in_full_gc (condemned_gen_number);
21069                     //we are out of memory, cancel the expansion
21070                     g_heaps[i]->gc_policy = policy_compact;
21071                 }
21072             }
21073         }
21074
21075         BOOL is_full_compacting_gc = FALSE;
21076
21077         if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
21078         {
21079             full_gc_counts[gc_type_compacting]++;
21080             is_full_compacting_gc = TRUE;
21081         }
21082
21083         for (i = 0; i < n_heaps; i++)
21084         {
21085             //copy the card and brick tables
21086             if (g_card_table!= g_heaps[i]->card_table)
21087             {
21088                 g_heaps[i]->copy_brick_card_table (TRUE);
21089             }
21090
21091             if (is_full_compacting_gc)
21092             {
21093                 g_heaps[i]->loh_alloc_since_cg = 0;
21094             }
21095         }
21096
21097         //start all threads on the roots.
21098         dprintf(3, ("Starting all gc threads after compaction decision"));
21099         gc_t_join.restart();
21100     }
21101
21102     //reset the local variable accordingly
21103     should_compact = (gc_policy >= policy_compact);
21104     should_expand  = (gc_policy >= policy_expand);
21105
21106 #else //MULTIPLE_HEAPS
21107
21108     //safe place to delete large heap segments
21109     if (condemned_gen_number == max_generation)
21110     {
21111         rearrange_large_heap_segments ();
21112     }
21113
21114     settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
21115
21116     if (should_compact && (condemned_gen_number == max_generation))
21117     {
21118         full_gc_counts[gc_type_compacting]++;
21119         loh_alloc_since_cg = 0;
21120     }
21121 #endif //MULTIPLE_HEAPS
21122
21123     if (should_compact)
21124     {
21125         dprintf (2,( "**** Doing Compacting GC ****"));
21126
21127         if (should_expand)
21128         {
21129 #ifndef MULTIPLE_HEAPS
21130             heap_segment* new_heap_segment = soh_get_segment_to_expand();
21131 #endif //!MULTIPLE_HEAPS
21132             if (new_heap_segment)
21133             {
21134                 consing_gen = expand_heap(condemned_gen_number,
21135                                           consing_gen,
21136                                           new_heap_segment);
21137             }
21138
21139             // If we couldn't get a new segment, or we were able to 
21140             // reserve one but no space to commit, we couldn't
21141             // expand heap.
21142             if (ephemeral_heap_segment != new_heap_segment)
21143             {
21144                 set_expand_in_full_gc (condemned_gen_number);
21145                 should_expand = FALSE;
21146             }
21147         }
21148         generation_allocation_limit (condemned_gen1) =
21149             generation_allocation_pointer (condemned_gen1);
21150         if ((condemned_gen_number < max_generation))
21151         {
21152             generation_allocator (older_gen)->commit_alloc_list_changes();
21153
21154             // Fix the allocation area of the older generation
21155             fix_older_allocation_area (older_gen);
21156         }
21157         assert (generation_allocation_segment (consing_gen) ==
21158                 ephemeral_heap_segment);
21159
21160 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21161         if (ShouldTrackMovementForProfilerOrEtw())
21162         {
21163             record_survived_for_profiler(condemned_gen_number, first_condemned_address);
21164         }
21165 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21166
21167         relocate_phase (condemned_gen_number, first_condemned_address);
21168         compact_phase (condemned_gen_number, first_condemned_address,
21169                        (!settings.demotion && settings.promotion));
21170         fix_generation_bounds (condemned_gen_number, consing_gen);
21171         assert (generation_allocation_limit (youngest_generation) ==
21172                 generation_allocation_pointer (youngest_generation));
21173         if (condemned_gen_number >= (max_generation -1))
21174         {
21175 #ifdef MULTIPLE_HEAPS
21176             // this needs be serialized just because we have one
21177             // segment_standby_list/seg_table for all heaps. We should make it at least
21178             // so that when hoarding is not on we don't need this join because
21179             // decommitting memory can take a long time.
21180             //must serialize on deleting segments
21181             gc_t_join.join(this, gc_join_rearrange_segs_compaction);
21182             if (gc_t_join.joined())
21183             {
21184                 for (int i = 0; i < n_heaps; i++)
21185                 {
21186                     g_heaps[i]->rearrange_heap_segments(TRUE);
21187                 }
21188                 gc_t_join.restart();
21189             }
21190 #else
21191             rearrange_heap_segments(TRUE);
21192 #endif //MULTIPLE_HEAPS
21193
21194             if (should_expand)
21195             {
21196                 //fix the start_segment for the ephemeral generations
21197                 for (int i = 0; i < max_generation; i++)
21198                 {
21199                     generation* gen = generation_of (i);
21200                     generation_start_segment (gen) = ephemeral_heap_segment;
21201                     generation_allocation_segment (gen) = ephemeral_heap_segment;
21202                 }
21203             }
21204         }
21205
21206         {
21207 #ifdef FEATURE_PREMORTEM_FINALIZATION
21208             finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
21209                                                        (!settings.demotion && settings.promotion));
21210 #endif // FEATURE_PREMORTEM_FINALIZATION
21211
21212 #ifdef MULTIPLE_HEAPS
21213             dprintf(3, ("Joining after end of compaction"));
21214             gc_t_join.join(this, gc_join_adjust_handle_age_compact);
21215             if (gc_t_join.joined())
21216 #endif //MULTIPLE_HEAPS
21217             {
21218 #ifdef MULTIPLE_HEAPS
21219                 //join all threads to make sure they are synchronized
21220                 dprintf(3, ("Restarting after Promotion granted"));
21221                 gc_t_join.restart();
21222 #endif //MULTIPLE_HEAPS
21223             }
21224
21225             ScanContext sc;
21226             sc.thread_number = heap_number;
21227             sc.promotion = FALSE;
21228             sc.concurrent = FALSE;
21229             // new generations bounds are set can call this guy
21230             if (settings.promotion && !settings.demotion)
21231             {
21232                 dprintf (2, ("Promoting EE roots for gen %d",
21233                              condemned_gen_number));
21234                 CNameSpace::GcPromotionsGranted(condemned_gen_number,
21235                                                 max_generation, &sc);
21236             }
21237             else if (settings.demotion)
21238             {
21239                 dprintf (2, ("Demoting EE roots for gen %d",
21240                              condemned_gen_number));
21241                 CNameSpace::GcDemote (condemned_gen_number, max_generation, &sc);
21242             }
21243         }
21244
21245         {
21246             gen0_big_free_spaces = 0;
21247
21248             reset_pinned_queue_bos();
21249             unsigned int  gen_number = min (max_generation, 1 + condemned_gen_number);
21250             generation*  gen = generation_of (gen_number);
21251             BYTE*  low = generation_allocation_start (generation_of (gen_number-1));
21252             BYTE*  high =  heap_segment_allocated (ephemeral_heap_segment);
21253             
21254             while (!pinned_plug_que_empty_p())
21255             {
21256                 mark*  m = pinned_plug_of (deque_pinned_plug());
21257                 size_t len = pinned_len (m);
21258                 BYTE*  arr = (pinned_plug (m) - len);
21259                 dprintf(3,("free [%Ix %Ix[ pin",
21260                             (size_t)arr, (size_t)arr + len));
21261                 if (len != 0)
21262                 {
21263                     assert (len >= Align (min_obj_size));
21264                     make_unused_array (arr, len);
21265                     // fix fully contained bricks + first one
21266                     // if the array goes beyong the first brick
21267                     size_t start_brick = brick_of (arr);
21268                     size_t end_brick = brick_of (arr + len);
21269                     if (end_brick != start_brick)
21270                     {
21271                         dprintf (3,
21272                                     ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
21273                                     start_brick, end_brick, (size_t)arr));
21274                         set_brick (start_brick,
21275                                     arr - brick_address (start_brick));
21276                         size_t brick = start_brick+1;
21277                         while (brick < end_brick)
21278                         {
21279                             set_brick (brick, start_brick - brick);
21280                             brick++;
21281                         }
21282                     }
21283
21284                     //when we take an old segment to make the new
21285                     //ephemeral segment. we can have a bunch of
21286                     //pinned plugs out of order going to the new ephemeral seg
21287                     //and then the next plugs go back to max_generation
21288                     if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
21289                         (heap_segment_reserved (ephemeral_heap_segment) > arr))
21290                     {
21291
21292                         while ((low <= arr) && (high > arr))
21293                         {
21294                             gen_number--;
21295                             assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
21296                                     settings.demotion || !settings.promotion);
21297                             dprintf (3, ("new free list generation %d", gen_number));
21298
21299                             gen = generation_of (gen_number);
21300                             if (gen_number >= 1)
21301                                 low = generation_allocation_start (generation_of (gen_number-1));
21302                             else
21303                                 low = high;
21304                         }
21305                     }
21306                     else
21307                     {
21308                         dprintf (3, ("new free list generation %d", max_generation));
21309                         gen_number = max_generation;
21310                         gen = generation_of (gen_number);
21311                     }
21312
21313                     dprintf(3,("threading it into generation %d", gen_number));
21314                     thread_gap (arr, len, gen);
21315                     add_gen_free (gen_number, len);
21316                 }
21317             }
21318         }
21319
21320 #ifdef _DEBUG
21321         for (int x = 0; x <= max_generation; x++)
21322         {
21323             assert (generation_allocation_start (generation_of (x)));
21324         }
21325 #endif //_DEBUG
21326
21327         if (!settings.demotion && settings.promotion)
21328         {
21329             //clear card for generation 1. generation 0 is empty
21330             clear_card_for_addresses (
21331                 generation_allocation_start (generation_of (1)),
21332                 generation_allocation_start (generation_of (0)));
21333         }
21334         if (settings.promotion && !settings.demotion)
21335         {
21336             BYTE* start = generation_allocation_start (youngest_generation);
21337             MAYBE_UNUSED_VAR(start);
21338             assert (heap_segment_allocated (ephemeral_heap_segment) ==
21339                     (start + Align (size (start))));
21340         }
21341     }
21342     else
21343     {
21344         //force promotion for sweep
21345         settings.promotion = TRUE;
21346         settings.compaction = FALSE;
21347
21348         ScanContext sc;
21349         sc.thread_number = heap_number;
21350         sc.promotion = FALSE;
21351         sc.concurrent = FALSE;
21352
21353         dprintf (2, ("**** Doing Mark and Sweep GC****"));
21354
21355         if ((condemned_gen_number < max_generation))
21356         {
21357             generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
21358             generation_free_list_space (older_gen) = r_free_list_space;
21359             generation_free_obj_space (older_gen) = r_free_obj_space;
21360             generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
21361             generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
21362             generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
21363             generation_allocation_limit (older_gen) = r_allocation_limit;
21364             generation_allocation_pointer (older_gen) = r_allocation_pointer;
21365             generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
21366             generation_allocation_segment (older_gen) = r_allocation_segment;
21367         }
21368
21369         if ((condemned_gen_number < max_generation))
21370         {
21371             // Fix the allocation area of the older generation
21372             fix_older_allocation_area (older_gen);
21373         }
21374
21375 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21376         if (ShouldTrackMovementForProfilerOrEtw())
21377         {
21378             record_survived_for_profiler(condemned_gen_number, first_condemned_address);
21379         }
21380 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
21381
21382         gen0_big_free_spaces = 0;
21383         make_free_lists (condemned_gen_number);
21384         recover_saved_pinned_info();
21385
21386 #ifdef FEATURE_PREMORTEM_FINALIZATION
21387         finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
21388 #endif // FEATURE_PREMORTEM_FINALIZATION
21389 // MTHTS: leave single thread for HT processing on plan_phase
21390 #ifdef MULTIPLE_HEAPS
21391         dprintf(3, ("Joining after end of sweep"));
21392         gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
21393         if (gc_t_join.joined())
21394 #endif //MULTIPLE_HEAPS
21395         {
21396             CNameSpace::GcPromotionsGranted(condemned_gen_number,
21397                                             max_generation, &sc);
21398             if (condemned_gen_number >= (max_generation -1))
21399             {
21400 #ifdef MULTIPLE_HEAPS
21401                 for (int i = 0; i < n_heaps; i++)
21402                 {
21403                     g_heaps[i]->rearrange_heap_segments(FALSE);
21404                 }
21405 #else
21406                 rearrange_heap_segments(FALSE);
21407 #endif //MULTIPLE_HEAPS
21408             }
21409
21410 #ifdef MULTIPLE_HEAPS
21411             //join all threads to make sure they are synchronized
21412             dprintf(3, ("Restarting after Promotion granted"));
21413             gc_t_join.restart();
21414 #endif //MULTIPLE_HEAPS
21415         }
21416
21417 #ifdef _DEBUG
21418         for (int x = 0; x <= max_generation; x++)
21419         {
21420             assert (generation_allocation_start (generation_of (x)));
21421         }
21422 #endif //_DEBUG
21423
21424         //clear card for generation 1. generation 0 is empty
21425         clear_card_for_addresses (
21426             generation_allocation_start (generation_of (1)),
21427             generation_allocation_start (generation_of (0)));
21428         assert ((heap_segment_allocated (ephemeral_heap_segment) ==
21429                  (generation_allocation_start (youngest_generation) +
21430                   Align (min_obj_size))));
21431     }
21432
21433     //verify_partial();
21434 }
21435 #ifdef _PREFAST_
21436 #pragma warning(pop)
21437 #endif //_PREFAST_
21438
21439
21440 /*****************************
21441 Called after compact phase to fix all generation gaps
21442 ********************************/
21443 void gc_heap::fix_generation_bounds (int condemned_gen_number,
21444                                      generation* consing_gen)
21445 {
21446     assert (generation_allocation_segment (consing_gen) ==
21447             ephemeral_heap_segment);
21448
21449     //assign the planned allocation start to the generation
21450     int gen_number = condemned_gen_number;
21451     int bottom_gen = 0;
21452
21453     while (gen_number >= bottom_gen)
21454     {
21455         generation*  gen = generation_of (gen_number);
21456         dprintf(3,("Fixing generation pointers for %Ix", gen_number));
21457         if ((gen_number < max_generation) && ephemeral_promotion)
21458         {
21459             make_unused_array (saved_ephemeral_plan_start[gen_number], 
21460                                saved_ephemeral_plan_start_size[gen_number]);
21461         }
21462         reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
21463         make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
21464         dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
21465         gen_number--;
21466     }
21467 #ifdef MULTIPLE_HEAPS
21468     if (ephemeral_promotion)
21469     {
21470         //we are creating a generation fault. set the cards.
21471         // and we are only doing this for multiple heaps because in the single heap scenario the 
21472         // new ephemeral generations will be empty and there'll be no need to set cards for the
21473         // old ephemeral generations that got promoted into max_generation.
21474         ptrdiff_t delta = 0;
21475 #ifdef SEG_MAPPING_TABLE
21476         heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
21477 #else //SEG_MAPPING_TABLE
21478         heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
21479 #endif //SEG_MAPPING_TABLE
21480
21481         assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
21482         size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
21483         size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
21484         while (card != end_card)
21485         {
21486             set_card (card);
21487             card++;
21488         }
21489     }
21490 #endif //MULTIPLE_HEAPS
21491     {
21492         alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
21493         //reset the allocated size
21494         BYTE* start = generation_allocation_start (youngest_generation);
21495         MAYBE_UNUSED_VAR(start);
21496         if (settings.promotion && !settings.demotion)
21497             assert ((start + Align (size (start))) ==
21498                     heap_segment_plan_allocated(ephemeral_heap_segment));
21499
21500         heap_segment_allocated(ephemeral_heap_segment)=
21501             heap_segment_plan_allocated(ephemeral_heap_segment);
21502     }
21503 }
21504
21505 BYTE* gc_heap::generation_limit (int gen_number)
21506 {
21507     if (settings.promotion)
21508     {
21509         if (gen_number <= 1)
21510             return heap_segment_reserved (ephemeral_heap_segment);
21511         else
21512             return generation_allocation_start (generation_of ((gen_number - 2)));
21513     }
21514     else
21515     {
21516         if (gen_number <= 0)
21517             return heap_segment_reserved (ephemeral_heap_segment);
21518         else
21519             return generation_allocation_start (generation_of ((gen_number - 1)));
21520     }
21521 }
21522
21523 BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
21524 {
21525     BYTE* start = heap_segment_allocated (ephemeral_heap_segment);
21526     size_t size = Align (min_obj_size)*(condemned_gen_number+1);
21527     assert ((start + size) <=
21528             heap_segment_reserved (ephemeral_heap_segment));
21529     if ((start + size) >
21530         heap_segment_committed (ephemeral_heap_segment))
21531     {
21532         if (!grow_heap_segment (ephemeral_heap_segment, start + size))
21533
21534         {
21535             return FALSE;
21536         }
21537     }
21538     return TRUE;
21539 }
21540
21541 BYTE* gc_heap::allocate_at_end (size_t size)
21542 {
21543     BYTE* start = heap_segment_allocated (ephemeral_heap_segment);
21544     size = Align (size);
21545     BYTE* result = start;
21546     // only called to allocate a min obj so can't overflow here.
21547     assert ((start + size) <=
21548             heap_segment_reserved (ephemeral_heap_segment));
21549     //ensure_gap_allocation took care of it
21550     assert ((start + size) <=
21551             heap_segment_committed (ephemeral_heap_segment));
21552     heap_segment_allocated (ephemeral_heap_segment) += size;
21553     return result;
21554 }
21555
21556
21557 void gc_heap::make_free_lists (int condemned_gen_number)
21558 {
21559 #ifdef TIME_GC
21560     unsigned start;
21561     unsigned finish;
21562     start = GetCycleCount32();
21563 #endif //TIME_GC
21564
21565     //Promotion has to happen in sweep case.
21566     assert (settings.promotion);
21567
21568     generation* condemned_gen = generation_of (condemned_gen_number);
21569     BYTE* start_address = generation_allocation_start (condemned_gen);
21570
21571     size_t  current_brick = brick_of (start_address);
21572     heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
21573
21574     PREFIX_ASSUME(current_heap_segment != NULL);
21575
21576     BYTE*  end_address = heap_segment_allocated (current_heap_segment);
21577     size_t  end_brick = brick_of (end_address-1);
21578     make_free_args args;
21579     args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
21580     args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
21581                               MAX_PTR :
21582                               (generation_limit (args.free_list_gen_number)));
21583     args.free_list_gen = generation_of (args.free_list_gen_number);
21584     args.highest_plug = 0;
21585
21586     if ((start_address < end_address) ||
21587         (condemned_gen_number == max_generation))
21588     {
21589         while (1)
21590         {
21591             if ((current_brick > end_brick))
21592             {
21593                 if (args.current_gen_limit == MAX_PTR)
21594                 {
21595                     //We had an empty segment
21596                     //need to allocate the generation start
21597
21598                     generation* gen = generation_of (max_generation);
21599
21600                     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21601
21602                     PREFIX_ASSUME(start_seg != NULL);
21603
21604                     BYTE* gap = heap_segment_mem (start_seg);
21605
21606                     generation_allocation_start (gen) = gap;
21607                     heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
21608                     make_unused_array (gap, Align (min_obj_size));
21609                     reset_allocation_pointers (gen, gap);
21610                     dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
21611                                  max_generation, (size_t)gap));
21612                     args.current_gen_limit = generation_limit (args.free_list_gen_number);
21613                 }
21614                 if (heap_segment_next_rw (current_heap_segment))
21615                 {
21616                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
21617                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
21618                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
21619
21620                     continue;
21621                 }
21622                 else
21623                 {
21624                     break;
21625                 }
21626             }
21627             {
21628                 int brick_entry =  brick_table [ current_brick ];
21629                 if ((brick_entry >= 0))
21630                 {
21631                     make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
21632                     dprintf(3,("Fixing brick entry %Ix to %Ix",
21633                                current_brick, (size_t)args.highest_plug));
21634                     set_brick (current_brick,
21635                                (args.highest_plug - brick_address (current_brick)));
21636                 }
21637                 else
21638                 {
21639                     if ((brick_entry > -32768))
21640                     {
21641
21642 #ifdef _DEBUG
21643                         ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
21644                         if ((brick_entry != -32767) && (! ((offset == brick_entry))))
21645                         {
21646                             assert ((brick_entry == -1));
21647                         }
21648 #endif //_DEBUG
21649                         //init to -1 for faster find_first_object
21650                         set_brick (current_brick, -1);
21651                     }
21652                 }
21653             }
21654             current_brick++;
21655         }
21656     }
21657     {
21658         int bottom_gen = 0;
21659         args.free_list_gen_number--;
21660         while (args.free_list_gen_number >= bottom_gen)
21661         {
21662             BYTE*  gap = 0;
21663             generation* gen2 = generation_of (args.free_list_gen_number);
21664             gap = allocate_at_end (Align(min_obj_size));
21665             generation_allocation_start (gen2) = gap;
21666             reset_allocation_pointers (gen2, gap);
21667             dprintf(3,("Fixing generation start of %d to: %Ix",
21668                        args.free_list_gen_number, (size_t)gap));
21669             PREFIX_ASSUME(gap != NULL);
21670             make_unused_array (gap, Align (min_obj_size));
21671
21672             args.free_list_gen_number--;
21673         }
21674
21675         //reset the allocated size
21676         BYTE* start2 = generation_allocation_start (youngest_generation);
21677         alloc_allocated = start2 + Align (size (start2));
21678     }
21679
21680 #ifdef TIME_GC
21681     finish = GetCycleCount32();
21682     sweep_time = finish - start;
21683 #endif //TIME_GC
21684 }
21685
21686 void gc_heap::make_free_list_in_brick (BYTE* tree, make_free_args* args)
21687 {
21688     assert ((tree >= 0));
21689     {
21690         int  right_node = node_right_child (tree);
21691         int left_node = node_left_child (tree);
21692         args->highest_plug = 0;
21693         if (! (0 == tree))
21694         {
21695             if (! (0 == left_node))
21696             {
21697                 make_free_list_in_brick (tree + left_node, args);
21698
21699             }
21700             {
21701                 BYTE*  plug = tree;
21702                 size_t  gap_size = node_gap_size (tree);
21703                 BYTE*  gap = (plug - gap_size);
21704                 dprintf (3,("Making free list %Ix len %d in %d",
21705                 //dprintf (3,("F: %Ix len %Ix in %d",
21706                         (size_t)gap, gap_size, args->free_list_gen_number));
21707                 args->highest_plug = tree;
21708 #ifdef SHORT_PLUGS
21709                 if (is_plug_padded (plug))
21710                 {
21711                     dprintf (3, ("%Ix padded", plug));
21712                     clear_plug_padded (plug);
21713                 }
21714 #endif //SHORT_PLUGS
21715             gen_crossing:
21716                 {
21717                     if ((args->current_gen_limit == MAX_PTR) ||
21718                         ((plug >= args->current_gen_limit) &&
21719                          ephemeral_pointer_p (plug)))
21720                     {
21721                         dprintf(3,(" Crossing Generation boundary at %Ix",
21722                                (size_t)args->current_gen_limit));
21723                         if (!(args->current_gen_limit == MAX_PTR))
21724                         {
21725                             args->free_list_gen_number--;
21726                             args->free_list_gen = generation_of (args->free_list_gen_number);
21727                         }
21728                         dprintf(3,( " Fixing generation start of %d to: %Ix",
21729                                 args->free_list_gen_number, (size_t)gap));
21730
21731                         reset_allocation_pointers (args->free_list_gen, gap);
21732                         args->current_gen_limit = generation_limit (args->free_list_gen_number);
21733
21734                         if ((gap_size >= (2*Align (min_obj_size))))
21735                         {
21736                             dprintf(3,(" Splitting the gap in two %Id left",
21737                                    gap_size));
21738                             make_unused_array (gap, Align(min_obj_size));
21739                             gap_size = (gap_size - Align(min_obj_size));
21740                             gap = (gap + Align(min_obj_size));
21741                         }
21742                         else
21743                         {
21744                             make_unused_array (gap, gap_size);
21745                             gap_size = 0;
21746                         }
21747                         goto gen_crossing;
21748                     }
21749                 }
21750
21751                 thread_gap (gap, gap_size, args->free_list_gen);
21752                 add_gen_free (args->free_list_gen->gen_num, gap_size);
21753             }
21754             if (! (0 == right_node))
21755             {
21756                 make_free_list_in_brick (tree + right_node, args);
21757             }
21758         }
21759     }
21760 }
21761
21762 void gc_heap::thread_gap (BYTE* gap_start, size_t size, generation*  gen)
21763 {
21764     assert (generation_allocation_start (gen));
21765     if ((size > 0))
21766     {
21767         if ((gen->gen_num == 0) && (size > CLR_SIZE))
21768         {
21769             gen0_big_free_spaces += size;
21770         }
21771
21772         assert ((heap_segment_rw (generation_start_segment (gen))!=
21773                  ephemeral_heap_segment) ||
21774                 (gap_start > generation_allocation_start (gen)));
21775         // The beginning of a segment gap is not aligned
21776         assert (size >= Align (min_obj_size));
21777         make_unused_array (gap_start, size, 
21778                           (!settings.concurrent && (gen != youngest_generation)),
21779                           (gen->gen_num == max_generation));
21780         dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
21781
21782         if ((size >= min_free_list))
21783         {
21784             generation_free_list_space (gen) += size;
21785             generation_allocator (gen)->thread_item (gap_start, size);
21786         }
21787         else
21788         {
21789             generation_free_obj_space (gen) += size;
21790         }
21791     }
21792 }
21793
21794 void gc_heap::loh_thread_gap_front (BYTE* gap_start, size_t size, generation*  gen)
21795 {
21796     assert (generation_allocation_start (gen));
21797     if (size >= min_free_list)
21798     {
21799         generation_free_list_space (gen) += size;
21800         generation_allocator (gen)->thread_item_front (gap_start, size);
21801     }
21802 }
21803
21804 void gc_heap::make_unused_array (BYTE* x, size_t size, BOOL clearp, BOOL resetp)
21805 {
21806     dprintf (3, ("Making unused array [%Ix, %Ix[",
21807         (size_t)x, (size_t)(x+size)));
21808     assert (size >= Align (min_obj_size));
21809
21810 //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
21811 //    check_batch_mark_array_bits (x, x+size);
21812 //#endif //VERIFY_HEAP && BACKGROUND_GC
21813
21814     if (resetp)
21815         reset_memory (x, size);
21816
21817     ((CObjectHeader*)x)->SetFree(size);
21818
21819 #ifdef _WIN64
21820
21821 #if BIGENDIAN
21822 #error "This won't work on big endian platforms"
21823 #endif
21824
21825     size_t size_as_object = (UINT32)(size - free_object_base_size) + free_object_base_size;
21826
21827     if (size_as_object < size)
21828     {
21829         //
21830         // If the size is more than 4GB, we need to create multiple objects because of
21831         // the Array::m_NumComponents is DWORD and the high 32 bits of unused array
21832         // size is ignored in regular object size computation.
21833         //
21834         BYTE * tmp = x + size_as_object;
21835         size_t remaining_size = size - size_as_object;
21836
21837         while (remaining_size > UINT32_MAX)
21838         {
21839             // Make sure that there will be at least Align(min_obj_size) left
21840             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
21841                 - Align (min_obj_size, get_alignment_constant (FALSE));
21842
21843             ((CObjectHeader*)tmp)->SetFree(current_size);
21844
21845             remaining_size -= current_size;
21846             tmp += current_size;
21847         }
21848
21849         ((CObjectHeader*)tmp)->SetFree(remaining_size);
21850     }
21851 #endif
21852
21853     if (clearp)
21854         clear_card_for_addresses (x, x + Align(size));
21855 }
21856
21857 // Clear memory set by make_unused_array.
21858 void gc_heap::clear_unused_array (BYTE* x, size_t size)
21859 {
21860     // Also clear the sync block
21861     *(((PTR_PTR)x)-1) = 0;
21862
21863     ((CObjectHeader*)x)->UnsetFree();
21864
21865 #ifdef _WIN64
21866
21867 #if BIGENDIAN
21868 #error "This won't work on big endian platforms"
21869 #endif
21870
21871     // The memory could have been cleared in the meantime. We have to mirror the algorithm
21872     // from make_unused_array since we cannot depend on the object sizes in memory.
21873     size_t size_as_object = (UINT32)(size - free_object_base_size) + free_object_base_size;
21874
21875     if (size_as_object < size)
21876     {
21877         BYTE * tmp = x + size_as_object;
21878         size_t remaining_size = size - size_as_object;
21879
21880         while (remaining_size > UINT32_MAX)
21881         {
21882             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
21883                 - Align (min_obj_size, get_alignment_constant (FALSE));
21884
21885             ((CObjectHeader*)tmp)->UnsetFree();
21886
21887             remaining_size -= current_size;
21888             tmp += current_size;
21889         }
21890
21891         ((CObjectHeader*)tmp)->UnsetFree();
21892     }
21893 #endif
21894 }
21895
21896 inline
21897 BYTE* tree_search (BYTE* tree, BYTE* old_address)
21898 {
21899     BYTE* candidate = 0;
21900     int cn;
21901     while (1)
21902     {
21903         if (tree < old_address)
21904         {
21905             if ((cn = node_right_child (tree)) != 0)
21906             {
21907                 assert (candidate < tree);
21908                 candidate = tree;
21909                 tree = tree + cn;
21910                 Prefetch (tree - 8);
21911                 continue;
21912             }
21913             else
21914                 break;
21915         }
21916         else if (tree > old_address)
21917         {
21918             if ((cn = node_left_child (tree)) != 0)
21919             {
21920                 tree = tree + cn;
21921                 Prefetch (tree - 8);
21922                 continue;
21923             }
21924             else
21925                 break;
21926         } else
21927             break;
21928     }
21929     if (tree <= old_address)
21930         return tree;
21931     else if (candidate)
21932         return candidate;
21933     else
21934         return tree;
21935 }
21936
21937 void gc_heap::relocate_address (BYTE** pold_address THREAD_NUMBER_DCL)
21938 {
21939     BYTE* old_address = *pold_address;
21940     if (!((old_address >= gc_low) && (old_address < gc_high)))
21941 #ifdef MULTIPLE_HEAPS
21942     {
21943         if (old_address == 0)
21944             return;
21945         gc_heap* hp = heap_of (old_address);
21946         if ((hp == this) ||
21947             !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
21948             return;
21949     }
21950 #else //MULTIPLE_HEAPS
21951         return ;
21952 #endif //MULTIPLE_HEAPS
21953     // delta translates old_address into address_gc (old_address);
21954     size_t  brick = brick_of (old_address);
21955     int    brick_entry =  brick_table [ brick ];
21956     BYTE*  new_address = old_address;
21957     if (! ((brick_entry == 0)))
21958     {
21959     retry:
21960         {
21961             while (brick_entry < 0)
21962             {
21963                 brick = (brick + brick_entry);
21964                 brick_entry =  brick_table [ brick ];
21965             }
21966             BYTE* old_loc = old_address;
21967
21968             BYTE* node = tree_search ((brick_address (brick) + brick_entry-1),
21969                                       old_loc);
21970             if ((node <= old_loc))
21971                 new_address = (old_address + node_relocation_distance (node));
21972             else
21973             {
21974                 if (node_left_p (node))
21975                 {
21976                     dprintf(3,(" L: %Ix", (size_t)node));
21977                     new_address = (old_address +
21978                                    (node_relocation_distance (node) +
21979                                     node_gap_size (node)));
21980                 }
21981                 else
21982                 {
21983                     brick = brick - 1;
21984                     brick_entry =  brick_table [ brick ];
21985                     goto retry;
21986                 }
21987             }
21988         }
21989
21990         *pold_address = new_address;
21991         return;
21992     }
21993
21994 #ifdef FEATURE_LOH_COMPACTION
21995     if (loh_compacted_p)
21996     {
21997         *pold_address = old_address + loh_node_relocation_distance (old_address);
21998     }
21999     else
22000 #endif //FEATURE_LOH_COMPACTION
22001     {
22002         *pold_address = new_address;
22003     }
22004 }
22005
22006 inline void 
22007 gc_heap::check_class_object_demotion (BYTE* obj)
22008 {
22009 #ifdef COLLECTIBLE_CLASS
22010     if (is_collectible(obj))
22011     {
22012         check_class_object_demotion_internal (obj);
22013     }
22014 #endif //COLLECTIBLE_CLASS
22015 }
22016
22017 #ifdef COLLECTIBLE_CLASS
22018 NOINLINE void 
22019 gc_heap::check_class_object_demotion_internal (BYTE* obj)
22020 {
22021     if (settings.demotion)
22022     {
22023 #ifdef MULTIPLE_HEAPS
22024         // We set the card without checking the demotion range 'cause at this point
22025         // the handle that points to the loader allocator object may or may not have
22026         // been relocated by other GC threads. 
22027         set_card (card_of (obj));
22028 #else
22029         THREAD_FROM_HEAP;
22030         BYTE* class_obj = get_class_object (obj);
22031         dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
22032         BYTE* temp_class_obj = class_obj;
22033         BYTE** temp = &temp_class_obj;
22034         relocate_address (temp THREAD_NUMBER_ARG);
22035
22036         check_demotion_helper (temp, obj);
22037 #endif //MULTIPLE_HEAPS
22038     }
22039 }
22040
22041 #endif //COLLECTIBLE_CLASS
22042
22043 inline void
22044 gc_heap::check_demotion_helper (BYTE** pval, BYTE* parent_obj)
22045 {
22046     // detect if we are demoting an object
22047     if ((*pval < demotion_high) &&
22048         (*pval >= demotion_low))
22049     {
22050         dprintf(3, ("setting card %Ix:%Ix",
22051                     card_of((BYTE*)pval),
22052                     (size_t)pval));
22053
22054         set_card (card_of (parent_obj));
22055     }
22056 #ifdef MULTIPLE_HEAPS
22057     else if (settings.demotion)
22058     {
22059         dprintf (4, ("Demotion active, computing heap_of object"));
22060         gc_heap* hp = heap_of (*pval);
22061         if ((*pval < hp->demotion_high) &&
22062             (*pval >= hp->demotion_low))
22063         {
22064             dprintf(3, ("setting card %Ix:%Ix",
22065                         card_of((BYTE*)pval),
22066                         (size_t)pval));
22067
22068             set_card (card_of (parent_obj));
22069         }
22070     }
22071 #endif //MULTIPLE_HEAPS
22072 }
22073
22074 inline void
22075 gc_heap::reloc_survivor_helper (BYTE** pval)
22076 {
22077     THREAD_FROM_HEAP;
22078     relocate_address (pval THREAD_NUMBER_ARG);
22079
22080     check_demotion_helper (pval, (BYTE*)pval);
22081 }
22082
22083 inline void
22084 gc_heap::relocate_obj_helper (BYTE* x, size_t s)
22085 {
22086     THREAD_FROM_HEAP;
22087     if (contain_pointers (x))
22088     {
22089         dprintf (3, ("$%Ix$", (size_t)x));
22090
22091         go_through_object_nostart (method_table(x), x, s, pval,
22092                             {
22093                                 BYTE* child = *pval;
22094                                 reloc_survivor_helper (pval);
22095                                 if (child)
22096                                 {
22097                                     dprintf (3, ("%Ix->%Ix->%Ix", (BYTE*)pval, child, *pval));
22098                                 }
22099                             });
22100
22101     }
22102     check_class_object_demotion (x);
22103 }
22104
22105 inline 
22106 void gc_heap::reloc_ref_in_shortened_obj (BYTE** address_to_set_card, BYTE** address_to_reloc)
22107 {
22108     THREAD_FROM_HEAP;
22109
22110     BYTE* old_val = (address_to_reloc ? *address_to_reloc : 0);
22111     relocate_address (address_to_reloc THREAD_NUMBER_ARG);
22112     if (address_to_reloc)
22113     {
22114         dprintf (3, ("SR %Ix: %Ix->%Ix", (BYTE*)address_to_reloc, old_val, *address_to_reloc));
22115     }
22116
22117     //check_demotion_helper (current_saved_info_to_relocate, (BYTE*)pval);
22118     BYTE* relocated_addr = *address_to_reloc;
22119     if ((relocated_addr < demotion_high) &&
22120         (relocated_addr >= demotion_low))
22121     {
22122         dprintf (3, ("set card for location %Ix(%Ix)",
22123                     (size_t)address_to_set_card, card_of((BYTE*)address_to_set_card)));
22124
22125         set_card (card_of ((BYTE*)address_to_set_card));
22126     }
22127 #ifdef MULTIPLE_HEAPS
22128     else if (settings.demotion)
22129     {
22130         gc_heap* hp = heap_of (relocated_addr);
22131         if ((relocated_addr < hp->demotion_high) &&
22132             (relocated_addr >= hp->demotion_low))
22133         {
22134             dprintf (3, ("%Ix on h#d, set card for location %Ix(%Ix)",
22135                         relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((BYTE*)address_to_set_card)));
22136
22137             set_card (card_of ((BYTE*)address_to_set_card));
22138         }
22139     }
22140 #endif //MULTIPLE_HEAPS
22141 }
22142
22143 void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
22144 {
22145     THREAD_FROM_HEAP;
22146     BYTE* plug = pinned_plug (pinned_plug_entry);
22147     BYTE* pre_plug_start = plug - sizeof (plug_and_gap);
22148     // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
22149     // address. Consider this scenario: 
22150     // gen1 start | 3-ptr sized NP | PP
22151     // 0          | 0x18           | 0x30
22152     // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
22153     // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
22154     // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree). 
22155     pre_plug_start += sizeof (BYTE*);
22156     BYTE** old_address = &pre_plug_start;
22157
22158     BYTE* old_val = (old_address ? *old_address : 0);
22159     relocate_address (old_address THREAD_NUMBER_ARG);
22160     if (old_address)
22161     {
22162         dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix", 
22163             (BYTE*)old_address, old_val, *old_address, (pre_plug_start - sizeof (BYTE*))));
22164     }
22165
22166     pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (BYTE*));
22167 }
22168
22169 inline
22170 void gc_heap::relocate_shortened_obj_helper (BYTE* x, size_t s, BYTE* end, mark* pinned_plug_entry, BOOL is_pinned)
22171 {
22172     THREAD_FROM_HEAP;
22173     BYTE* plug = pinned_plug (pinned_plug_entry);
22174
22175     if (!is_pinned)
22176     {
22177         //// Temporary - we just wanna make sure we are doing things right when padding is needed.
22178         //if ((x + s) < plug)
22179         //{
22180         //    dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix", 
22181         //        x, (x + s), (plug- (x + s)), plug));
22182         //    DebugBreak();
22183         //}
22184
22185         relocate_pre_plug_info (pinned_plug_entry);
22186     }
22187
22188     verify_pins_with_post_plug_info("after relocate_pre_plug_info");
22189
22190     BYTE* saved_plug_info_start = 0;
22191     BYTE** saved_info_to_relocate = 0;
22192
22193     if (is_pinned)
22194     {
22195         saved_plug_info_start = (BYTE*)(pinned_plug_entry->get_post_plug_info_start());
22196         saved_info_to_relocate = (BYTE**)(pinned_plug_entry->get_post_plug_reloc_info());
22197     }
22198     else
22199     {
22200         saved_plug_info_start = (plug - sizeof (plug_and_gap));
22201         saved_info_to_relocate = (BYTE**)(pinned_plug_entry->get_pre_plug_reloc_info());
22202     }
22203     
22204     BYTE** current_saved_info_to_relocate = 0;
22205     BYTE* child = 0;
22206
22207     dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
22208
22209     if (contain_pointers (x))
22210     {
22211         dprintf (3,("$%Ix$", (size_t)x));
22212
22213         go_through_object_nostart (method_table(x), x, s, pval,
22214         {
22215             dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (BYTE*)pval, *pval));
22216
22217             if ((BYTE*)pval >= end)
22218             {
22219                 current_saved_info_to_relocate = saved_info_to_relocate + ((BYTE*)pval - saved_plug_info_start) / sizeof (BYTE**);
22220                 child = *current_saved_info_to_relocate;
22221                 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
22222                 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
22223                     (BYTE*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
22224             }
22225             else
22226             {
22227                 reloc_survivor_helper (pval);
22228             }
22229         });
22230     }
22231
22232     check_class_object_demotion (x);
22233 }
22234
22235 void gc_heap::relocate_survivor_helper (BYTE* plug, BYTE* plug_end)
22236 {
22237     BYTE*  x = plug;
22238     while (x < plug_end)
22239     {
22240         size_t s = size (x);
22241         BYTE* next_obj = x + Align (s);
22242         Prefetch (next_obj);
22243         relocate_obj_helper (x, s);
22244         assert (s > 0);
22245         x = next_obj;
22246     }
22247 }
22248
22249 // if we expanded, right now we are not handling it as We are not saving the new reloc info.
22250 void gc_heap::verify_pins_with_post_plug_info (const char* msg)
22251 {
22252 #if defined  (_DEBUG) && defined (VERIFY_HEAP)
22253     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
22254     {
22255         if (!verify_pinned_queue_p)
22256             return;
22257
22258         if (settings.heap_expansion)
22259             return;
22260
22261         for (size_t i = 0; i < mark_stack_tos; i++)
22262         {
22263             mark& m = mark_stack_array[i];
22264
22265             mark* pinned_plug_entry = pinned_plug_of(i);
22266
22267             if (pinned_plug_entry->has_post_plug_info() && 
22268                 pinned_plug_entry->post_short_p() && 
22269                 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
22270             {
22271                 BYTE* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
22272                 // object after pin
22273                 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d", 
22274                     next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
22275                     (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
22276
22277                 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
22278
22279                 if (node_gap_size (next_obj) != *post_plug_debug)
22280                 {
22281                     dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix", 
22282                         next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
22283                     FATAL_GC_ERROR();
22284                 }
22285                 post_plug_debug++;
22286                 // can't do node_relocation_distance here as it clears the left bit.
22287                 //if (node_relocation_distance (next_obj) != *post_plug_debug)
22288                 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
22289                 {
22290                     dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix", 
22291                         next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
22292                     FATAL_GC_ERROR();
22293                 }
22294                 if (node_left_child (next_obj) > 0)
22295                 {
22296                     dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
22297                     FATAL_GC_ERROR();
22298                 }
22299             }
22300         }
22301
22302         dprintf (3, ("%s verified", msg));
22303     }
22304 #endif // _DEBUG && VERIFY_HEAP
22305 }
22306
22307 #ifdef COLLECTIBLE_CLASS
22308 // We don't want to burn another ptr size space for pinned plugs to record this so just 
22309 // set the card unconditionally for collectible objects if we are demoting.
22310 inline void
22311 gc_heap::unconditional_set_card_collectible (BYTE* obj)
22312 {
22313     if (settings.demotion)
22314     {
22315         set_card (card_of (obj));
22316     }
22317 }
22318 #endif //COLLECTIBLE_CLASS
22319
22320 void gc_heap::relocate_shortened_survivor_helper (BYTE* plug, BYTE* plug_end, mark* pinned_plug_entry)
22321 {
22322     BYTE*  x = plug;
22323     BYTE* p_plug = pinned_plug (pinned_plug_entry);
22324     BOOL is_pinned = (plug == p_plug);
22325     BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
22326
22327     plug_end += sizeof (gap_reloc_pair);
22328
22329     //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
22330     dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
22331
22332     verify_pins_with_post_plug_info("begin reloc short surv");
22333
22334     while (x < plug_end)
22335     {
22336         if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
22337         {
22338             dprintf (3, ("last obj %Ix is short", x));
22339
22340             if (is_pinned)
22341             {
22342 #ifdef COLLECTIBLE_CLASS
22343                 if (pinned_plug_entry->post_short_collectible_p())
22344                     unconditional_set_card_collectible (x);
22345 #endif //COLLECTIBLE_CLASS
22346
22347                 // Relocate the saved references based on bits set.
22348                 BYTE** saved_plug_info_start = (BYTE**)(pinned_plug_entry->get_post_plug_info_start());
22349                 BYTE** saved_info_to_relocate = (BYTE**)(pinned_plug_entry->get_post_plug_reloc_info());
22350                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
22351                 {
22352                     if (pinned_plug_entry->post_short_bit_p (i))
22353                     {
22354                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
22355                     }
22356                 }
22357             }
22358             else
22359             {
22360 #ifdef COLLECTIBLE_CLASS
22361                 if (pinned_plug_entry->pre_short_collectible_p())
22362                     unconditional_set_card_collectible (x);
22363 #endif //COLLECTIBLE_CLASS
22364
22365                 relocate_pre_plug_info (pinned_plug_entry);
22366
22367                 // Relocate the saved references based on bits set.
22368                 BYTE** saved_plug_info_start = (BYTE**)(p_plug - sizeof (plug_and_gap));
22369                 BYTE** saved_info_to_relocate = (BYTE**)(pinned_plug_entry->get_pre_plug_reloc_info());
22370                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
22371                 {
22372                     if (pinned_plug_entry->pre_short_bit_p (i))
22373                     {
22374                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
22375                     }
22376                 }
22377             }
22378
22379             break;
22380         }
22381
22382         size_t s = size (x);
22383         BYTE* next_obj = x + Align (s);
22384         Prefetch (next_obj);
22385
22386         if (next_obj >= plug_end) 
22387         {
22388             dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix", 
22389                 next_obj, plug, plug_end));
22390
22391             verify_pins_with_post_plug_info("before reloc short obj");
22392
22393             relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
22394         }
22395         else
22396         {
22397             relocate_obj_helper (x, s);
22398         }
22399
22400         assert (s > 0);
22401         x = next_obj;
22402     }
22403
22404     verify_pins_with_post_plug_info("end reloc short surv");
22405 }
22406
22407 void gc_heap::relocate_survivors_in_plug (BYTE* plug, BYTE* plug_end,
22408                                           BOOL check_last_object_p, 
22409                                           mark* pinned_plug_entry)
22410 {
22411     //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
22412     dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
22413
22414     if (check_last_object_p)
22415     {
22416         relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
22417     }
22418     else
22419     {
22420         relocate_survivor_helper (plug, plug_end);
22421     }
22422 }
22423
22424 void gc_heap::relocate_survivors_in_brick (BYTE* tree, relocate_args* args)
22425 {
22426     assert ((tree != 0));
22427
22428     dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
22429         tree, args->last_plug, 
22430         (tree + node_left_child (tree)),
22431         (tree + node_right_child (tree)),
22432         node_gap_size (tree)));
22433
22434     if (node_left_child (tree))
22435     {
22436         relocate_survivors_in_brick (tree + node_left_child (tree), args);
22437     }
22438     {
22439         BYTE*  plug = tree;
22440         BOOL   has_post_plug_info_p = FALSE;
22441         BOOL   has_pre_plug_info_p = FALSE;
22442
22443         if (tree == oldest_pinned_plug)
22444         {
22445             args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
22446                                                                &has_post_plug_info_p);
22447             assert (tree == pinned_plug (args->pinned_plug_entry));
22448
22449             dprintf (3, ("tree is the oldest pin: %Ix", tree));
22450         }
22451         if (args->last_plug)
22452         {
22453             size_t  gap_size = node_gap_size (tree);
22454             BYTE*  gap = (plug - gap_size);
22455             dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
22456             assert (gap_size >= Align (min_obj_size));
22457             BYTE*  last_plug_end = gap;
22458
22459             BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
22460
22461             {
22462                 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
22463             }
22464         }
22465         else
22466         {
22467             assert (!has_pre_plug_info_p);
22468         }
22469
22470         args->last_plug = plug;
22471         args->is_shortened = has_post_plug_info_p;
22472         if (has_post_plug_info_p)
22473         {
22474             dprintf (3, ("setting %Ix as shortened", plug));
22475         }
22476         dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
22477     }
22478     if (node_right_child (tree))
22479     {
22480         relocate_survivors_in_brick (tree + node_right_child (tree), args);
22481     }
22482 }
22483
22484 inline
22485 void gc_heap::update_oldest_pinned_plug()
22486 {
22487     oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
22488 }
22489
22490 void gc_heap::relocate_survivors (int condemned_gen_number,
22491                                   BYTE* first_condemned_address)
22492 {
22493     generation* condemned_gen = generation_of (condemned_gen_number);
22494     BYTE*  start_address = first_condemned_address;
22495     size_t  current_brick = brick_of (start_address);
22496     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
22497
22498     PREFIX_ASSUME(current_heap_segment != NULL);
22499
22500     BYTE*  end_address = 0;
22501
22502     reset_pinned_queue_bos();
22503     update_oldest_pinned_plug();
22504     
22505     end_address = heap_segment_allocated (current_heap_segment);
22506
22507     size_t  end_brick = brick_of (end_address - 1);
22508     relocate_args args;
22509     args.low = gc_low;
22510     args.high = gc_high;
22511     args.is_shortened = FALSE;
22512     args.pinned_plug_entry = 0;
22513     args.last_plug = 0;
22514     while (1)
22515     {
22516         if (current_brick > end_brick)
22517         {
22518             if (args.last_plug)
22519             {
22520                 {
22521                     assert (!(args.is_shortened));
22522                     relocate_survivors_in_plug (args.last_plug,
22523                                                 heap_segment_allocated (current_heap_segment),
22524                                                 args.is_shortened, 
22525                                                 args.pinned_plug_entry);
22526                 }
22527
22528                 args.last_plug = 0;
22529             }
22530
22531             if (heap_segment_next_rw (current_heap_segment))
22532             {
22533                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
22534                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
22535                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
22536                 continue;
22537             }
22538             else
22539             {
22540                 break;
22541             }
22542         }
22543         {
22544             int brick_entry =  brick_table [ current_brick ];
22545
22546             if (brick_entry >= 0)
22547             {
22548                 relocate_survivors_in_brick (brick_address (current_brick) +
22549                                              brick_entry -1,
22550                                              &args);
22551             }
22552         }
22553         current_brick++;
22554     }
22555 }
22556
22557 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
22558 void gc_heap::walk_plug (BYTE* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args, size_t profiling_context)
22559 {
22560     if (check_last_object_p)
22561     {
22562         size += sizeof (gap_reloc_pair);
22563         mark* entry = args->pinned_plug_entry;
22564
22565         if (args->is_shortened)
22566         {
22567             assert (entry->has_post_plug_info());
22568             entry->swap_post_plug_and_saved_for_profiler();
22569         }
22570         else
22571         {
22572             assert (entry->has_pre_plug_info());
22573             entry->swap_pre_plug_and_saved_for_profiler();
22574         }
22575     }
22576
22577     ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
22578     ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
22579
22580     STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
22581
22582 #ifdef FEATURE_EVENT_TRACE
22583     ETW::GCLog::MovedReference(plug,
22584                                (plug + size),
22585                                reloc,
22586                                profiling_context,
22587                                settings.compaction);
22588 #endif
22589
22590     if (check_last_object_p)
22591     {
22592         mark* entry = args->pinned_plug_entry;
22593
22594         if (args->is_shortened)
22595         {
22596             entry->swap_post_plug_and_saved_for_profiler();
22597         }
22598         else
22599         {
22600             entry->swap_pre_plug_and_saved_for_profiler();
22601         }
22602     }
22603 }
22604
22605 void gc_heap::walk_relocation_in_brick (BYTE* tree, walk_relocate_args* args, size_t profiling_context)
22606 {
22607     assert ((tree != 0));
22608     if (node_left_child (tree))
22609     {
22610         walk_relocation_in_brick (tree + node_left_child (tree), args, profiling_context);
22611     }
22612
22613     BYTE*  plug = tree;
22614     BOOL   has_pre_plug_info_p = FALSE;
22615     BOOL   has_post_plug_info_p = FALSE;
22616
22617     if (tree == oldest_pinned_plug)
22618     {
22619         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
22620                                                            &has_post_plug_info_p);
22621         assert (tree == pinned_plug (args->pinned_plug_entry));
22622     }
22623
22624     if (args->last_plug != 0)
22625     {
22626         size_t gap_size = node_gap_size (tree);
22627         BYTE*  gap = (plug - gap_size);
22628         BYTE*  last_plug_end = gap;
22629         size_t last_plug_size = (last_plug_end - args->last_plug);
22630         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
22631             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
22632         
22633         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
22634         if (!check_last_object_p)
22635             assert (last_plug_size >= Align (min_obj_size));
22636
22637         walk_plug (args->last_plug, last_plug_size, check_last_object_p, args, profiling_context);
22638     }
22639     else
22640     {
22641         assert (!has_pre_plug_info_p);
22642     }
22643
22644     dprintf (3, ("set args last plug to plug: %Ix", plug));
22645     args->last_plug = plug;
22646     args->is_shortened = has_post_plug_info_p;
22647
22648     if (node_right_child (tree))
22649     {
22650         walk_relocation_in_brick (tree + node_right_child (tree), args, profiling_context);
22651
22652     }
22653 }
22654
22655 void gc_heap::walk_relocation (int condemned_gen_number,
22656                                BYTE* first_condemned_address,
22657                                size_t profiling_context)
22658
22659 {
22660     generation* condemned_gen = generation_of (condemned_gen_number);
22661     BYTE*  start_address = first_condemned_address;
22662     size_t  current_brick = brick_of (start_address);
22663     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
22664
22665     PREFIX_ASSUME(current_heap_segment != NULL);
22666
22667     reset_pinned_queue_bos();
22668     update_oldest_pinned_plug();
22669     size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
22670     walk_relocate_args args;
22671     args.is_shortened = FALSE;
22672     args.pinned_plug_entry = 0;
22673     args.last_plug = 0;
22674
22675     while (1)
22676     {
22677         if (current_brick > end_brick)
22678         {
22679             if (args.last_plug)
22680             {
22681                 walk_plug (args.last_plug, 
22682                            (heap_segment_allocated (current_heap_segment) - args.last_plug), 
22683                            args.is_shortened, 
22684                            &args, profiling_context);
22685                 args.last_plug = 0;
22686             }
22687             if (heap_segment_next_rw (current_heap_segment))
22688             {
22689                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
22690                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
22691                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
22692                 continue;
22693             }
22694             else
22695             {
22696                 break;
22697             }
22698         }
22699         {
22700             int brick_entry =  brick_table [ current_brick ];
22701             if (brick_entry >= 0)
22702             {
22703                 walk_relocation_in_brick (brick_address (current_brick) +
22704                                           brick_entry - 1,
22705                                           &args,
22706                                           profiling_context);
22707             }
22708         }
22709         current_brick++;
22710     }
22711 }
22712
22713 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
22714 void gc_heap::walk_relocation_for_bgc(size_t profiling_context)
22715 {
22716     // This should only be called for BGCs
22717     assert(settings.concurrent);
22718
22719     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
22720
22721     BOOL small_object_segments = TRUE;
22722     int align_const = get_alignment_constant (small_object_segments);
22723
22724     while (1)
22725     {
22726         if (seg == 0)
22727         {
22728             if (small_object_segments)
22729             {
22730                 //switch to large segment
22731                 small_object_segments = FALSE;
22732
22733                 align_const = get_alignment_constant (small_object_segments);
22734                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
22735
22736                 PREFIX_ASSUME(seg != NULL);
22737
22738                 continue;
22739             }
22740             else 
22741                 break;
22742         }
22743
22744         BYTE* o = heap_segment_mem (seg);
22745         BYTE* end = heap_segment_allocated (seg);
22746
22747         while (o < end)
22748         {   
22749
22750             if (method_table(o) == g_pFreeObjectMethodTable)
22751             {
22752                 o += Align (size (o), align_const);
22753                 continue;
22754             }
22755
22756             // It's survived. Make a fake plug, starting at o,
22757             // and send the event
22758
22759             BYTE* plug_start = o;
22760
22761             while (method_table(o) != g_pFreeObjectMethodTable)
22762             {
22763                 o += Align (size (o), align_const);
22764                 if (o >= end)
22765                 {
22766                     break;
22767                 }
22768             }
22769                 
22770             BYTE* plug_end = o;
22771
22772             // Note on last parameter: since this is for bgc, only ETW
22773             // should be sending these events so that existing profapi profilers
22774             // don't get confused.
22775             ETW::GCLog::MovedReference(
22776                 plug_start,
22777                 plug_end,
22778                 0,              // Reloc distance == 0 as this is non-compacting
22779                 profiling_context,
22780                 FALSE,          // Non-compacting
22781                 FALSE);         // fAllowProfApiNotification
22782         }
22783
22784         seg = heap_segment_next (seg);
22785     }
22786 }
22787
22788 void gc_heap::make_free_lists_for_profiler_for_bgc ()
22789 {
22790     assert(settings.concurrent);
22791
22792     size_t profiling_context = 0;
22793     ETW::GCLog::BeginMovedReferences(&profiling_context);
22794
22795     // This provides the profiler with information on what blocks of
22796     // memory are moved during a gc.
22797
22798     walk_relocation_for_bgc(profiling_context);
22799
22800     // Notify the EE-side profiling code that all the references have been traced for
22801     // this heap, and that it needs to flush all cached data it hasn't sent to the
22802     // profiler and release resources it no longer needs.  Since this is for bgc, only
22803     // ETW should be sending these events so that existing profapi profilers don't get confused.
22804     ETW::GCLog::EndMovedReferences(profiling_context, FALSE /* fAllowProfApiNotification */);
22805
22806 #ifdef MULTIPLE_HEAPS
22807     bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
22808     if (bgc_t_join.joined())
22809     {
22810         bgc_t_join.restart();
22811     }
22812 #endif // MULTIPLE_HEAPS
22813 }
22814
22815 #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
22816 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
22817
22818 void gc_heap::relocate_phase (int condemned_gen_number,
22819                               BYTE* first_condemned_address)
22820 {
22821     ScanContext sc;
22822     sc.thread_number = heap_number;
22823     sc.promotion = FALSE;
22824     sc.concurrent = FALSE;
22825
22826
22827 #ifdef TIME_GC
22828         unsigned start;
22829         unsigned finish;
22830         start = GetCycleCount32();
22831 #endif //TIME_GC
22832
22833 //  %type%  category = quote (relocate);
22834     dprintf (2,("---- Relocate phase -----"));
22835
22836 #ifdef MULTIPLE_HEAPS
22837     //join all threads to make sure they are synchronized
22838     dprintf(3, ("Joining after end of plan"));
22839     gc_t_join.join(this, gc_join_begin_relocate_phase);
22840     if (gc_t_join.joined())
22841 #endif //MULTIPLE_HEAPS
22842
22843     {
22844 #ifdef MULTIPLE_HEAPS
22845
22846         //join all threads to make sure they are synchronized
22847         dprintf(3, ("Restarting for relocation"));
22848         gc_t_join.restart();
22849 #endif //MULTIPLE_HEAPS
22850     }
22851
22852     dprintf(3,("Relocating roots"));
22853     CNameSpace::GcScanRoots(GCHeap::Relocate,
22854                             condemned_gen_number, max_generation, &sc);
22855
22856     verify_pins_with_post_plug_info("after reloc stack");
22857
22858 #ifdef BACKGROUND_GC
22859     if (recursive_gc_sync::background_running_p())
22860     {
22861         scan_background_roots (GCHeap::Relocate, heap_number, &sc);
22862     }
22863 #endif //BACKGROUND_GC
22864
22865     if (condemned_gen_number != max_generation)
22866     {
22867         dprintf(3,("Relocating cross generation pointers"));
22868         mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
22869         verify_pins_with_post_plug_info("after reloc cards");
22870     }
22871     if (condemned_gen_number != max_generation)
22872     {
22873         dprintf(3,("Relocating cross generation pointers for large objects"));
22874         mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
22875     }
22876     else
22877     {
22878 #ifdef FEATURE_LOH_COMPACTION
22879         if (loh_compacted_p)
22880         {
22881             assert (settings.condemned_generation == max_generation);
22882             relocate_in_loh_compact();
22883         }
22884         else
22885 #endif //FEATURE_LOH_COMPACTION
22886         {
22887             relocate_in_large_objects ();
22888         }
22889     }
22890     {
22891         dprintf(3,("Relocating survivors"));
22892         relocate_survivors (condemned_gen_number,
22893                             first_condemned_address);
22894     }
22895
22896 #ifdef FEATURE_PREMORTEM_FINALIZATION
22897         dprintf(3,("Relocating finalization data"));
22898         finalize_queue->RelocateFinalizationData (condemned_gen_number,
22899                                                        __this);
22900 #endif // FEATURE_PREMORTEM_FINALIZATION
22901
22902
22903 // MTHTS
22904     {
22905         dprintf(3,("Relocating handle table"));
22906         CNameSpace::GcScanHandles(GCHeap::Relocate,
22907                                   condemned_gen_number, max_generation, &sc);
22908     }
22909
22910 #ifdef MULTIPLE_HEAPS
22911     //join all threads to make sure they are synchronized
22912     dprintf(3, ("Joining after end of relocation"));
22913     gc_t_join.join(this, gc_join_relocate_phase_done);
22914
22915 #endif //MULTIPLE_HEAPS
22916
22917 #ifdef TIME_GC
22918         finish = GetCycleCount32();
22919         reloc_time = finish - start;
22920 #endif //TIME_GC
22921
22922     dprintf(2,( "---- End of Relocate phase ----"));
22923 }
22924
22925 // This compares to see if tree is the current pinned plug and returns info
22926 // for this pinned plug. Also advances the pinned queue if that's the case.
22927 //
22928 // We don't change the values of the plug info if tree is not the same as 
22929 // the current pinned plug - the caller is responsible for setting the right
22930 // values to begin with.
22931 //
22932 // POPO TODO: We are keeping this temporarily as this is also used by realloc 
22933 // where it passes FALSE to deque_p, change it to use the same optimization 
22934 // as relocate. Not as essential since realloc is already a slow path.
22935 mark* gc_heap::get_next_pinned_entry (BYTE* tree, 
22936                                       BOOL* has_pre_plug_info_p, 
22937                                       BOOL* has_post_plug_info_p,
22938                                       BOOL deque_p)
22939 {
22940     if (!pinned_plug_que_empty_p())
22941     {
22942         mark* oldest_entry = oldest_pin();
22943         BYTE* oldest_plug = pinned_plug (oldest_entry);
22944         if (tree == oldest_plug)
22945         {
22946             *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
22947             *has_post_plug_info_p = oldest_entry->has_post_plug_info();
22948
22949             if (deque_p)
22950             {
22951                 deque_pinned_plug();
22952             }
22953
22954             dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d", 
22955                 tree, 
22956                 (*has_pre_plug_info_p ? 1 : 0),
22957                 (*has_post_plug_info_p ? 1 : 0)));
22958
22959             return oldest_entry;
22960         }
22961     }
22962
22963     return NULL;
22964 }
22965
22966 // This also deques the oldest entry and update the oldest plug
22967 mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p, 
22968                                         BOOL* has_post_plug_info_p)
22969 {
22970     mark* oldest_entry = oldest_pin();
22971     *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
22972     *has_post_plug_info_p = oldest_entry->has_post_plug_info();
22973
22974     deque_pinned_plug();
22975     update_oldest_pinned_plug();
22976     return oldest_entry;
22977 }
22978
22979 inline
22980 void gc_heap::copy_cards_range (BYTE* dest, BYTE* src, size_t len, BOOL copy_cards_p)
22981 {
22982     if (copy_cards_p)
22983         copy_cards_for_addresses (dest, src, len);
22984     else
22985         clear_card_for_addresses (dest, dest + len);
22986 }
22987
22988 // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
22989 // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
22990 // we won't need to individually recover each overwritten part of plugs.
22991 inline
22992 void  gc_heap::gcmemcopy (BYTE* dest, BYTE* src, size_t len, BOOL copy_cards_p)
22993 {
22994     if (dest != src)
22995     {
22996 #ifdef BACKGROUND_GC
22997         if (current_c_gc_state == c_gc_state_marking) 
22998         {
22999             //TODO: should look to see whether we should consider changing this
23000             // to copy a consecutive region of the mark array instead.
23001             copy_mark_bits_for_addresses (dest, src, len);
23002         }
23003 #endif //BACKGROUND_GC
23004         //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
23005         dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
23006         memcopy (dest - plug_skew, src - plug_skew, (int)len);
23007         copy_cards_range (dest, src, len, copy_cards_p);
23008     }
23009 }
23010
23011 void gc_heap::compact_plug (BYTE* plug, size_t size, BOOL check_last_object_p, compact_args* args)
23012 {
23013     args->print();
23014     BYTE* reloc_plug = plug + args->last_plug_relocation;
23015
23016     if (check_last_object_p)
23017     {
23018         size += sizeof (gap_reloc_pair);
23019         mark* entry = args->pinned_plug_entry;
23020
23021         if (args->is_shortened)
23022         {
23023             assert (entry->has_post_plug_info());
23024             entry->swap_post_plug_and_saved();
23025         }
23026         else
23027         {
23028             assert (entry->has_pre_plug_info());
23029             entry->swap_pre_plug_and_saved();
23030         }
23031     }
23032
23033     int  old_brick_entry =  brick_table [brick_of (plug)];
23034
23035     assert (node_relocation_distance (plug) == args->last_plug_relocation);
23036
23037 #ifdef FEATURE_STRUCTALIGN
23038     ptrdiff_t alignpad = node_alignpad(plug);
23039     if (alignpad)
23040     {
23041         make_unused_array (reloc_plug - alignpad, alignpad);
23042         if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
23043         {
23044             // The alignment padding is straddling one or more bricks;
23045             // it has to be the last "object" of its first brick.
23046             fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
23047         }
23048     }
23049 #else // FEATURE_STRUCTALIGN
23050     size_t unused_arr_size = 0; 
23051     BOOL  already_padded_p = FALSE;
23052 #ifdef SHORT_PLUGS
23053     if (is_plug_padded (plug))
23054     {
23055         already_padded_p = TRUE;
23056         clear_plug_padded (plug);
23057         unused_arr_size = Align (min_obj_size);
23058     }
23059 #endif //SHORT_PLUGS
23060     if (node_realigned (plug))
23061     {
23062         unused_arr_size += switch_alignment_size (already_padded_p);
23063     }
23064
23065     if (unused_arr_size != 0) 
23066     {
23067         make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
23068
23069         if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
23070         {
23071             dprintf (3, ("fix B for padding: %Id: %Ix->%Ix", 
23072                 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
23073             // The alignment padding is straddling one or more bricks;
23074             // it has to be the last "object" of its first brick.
23075             fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
23076         }
23077     }
23078 #endif // FEATURE_STRUCTALIGN
23079
23080 #ifdef SHORT_PLUGS
23081     if (is_plug_padded (plug))
23082     {
23083         make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
23084
23085         if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
23086         {
23087             // The alignment padding is straddling one or more bricks;
23088             // it has to be the last "object" of its first brick.
23089             fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
23090         }
23091     }
23092 #endif //SHORT_PLUGS
23093
23094     gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
23095
23096     if (args->check_gennum_p)
23097     {
23098         int src_gennum = args->src_gennum;
23099         if (src_gennum == -1)
23100         {
23101             src_gennum = object_gennum (plug);
23102         }
23103
23104         int dest_gennum = object_gennum_plan (reloc_plug);
23105
23106         if (src_gennum < dest_gennum)
23107         {
23108             generation_allocation_size (generation_of (dest_gennum)) += size;
23109         }
23110     }
23111
23112     size_t current_reloc_brick = args->current_compacted_brick;
23113
23114     if (brick_of (reloc_plug) != current_reloc_brick)
23115     {
23116         dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix", 
23117             current_reloc_brick, brick_of (reloc_plug)));
23118
23119         if (args->before_last_plug)
23120         {
23121             dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
23122                      current_reloc_brick,
23123                      args->before_last_plug, 
23124                      (args->before_last_plug - brick_address (current_reloc_brick))));
23125
23126             {
23127                 set_brick (current_reloc_brick,
23128                         args->before_last_plug - brick_address (current_reloc_brick));
23129             }
23130         }
23131         current_reloc_brick = brick_of (reloc_plug);
23132     }
23133     size_t end_brick = brick_of (reloc_plug + size-1);
23134     if (end_brick != current_reloc_brick)
23135     {
23136         // The plug is straddling one or more bricks
23137         // It has to be the last plug of its first brick
23138         dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
23139                  current_reloc_brick, (size_t)reloc_plug,
23140                  (reloc_plug - brick_address (current_reloc_brick))));
23141
23142         {
23143             set_brick (current_reloc_brick,
23144                     reloc_plug - brick_address (current_reloc_brick));
23145         }
23146         // update all intervening brick
23147         size_t brick = current_reloc_brick + 1;
23148         dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
23149             brick, (end_brick - 1)));
23150         while (brick < end_brick)
23151         {
23152             set_brick (brick, -1);
23153             brick++;
23154         }
23155         // code last brick offset as a plug address
23156         args->before_last_plug = brick_address (end_brick) -1;
23157         current_reloc_brick = end_brick;
23158         dprintf (3, ("setting before last to %Ix, last brick to %Ix",
23159             args->before_last_plug, current_reloc_brick));
23160     } 
23161     else
23162     {
23163         dprintf (3, ("still in the same brick: %Ix", end_brick));
23164         args->before_last_plug = reloc_plug;
23165     }
23166     args->current_compacted_brick = current_reloc_brick;
23167
23168     if (check_last_object_p)
23169     {
23170         mark* entry = args->pinned_plug_entry;
23171
23172         if (args->is_shortened)
23173         {
23174             entry->swap_post_plug_and_saved();
23175         }
23176         else
23177         {
23178             entry->swap_pre_plug_and_saved();
23179         }
23180     }
23181 }
23182
23183 void gc_heap::compact_in_brick (BYTE* tree, compact_args* args)
23184 {
23185     assert (tree >= 0);
23186     int   left_node = node_left_child (tree);
23187     int   right_node = node_right_child (tree);
23188     ptrdiff_t relocation = node_relocation_distance (tree);
23189
23190     args->print();
23191
23192     if (left_node)
23193     {
23194         dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
23195         compact_in_brick ((tree + left_node), args);
23196     }
23197
23198     BYTE*  plug = tree;
23199     BOOL   has_pre_plug_info_p = FALSE;
23200     BOOL   has_post_plug_info_p = FALSE;
23201
23202     if (tree == oldest_pinned_plug)
23203     {
23204         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
23205                                                            &has_post_plug_info_p);
23206         assert (tree == pinned_plug (args->pinned_plug_entry));
23207     }
23208
23209     if (args->last_plug != 0)
23210     {
23211         size_t gap_size = node_gap_size (tree);
23212         BYTE*  gap = (plug - gap_size);
23213         BYTE*  last_plug_end = gap;
23214         size_t last_plug_size = (last_plug_end - args->last_plug);
23215         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
23216             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
23217         
23218         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
23219         if (!check_last_object_p)
23220             assert (last_plug_size >= Align (min_obj_size));
23221
23222         compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
23223     }
23224     else
23225     {
23226         assert (!has_pre_plug_info_p);
23227     }
23228
23229     dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
23230     args->last_plug = plug;
23231     args->last_plug_relocation = relocation;
23232     args->is_shortened = has_post_plug_info_p;
23233
23234     if (right_node)
23235     {
23236         dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
23237         compact_in_brick ((tree + right_node), args);
23238     }
23239 }
23240
23241 void gc_heap::recover_saved_pinned_info()
23242 {
23243     reset_pinned_queue_bos();
23244
23245     while (!(pinned_plug_que_empty_p()))
23246     {
23247         mark* oldest_entry = oldest_pin();
23248         oldest_entry->recover_plug_info();
23249         deque_pinned_plug();
23250     }
23251 }
23252
23253 void gc_heap::compact_phase (int condemned_gen_number,
23254                              BYTE*  first_condemned_address,
23255                              BOOL clear_cards)
23256 {
23257 //  %type%  category = quote (compact);
23258 #ifdef TIME_GC
23259         unsigned start;
23260         unsigned finish;
23261         start = GetCycleCount32();
23262 #endif //TIME_GC
23263     generation*   condemned_gen = generation_of (condemned_gen_number);
23264     BYTE*  start_address = first_condemned_address;
23265     size_t   current_brick = brick_of (start_address);
23266     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23267
23268     PREFIX_ASSUME(current_heap_segment != NULL);
23269
23270     reset_pinned_queue_bos();
23271     update_oldest_pinned_plug();
23272
23273     BOOL reused_seg = FALSE;
23274     int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
23275     if ((heap_expand_mechanism == expand_reuse_bestfit) || 
23276         (heap_expand_mechanism == expand_reuse_normal))
23277     {
23278         reused_seg = TRUE;
23279
23280         for (int i = 1; i <= max_generation; i++)
23281         {
23282             generation_allocation_size (generation_of (i)) = 0;
23283         }
23284     }
23285
23286     BYTE*  end_address = heap_segment_allocated (current_heap_segment);
23287
23288     size_t  end_brick = brick_of (end_address-1);
23289     compact_args args;
23290     args.last_plug = 0;
23291     args.before_last_plug = 0;
23292     args.current_compacted_brick = ~((size_t)1);
23293     args.is_shortened = FALSE;
23294     args.pinned_plug_entry = 0;
23295     args.copy_cards_p =  (condemned_gen_number >= 1) || !clear_cards;
23296     args.check_gennum_p = reused_seg;
23297     if (args.check_gennum_p)
23298     {
23299         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
23300     }
23301
23302     dprintf (2,("---- Compact Phase: %Ix(%Ix)----", 
23303         first_condemned_address, brick_of (first_condemned_address)));
23304
23305 #ifdef MULTIPLE_HEAPS
23306     //restart
23307     if (gc_t_join.joined())
23308     {
23309 #endif //MULTIPLE_HEAPS
23310
23311 #ifdef MULTIPLE_HEAPS
23312         dprintf(3, ("Restarting for compaction"));
23313         gc_t_join.restart();
23314     }
23315 #endif //MULTIPLE_HEAPS
23316
23317     reset_pinned_queue_bos();
23318
23319 #ifdef FEATURE_LOH_COMPACTION
23320     if (loh_compacted_p)
23321     {
23322         compact_loh();
23323     }
23324 #endif //FEATURE_LOH_COMPACTION
23325
23326     if ((start_address < end_address) ||
23327         (condemned_gen_number == max_generation))
23328     {
23329         while (1)
23330         {
23331             if (current_brick > end_brick)
23332             {
23333                 if (args.last_plug != 0)
23334                 {
23335                     dprintf (3, ("compacting last plug: %Ix", args.last_plug))
23336                     compact_plug (args.last_plug,
23337                                   (heap_segment_allocated (current_heap_segment) - args.last_plug),
23338                                   args.is_shortened,
23339                                   &args);
23340                 }
23341
23342                 if (heap_segment_next_rw (current_heap_segment))
23343                 {
23344                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
23345                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
23346                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23347                     args.last_plug = 0;
23348                     if (args.check_gennum_p)
23349                     {
23350                         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
23351                     }
23352                     continue;
23353                 }
23354                 else
23355                 {
23356                     if (args.before_last_plug !=0)
23357                     {
23358                         dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
23359                                     args.current_compacted_brick, (size_t)args.before_last_plug));
23360                         assert (args.current_compacted_brick != ~1u);
23361                         set_brick (args.current_compacted_brick,
23362                                    args.before_last_plug - brick_address (args.current_compacted_brick));
23363                     }
23364                     break;
23365                 }
23366             }
23367             {
23368                 int  brick_entry =  brick_table [ current_brick ];
23369                 dprintf (3, ("B: %Ix(%Ix)->%Ix", 
23370                     current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
23371
23372                 if (brick_entry >= 0)
23373                 {
23374                     compact_in_brick ((brick_address (current_brick) + brick_entry -1),
23375                                       &args);
23376
23377                 }
23378             }
23379             current_brick++;
23380         }
23381     }
23382
23383     recover_saved_pinned_info();
23384
23385 #ifdef TIME_GC
23386     finish = GetCycleCount32();
23387     compact_time = finish - start;
23388 #endif //TIME_GC
23389
23390     concurrent_print_time_delta ("compact end");
23391
23392     dprintf(2,("---- End of Compact phase ----"));
23393 }
23394
23395 #ifndef FEATURE_REDHAWK
23396 // This function is the filter function for the "__except" setup in the server and 
23397 // concurrent gc thread base (gc_heap::gc_thread_stub()) in gc.cpp. If an
23398 // exception leaks out during GC, or from the implementation of gc_thread_function,
23399 // this filter will be invoked and we will kick in our unhandled exception processing
23400 // without relying on the OS UEF invocation mechanism.
23401 //
23402 // Also, any exceptions that escape out of the GC thread are fatal. Thus, once
23403 // we do our unhandled exception processing, we shall failfast.
23404 inline LONG GCUnhandledExceptionFilter(EXCEPTION_POINTERS* pExceptionPointers, PVOID pv)
23405 {
23406     WRAPPER_NO_CONTRACT;
23407
23408     LONG result = CLRVectoredExceptionHandler(pExceptionPointers);
23409     if (result == EXCEPTION_CONTINUE_EXECUTION)
23410     {
23411         // Since VEH has asked to continue execution, lets do that...
23412         return result;
23413     }
23414
23415     if ((pExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) ||
23416          (pExceptionPointers->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP))
23417     {
23418         // We dont want to fail fast on debugger exceptions
23419         return result;
23420     }
23421
23422     // VEH shouldnt be returning EXCEPTION_EXECUTE_HANDLER for a fault
23423     // in the GC thread!
23424     _ASSERTE(result != EXCEPTION_EXECUTE_HANDLER);
23425
23426     // Exceptions in GC threads are fatal - invoke our unhandled exception
23427     // processing...
23428     result = InternalUnhandledExceptionFilter_Worker(pExceptionPointers);
23429     
23430 #ifdef FEATURE_UEF_CHAINMANAGER
23431     if (g_pUEFManager && (result == EXCEPTION_CONTINUE_SEARCH))
23432     {
23433         // Since the "UEF" of this runtime instance didnt handle the exception,
23434         // invoke the other registered UEF callbacks as well
23435         result = g_pUEFManager->InvokeUEFCallbacks(pExceptionPointers);
23436     }
23437 #endif // FEATURE_UEF_CHAINMANAGER
23438
23439     // ...and then proceed to failfast.
23440     EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
23441     _ASSERTE(!"We shouldnt reach here incase of exceptions in GC threads!");
23442
23443     // Returning this will ensure our filter handler gets executed so that
23444     // it can failfast the runtime.
23445     return EXCEPTION_EXECUTE_HANDLER;
23446 }
23447 #endif // FEATURE_REDHAWK
23448
23449 #ifdef MULTIPLE_HEAPS
23450
23451 #ifdef _MSC_VER
23452 #pragma warning(push)
23453 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
23454 #endif //_MSC_VER
23455 DWORD __stdcall gc_heap::gc_thread_stub (void* arg)
23456 {
23457     ClrFlsSetThreadType (ThreadType_GC);
23458     STRESS_LOG_RESERVE_MEM (GC_STRESSLOG_MULTIPLY);
23459
23460     // We commit the thread's entire stack to ensure we're robust in low memory conditions.
23461     BOOL fSuccess = Thread::CommitThreadStack(NULL);
23462
23463     if (!fSuccess)
23464     {
23465 #ifdef BACKGROUND_GC
23466         // For background GC we revert to doing a blocking GC.
23467         return 0;
23468 #else
23469         STRESS_LOG0(LF_GC, LL_ALWAYS, "Thread::CommitThreadStack failed.");
23470         _ASSERTE(!"Thread::CommitThreadStack failed.");
23471         EEPOLICY_HANDLE_FATAL_ERROR(COR_E_STACKOVERFLOW);
23472 #endif //BACKGROUND_GC
23473     }
23474
23475 #ifndef NO_CATCH_HANDLERS
23476     PAL_TRY
23477     {
23478 #endif // NO_CATCH_HANDLERS
23479         gc_heap* heap = (gc_heap*)arg;
23480         _alloca (256*heap->heap_number);
23481         return heap->gc_thread_function();
23482
23483 #ifndef NO_CATCH_HANDLERS
23484     }
23485     PAL_EXCEPT_FILTER(GCUnhandledExceptionFilter, NULL)
23486     {
23487         ASSERTE(!"Exception caught escaping out of the GC thread!");
23488         EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
23489     }
23490     PAL_ENDTRY;
23491 #endif // NO_CATCH_HANDLERS
23492 }
23493 #ifdef _MSC_VER
23494 #pragma warning(pop)
23495 #endif //_MSC_VER
23496
23497 #endif //MULTIPLE_HEAPS
23498
23499 #ifdef BACKGROUND_GC
23500
23501 #ifdef _MSC_VER
23502 #pragma warning(push)
23503 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
23504 #endif //_MSC_VER
23505 DWORD __stdcall gc_heap::bgc_thread_stub (void* arg)
23506 {
23507     gc_heap* heap = (gc_heap*)arg;
23508
23509     // TODO: need to check if we are still fine for these APIs:
23510     // Thread::BeginThreadAffinity
23511     // Thread::EndThreadAffinity()
23512     // since now GC threads can be managed threads.
23513     ClrFlsSetThreadType (ThreadType_GC);
23514     assert (heap->bgc_thread != NULL);
23515     heap->bgc_thread->SetGCSpecial(true);
23516     STRESS_LOG_RESERVE_MEM (GC_STRESSLOG_MULTIPLY);
23517
23518     // We commit the thread's entire stack to ensure we're robust in low memory conditions.
23519     /*
23520     BOOL fSuccess = Thread::CommitThreadStack();
23521
23522     if (!fSuccess)
23523     {
23524         // For background GC we revert to doing a blocking GC.
23525         return 0;
23526     }
23527     */
23528
23529 #ifndef NO_CATCH_HANDLERS
23530     PAL_TRY
23531     {
23532 #endif // NO_CATCH_HANDLERS
23533         return heap->bgc_thread_function();
23534
23535 #ifndef NO_CATCH_HANDLERS
23536     }
23537     PAL_EXCEPT_FILTER(GCUnhandledExceptionFilter, NULL)
23538     {
23539         ASSERTE(!"Exception caught escaping out of the GC thread!");
23540         EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
23541     }
23542     PAL_ENDTRY;
23543 #endif // NO_CATCH_HANDLERS
23544 }
23545 #ifdef _MSC_VER
23546 #pragma warning(pop)
23547 #endif //_MSC_VER
23548
23549 #endif //BACKGROUND_GC
23550
23551 /*------------------ Background GC ----------------------------*/
23552
23553 #ifdef BACKGROUND_GC
23554
23555 void gc_heap::background_drain_mark_list (int thread)
23556 {
23557     size_t saved_c_mark_list_index = c_mark_list_index;
23558
23559     if (saved_c_mark_list_index)
23560     {
23561         concurrent_print_time_delta ("SML");
23562     }
23563     while (c_mark_list_index != 0)
23564     {
23565         size_t current_index = c_mark_list_index - 1;
23566         BYTE* o = c_mark_list [current_index];
23567         background_mark_object (o THREAD_NUMBER_ARG);
23568         c_mark_list_index--;
23569     }
23570     if (saved_c_mark_list_index)
23571     {
23572
23573         concurrent_print_time_delta ("EML");
23574     }
23575
23576     fire_drain_mark_list_event (saved_c_mark_list_index);
23577 }
23578
23579
23580 // The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
23581 #ifdef MULTIPLE_HEAPS
23582 // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
23583 // them. So we can use the same static variables.
23584 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
23585 {
23586     // Whenever we call this method there may have been preceding object promotions. So set
23587     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
23588     // based on the how the scanning proceeded).
23589     s_fUnscannedPromotions = TRUE;
23590
23591     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
23592     // the state of this thread's portion of the dependent handle table. That's because promotions on other
23593     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
23594     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
23595     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
23596     // as all the others or they'll get out of step).
23597     while (true)
23598     {
23599         // The various worker threads are all currently racing in this code. We need to work out if at least
23600         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
23601         // dependent handle table when both of the following conditions apply:
23602         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
23603         //     object happens to correspond to a primary in one of our handles we might potentially have to
23604         //     promote the associated secondary).
23605         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
23606         //
23607         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
23608         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
23609         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
23610         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
23611         // follows below. Note that we can't read this outside of the join since on any iteration apart from
23612         // the first threads will be racing between reading this value and completing their previous
23613         // iteration's table scan.
23614         //
23615         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
23616         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
23617         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
23618         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
23619         // we're safely joined.
23620         if (CNameSpace::GcDhUnpromotedHandlesExist(sc))
23621             s_fUnpromotedHandles = TRUE;
23622
23623         // Synchronize all the threads so we can read our state variables safely. The following shared
23624         // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
23625         // single thread inside the join.
23626         bgc_t_join.join(this, gc_join_scan_dependent_handles);
23627         if (bgc_t_join.joined())
23628         {
23629             // We're synchronized so it's safe to read our shared state variables. We update another shared
23630             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
23631             // the loop. We scan if there has been at least one object promotion since last time and at least
23632             // one thread has a dependent handle table with a potential handle promotion possible.
23633             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
23634
23635             // Reset our shared state variables (ready to be set again on this scan or with a good initial
23636             // value for the next call if we're terminating the loop).
23637             s_fUnscannedPromotions = FALSE;
23638             s_fUnpromotedHandles = FALSE;
23639
23640             if (!s_fScanRequired)
23641             {
23642                 BYTE* all_heaps_max = 0;
23643                 BYTE* all_heaps_min = MAX_PTR;
23644                 int i;
23645                 for (i = 0; i < n_heaps; i++)
23646                 {
23647                     if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
23648                         all_heaps_max = g_heaps[i]->background_max_overflow_address;
23649                     if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
23650                         all_heaps_min = g_heaps[i]->background_min_overflow_address;
23651                 }
23652                 for (i = 0; i < n_heaps; i++)
23653                 {
23654                     g_heaps[i]->background_max_overflow_address = all_heaps_max;
23655                     g_heaps[i]->background_min_overflow_address = all_heaps_min;
23656                 }
23657             }
23658
23659             // Restart all the workers.
23660             dprintf(2, ("Starting all gc thread mark stack overflow processing"));
23661             bgc_t_join.restart();
23662         }
23663
23664         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
23665         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
23666         // global flag indicating that at least one object promotion may have occurred (the usual comment
23667         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
23668         // exit the method since we unconditionally set this variable on method entry anyway).
23669         if (background_process_mark_overflow (sc->concurrent))
23670             s_fUnscannedPromotions = TRUE;
23671
23672         // If we decided that no scan was required we can terminate the loop now.
23673         if (!s_fScanRequired)
23674             break;
23675
23676         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
23677         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
23678         // could miss noting the promotion of some primary objects).
23679         bgc_t_join.join(this, gc_join_rescan_dependent_handles);
23680         if (bgc_t_join.joined())
23681         {
23682             // Restart all the workers.
23683             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
23684             bgc_t_join.restart();
23685         }
23686
23687         // If the portion of the dependent handle table managed by this worker has handles that could still be
23688         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
23689         // could require a rescan of handles on this or other workers.
23690         if (CNameSpace::GcDhUnpromotedHandlesExist(sc))
23691             if (CNameSpace::GcDhReScan(sc))
23692                 s_fUnscannedPromotions = TRUE;
23693     }
23694 }
23695 #else
23696 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
23697 {
23698     // Whenever we call this method there may have been preceding object promotions. So set
23699     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
23700     // based on the how the scanning proceeded).
23701     bool fUnscannedPromotions = true;
23702
23703     // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
23704     // scan without performing any new promotions.
23705     while (CNameSpace::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
23706     {
23707         // On each iteration of the loop start with the assumption that no further objects have been promoted.
23708         fUnscannedPromotions = false;
23709
23710         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
23711         // being visible. If there was an overflow (background_process_mark_overflow returned true) then
23712         // additional objects now appear to be promoted and we should set the flag.
23713         if (background_process_mark_overflow (sc->concurrent))
23714             fUnscannedPromotions = true;
23715
23716         // Perform the scan and set the flag if any promotions resulted.
23717         if (CNameSpace::GcDhReScan (sc))
23718             fUnscannedPromotions = true;
23719     }
23720
23721     // Perform a last processing of any overflowed mark stack.
23722     background_process_mark_overflow (sc->concurrent);
23723 }
23724 #endif //MULTIPLE_HEAPS
23725
23726 void gc_heap::recover_bgc_settings()
23727 {
23728     if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
23729     {
23730         dprintf (2, ("restoring bgc settings"));
23731         settings = saved_bgc_settings;
23732         GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
23733     }
23734 }
23735
23736 inline
23737 void gc_heap::save_bgc_data_per_heap()
23738 {
23739     if (!bgc_data_saved_p)
23740     {
23741         memset (&saved_bgc_data_per_heap, 0, sizeof (saved_bgc_data_per_heap));
23742         memcpy (&saved_bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
23743         bgc_data_saved_p = TRUE;
23744     }
23745 }
23746
23747 void gc_heap::allow_fgc()
23748 {
23749     assert (bgc_thread == GetThread());
23750
23751     if (bgc_thread->PreemptiveGCDisabled() && bgc_thread->CatchAtSafePoint())
23752     {
23753         bgc_thread->EnablePreemptiveGC();
23754         bgc_thread->DisablePreemptiveGC();
23755     }
23756 }
23757
23758 BOOL gc_heap::should_commit_mark_array()
23759 {
23760     return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
23761 }
23762
23763 void gc_heap::clear_commit_flag()
23764 {
23765     generation* gen = generation_of (max_generation);
23766     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
23767     while (1)
23768     {
23769         if (seg == 0)
23770         {
23771             if (gen != large_object_generation)
23772             {
23773                 gen = large_object_generation;
23774                 seg = heap_segment_in_range (generation_start_segment (gen));
23775             }
23776             else
23777             {
23778                 break;
23779             }
23780         }
23781
23782         if (seg->flags & heap_segment_flags_ma_committed)
23783         {
23784             seg->flags &= ~heap_segment_flags_ma_committed;
23785         }
23786
23787         if (seg->flags & heap_segment_flags_ma_pcommitted)
23788         {
23789             seg->flags &= ~heap_segment_flags_ma_pcommitted;
23790         }
23791
23792         seg = heap_segment_next (seg);
23793     }
23794 }
23795
23796 void gc_heap::clear_commit_flag_global()
23797 {
23798 #ifdef MULTIPLE_HEAPS
23799     for (int i = 0; i < n_heaps; i++)
23800     {
23801         g_heaps[i]->clear_commit_flag();
23802     }
23803 #else
23804     clear_commit_flag();
23805 #endif //MULTIPLE_HEAPS
23806 }
23807
23808 void gc_heap::verify_mark_array_cleared (BYTE* begin, BYTE* end, DWORD* mark_array_addr)
23809 {
23810 #ifdef _DEBUG
23811     size_t  markw = mark_word_of (begin);
23812     size_t  markw_end = mark_word_of (end);
23813
23814     while (markw < markw_end)
23815     {
23816         if (mark_array_addr[markw])
23817         {
23818             dprintf  (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
23819                             markw, mark_array_addr[markw], mark_word_address (markw)));
23820             FATAL_GC_ERROR();
23821         }
23822         markw++;
23823     }
23824 #endif //_DEBUG
23825 }
23826
23827 void gc_heap::verify_mark_array_cleared (heap_segment* seg, DWORD* mark_array_addr)
23828 {
23829     verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
23830 }
23831
23832 BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, 
23833                                          heap_segment* seg,
23834                                          BYTE* new_lowest_address)
23835 {
23836     BYTE* start = (BYTE*)seg;
23837     BYTE* end = heap_segment_reserved (seg);
23838
23839     BYTE* lowest = hp->background_saved_lowest_address;
23840     BYTE* highest = hp->background_saved_highest_address;
23841
23842     BYTE* commit_start = NULL;
23843     BYTE* commit_end = NULL;
23844     size_t commit_flag = 0;
23845
23846     if ((highest >= start) &&
23847         (lowest <= end))
23848     {
23849         if ((start >= lowest) && (end <= highest))
23850         {
23851             dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
23852                                     start, end, lowest, highest));
23853             commit_flag = heap_segment_flags_ma_committed;
23854         }
23855         else
23856         {
23857             dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
23858                                     start, end, lowest, highest));
23859             commit_flag = heap_segment_flags_ma_pcommitted;
23860         }
23861
23862         commit_start = max (lowest, start);
23863         commit_end = min (highest, end);
23864
23865         if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
23866         {
23867             return FALSE;
23868         }
23869
23870         if (hp->card_table != g_card_table)
23871         {
23872             if (new_lowest_address == 0)
23873             {
23874                 new_lowest_address = g_lowest_address;
23875             }
23876
23877             DWORD* ct = &g_card_table[card_word (gcard_of (new_lowest_address))];
23878             DWORD* ma = (DWORD*)((BYTE*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
23879
23880             dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix", 
23881                                     hp->card_table, g_card_table,
23882                                     hp->mark_array, ma));
23883
23884             if (!commit_mark_array_by_range (commit_start, commit_end, ma))
23885             {
23886                 return FALSE;
23887             }
23888         }
23889
23890         seg->flags |= commit_flag;
23891     }
23892
23893     return TRUE;
23894 }
23895
23896 BOOL gc_heap::commit_mark_array_by_range (BYTE* begin, BYTE* end, DWORD* mark_array_addr)
23897 {
23898     size_t beg_word = mark_word_of (begin);
23899     size_t end_word = mark_word_of (align_on_mark_word (end));
23900     BYTE* commit_start = align_lower_page ((BYTE*)&mark_array_addr[beg_word]);
23901     BYTE* commit_end = align_on_page ((BYTE*)&mark_array_addr[end_word]);
23902     size_t size = (size_t)(commit_end - commit_start);
23903
23904 #ifdef SIMPLE_DPRINTF
23905     dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
23906                             begin, end,
23907                             beg_word, end_word,
23908                             (end_word - beg_word) * sizeof (DWORD), 
23909                             &mark_array_addr[beg_word],
23910                             &mark_array_addr[end_word],
23911                             (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
23912                             commit_start, commit_end,
23913                             size));
23914 #endif //SIMPLE_DPRINTF
23915
23916     if (VirtualAlloc (commit_start, size, MEM_COMMIT, PAGE_READWRITE))
23917     {
23918         // We can only verify the mark array is cleared from begin to end, the first and the last
23919         // page aren't necessarily all cleared 'cause they could be used by other segments or 
23920         // card bundle.
23921         verify_mark_array_cleared (begin, end, mark_array_addr);
23922         return TRUE;
23923     }
23924     else
23925     {
23926         dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (DWORD)));
23927         return FALSE;
23928     }
23929 }
23930
23931 BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, DWORD* new_mark_array_addr)
23932 {
23933     BYTE* start = (BYTE*)seg;
23934     BYTE* end = heap_segment_reserved (seg);
23935
23936 #ifdef MULTIPLE_HEAPS
23937     BYTE* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
23938     BYTE* highest = heap_segment_heap (seg)->background_saved_highest_address;
23939 #else
23940     BYTE* lowest = background_saved_lowest_address;
23941     BYTE* highest = background_saved_highest_address;
23942 #endif //MULTIPLE_HEAPS
23943
23944     if ((highest >= start) &&
23945         (lowest <= end))
23946     {
23947         start = max (lowest, start);
23948         end = min (highest, end);
23949         if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
23950         {
23951             return FALSE;
23952         }
23953     }
23954
23955     return TRUE;
23956 }
23957
23958 BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, DWORD* mark_array_addr)
23959 {
23960     dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix", 
23961                             seg, 
23962                             heap_segment_reserved (seg),
23963                             mark_array_addr));
23964     return commit_mark_array_by_range ((BYTE*)seg, heap_segment_reserved (seg), mark_array_addr);
23965 }
23966
23967 BOOL gc_heap::commit_mark_array_bgc_init (DWORD* mark_array_addr)
23968 {
23969     dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix", 
23970                             lowest_address, highest_address, mark_array));
23971
23972     generation* gen = generation_of (max_generation);
23973     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
23974     while (1)
23975     {
23976         if (seg == 0)
23977         {
23978             if (gen != large_object_generation)
23979             {
23980                 gen = large_object_generation;
23981                 seg = heap_segment_in_range (generation_start_segment (gen));
23982             }
23983             else
23984             {
23985                 break;
23986             }
23987         }
23988
23989         dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
23990
23991         if (!(seg->flags & heap_segment_flags_ma_committed))
23992         {
23993             // For ro segments they could always be only partially in range so we'd
23994             // be calling this at the beginning of every BGC. We are not making this 
23995             // more efficient right now - ro segments are currently only used by redhawk.
23996             if (heap_segment_read_only_p (seg))
23997             {
23998                 if ((heap_segment_mem (seg) >= lowest_address) && 
23999                     (heap_segment_reserved (seg) <= highest_address))
24000                 {
24001                     if (commit_mark_array_by_seg (seg, mark_array))
24002                     {
24003                         seg->flags |= heap_segment_flags_ma_committed;
24004                     }
24005                     else
24006                     {
24007                         return FALSE;
24008                     }
24009                 }
24010                 else
24011                 {
24012                     BYTE* start = max (lowest_address, (BYTE*)seg);
24013                     BYTE* end = min (highest_address, heap_segment_reserved (seg));
24014                     if (commit_mark_array_by_range (start, end, mark_array))
24015                     {
24016                         seg->flags |= heap_segment_flags_ma_pcommitted;
24017                     }
24018                     else
24019                     {
24020                         return FALSE;
24021                     }
24022                 }
24023             }
24024             else
24025             {
24026                 // For normal segments they are by design completely in range so just 
24027                 // commit the whole mark array for each seg.
24028                 if (commit_mark_array_by_seg (seg, mark_array))
24029                 {
24030                     if (seg->flags & heap_segment_flags_ma_pcommitted)
24031                     {
24032                         seg->flags &= ~heap_segment_flags_ma_pcommitted;
24033                     }
24034                     seg->flags |= heap_segment_flags_ma_committed;
24035                 }
24036                 else
24037                 {
24038                     return FALSE;
24039                 }
24040             }
24041         }
24042
24043         seg = heap_segment_next (seg);
24044     }
24045
24046     return TRUE;
24047 }
24048
24049 // This function doesn't check the commit flag since it's for a new array -
24050 // the mark_array flag for these segments will remain the same.
24051 BOOL gc_heap::commit_new_mark_array (DWORD* new_mark_array_addr)
24052 {
24053     dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
24054     generation* gen = generation_of (max_generation);
24055     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
24056     while (1)
24057     {
24058         if (seg == 0)
24059         {
24060             if (gen != large_object_generation)
24061             {
24062                 gen = large_object_generation;
24063                 seg = heap_segment_in_range (generation_start_segment (gen));
24064             }
24065             else
24066             {
24067                 break;
24068             }
24069         }
24070
24071         if (!commit_mark_array_with_check (seg, new_mark_array_addr))
24072         {
24073             return FALSE;
24074         }
24075
24076         seg = heap_segment_next (seg);
24077     }
24078
24079 #ifdef MULTIPLE_HEAPS
24080     if (new_heap_segment)
24081     {
24082         if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
24083         {
24084             return FALSE;
24085         }        
24086     }
24087 #endif //MULTIPLE_HEAPS
24088
24089     return TRUE;
24090 }
24091
24092 BOOL gc_heap::commit_new_mark_array_global (DWORD* new_mark_array)
24093 {
24094 #ifdef MULTIPLE_HEAPS
24095     for (int i = 0; i < n_heaps; i++)
24096     {
24097         if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
24098         {
24099             return FALSE;
24100         }
24101     }
24102 #else
24103     if (!commit_new_mark_array (new_mark_array))
24104     {
24105         return FALSE;
24106     }
24107 #endif //MULTIPLE_HEAPS
24108
24109     return TRUE;
24110 }
24111
24112 void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
24113 {
24114     // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
24115     // been set to NULL. 
24116     if (mark_array == NULL)
24117     {
24118         return;
24119     }
24120
24121     dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
24122
24123     size_t flags = seg->flags;
24124
24125     if ((flags & heap_segment_flags_ma_committed) ||
24126         (flags & heap_segment_flags_ma_pcommitted))
24127     {
24128         BYTE* start = (BYTE*)seg;
24129         BYTE* end = heap_segment_reserved (seg);
24130
24131         if (flags & heap_segment_flags_ma_pcommitted)
24132         {
24133             start = max (lowest_address, start);
24134             end = min (highest_address, end);
24135         }
24136
24137         size_t beg_word = mark_word_of (start);
24138         size_t end_word = mark_word_of (align_on_mark_word (end));
24139         BYTE* decommit_start = align_on_page ((BYTE*)&mark_array[beg_word]);
24140         BYTE* decommit_end = align_lower_page ((BYTE*)&mark_array[end_word]);
24141         size_t size = (size_t)(decommit_end - decommit_start);
24142
24143 #ifdef SIMPLE_DPRINTF
24144         dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
24145                                 seg,
24146                                 beg_word, end_word,
24147                                 (end_word - beg_word) * sizeof (DWORD), 
24148                                 &mark_array[beg_word],
24149                                 &mark_array[end_word],
24150                                 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
24151                                 decommit_start, decommit_end,
24152                                 size));
24153 #endif //SIMPLE_DPRINTF
24154         
24155         if (decommit_start < decommit_end)
24156         {
24157             if (!VirtualFree (decommit_start, size, MEM_DECOMMIT))
24158             {
24159                 dprintf (GC_TABLE_LOG, ("VirtualFree on %Ix for %Id bytes failed: %d", 
24160                                         decommit_start, size, GetLastError()));
24161                 assert (!"decommit failed");
24162             }
24163         }
24164
24165         dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
24166     }
24167 }
24168
24169 void gc_heap::background_mark_phase ()
24170 {
24171     verify_mark_array_cleared();
24172
24173     ScanContext sc;
24174     sc.thread_number = heap_number;
24175     sc.promotion = TRUE;
24176     sc.concurrent = FALSE;
24177
24178     THREAD_FROM_HEAP;
24179     Thread* current_thread = GetThread();
24180     BOOL cooperative_mode = TRUE;
24181 #ifndef MULTIPLE_HEAPS
24182     const int thread = heap_number;
24183 #endif //!MULTIPLE_HEAPS
24184
24185     dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
24186
24187     assert (settings.concurrent);
24188
24189 #ifdef TIME_GC
24190     unsigned start;
24191     unsigned finish;
24192     start = GetCycleCount32();
24193 #endif //TIME_GC
24194
24195 #ifdef FFIND_OBJECT
24196     if (gen0_must_clear_bricks > 0)
24197         gen0_must_clear_bricks--;
24198 #endif //FFIND_OBJECT
24199
24200     background_soh_alloc_count = 0;
24201     background_loh_alloc_count = 0;
24202     bgc_overflow_count = 0;
24203
24204     bpromoted_bytes (heap_number) = 0;
24205     static DWORD num_sizedrefs = 0;
24206
24207     background_min_overflow_address = MAX_PTR;
24208     background_max_overflow_address = 0;
24209     background_min_soh_overflow_address = MAX_PTR;
24210     background_max_soh_overflow_address = 0;
24211     processed_soh_overflow_p = FALSE;
24212
24213     {
24214         //set up the mark lists from g_mark_list
24215         assert (g_mark_list);
24216         mark_list = g_mark_list;
24217         //dont use the mark list for full gc
24218         //because multiple segments are more complex to handle and the list
24219         //is likely to overflow
24220         mark_list_end = &mark_list [0];
24221         mark_list_index = &mark_list [0];
24222
24223         c_mark_list_index = 0;
24224
24225         shigh = (BYTE*) 0;
24226         slow  = MAX_PTR;
24227
24228         generation*   gen = generation_of (max_generation);
24229
24230         dprintf(3,("BGC: stack marking"));
24231         sc.concurrent = TRUE;
24232
24233         CNameSpace::GcScanRoots(background_promote_callback,
24234                                 max_generation, max_generation,
24235                                 &sc);
24236     }
24237
24238     {
24239         dprintf(3,("BGC: finalization marking"));
24240         finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
24241     }
24242
24243     size_t total_loh_size = generation_size (max_generation + 1);
24244     bgc_begin_loh_size = total_loh_size;
24245     bgc_alloc_spin_loh = 0;
24246     bgc_loh_size_increased = 0;
24247     bgc_loh_allocated_in_free = 0;
24248     size_t total_soh_size = generation_sizes (generation_of (max_generation));
24249
24250     dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
24251
24252     {
24253         //concurrent_print_time_delta ("copying stack roots");
24254         concurrent_print_time_delta ("CS");
24255
24256         fire_bgc_event (BGC1stNonConEnd);
24257
24258         expanded_in_fgc = FALSE;
24259         saved_overflow_ephemeral_seg = 0;
24260         current_bgc_state = bgc_reset_ww;
24261
24262         // we don't need a join here - just whichever thread that gets here
24263         // first can change the states and call restart_vm.
24264         // this is not true - we can't let the EE run when we are scanning stack.
24265         // since we now allow reset ww to run concurrently and have a join for it,
24266         // we can do restart ee on the 1st thread that got here. Make sure we handle the 
24267         // sizedref handles correctly.
24268 #ifdef MULTIPLE_HEAPS
24269         bgc_t_join.join(this, gc_join_restart_ee);
24270         if (bgc_t_join.joined())
24271 #endif //MULTIPLE_HEAPS
24272         {
24273             num_sizedrefs = SystemDomain::System()->GetTotalNumSizedRefHandles();
24274
24275             // this c_write is not really necessary because restart_vm
24276             // has an instruction that will flush the cpu cache (interlocked
24277             // or whatever) but we don't want to rely on that.
24278             dprintf (BGC_LOG, ("setting cm_in_progress"));
24279             c_write (cm_in_progress, TRUE);
24280
24281             //restart all thread, doing the marking from the array
24282             assert (dont_restart_ee_p);
24283             dont_restart_ee_p = FALSE;
24284
24285             restart_vm();
24286             __SwitchToThread (0, CALLER_LIMITS_SPINNING);
24287 #ifdef MULTIPLE_HEAPS
24288             dprintf(3, ("Starting all gc threads for gc"));
24289             bgc_t_join.restart();
24290 #endif //MULTIPLE_HEAPS
24291         }
24292
24293 #ifdef MULTIPLE_HEAPS
24294         bgc_t_join.join(this, gc_join_after_reset);
24295         if (bgc_t_join.joined())
24296 #endif //MULTIPLE_HEAPS
24297         {
24298             disable_preemptive (current_thread, TRUE);
24299
24300 #ifdef WRITE_WATCH
24301             concurrent_print_time_delta ("CRWW begin");
24302
24303 #ifdef MULTIPLE_HEAPS
24304             int i;
24305             for (i = 0; i < n_heaps; i++)
24306             {
24307                 g_heaps[i]->reset_write_watch (TRUE);
24308             }
24309 #else
24310             reset_write_watch (TRUE);
24311 #endif //MULTIPLE_HEAPS
24312
24313             concurrent_print_time_delta ("CRWW");
24314 #endif //WRITE_WATCH
24315
24316 #ifdef MULTIPLE_HEAPS
24317             for (i = 0; i < n_heaps; i++)
24318             {
24319                 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
24320             }
24321 #else
24322             revisit_written_pages (TRUE, TRUE);
24323 #endif //MULTIPLE_HEAPS
24324
24325             concurrent_print_time_delta ("CRW");
24326
24327 #ifdef MULTIPLE_HEAPS
24328             for (i = 0; i < n_heaps; i++)
24329             {
24330                 g_heaps[i]->current_bgc_state = bgc_mark_handles;
24331             }
24332 #else
24333             current_bgc_state = bgc_mark_handles;
24334 #endif //MULTIPLE_HEAPS
24335
24336             current_c_gc_state = c_gc_state_marking;
24337
24338             enable_preemptive (current_thread);
24339
24340 #ifdef MULTIPLE_HEAPS
24341             dprintf(3, ("Joining BGC threads after resetting writewatch"));
24342             bgc_t_join.restart();
24343 #endif //MULTIPLE_HEAPS
24344         }
24345
24346         disable_preemptive (current_thread, TRUE);
24347
24348         if (num_sizedrefs > 0)
24349         {
24350             CNameSpace::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
24351
24352             enable_preemptive (current_thread);
24353
24354 #ifdef MULTIPLE_HEAPS
24355             bgc_t_join.join(this, gc_join_scan_sizedref_done);
24356             if (bgc_t_join.joined())
24357             {
24358                 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
24359                 bgc_t_join.restart();
24360             }
24361 #endif //MULTIPLE_HEAPS
24362
24363             disable_preemptive (current_thread, TRUE);
24364         }
24365
24366         dprintf (3,("BGC: handle table marking"));
24367         CNameSpace::GcScanHandles(background_promote,
24368                                   max_generation, max_generation,
24369                                   &sc);
24370         //concurrent_print_time_delta ("concurrent marking handle table");
24371         concurrent_print_time_delta ("CRH");
24372
24373         current_bgc_state = bgc_mark_stack;
24374         dprintf (2,("concurrent draining mark list"));
24375         background_drain_mark_list (thread);
24376         //concurrent_print_time_delta ("concurrent marking stack roots");
24377         concurrent_print_time_delta ("CRS");
24378
24379         dprintf (2,("concurrent revisiting dirtied pages"));
24380         revisit_written_pages (TRUE);
24381         revisit_written_pages (TRUE);
24382         //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
24383         concurrent_print_time_delta ("CRre");
24384
24385         enable_preemptive (current_thread);
24386
24387 #ifdef MULTIPLE_HEAPS
24388         bgc_t_join.join(this, gc_join_concurrent_overflow);
24389         if (bgc_t_join.joined())
24390         {
24391             BYTE* all_heaps_max = 0;
24392             BYTE* all_heaps_min = MAX_PTR;
24393             int i;
24394             for (i = 0; i < n_heaps; i++)
24395             {
24396                 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix", 
24397                     i,
24398                     g_heaps[i]->background_max_overflow_address,
24399                     g_heaps[i]->background_min_overflow_address));
24400                 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
24401                     all_heaps_max = g_heaps[i]->background_max_overflow_address;
24402                 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
24403                     all_heaps_min = g_heaps[i]->background_min_overflow_address;
24404             }
24405             for (i = 0; i < n_heaps; i++)
24406             {
24407                 g_heaps[i]->background_max_overflow_address = all_heaps_max;
24408                 g_heaps[i]->background_min_overflow_address = all_heaps_min;
24409             }
24410             dprintf(3, ("Starting all bgc threads after updating the overflow info"));
24411             bgc_t_join.restart();
24412         }
24413 #endif //MULTIPLE_HEAPS
24414
24415         disable_preemptive (current_thread, TRUE);
24416
24417         dprintf (2, ("before CRov count: %d", bgc_overflow_count));
24418         bgc_overflow_count = 0;
24419         background_process_mark_overflow (TRUE);
24420         dprintf (2, ("after CRov count: %d", bgc_overflow_count));
24421         bgc_overflow_count = 0;
24422         //concurrent_print_time_delta ("concurrent processing mark overflow");
24423         concurrent_print_time_delta ("CRov");
24424
24425         // Stop all threads, crawl all stacks and revisit changed pages.
24426         fire_bgc_event (BGC1stConEnd);
24427
24428         dprintf (2, ("Stopping the EE"));
24429
24430         enable_preemptive (current_thread);
24431
24432 #ifdef MULTIPLE_HEAPS
24433         bgc_t_join.join(this, gc_join_suspend_ee);
24434         if (bgc_t_join.joined())
24435         {
24436             bgc_threads_sync_event.Reset();
24437
24438             dprintf(3, ("Joining BGC threads for non concurrent final marking"));
24439             bgc_t_join.restart();
24440         }
24441 #endif //MULTIPLE_HEAPS
24442
24443         if (heap_number == 0)
24444         {
24445             enter_spin_lock (&gc_lock);
24446
24447             bgc_suspend_EE ();
24448             //suspend_EE ();
24449             bgc_threads_sync_event.Set();
24450         }
24451         else
24452         {
24453             bgc_threads_sync_event.Wait(INFINITE, FALSE);
24454             dprintf (2, ("bgc_threads_sync_event is signalled"));
24455         }
24456
24457         assert (settings.concurrent);
24458         assert (settings.condemned_generation == max_generation);
24459
24460         dprintf (2, ("clearing cm_in_progress"));
24461         c_write (cm_in_progress, FALSE);
24462
24463         bgc_alloc_lock->check();
24464
24465         current_bgc_state = bgc_final_marking;
24466
24467         //concurrent_print_time_delta ("concurrent marking ended");
24468         concurrent_print_time_delta ("CR");
24469
24470         fire_bgc_event (BGC2ndNonConBegin);
24471
24472         mark_absorb_new_alloc();
24473
24474         // We need a join here 'cause find_object would complain if the gen0
24475         // bricks of another heap haven't been fixed up. So we need to make sure
24476         // that every heap's gen0 bricks are fixed up before we proceed.
24477 #ifdef MULTIPLE_HEAPS
24478         bgc_t_join.join(this, gc_join_after_absorb);
24479         if (bgc_t_join.joined())
24480         {
24481             dprintf(3, ("Joining BGC threads after absorb"));
24482             bgc_t_join.restart();
24483         }
24484 #endif //MULTIPLE_HEAPS
24485
24486         // give VM a chance to do work
24487         GCToEEInterface::GcBeforeBGCSweepWork();
24488
24489         //reset the flag, indicating that the EE no longer expect concurrent
24490         //marking
24491         sc.concurrent = FALSE;
24492
24493         total_loh_size = generation_size (max_generation + 1);
24494         total_soh_size = generation_sizes (generation_of (max_generation));
24495
24496         dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
24497
24498         dprintf (2, ("nonconcurrent marking stack roots"));
24499         CNameSpace::GcScanRoots(background_promote,
24500                                 max_generation, max_generation,
24501                                 &sc);
24502         //concurrent_print_time_delta ("nonconcurrent marking stack roots");
24503         concurrent_print_time_delta ("NRS");
24504
24505 //        finalize_queue->EnterFinalizeLock();
24506         finalize_queue->GcScanRoots(background_promote, heap_number, 0);
24507 //        finalize_queue->LeaveFinalizeLock();
24508
24509         dprintf (2, ("nonconcurrent marking handle table"));
24510         CNameSpace::GcScanHandles(background_promote,
24511                                   max_generation, max_generation,
24512                                   &sc);
24513         //concurrent_print_time_delta ("nonconcurrent marking handle table");
24514         concurrent_print_time_delta ("NRH");
24515
24516         dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
24517         revisit_written_pages (FALSE);
24518         //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
24519         concurrent_print_time_delta ("NRre LOH");
24520
24521         dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
24522         bgc_overflow_count = 0;
24523
24524         // Dependent handles need to be scanned with a special algorithm (see the header comment on
24525         // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
24526         // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
24527         // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
24528         // The call to background_scan_dependent_handles is what will cycle through more iterations if
24529         // required and will also perform processing of any mark stack overflow once the dependent handle
24530         // table has been fully promoted.
24531         dprintf (2, ("1st dependent handle scan and process mark overflow"));
24532         CNameSpace::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
24533         background_scan_dependent_handles (&sc);
24534         //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
24535         concurrent_print_time_delta ("NR 1st Hov");
24536
24537         dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
24538         bgc_overflow_count = 0;
24539
24540 #ifdef MULTIPLE_HEAPS
24541         bgc_t_join.join(this, gc_join_null_dead_short_weak);
24542         if (bgc_t_join.joined())
24543 #endif //MULTIPLE_HEAPS
24544         {
24545             GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
24546
24547 #ifdef MULTIPLE_HEAPS
24548             dprintf(3, ("Joining BGC threads for short weak handle scan"));
24549             bgc_t_join.restart();
24550 #endif //MULTIPLE_HEAPS
24551         }
24552
24553         // null out the target of short weakref that were not promoted.
24554         CNameSpace::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
24555
24556         //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
24557         concurrent_print_time_delta ("NR GcShortWeakPtrScan");
24558     }
24559
24560     {
24561 #ifdef MULTIPLE_HEAPS
24562         bgc_t_join.join(this, gc_join_scan_finalization);
24563         if (bgc_t_join.joined())
24564         {
24565             dprintf(3, ("Joining BGC threads for finalization"));
24566             bgc_t_join.restart();
24567         }
24568 #endif //MULTIPLE_HEAPS
24569
24570         //Handle finalization.
24571         dprintf(3,("Marking finalization data"));
24572         //concurrent_print_time_delta ("bgc joined to mark finalization");
24573         concurrent_print_time_delta ("NRj");
24574
24575 //        finalize_queue->EnterFinalizeLock();
24576         finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
24577 //        finalize_queue->LeaveFinalizeLock();
24578
24579         concurrent_print_time_delta ("NRF");
24580     }
24581
24582     dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
24583     bgc_overflow_count = 0;
24584
24585     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
24586     // for finalization. As before background_scan_dependent_handles will also process any mark stack
24587     // overflow.
24588     dprintf (2, ("2nd dependent handle scan and process mark overflow"));
24589     background_scan_dependent_handles (&sc);
24590     //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
24591     concurrent_print_time_delta ("NR 2nd Hov");
24592
24593 #ifdef MULTIPLE_HEAPS
24594     bgc_t_join.join(this, gc_join_null_dead_long_weak);
24595     if (bgc_t_join.joined())
24596     {
24597         dprintf(2, ("Joining BGC threads for weak pointer deletion"));
24598         bgc_t_join.restart();
24599     }
24600 #endif //MULTIPLE_HEAPS
24601
24602     // null out the target of long weakref that were not promoted.
24603     CNameSpace::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
24604     concurrent_print_time_delta ("NR GcWeakPtrScan");
24605
24606 #ifdef MULTIPLE_HEAPS
24607     bgc_t_join.join(this, gc_join_null_dead_syncblk);
24608     if (bgc_t_join.joined())
24609 #endif //MULTIPLE_HEAPS
24610     {
24611         dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
24612         // scan for deleted entries in the syncblk cache
24613         CNameSpace::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
24614         concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
24615 #ifdef MULTIPLE_HEAPS
24616         dprintf(2, ("Starting BGC threads for end of background mark phase"));
24617         bgc_t_join.restart();
24618 #endif //MULTIPLE_HEAPS
24619     }
24620
24621     gen0_bricks_cleared = FALSE;
24622
24623     dprintf (2, ("end of bgc mark: loh: %d, soh: %d", 
24624                  generation_size (max_generation + 1), 
24625                  generation_sizes (generation_of (max_generation))));
24626
24627     for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
24628     {
24629         generation* gen = generation_of (gen_idx);
24630         dynamic_data* dd = dynamic_data_of (gen_idx);
24631         dd_begin_data_size (dd) = generation_size (gen_idx) - 
24632                                    (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
24633                                    Align (size (generation_allocation_start (gen)));
24634         dd_survived_size (dd) = 0;
24635         dd_pinned_survived_size (dd) = 0;
24636         dd_artificial_pinned_survived_size (dd) = 0;
24637         dd_added_pinned_size (dd) = 0;
24638     }
24639
24640     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24641     PREFIX_ASSUME(seg != NULL);
24642
24643     while (seg)
24644     {
24645         seg->flags &= ~heap_segment_flags_swept;
24646
24647         if (heap_segment_allocated (seg) == heap_segment_mem (seg))
24648         {
24649             // This can't happen...
24650             FATAL_GC_ERROR();
24651         }
24652
24653         if (seg == ephemeral_heap_segment)
24654         {
24655             heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
24656         }
24657         else
24658         {
24659             heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
24660         }
24661
24662         dprintf (2, ("seg %Ix background allocated is %Ix", 
24663                       heap_segment_mem (seg), 
24664                       heap_segment_background_allocated (seg)));
24665         seg = heap_segment_next_rw (seg);
24666     }
24667
24668     // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
24669     // we can't let the user code consume the left over parts in these alloc contexts.
24670     repair_allocation_contexts (FALSE);
24671
24672 #ifdef TIME_GC
24673         finish = GetCycleCount32();
24674         mark_time = finish - start;
24675 #endif //TIME_GC
24676
24677     dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d", 
24678         generation_free_list_space (generation_of (max_generation)), 
24679         generation_free_obj_space (generation_of (max_generation))));
24680
24681     dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
24682 }
24683
24684 void
24685 gc_heap::suspend_EE ()
24686 {
24687     dprintf (2, ("suspend_EE"));
24688 #ifdef MULTIPLE_HEAPS
24689     gc_heap* hp = gc_heap::g_heaps[0];
24690     GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC_PREP);
24691 #else
24692     GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC_PREP);
24693 #endif //MULTIPLE_HEAPS
24694 }
24695
24696 #ifdef MULTIPLE_HEAPS
24697 void
24698 gc_heap::bgc_suspend_EE ()
24699 {
24700     for (int i = 0; i < n_heaps; i++)
24701     {
24702         gc_heap::g_heaps[i]->reset_gc_done();
24703     }
24704     gc_started = TRUE;
24705     dprintf (2, ("bgc_suspend_EE"));
24706     GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC_PREP);
24707
24708     gc_started = FALSE;
24709     for (int i = 0; i < n_heaps; i++)
24710     {
24711         gc_heap::g_heaps[i]->set_gc_done();
24712     }
24713 }
24714 #else
24715 void
24716 gc_heap::bgc_suspend_EE ()
24717 {
24718     reset_gc_done();
24719     gc_started = TRUE;
24720     dprintf (2, ("bgc_suspend_EE"));
24721     GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC_PREP);
24722     gc_started = FALSE;
24723     set_gc_done();
24724 }
24725 #endif //MULTIPLE_HEAPS
24726
24727 void
24728 gc_heap::restart_EE ()
24729 {
24730     dprintf (2, ("restart_EE"));
24731 #ifdef MULTIPLE_HEAPS
24732     GCToEEInterface::RestartEE(FALSE);
24733 #else
24734     GCToEEInterface::RestartEE(FALSE);
24735 #endif //MULTIPLE_HEAPS
24736 }
24737
24738 inline BYTE* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
24739 {
24740     if (concurrent_p)
24741     {
24742         BYTE* end = ((seg == ephemeral_heap_segment) ? 
24743                      generation_allocation_start (generation_of (max_generation-1)) :
24744                      heap_segment_allocated (seg));
24745         return align_lower_page (end);
24746     }
24747     else 
24748     {
24749         return heap_segment_allocated (seg);
24750     }
24751 }
24752
24753 void gc_heap::revisit_written_page (BYTE* page,
24754                                     BYTE* end,
24755                                     BOOL concurrent_p,
24756                                     heap_segment* seg,
24757                                     BYTE*& last_page,
24758                                     BYTE*& last_object,
24759                                     BOOL large_objects_p,
24760                                     size_t& num_marked_objects)
24761 {
24762     BYTE*   start_address = page;
24763     BYTE*   o             = 0;
24764     int align_const = get_alignment_constant (!large_objects_p);
24765     BYTE* high_address = end;
24766     BYTE* current_lowest_address = background_saved_lowest_address;
24767     BYTE* current_highest_address = background_saved_highest_address;
24768     BOOL no_more_loop_p = FALSE;
24769
24770     THREAD_FROM_HEAP;
24771 #ifndef MULTIPLE_HEAPS
24772     const int thread = heap_number;
24773 #endif //!MULTIPLE_HEAPS
24774
24775     if (large_objects_p)
24776     {
24777         o = last_object;
24778     }
24779     else
24780     {
24781         if (((last_page + OS_PAGE_SIZE) == page)
24782             || (start_address <= last_object))
24783         {
24784             o = last_object;
24785         }
24786         else
24787         {
24788             o = find_first_object (start_address, last_object);
24789             // We can visit the same object again, but on a different page.
24790             assert (o >= last_object);
24791         }
24792     }
24793
24794     dprintf (3,("page %Ix start: %Ix, %Ix[ ",
24795                (size_t)page, (size_t)o,
24796                (size_t)(min (high_address, page + OS_PAGE_SIZE))));
24797
24798     while (o < (min (high_address, page + OS_PAGE_SIZE)))
24799     {
24800         size_t s;
24801
24802         if (concurrent_p && large_objects_p)
24803         {
24804             bgc_alloc_lock->bgc_mark_set (o);
24805
24806             if (((CObjectHeader*)o)->IsFree())
24807             {
24808                 s = unused_array_size (o);
24809             }
24810             else
24811             {
24812                 s = size (o);
24813             }
24814         }
24815         else
24816         {
24817             s = size (o);
24818         }
24819
24820         dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
24821
24822         assert (Align (s) >= Align (min_obj_size));
24823
24824         BYTE* next_o =  o + Align (s, align_const);
24825
24826         if (next_o >= start_address) 
24827         {
24828 #ifdef MULTIPLE_HEAPS
24829             if (concurrent_p)
24830             {
24831                 // We set last_object here for SVR BGC here because SVR BGC has more than 
24832                 // one GC thread. When we have more than one GC thread we would run into this 
24833                 // situation if we skipped unmarked objects:
24834                 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it 
24835                 // for revisit. 
24836                 // bgc thread 2 marks X and all its current children.
24837                 // user thread comes along and dirties more (and later) pages in X.
24838                 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
24839                 // on them because it had already skipped X. We need to detect that this object is now
24840                 // marked and mark the children on the dirtied pages.
24841                 // In the future if we have less BGC threads than we have heaps we should add
24842                 // the check to the number of BGC threads.
24843                 last_object = o;
24844             }
24845 #endif //MULTIPLE_HEAPS
24846
24847             if (contain_pointers (o) &&
24848                 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
24849                 background_marked (o)))
24850             {
24851                 dprintf (3, ("going through %Ix", (size_t)o));
24852                 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
24853                                     if ((BYTE*)poo >= min (high_address, page + OS_PAGE_SIZE))
24854                                     {
24855                                         no_more_loop_p = TRUE;
24856                                         goto end_limit;
24857                                     }
24858                                     BYTE* oo = *poo;
24859
24860                                     num_marked_objects++;
24861                                     background_mark_object (oo THREAD_NUMBER_ARG);
24862                                 );
24863             }
24864             else if (concurrent_p && large_objects_p && ((CObjectHeader*)o)->IsFree() && (next_o > min (high_address, page + OS_PAGE_SIZE)))
24865             {
24866                 // We need to not skip the object here because of this corner scenario:
24867                 // A large object was being allocated during BGC mark so we first made it 
24868                 // into a free object, then cleared its memory. In this loop we would detect
24869                 // that it's a free object which normally we would skip. But by the next time
24870                 // we call GetWriteWatch we could still be on this object and the object had
24871                 // been made into a valid object and some of its memory was changed. We need
24872                 // to be sure to process those written pages so we can't skip the object just
24873                 // yet.
24874                 no_more_loop_p = TRUE;
24875                 goto end_limit;                
24876             }
24877         }
24878 end_limit:
24879         if (concurrent_p && large_objects_p)
24880         {
24881             bgc_alloc_lock->bgc_mark_done ();
24882         }
24883         if (no_more_loop_p)
24884         {
24885             break;
24886         }
24887         o = next_o;
24888     }
24889
24890 #ifdef MULTIPLE_HEAPS
24891     if (concurrent_p)
24892     {
24893         assert (last_object < (min (high_address, page + OS_PAGE_SIZE)));
24894     }
24895     else
24896 #endif //MULTIPLE_HEAPS
24897     {
24898         last_object = o;
24899     }
24900
24901     dprintf (3,("Last object: %Ix", (size_t)last_object));
24902     last_page = align_lower_page (o);
24903 }
24904
24905 // When reset_only_p is TRUE, we should only reset pages that are in range
24906 // because we need to consider the segments or part of segments that were
24907 // allocated out of range all live.
24908 void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
24909 {
24910 #ifdef WRITE_WATCH
24911     if (concurrent_p && !reset_only_p)
24912     {
24913         current_bgc_state = bgc_revisit_soh;
24914     }
24915
24916     size_t total_dirtied_pages = 0;
24917     size_t total_marked_objects = 0;
24918
24919     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24920
24921     PREFIX_ASSUME(seg != NULL);
24922
24923     DWORD granularity;
24924     int mode = concurrent_p ? 1 : 0;
24925     BOOL small_object_segments = TRUE;
24926     int align_const = get_alignment_constant (small_object_segments);
24927
24928     while (1)
24929     {
24930         if (seg == 0)
24931         {
24932             if (small_object_segments)
24933             {
24934                 //switch to large segment
24935                 if (concurrent_p && !reset_only_p)
24936                 {
24937                     current_bgc_state = bgc_revisit_loh;
24938                 }
24939
24940                 if (!reset_only_p)
24941                 {
24942                     dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
24943                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
24944                     concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
24945                     total_dirtied_pages = 0;
24946                     total_marked_objects = 0;
24947                 }
24948
24949                 small_object_segments = FALSE;
24950                 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
24951
24952                 dprintf (3, ("now revisiting large object segments"));
24953                 align_const = get_alignment_constant (small_object_segments);
24954                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24955
24956                 PREFIX_ASSUME(seg != NULL);
24957
24958                 continue;
24959             }
24960             else
24961             {
24962                 if (reset_only_p)
24963                 {
24964                     dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
24965                 } 
24966                 else
24967                 {
24968                     dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
24969                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
24970                 }
24971                 break;
24972             }
24973         }
24974         BYTE* base_address = (BYTE*)heap_segment_mem (seg);
24975         //we need to truncate to the base of the page because
24976         //some newly allocated could exist beyond heap_segment_allocated
24977         //and if we reset the last page write watch status,
24978         // they wouldn't be guaranteed to be visited -> gc hole.
24979         ULONG_PTR bcount = array_size;
24980         BYTE* last_page = 0;
24981         BYTE* last_object = heap_segment_mem (seg);
24982         BYTE* high_address = 0;
24983
24984         BOOL skip_seg_p = FALSE;
24985
24986         if (reset_only_p)
24987         {
24988             if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
24989                 (heap_segment_reserved (seg) <= background_saved_highest_address))
24990             {
24991                 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number, 
24992                     heap_segment_mem (seg), heap_segment_reserved (seg)));
24993                 skip_seg_p = TRUE;
24994             }
24995         }
24996
24997         if (!skip_seg_p)
24998         {
24999             dprintf (3, ("looking at seg %Ix", (size_t)last_object));
25000
25001             if (reset_only_p)
25002             {
25003                 base_address = max (base_address, background_saved_lowest_address);
25004                 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
25005             }
25006
25007             dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address, 
25008                 heap_segment_mem (seg), heap_segment_reserved (seg)));
25009
25010
25011             while (1)
25012             {
25013                 if (reset_only_p)
25014                 {
25015                     high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
25016                     high_address = min (high_address, background_saved_highest_address);
25017                 }
25018                 else
25019                 {
25020                     high_address = high_page (seg, concurrent_p);
25021                 }
25022
25023                 if ((base_address < high_address) &&
25024                     (bcount >= array_size))
25025                 {
25026                     ptrdiff_t region_size = high_address - base_address;
25027                     dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
25028
25029                     UINT status = GetWriteWatch (mode, base_address, region_size,
25030                                                 (PVOID*)background_written_addresses,
25031                                                 &bcount, &granularity);
25032
25033     //#ifdef _DEBUG
25034                     if (status != 0)
25035                     {
25036                         printf ("GetWriteWatch Error ");
25037                         printf ("Probing pages [%Ix, %Ix[\n", (size_t)base_address, (size_t)high_address);
25038                     }
25039     //#endif
25040                     assert (status == 0);
25041                     assert (granularity == OS_PAGE_SIZE);
25042
25043                     if (bcount != 0)
25044                     {
25045                         total_dirtied_pages += bcount;
25046
25047                         dprintf (3, ("Found %d pages [%Ix, %Ix[", 
25048                                         bcount, (size_t)base_address, (size_t)high_address));
25049                     }
25050
25051                     if (!reset_only_p)
25052                     {
25053                         for (unsigned i = 0; i < bcount; i++)
25054                         {
25055     #ifdef NO_WRITE_BARRIER
25056                             card_table [card_word (card_of (background_written_addresses [i]))] = ~0u;
25057                             dprintf (3,("Set Cards [%p:%p, %p:%p[",
25058                                         card_of (background_written_addresses [i]), g_addresses [i],
25059                                         card_of (background_written_addresses [i]+OS_PAGE_SIZE), background_written_addresses [i]+OS_PAGE_SIZE));
25060     #endif //NO_WRITE_BARRIER
25061                             BYTE* page = (BYTE*)background_written_addresses[i];
25062                             dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i, 
25063                                 (size_t)page, (size_t)high_address));
25064                             if (page < high_address)
25065                             {
25066                                 //search for marked objects in the page
25067                                 revisit_written_page (page, high_address, concurrent_p,
25068                                                     seg, last_page, last_object,
25069                                                     !small_object_segments,
25070                                                     total_marked_objects);
25071                             }
25072                             else
25073                             {
25074                                 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
25075                                 assert (!"page shouldn't have exceeded limit");
25076                             }
25077                         }
25078                     }
25079
25080                     if (bcount >= array_size){
25081                         base_address = background_written_addresses [array_size-1] + OS_PAGE_SIZE;
25082                         bcount = array_size;
25083                     }
25084                 }
25085                 else
25086                 {
25087                     break;
25088                 }
25089             }
25090         }
25091
25092         seg = heap_segment_next_rw (seg);
25093     }
25094
25095 #endif //WRITE_WATCH
25096 }
25097
25098 void gc_heap::background_grow_c_mark_list()
25099 {
25100     assert (c_mark_list_index >= c_mark_list_length);
25101     BOOL should_drain_p = FALSE;
25102     THREAD_FROM_HEAP;
25103 #ifndef MULTIPLE_HEAPS
25104     const int thread = heap_number;
25105 #endif //!MULTIPLE_HEAPS
25106
25107     dprintf (2, ("stack copy buffer overflow"));
25108     BYTE** new_c_mark_list = 0;
25109     {
25110         FAULT_NOT_FATAL();
25111         if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (BYTE*))))
25112         {
25113             should_drain_p = TRUE;
25114         }
25115         else
25116         {
25117             new_c_mark_list = new (nothrow) (BYTE*[c_mark_list_length*2]);
25118             if (new_c_mark_list == 0)
25119             {
25120                 should_drain_p = TRUE;
25121             }
25122         }
25123     }
25124     if (should_drain_p)
25125
25126     {
25127         dprintf (2, ("No more memory for the stacks copy, draining.."));
25128         //drain the list by marking its elements
25129         background_drain_mark_list (thread);
25130     }
25131     else
25132     {
25133         assert (new_c_mark_list);
25134         memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(BYTE*));
25135         c_mark_list_length = c_mark_list_length*2;
25136         delete c_mark_list;
25137         c_mark_list = new_c_mark_list;
25138     }
25139 }
25140
25141 void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
25142                                   DWORD flags)
25143 {
25144     sc = sc;
25145     //in order to save space on the array, mark the object,
25146     //knowing that it will be visited later
25147     assert (settings.concurrent);
25148
25149     THREAD_NUMBER_FROM_CONTEXT;
25150 #ifndef MULTIPLE_HEAPS
25151     const int thread = 0;
25152 #endif //!MULTIPLE_HEAPS
25153
25154     BYTE* o = (BYTE*)*ppObject;
25155
25156     if (o == 0)
25157         return;
25158
25159     HEAP_FROM_THREAD;
25160
25161     gc_heap* hp = gc_heap::heap_of (o);
25162
25163     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
25164     {
25165         return;
25166     }
25167
25168 #ifdef INTERIOR_POINTERS
25169     if (flags & GC_CALL_INTERIOR)
25170     {
25171         o = hp->find_object (o, hp->background_saved_lowest_address);
25172         if (o == 0)
25173             return;
25174     }
25175 #endif //INTERIOR_POINTERS
25176
25177 #ifdef FEATURE_CONSERVATIVE_GC
25178     // For conservative GC, a value on stack may point to middle of a free object.
25179     // In this case, we don't need to promote the pointer.
25180     if (g_pConfig->GetGCConservative() && ((CObjectHeader*)o)->IsFree())
25181     {
25182         return;
25183     }
25184 #endif //FEATURE_CONSERVATIVE_GC
25185
25186 #ifdef _DEBUG
25187     ((CObjectHeader*)o)->Validate();
25188 #endif //_DEBUG
25189
25190     dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
25191     if (o && (size (o) > LARGE_OBJECT_SIZE))
25192     {
25193         dprintf (3, ("Brc %Ix", (size_t)o));
25194     }
25195
25196     if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
25197     {
25198         hpt->background_grow_c_mark_list();
25199     }
25200     dprintf (3, ("pushing %08x into mark_list", (size_t)o));
25201     hpt->c_mark_list [hpt->c_mark_list_index++] = o;
25202
25203     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Background Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetMethodTable() : NULL);
25204 }
25205
25206 void gc_heap::mark_absorb_new_alloc()
25207 {
25208     fix_allocation_contexts (FALSE);
25209     
25210     gen0_bricks_cleared = FALSE;
25211
25212     clear_gen0_bricks();
25213 }
25214
25215 BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
25216 {
25217     BOOL success = FALSE;
25218     BOOL thread_created = FALSE;
25219     dprintf (2, ("Preparing gc thread"));
25220
25221     EnterCriticalSection (&(gh->bgc_threads_timeout_cs));
25222     if (!(gh->bgc_thread_running))
25223     {
25224         dprintf (2, ("GC thread not runnning"));
25225         if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
25226         {
25227             success = TRUE;
25228             thread_created = TRUE;
25229         }
25230     }
25231     else
25232     {
25233         dprintf (3, ("GC thread already running"));
25234         success = TRUE;
25235     }
25236     LeaveCriticalSection (&(gh->bgc_threads_timeout_cs));
25237
25238     if(thread_created)
25239         FireEtwGCCreateConcurrentThread_V1(GetClrInstanceId());
25240
25241     return success;
25242 }
25243
25244 BOOL gc_heap::create_bgc_thread(gc_heap* gh)
25245 {
25246     BOOL ret = FALSE;
25247
25248     assert (background_gc_done_event.IsValid());
25249
25250     //dprintf (2, ("Creating BGC thread"));
25251
25252 #ifdef FEATURE_REDHAWK
25253
25254     // Thread creation is handled a little differently in Redhawk. We create the thread by a call to the OS
25255     // (via the PAL) and it starts running immediately. We place a wrapper around the start routine to
25256     // initialize the Redhawk Thread context (since that must be done from the thread itself) and also destroy
25257     // it as the thread exits. We also set gh->bgc_thread from this wrapper since otherwise we'd have to
25258     // search the thread store for one with the matching ID. gc->bgc_thread will be valid by the time we've
25259     // finished the event wait below.
25260
25261     rh_bgc_thread_ctx sContext;
25262     sContext.m_pRealStartRoutine = gh->bgc_thread_stub;
25263     sContext.m_pRealContext = gh;
25264
25265     if (!PalStartBackgroundGCThread(gh->rh_bgc_thread_stub, &sContext))
25266     {
25267         goto cleanup;
25268     }
25269
25270 #else // FEATURE_REDHAWK
25271
25272     Thread* current_bgc_thread;
25273
25274     gh->bgc_thread = SetupUnstartedThread(FALSE);
25275     if (!(gh->bgc_thread))
25276     {
25277         goto cleanup;
25278     }
25279     
25280     current_bgc_thread = gh->bgc_thread;
25281     if (!current_bgc_thread->CreateNewThread (0, &(gh->bgc_thread_stub), gh))
25282     {
25283         goto cleanup;
25284     }
25285
25286     current_bgc_thread->SetBackground (TRUE, FALSE);
25287
25288     // wait for the thread to be in its main loop, this is to detect the situation
25289     // where someone triggers a GC during dll loading where the loader lock is
25290     // held.
25291     current_bgc_thread->StartThread();
25292
25293 #endif // FEATURE_REDHAWK
25294
25295     {
25296         dprintf (2, ("waiting for the thread to reach its main loop"));
25297         // In chk builds this can easily time out since
25298         // now we need to set the thread up into a managed thead. 
25299         // And since it's a managed thread we also need to make sure that we don't
25300         // clean up here and are still executing code on that thread (it'll
25301         // trigger all sorts of asserts.
25302         //DWORD res = gh->background_gc_create_event.Wait(20,FALSE);
25303         DWORD res = gh->background_gc_create_event.Wait(INFINITE,FALSE);
25304         if (res == WAIT_TIMEOUT)
25305         {
25306             dprintf (2, ("waiting for the thread to reach its main loop Timeout."));
25307             goto cleanup;
25308         }
25309         if (!gh->bgc_thread_running)
25310         {
25311             dprintf(2, ("background GC thread failed to start."));
25312             goto cleanup;
25313         }
25314         //dprintf (2, ("waiting for the thread to reach its main loop Done."));
25315
25316         ret = TRUE;
25317     }
25318
25319 cleanup:
25320
25321     if (!ret)
25322     {
25323         if (gh->bgc_thread)
25324         {
25325             gh->bgc_thread = 0;
25326         }
25327     }
25328     return ret;
25329 }
25330
25331 BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
25332 {
25333     BOOL ret = FALSE;
25334     dprintf (3, ("Creating concurrent GC thread for the first time"));
25335     background_gc_done_event.CreateManualEvent(TRUE);
25336     if (!background_gc_done_event.IsValid())
25337     {
25338         goto cleanup;
25339     }
25340     bgc_threads_sync_event.CreateManualEvent(FALSE);
25341     if (!bgc_threads_sync_event.IsValid())
25342     {
25343         goto cleanup;
25344     }
25345     ee_proceed_event.CreateAutoEvent(FALSE);
25346     if (!ee_proceed_event.IsValid())
25347     {
25348         goto cleanup;
25349     }
25350     bgc_start_event.CreateManualEvent(FALSE);
25351     if (!bgc_start_event.IsValid())
25352     {
25353         goto cleanup;
25354     }
25355
25356 #ifdef MULTIPLE_HEAPS
25357     bgc_t_join.init (number_of_heaps, join_flavor_bgc);
25358 #endif //MULTIPLE_HEAPS
25359
25360     ret = TRUE;
25361
25362 cleanup:
25363
25364     if (!ret)
25365     {
25366         if (background_gc_done_event.IsValid())
25367         {
25368             background_gc_done_event.CloseEvent();
25369         }
25370         if (bgc_threads_sync_event.IsValid())
25371         {
25372             bgc_threads_sync_event.CloseEvent();
25373         }
25374         if (ee_proceed_event.IsValid())
25375         {
25376             ee_proceed_event.CloseEvent();
25377         }
25378         if (bgc_start_event.IsValid())
25379         {
25380             bgc_start_event.CloseEvent();
25381         }
25382     }
25383
25384     return ret;
25385 }
25386
25387 BOOL gc_heap::create_bgc_thread_support()
25388 {
25389     BOOL ret = FALSE;
25390     BYTE** parr;
25391     
25392     gc_lh_block_event.CreateManualEvent(FALSE);
25393     if (!gc_lh_block_event.IsValid())
25394     {
25395         goto cleanup;
25396     }
25397
25398     background_gc_create_event.CreateAutoEvent(FALSE);
25399     if (!background_gc_create_event.IsValid())
25400     {
25401         goto cleanup;
25402     }
25403
25404     //needs to have room for enough smallest objects fitting on a page
25405     parr = new (nothrow) (BYTE* [1 + page_size / MIN_OBJECT_SIZE]);
25406     if (!parr)
25407     {
25408         goto cleanup;
25409     }
25410
25411     make_c_mark_list (parr);
25412
25413     ret = TRUE;
25414
25415 cleanup:
25416
25417     if (!ret)
25418     {
25419         if (gc_lh_block_event.IsValid())
25420         {
25421             gc_lh_block_event.CloseEvent();
25422         }
25423         if (background_gc_create_event.IsValid())
25424         {
25425             background_gc_create_event.CloseEvent();
25426         }
25427     }
25428
25429     return ret;
25430 }
25431
25432 int gc_heap::check_for_ephemeral_alloc()
25433 {
25434     int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
25435
25436     if (gen == -1)
25437     {
25438 #ifdef MULTIPLE_HEAPS
25439         for (int heap_index = 0; heap_index < n_heaps; heap_index++)
25440 #endif //MULTIPLE_HEAPS
25441         {
25442             for (int i = 0; i <= (max_generation - 1); i++)
25443             {
25444 #ifdef MULTIPLE_HEAPS
25445                 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
25446 #else
25447                 if (get_new_allocation (i) <= 0)
25448 #endif //MULTIPLE_HEAPS
25449                 {
25450                     gen = max (gen, i);
25451                 }
25452                 else
25453                     break;
25454             }
25455         }
25456     }
25457
25458     return gen;
25459 }
25460
25461 // Wait for gc to finish sequential part
25462 void gc_heap::wait_to_proceed()
25463 {
25464     assert (background_gc_done_event.IsValid());
25465     assert (bgc_start_event.IsValid());
25466
25467     user_thread_wait(&ee_proceed_event, FALSE);
25468 }
25469
25470 // Start a new concurrent gc
25471 void gc_heap::start_c_gc()
25472 {
25473     assert (background_gc_done_event.IsValid());
25474     assert (bgc_start_event.IsValid());
25475
25476 //Need to make sure that the gc thread is in the right place.
25477     background_gc_done_event.Wait(INFINITE, FALSE);
25478     background_gc_done_event.Reset();
25479     bgc_start_event.Set();
25480 }
25481
25482 void gc_heap::do_background_gc()
25483 {
25484     dprintf (2, ("starting a BGC"));
25485 #ifdef MULTIPLE_HEAPS
25486     for (int i = 0; i < n_heaps; i++)
25487     {
25488         g_heaps[i]->init_background_gc();
25489     }
25490 #else
25491     init_background_gc();
25492 #endif //MULTIPLE_HEAPS
25493     //start the background gc
25494     start_c_gc ();
25495
25496     //wait until we get restarted by the BGC.
25497     wait_to_proceed();
25498 }
25499
25500 void gc_heap::kill_gc_thread()
25501 {
25502     //assert (settings.concurrent == FALSE);
25503
25504     // We are doing a two-stage shutdown now.
25505     // In the first stage, we do minimum work, and call ExitProcess at the end.
25506     // In the secodn stage, we have the Loader lock and only one thread is
25507     // alive.  Hence we do not need to kill gc thread.
25508     DestroyThread (bgc_thread);
25509     background_gc_done_event.CloseEvent();
25510     gc_lh_block_event.CloseEvent();
25511     bgc_start_event.CloseEvent();
25512     DeleteCriticalSection (&bgc_threads_timeout_cs);
25513     bgc_thread = 0;
25514     recursive_gc_sync::shutdown();
25515 }
25516
25517 DWORD gc_heap::bgc_thread_function()
25518 {
25519     assert (background_gc_done_event.IsValid());
25520     assert (bgc_start_event.IsValid());
25521
25522     dprintf (3, ("gc_thread thread starting..."));
25523
25524     BOOL do_exit = FALSE;
25525     Thread* thread_to_destroy = 0;
25526
25527 #ifndef FEATURE_REDHAWK
25528     // see comments in create_bgc_thread - we need
25529     // to make sure that thread doesn't clean up this thread
25530     // while we run code here. 
25531     if (!bgc_thread->HasStarted(FALSE))
25532     {
25533         dprintf (2, ("HasStarted failed"));
25534         bgc_thread_running = FALSE;
25535         background_gc_create_event.Set();
25536         return 0;
25537     }
25538 #endif //FEATURE_REDHAWK
25539
25540     bgc_thread_running = TRUE;    
25541     Thread* current_thread = GetThread();
25542     BOOL cooperative_mode = TRUE;
25543     bgc_thread_id = GetCurrentThreadId();
25544     dprintf (1, ("bgc_thread_id is set to %Ix", bgc_thread_id));
25545     //this also indicates that the thread is ready.
25546     background_gc_create_event.Set();
25547     while (1)
25548     {
25549         // Wait for work to do...
25550         dprintf (3, ("bgc thread: waiting..."));
25551
25552         cooperative_mode = enable_preemptive (current_thread);
25553         //current_thread->m_fPreemptiveGCDisabled = 0;
25554
25555         DWORD result = bgc_start_event.Wait(
25556 #ifdef _DEBUG
25557 #ifdef MULTIPLE_HEAPS
25558                                              INFINITE,
25559 #else
25560                                              2000,
25561 #endif //MULTIPLE_HEAPS
25562 #else //_DEBUG
25563 #ifdef MULTIPLE_HEAPS
25564                                              INFINITE,
25565 #else
25566                                              20000,
25567 #endif //MULTIPLE_HEAPS
25568 #endif //_DEBUG
25569             FALSE);
25570         dprintf (2, ("gc thread: finished waiting"));
25571
25572         // not calling disable_preemptive here 'cause we 
25573         // can't wait for GC complete here - RestartEE will be called 
25574         // when we've done the init work.
25575
25576         if (result == WAIT_TIMEOUT)
25577         {
25578             // Should join the bgc threads and terminate all of them
25579             // at once.
25580             dprintf (1, ("GC thread timeout"));
25581             EnterCriticalSection (&bgc_threads_timeout_cs);
25582             if (!keep_bgc_threads_p)
25583             {
25584                 dprintf (2, ("GC thread exiting"));
25585                 bgc_thread_running = FALSE;
25586                 // We can't call DestroyThread here 'cause EnterCriticalSection 
25587                 // increases the thread's m_dwLockCount and DestroyThread will
25588                 // assert if the lock count is not 0.
25589                 thread_to_destroy = bgc_thread;
25590                 bgc_thread = 0;
25591                 bgc_thread_id = 0;
25592                 do_exit = TRUE;
25593             }
25594             LeaveCriticalSection (&bgc_threads_timeout_cs);
25595             if (do_exit)
25596                 break;
25597             else
25598             {
25599                 dprintf (3, ("GC thread needed, not exiting"));
25600                 continue;
25601             }
25602         }
25603         // if we signal the thread with no concurrent work to do -> exit
25604         if (!settings.concurrent)
25605         {
25606             dprintf (3, ("no concurrent GC needed, exiting"));
25607             break;
25608         }
25609 #ifdef TRACE_GC
25610         //trace_gc = TRUE;
25611 #endif //TRACE_GC
25612         recursive_gc_sync::begin_background();
25613         dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d", 
25614             generation_free_list_space (generation_of (max_generation)),
25615             generation_free_obj_space (generation_of (max_generation)),
25616             dd_fragmentation (dynamic_data_of (max_generation))));
25617
25618         gc1();
25619
25620         current_bgc_state = bgc_not_in_process;
25621
25622 #ifdef TRACE_GC
25623         //trace_gc = FALSE;
25624 #endif //TRACE_GC
25625
25626         enable_preemptive (current_thread);
25627 #ifdef MULTIPLE_HEAPS
25628         bgc_t_join.join(this, gc_join_done);
25629         if (bgc_t_join.joined())
25630 #endif //MULTIPLE_HEAPS
25631         {
25632             enter_spin_lock (&gc_lock);
25633             dprintf (SPINLOCK_LOG, ("bgc Egc"));
25634             
25635             bgc_start_event.Reset();
25636             do_post_gc();
25637 #ifdef MULTIPLE_HEAPS
25638             for (int gen = max_generation; gen <= (max_generation + 1); gen++)
25639             {
25640                 size_t desired_per_heap = 0;
25641                 size_t total_desired = 0;
25642                 gc_heap* hp = 0;
25643                 dynamic_data* dd;
25644                 for (int i = 0; i < n_heaps; i++)
25645                 {
25646                     hp = g_heaps[i];
25647                     dd = hp->dynamic_data_of (gen);
25648                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
25649                     if (temp_total_desired < total_desired)
25650                     {
25651                         // we overflowed.
25652                         total_desired = (size_t)MAX_PTR;
25653                         break;
25654                     }
25655                     total_desired = temp_total_desired;
25656                 }
25657
25658                 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
25659
25660                 for (int i = 0; i < n_heaps; i++)
25661                 {
25662                     hp = gc_heap::g_heaps[i];
25663                     dd = hp->dynamic_data_of (gen);
25664                     dd_desired_allocation (dd) = desired_per_heap;
25665                     dd_gc_new_allocation (dd) = desired_per_heap;
25666                     dd_new_allocation (dd) = desired_per_heap;
25667                 }
25668             }
25669 #endif //MULTIPLE_HEAPS
25670 #ifdef MULTIPLE_HEAPS
25671             fire_pevents();
25672 #endif //MULTIPLE_HEAPS
25673
25674             c_write (settings.concurrent, FALSE);
25675             recursive_gc_sync::end_background();
25676             keep_bgc_threads_p = FALSE;
25677             background_gc_done_event.Set();
25678
25679             dprintf (SPINLOCK_LOG, ("bgc Lgc"));
25680             leave_spin_lock (&gc_lock);
25681 #ifdef MULTIPLE_HEAPS
25682             dprintf(1, ("End of BGC - starting all BGC threads"));
25683             bgc_t_join.restart();
25684 #endif //MULTIPLE_HEAPS
25685         }
25686         // We can't disable preempt here because there might've been a GC already
25687         // started and decided to do a BGC and waiting for a BGC thread to restart 
25688         // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
25689         // to restart the VM so we deadlock.
25690         //gc_heap::disable_preemptive (current_thread, TRUE);
25691     }
25692
25693     if (thread_to_destroy)
25694     {
25695         DestroyThread(thread_to_destroy);
25696     }
25697     
25698     FireEtwGCTerminateConcurrentThread_V1(GetClrInstanceId());
25699
25700     dprintf (3, ("bgc_thread thread exiting"));
25701     return 0;
25702 }
25703
25704 #endif //BACKGROUND_GC
25705
25706 //Clear the cards [start_card, end_card[
25707 void gc_heap::clear_cards (size_t start_card, size_t end_card)
25708 {
25709     if (start_card < end_card)
25710     {
25711         size_t start_word = card_word (start_card);
25712         size_t end_word = card_word (end_card);
25713         if (start_word < end_word)
25714         {
25715             unsigned bits = card_bit (start_card);
25716             card_table [start_word] &= lowbits (~0, bits);
25717             for (size_t i = start_word+1; i < end_word; i++)
25718                 card_table [i] = 0;
25719             bits = card_bit (end_card);
25720             // Don't write beyond end_card (and possibly uncommitted card table space).
25721             if (bits != 0)
25722             {
25723                 card_table [end_word] &= highbits (~0, bits);
25724             }
25725         }
25726         else
25727         {
25728             card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
25729                                         highbits (~0, card_bit (end_card)));
25730         }
25731 #ifdef VERYSLOWDEBUG
25732         size_t  card = start_card;
25733         while (card < end_card)
25734         {
25735             assert (! (card_set_p (card)));
25736             card++;
25737         }
25738 #endif //VERYSLOWDEBUG
25739         dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
25740                   start_card, (size_t)card_address (start_card),
25741                   end_card, (size_t)card_address (end_card)));
25742     }
25743 }
25744
25745 void gc_heap::clear_card_for_addresses (BYTE* start_address, BYTE* end_address)
25746 {
25747     size_t   start_card = card_of (align_on_card (start_address));
25748     size_t   end_card = card_of (align_lower_card (end_address));
25749     clear_cards (start_card, end_card);
25750 }
25751
25752 // copy [srccard, ...[ to [dst_card, end_card[
25753 // This will set the same bit twice. Can be optimized.
25754 inline
25755 void gc_heap::copy_cards (size_t dst_card, size_t src_card,
25756                  size_t end_card, BOOL nextp)
25757 {
25758     // If the range is empty, this function is a no-op - with the subtlety that
25759     // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
25760     // outside the committed region.  To avoid the access, leave early.
25761     if (!(dst_card < end_card))
25762         return;
25763
25764     unsigned int srcbit = card_bit (src_card);
25765     unsigned int dstbit = card_bit (dst_card);
25766     size_t srcwrd = card_word (src_card);
25767     size_t dstwrd = card_word (dst_card);
25768     unsigned int srctmp = card_table[srcwrd];
25769     unsigned int dsttmp = card_table[dstwrd];
25770     for (size_t card = dst_card; card < end_card; card++)
25771     {
25772         if (srctmp & (1 << srcbit))
25773             dsttmp |= 1 << dstbit;
25774         else
25775             dsttmp &= ~(1 << dstbit);
25776         if (!(++srcbit % 32))
25777         {
25778             srctmp = card_table[++srcwrd];
25779             srcbit = 0;
25780         }
25781         if (nextp)
25782         {
25783             if (srctmp & (1 << srcbit))
25784                 dsttmp |= 1 << dstbit;
25785         }
25786         if (!(++dstbit % 32))
25787         {
25788             card_table[dstwrd] = dsttmp;
25789             dstwrd++;
25790             dsttmp = card_table[dstwrd];
25791             dstbit = 0;
25792         }
25793     }
25794     card_table[dstwrd] = dsttmp;
25795 }
25796
25797 void gc_heap::copy_cards_for_addresses (BYTE* dest, BYTE* src, size_t len)
25798 {
25799     ptrdiff_t relocation_distance = src - dest;
25800     size_t start_dest_card = card_of (align_on_card (dest));
25801     size_t end_dest_card = card_of (dest + len - 1);
25802     size_t dest_card = start_dest_card;
25803     size_t src_card = card_of (card_address (dest_card)+relocation_distance);
25804     dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
25805                  src_card, (size_t)src, dest_card, (size_t)dest));
25806     dprintf (3,(" %Ix->%Ix:%Ix[",
25807               (size_t)src+len, end_dest_card, (size_t)dest+len));
25808
25809     dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
25810         dest, src, len, relocation_distance, (align_on_card (dest))));
25811
25812     dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
25813         start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
25814
25815     //First card has two boundaries
25816     if (start_dest_card != card_of (dest))
25817     {
25818         if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
25819             card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
25820         {
25821             dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
25822                     (card_address (start_dest_card) + relocation_distance),
25823                     card_of (card_address (start_dest_card) + relocation_distance),
25824                     (src + len - 1),
25825                     card_of (src + len - 1)));
25826
25827             dprintf (3, ("setting card: %Ix", card_of (dest)));
25828             set_card (card_of (dest));
25829         }
25830     }
25831
25832     if (card_set_p (card_of (src)))
25833         set_card (card_of (dest));
25834
25835
25836     copy_cards (dest_card, src_card, end_dest_card,
25837                 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
25838
25839     //Last card has two boundaries.
25840     if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
25841         card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
25842     {
25843         dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
25844                 (card_address (end_dest_card) + relocation_distance),
25845                 card_of (card_address (end_dest_card) + relocation_distance),
25846                 src,
25847                 card_of (src)));
25848
25849         dprintf (3, ("setting card: %Ix", end_dest_card));
25850         set_card (end_dest_card);
25851     }
25852
25853     if (card_set_p (card_of (src + len - 1)))
25854         set_card (end_dest_card);
25855 }
25856
25857 #ifdef BACKGROUND_GC
25858 // this does not need the Interlocked version of mark_array_set_marked.
25859 void gc_heap::copy_mark_bits_for_addresses (BYTE* dest, BYTE* src, size_t len)
25860 {
25861     dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
25862                  (size_t)src, (size_t)dest,
25863                  (size_t)src+len, (size_t)dest+len));
25864
25865     BYTE* src_o = src;
25866     BYTE* dest_o;
25867     BYTE* src_end = src + len;
25868     int align_const = get_alignment_constant (TRUE);
25869     ptrdiff_t reloc = dest - src;
25870
25871     while (src_o < src_end)
25872     {
25873         BYTE*  next_o = src_o + Align (size (src_o), align_const);
25874
25875         if (background_object_marked (src_o, TRUE))
25876         {
25877             dest_o = src_o + reloc;
25878
25879             //if (background_object_marked (dest_o, FALSE))
25880             //{
25881             //    dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
25882             //    FATAL_GC_ERROR();
25883             //}
25884
25885             background_mark (dest_o, 
25886                              background_saved_lowest_address, 
25887                              background_saved_highest_address);
25888             dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
25889         }
25890
25891         src_o = next_o;
25892     }
25893 }
25894 #endif //BACKGROUND_GC
25895
25896 void gc_heap::fix_brick_to_highest (BYTE* o, BYTE* next_o)
25897 {
25898     size_t new_current_brick = brick_of (o);
25899     set_brick (new_current_brick,
25900                (o - brick_address (new_current_brick)));
25901     size_t b = 1 + new_current_brick;
25902     size_t limit = brick_of (next_o);
25903     //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
25904     dprintf(3,("b:%Ix->%Ix-%Ix", 
25905                new_current_brick, (size_t)o, (size_t)next_o, limit));
25906     while (b < limit)
25907     {
25908         set_brick (b,(new_current_brick - b));
25909         b++;
25910     }
25911 }
25912
25913 // start can not be >= heap_segment_allocated for the segment.
25914 BYTE* gc_heap::find_first_object (BYTE* start, BYTE* first_object)
25915 {
25916     size_t brick = brick_of (start);
25917     BYTE* o = 0;
25918     //last_object == null -> no search shortcut needed
25919     if ((brick == brick_of (first_object) || (start <= first_object)))
25920     {
25921         o = first_object;
25922     }
25923     else
25924     {
25925         ptrdiff_t  min_brick = (ptrdiff_t)brick_of (first_object);
25926         ptrdiff_t  prev_brick = (ptrdiff_t)brick - 1;
25927         int         brick_entry = 0;
25928         while (1)
25929         {
25930             if (prev_brick < min_brick)
25931             {
25932                 break;
25933             }
25934             if ((brick_entry =  brick_table [ prev_brick ]) >= 0)
25935             {
25936                 break;
25937             }
25938             assert (! ((brick_entry == 0)));
25939             prev_brick = (brick_entry + prev_brick);
25940
25941         }
25942         o = ((prev_brick < min_brick) ? first_object :
25943                       brick_address (prev_brick) + brick_entry - 1);
25944         assert (o <= start);
25945     }
25946
25947     assert (Align (size (o)) >= Align (min_obj_size));
25948     BYTE*  next_o = o + Align (size (o));
25949     size_t curr_cl = (size_t)next_o / brick_size;
25950     size_t min_cl = (size_t)first_object / brick_size;
25951
25952     //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
25953 #ifdef TRACE_GC
25954     unsigned int n_o = 1;
25955 #endif //TRACE_GC
25956
25957     BYTE* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
25958
25959     while (next_o <= start)
25960     {
25961         do
25962         {
25963 #ifdef TRACE_GC
25964             n_o++;
25965 #endif //TRACE_GC
25966             o = next_o;
25967             assert (Align (size (o)) >= Align (min_obj_size));
25968             next_o = o + Align (size (o));
25969             Prefetch (next_o);
25970         }while (next_o < next_b);
25971
25972         if (((size_t)next_o / brick_size) != curr_cl)
25973         {
25974             if (curr_cl >= min_cl)
25975             {
25976                 fix_brick_to_highest (o, next_o);
25977             }
25978             curr_cl = (size_t) next_o / brick_size;
25979         }
25980         next_b = min (align_lower_brick (next_o) + brick_size, start+1);
25981     }
25982
25983     size_t bo = brick_of (o);
25984     //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix", 
25985     dprintf (3, ("%Id o, [%Ix-[%Ix", 
25986         n_o, bo, brick));
25987     if (bo < brick)
25988     {
25989         set_brick (bo, (o - brick_address(bo)));
25990         size_t b = 1 + bo;
25991         int x = -1;
25992         while (b < brick)
25993         {
25994             set_brick (b,x--);
25995             b++;
25996         }
25997     }
25998
25999     return o;
26000 }
26001
26002 #ifdef CARD_BUNDLE
26003 BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
26004 {
26005     dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
26006                  dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
26007
26008     if (card_bundles_enabled())
26009     {
26010         size_t cardb = cardw_card_bundle (cardw);
26011         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
26012         while (1)
26013         {
26014             //find a non null bundle
26015             while ((cardb < end_cardb) &&
26016                    (card_bundle_set_p (cardb)==0))
26017             {
26018                 cardb++;
26019             }
26020             if (cardb == end_cardb)
26021                 return FALSE;
26022             //find a non empty card word
26023
26024             DWORD* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
26025             DWORD* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
26026             while ((card_word < card_word_end) &&
26027                    !(*card_word))
26028             {
26029                 card_word++;
26030             }
26031             if (card_word != card_word_end)
26032             {
26033                 cardw = (card_word - &card_table [0]);
26034                 return TRUE;
26035             }
26036             else if ((cardw <= card_bundle_cardw (cardb)) &&
26037                      (card_word == &card_table [card_bundle_cardw (cardb+1)]))
26038             {
26039                 // a whole bundle was explored and is empty
26040                 dprintf  (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
26041                         dd_collection_count (dynamic_data_of (0)), 
26042                         cardb, card_bundle_cardw (cardb),
26043                         card_bundle_cardw (cardb+1)));
26044                 card_bundle_clear (cardb);
26045             }
26046             cardb++;
26047         }
26048     }
26049     else
26050     {
26051         DWORD* card_word = &card_table[cardw];
26052         DWORD* card_word_end = &card_table [cardw_end];
26053
26054         while (card_word < card_word_end)
26055         {
26056             if ((*card_word) != 0)
26057             {
26058                 cardw = (card_word - &card_table [0]);
26059                 return TRUE;
26060             }
26061             card_word++;
26062         }
26063         return FALSE;
26064
26065     }
26066
26067 }
26068
26069 #endif //CARD_BUNDLE
26070
26071 BOOL gc_heap::find_card (DWORD* card_table, size_t& card,
26072                 size_t card_word_end, size_t& end_card)
26073 {
26074     DWORD* last_card_word;
26075     DWORD y;
26076     DWORD z;
26077     // Find the first card which is set
26078
26079     last_card_word = &card_table [card_word (card)];
26080     z = card_bit (card);
26081     y = (*last_card_word) >> z;
26082     if (!y)
26083     {
26084         z = 0;
26085 #ifdef CARD_BUNDLE
26086         size_t lcw = card_word(card)+1;
26087         if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
26088             return FALSE;
26089         else
26090         {
26091             last_card_word = &card_table [lcw];
26092             y = *last_card_word;
26093         }
26094
26095 #else //CARD_BUNDLE
26096         do
26097         {
26098             ++last_card_word;
26099         }
26100
26101         while ((last_card_word < &card_table [card_word_end]) &&
26102                !(*last_card_word));
26103         if (last_card_word < &card_table [card_word_end])
26104             y = *last_card_word;
26105         else
26106             return FALSE;
26107 #endif //CARD_BUNDLE
26108     }
26109
26110
26111     // Look for the lowest bit set
26112     if (y)
26113     {
26114         while (!(y & 1))
26115         {
26116             z++;
26117             y = y / 2;
26118         }
26119     }
26120     card = (last_card_word - &card_table [0])* card_word_width + z;
26121     do
26122     {
26123         z++;
26124         y = y / 2;
26125         if ((z == card_word_width) &&
26126             (last_card_word < &card_table [card_word_end]))
26127         {
26128
26129             do
26130             {
26131                 y = *(++last_card_word);
26132             }while ((last_card_word < &card_table [card_word_end]) &&
26133 #ifdef _MSC_VER
26134                      (y == (1 << card_word_width)-1)
26135 #else
26136                      // if left shift count >= width of type,
26137                      // gcc reports error.
26138                      (y == ~0u)
26139 #endif // _MSC_VER
26140                 );
26141             z = 0;
26142         }
26143     } while (y & 1);
26144
26145     end_card = (last_card_word - &card_table [0])* card_word_width + z;
26146     //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
26147     dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
26148     return TRUE;
26149 }
26150
26151
26152     //because of heap expansion, computing end is complicated.
26153 BYTE* compute_next_end (heap_segment* seg, BYTE* low)
26154 {
26155     if ((low >=  heap_segment_mem (seg)) &&
26156         (low < heap_segment_allocated (seg)))
26157         return low;
26158     else
26159         return heap_segment_allocated (seg);
26160 }
26161
26162 BYTE*
26163 gc_heap::compute_next_boundary (BYTE* low, int gen_number,
26164                                 BOOL relocating)
26165 {
26166     //when relocating, the fault line is the plan start of the younger
26167     //generation because the generation is promoted.
26168     if (relocating && (gen_number == (settings.condemned_generation + 1)))
26169     {
26170         generation* gen = generation_of (gen_number - 1);
26171         BYTE* gen_alloc = generation_plan_allocation_start (gen);
26172         assert (gen_alloc);
26173         return gen_alloc;
26174     }
26175     else
26176     {
26177         assert (gen_number > settings.condemned_generation);
26178         return generation_allocation_start (generation_of (gen_number - 1 ));
26179     }
26180
26181 }
26182
26183 inline void
26184 gc_heap::keep_card_live (BYTE* o, size_t& n_gen,
26185                          size_t& cg_pointers_found)
26186 {
26187     THREAD_FROM_HEAP;
26188     if ((gc_low <= o) && (gc_high > o))
26189     {
26190         n_gen++;
26191     }
26192 #ifdef MULTIPLE_HEAPS
26193     else if (o)
26194     {
26195         gc_heap* hp = heap_of (o);
26196         if (hp != this)
26197         {
26198             if ((hp->gc_low <= o) &&
26199                 (hp->gc_high > o))
26200             {
26201                 n_gen++;
26202             }
26203         }
26204     }
26205 #endif //MULTIPLE_HEAPS
26206     cg_pointers_found ++;
26207     dprintf (4, ("keep card live for %Ix", o));
26208 }
26209
26210 inline void
26211 gc_heap::mark_through_cards_helper (BYTE** poo, size_t& n_gen,
26212                                     size_t& cg_pointers_found,
26213                                     card_fn fn, BYTE* nhigh,
26214                                     BYTE* next_boundary)
26215 {
26216     THREAD_FROM_HEAP;
26217     if ((gc_low <= *poo) && (gc_high > *poo))
26218     {
26219         n_gen++;
26220         call_fn(fn) (poo THREAD_NUMBER_ARG);
26221     }
26222 #ifdef MULTIPLE_HEAPS
26223     else if (*poo)
26224     {
26225         gc_heap* hp = heap_of_gc (*poo);
26226         if (hp != this)
26227         {
26228             if ((hp->gc_low <= *poo) &&
26229                 (hp->gc_high > *poo))
26230             {
26231                 n_gen++;
26232                 call_fn(fn) (poo THREAD_NUMBER_ARG);
26233             }
26234             if ((fn == &gc_heap::relocate_address) ||
26235                 ((hp->ephemeral_low <= *poo) &&
26236                  (hp->ephemeral_high > *poo)))
26237             {
26238                 cg_pointers_found++;
26239             }
26240         }
26241     }
26242 #endif //MULTIPLE_HEAPS
26243     if ((next_boundary <= *poo) && (nhigh > *poo))
26244     {
26245         cg_pointers_found ++;
26246         dprintf (4, ("cg pointer %Ix found, %Id so far",
26247                      (size_t)*poo, cg_pointers_found ));
26248
26249     }
26250 }
26251
26252 BOOL gc_heap::card_transition (BYTE* po, BYTE* end, size_t card_word_end,
26253                                size_t& cg_pointers_found, 
26254                                size_t& n_eph, size_t& n_card_set,
26255                                size_t& card, size_t& end_card,
26256                                BOOL& foundp, BYTE*& start_address,
26257                                BYTE*& limit, size_t& n_cards_cleared)
26258 {
26259     dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
26260     dprintf (3, ("ct: %Id cg", cg_pointers_found));
26261     BOOL passed_end_card_p = FALSE;
26262     foundp = FALSE;
26263
26264     if (cg_pointers_found == 0)
26265     {
26266         //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
26267         dprintf(3,(" CC [%Ix, %Ix[ ",
26268                 (size_t)card_address(card), (size_t)po));
26269         clear_cards (card, card_of(po));
26270         n_card_set -= (card_of (po) - card);
26271         n_cards_cleared += (card_of (po) - card);
26272
26273     }
26274     n_eph +=cg_pointers_found;
26275     cg_pointers_found = 0;
26276     card = card_of (po);
26277     if (card >= end_card)
26278     {
26279         passed_end_card_p = TRUE;
26280         dprintf (3, ("card %Ix exceeding end_card %Ix",
26281                     (size_t)card, (size_t)end_card));
26282         foundp = find_card (card_table, card, card_word_end, end_card);
26283         if (foundp)
26284         {
26285             n_card_set+= end_card - card;
26286             start_address = card_address (card);
26287             dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
26288                         (size_t)card, (size_t)start_address,
26289                         (size_t)card_address (end_card)));
26290         }
26291         limit = min (end, card_address (end_card));
26292
26293         assert (!((limit == card_address (end_card))&&
26294                 card_set_p (end_card)));
26295     }
26296
26297     return passed_end_card_p;
26298 }
26299
26300 void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
26301 {
26302 #ifdef BACKGROUND_GC
26303     dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
26304                  current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
26305     heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26306     PREFIX_ASSUME(soh_seg  != NULL);
26307     while (soh_seg )
26308     {
26309         dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix", 
26310             soh_seg, 
26311             heap_segment_background_allocated (soh_seg),
26312             heap_segment_allocated (soh_seg)));
26313         soh_seg = heap_segment_next_rw (soh_seg);
26314     }
26315 #endif //BACKGROUND_GC
26316
26317     BYTE* low = gc_low;
26318     BYTE* high = gc_high;
26319     size_t  end_card          = 0;
26320     generation*   oldest_gen        = generation_of (max_generation);
26321     int           curr_gen_number   = max_generation;
26322     BYTE*         gen_boundary      = generation_allocation_start
26323         (generation_of (curr_gen_number - 1));
26324     BYTE*         next_boundary     = (compute_next_boundary
26325                                        (gc_low, curr_gen_number, relocating));
26326     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
26327
26328     PREFIX_ASSUME(seg != NULL);
26329
26330     BYTE*         beg               = generation_allocation_start (oldest_gen);
26331     BYTE*         end               = compute_next_end (seg, low);
26332     BYTE*         last_object       = beg;
26333
26334     size_t  cg_pointers_found = 0;
26335
26336     size_t  card_word_end = (card_of (align_on_card_word (end)) /
26337                              card_word_width);
26338
26339     size_t        n_eph             = 0;
26340     size_t        n_gen             = 0;
26341     size_t        n_card_set        = 0;
26342     BYTE*         nhigh             = (relocating ?
26343                                        heap_segment_plan_allocated (ephemeral_heap_segment) : high);
26344
26345     BOOL          foundp            = FALSE;
26346     BYTE*         start_address     = 0;
26347     BYTE*         limit             = 0;
26348     size_t        card              = card_of (beg);
26349 #ifdef BACKGROUND_GC
26350     BOOL consider_bgc_mark_p        = FALSE;
26351     BOOL check_current_sweep_p      = FALSE;
26352     BOOL check_saved_sweep_p        = FALSE;
26353     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
26354 #endif //BACKGROUND_GC
26355
26356     dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
26357     size_t total_cards_cleared = 0;
26358
26359     while (1)
26360     {
26361         if (card_of(last_object) > card)
26362         {
26363             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
26364             if (cg_pointers_found == 0)
26365             {
26366                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
26367                 clear_cards (card, card_of(last_object));
26368                 n_card_set -= (card_of (last_object) - card);
26369                 total_cards_cleared += (card_of (last_object) - card);
26370             }
26371             n_eph +=cg_pointers_found;
26372             cg_pointers_found = 0;
26373             card = card_of (last_object);
26374         }
26375         if (card >= end_card)
26376         {
26377             foundp = find_card (card_table, card, card_word_end, end_card);
26378             if (foundp)
26379             {
26380                 n_card_set+= end_card - card;
26381                 start_address = max (beg, card_address (card));
26382             }
26383             limit = min (end, card_address (end_card));
26384         }
26385         if ((!foundp) || (last_object >= end) || (card_address (card) >= end))
26386         {
26387             if ((foundp) && (cg_pointers_found == 0))
26388             {
26389                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
26390                            (size_t)end));
26391                 clear_cards (card, card_of (end));
26392                 n_card_set -= (card_of (end) - card);
26393                 total_cards_cleared += (card_of (end) - card);
26394             }
26395             n_eph +=cg_pointers_found;
26396             cg_pointers_found = 0;
26397             if ((seg = heap_segment_next_in_range (seg)) != 0)
26398             {
26399 #ifdef BACKGROUND_GC
26400                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
26401 #endif //BACKGROUND_GC
26402                 beg = heap_segment_mem (seg);
26403                 end = compute_next_end (seg, low);
26404                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
26405                 card = card_of (beg);
26406                 last_object = beg;
26407                 end_card = 0;
26408                 continue;
26409             }
26410             else
26411             {
26412                 break;
26413             }
26414         }
26415
26416         assert (card_set_p (card));
26417         {
26418             BYTE*   o             = last_object;
26419
26420             o = find_first_object (start_address, last_object);
26421                 //Never visit an object twice.
26422                 assert (o >= last_object);
26423
26424                 //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
26425                 dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
26426                        card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
26427
26428             while (o < limit)
26429             {
26430                 assert (Align (size (o)) >= Align (min_obj_size));
26431                 size_t s = size (o);
26432
26433                 BYTE* next_o =  o + Align (s);
26434                 Prefetch (next_o);
26435
26436                 if ((o >= gen_boundary) &&
26437                     (seg == ephemeral_heap_segment))
26438                 {
26439                     dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
26440                     curr_gen_number--;
26441                     assert ((curr_gen_number > 0));
26442                     gen_boundary = generation_allocation_start
26443                         (generation_of (curr_gen_number - 1));
26444                     next_boundary = (compute_next_boundary
26445                                      (low, curr_gen_number, relocating));
26446                 }
26447
26448                 dprintf (4, ("|%Ix|", (size_t)o));
26449
26450                 if (next_o < start_address)
26451                 {
26452                     goto end_object;
26453                 }
26454
26455 #ifdef BACKGROUND_GC
26456                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
26457                 {
26458                     goto end_object;
26459                 }
26460 #endif //BACKGROUND_GC
26461
26462 #ifdef COLLECTIBLE_CLASS
26463                 if (is_collectible(o))
26464                 {
26465                     BOOL passed_end_card_p = FALSE;
26466
26467                     if (card_of (o) > card)
26468                     {
26469                         passed_end_card_p = card_transition (o, end, card_word_end,
26470                             cg_pointers_found, 
26471                             n_eph, n_card_set,
26472                             card, end_card,
26473                             foundp, start_address,
26474                             limit, total_cards_cleared);
26475                     }
26476
26477                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
26478                     {
26479                         // card is valid and it covers the head of the object
26480                         if (fn == &gc_heap::relocate_address)
26481                         {
26482                             keep_card_live (o, n_gen, cg_pointers_found);
26483                         }
26484                         else
26485                         {
26486                             BYTE* class_obj = get_class_object (o);
26487                             mark_through_cards_helper (&class_obj, n_gen,
26488                                                     cg_pointers_found, fn,
26489                                                     nhigh, next_boundary);
26490                         }
26491                     }
26492
26493                     if (passed_end_card_p)
26494                     {
26495                         if (foundp && (card_address (card) < next_o))
26496                         {
26497                             goto go_through_refs;
26498                         }
26499                         else if (foundp && (start_address < limit))
26500                         {
26501                             next_o = find_first_object (start_address, o);
26502                             goto end_object;
26503                         }
26504                         else
26505                             goto end_limit;                            
26506                     }
26507                 }
26508
26509 go_through_refs:
26510 #endif //COLLECTIBLE_CLASS
26511
26512                 if (contain_pointers (o))
26513                 {
26514                     dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
26515
26516                     {
26517                         dprintf (4, ("normal object path"));
26518                         go_through_object
26519                             (method_table(o), o, s, poo,
26520                              start_address, use_start, (o + s),
26521                              {
26522                                  dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
26523                                  if (card_of ((BYTE*)poo) > card)
26524                                  {
26525                                     BOOL passed_end_card_p  = card_transition ((BYTE*)poo, end,
26526                                             card_word_end,
26527                                             cg_pointers_found, 
26528                                             n_eph, n_card_set,
26529                                             card, end_card,
26530                                             foundp, start_address,
26531                                             limit, total_cards_cleared);
26532
26533                                      if (passed_end_card_p)
26534                                      {
26535                                         if (foundp && (card_address (card) < next_o))
26536                                         {
26537                                              //new_start();
26538                                              {
26539                                                  if (ppstop <= (BYTE**)start_address)
26540                                                      {break;}
26541                                                  else if (poo < (BYTE**)start_address)
26542                                                      {poo = (BYTE**)start_address;}
26543                                              }
26544                                         }
26545                                         else if (foundp && (start_address < limit))
26546                                         {
26547                                             next_o = find_first_object (start_address, o);
26548                                             goto end_object;
26549                                         }
26550                                          else
26551                                             goto end_limit;
26552                                      }
26553                                  }
26554
26555                                  mark_through_cards_helper (poo, n_gen,
26556                                                             cg_pointers_found, fn,
26557                                                             nhigh, next_boundary);
26558                              }
26559                             );
26560                     }
26561                 }
26562
26563             end_object:
26564                 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
26565                 {
26566                     if (brick_table [brick_of (o)] <0)
26567                         fix_brick_to_highest (o, next_o);
26568                 }
26569                 o = next_o;
26570             }
26571         end_limit:
26572             last_object = o;
26573         }
26574     }
26575     // compute the efficiency ratio of the card table
26576     if (!relocating)
26577     {
26578         generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
26579         dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
26580             n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
26581     }
26582     else
26583     {
26584         dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
26585             n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
26586     }
26587 }
26588
26589 #ifdef SEG_REUSE_STATS
26590 size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
26591 {
26592     size_t total_items = 0;
26593     *total_size = 0;
26594     for (int i = 0; i < count; i++)
26595     {
26596         total_items += ordered_indices[i];
26597         *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
26598         dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
26599     } 
26600     dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
26601     return total_items;
26602 }
26603 #endif // SEG_REUSE_STATS
26604
26605 void gc_heap::count_plug (size_t last_plug_size, BYTE*& last_plug)
26606 {
26607     // detect pinned plugs
26608     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
26609     {
26610         deque_pinned_plug();
26611         update_oldest_pinned_plug();
26612         dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
26613     }
26614     else
26615     {
26616         size_t plug_size = last_plug_size + Align(min_obj_size);
26617         BOOL is_padded = FALSE;
26618
26619 #ifdef SHORT_PLUGS
26620         plug_size += Align (min_obj_size);
26621         is_padded = TRUE;
26622 #endif //SHORT_PLUGS
26623
26624 #ifdef RESPECT_LARGE_ALIGNMENT
26625         plug_size += switch_alignment_size (is_padded);
26626 #endif //RESPECT_LARGE_ALIGNMENT
26627
26628         total_ephemeral_plugs += plug_size;
26629         size_t plug_size_power2 = round_up_power2 (plug_size);
26630         ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
26631         dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array", 
26632             heap_number, 
26633             last_plug, 
26634             plug_size, 
26635             (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
26636     }
26637 }
26638
26639 void gc_heap::count_plugs_in_brick (BYTE* tree, BYTE*& last_plug)
26640 {
26641     assert ((tree != 0));
26642     if (node_left_child (tree))
26643     {
26644         count_plugs_in_brick (tree + node_left_child (tree), last_plug);
26645     }
26646
26647     if (last_plug != 0)
26648     {
26649         BYTE*  plug = tree;
26650         size_t gap_size = node_gap_size (plug);
26651         BYTE*   gap = (plug - gap_size);
26652         BYTE*  last_plug_end = gap;
26653         size_t  last_plug_size = (last_plug_end - last_plug);
26654         dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
26655             tree, last_plug, gap_size, gap, last_plug_size));
26656
26657         if (tree == oldest_pinned_plug)
26658         {
26659             dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
26660                 tree, last_plug, last_plug_size));
26661             mark* m = oldest_pin();
26662             if (m->has_pre_plug_info())
26663             {
26664                 last_plug_size += sizeof (gap_reloc_pair);
26665                 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
26666             }
26667         }
26668         // Can't assert here - if it's a pinned plug it can be less.
26669         //assert (last_plug_size >= Align (min_obj_size));
26670
26671         count_plug (last_plug_size, last_plug);
26672     }
26673
26674     last_plug = tree;
26675
26676     if (node_right_child (tree))
26677     {
26678         count_plugs_in_brick (tree + node_right_child (tree), last_plug);
26679     }
26680 }
26681
26682 void gc_heap::build_ordered_plug_indices ()
26683 {
26684     memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
26685     memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
26686
26687     BYTE*  start_address = generation_limit (max_generation);
26688     BYTE* end_address = heap_segment_allocated (ephemeral_heap_segment);
26689     size_t  current_brick = brick_of (start_address);
26690     size_t  end_brick = brick_of (end_address - 1);
26691     BYTE* last_plug = 0;
26692
26693     //Look for the right pinned plug to start from.
26694     reset_pinned_queue_bos();
26695     while (!pinned_plug_que_empty_p())
26696     {
26697         mark* m = oldest_pin();
26698         if ((m->first >= start_address) && (m->first < end_address))
26699         {
26700             dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
26701
26702             break;
26703         }
26704         else
26705             deque_pinned_plug();
26706     }
26707     
26708     update_oldest_pinned_plug();
26709
26710     while (current_brick <= end_brick)
26711     {
26712         int brick_entry =  brick_table [ current_brick ];
26713         if (brick_entry >= 0)
26714         {
26715             count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
26716         }
26717
26718         current_brick++;
26719     }
26720
26721     if (last_plug !=0)
26722     {
26723         count_plug (end_address - last_plug, last_plug);
26724     }
26725
26726     // we need to make sure that after fitting all the existing plugs, we
26727     // have big enough free space left to guarantee that the next allocation
26728     // will succeed.
26729     size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
26730     total_ephemeral_plugs += extra_size;
26731     dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
26732     ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
26733     
26734     memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
26735
26736 #ifdef SEG_REUSE_STATS
26737     dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
26738     size_t total_plug_power2 = 0;
26739     dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
26740     dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))", 
26741                 total_ephemeral_plugs, 
26742                 total_plug_power2, 
26743                 (total_ephemeral_plugs ? 
26744                     (total_plug_power2 * 100 / total_ephemeral_plugs) :
26745                     0)));
26746     dprintf (SEG_REUSE_LOG_0, ("-------------------"));
26747 #endif // SEG_REUSE_STATS
26748 }
26749
26750 void gc_heap::init_ordered_free_space_indices ()
26751 {
26752     memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
26753     memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
26754 }
26755
26756 void gc_heap::trim_free_spaces_indices ()
26757 {
26758     trimmed_free_space_index = -1;
26759     size_t max_count = max_free_space_items - 1;
26760     size_t count = 0;
26761     int i = 0;
26762     for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
26763     {
26764         count += ordered_free_space_indices[i];
26765
26766         if (count >= max_count)
26767         {
26768             break;
26769         }
26770     }
26771
26772     SSIZE_T extra_free_space_items = count - max_count;
26773
26774     if (extra_free_space_items > 0)
26775     {
26776         ordered_free_space_indices[i] -= extra_free_space_items;
26777         free_space_items = max_count;
26778         trimmed_free_space_index = i;
26779     }
26780     else
26781     {
26782         free_space_items = count;
26783     }
26784
26785     if (i == -1)
26786     {
26787         i = 0;
26788     }
26789
26790     free_space_buckets = MAX_NUM_BUCKETS - i;
26791
26792     for (--i; i >= 0; i--)
26793     {
26794         ordered_free_space_indices[i] = 0;
26795     }
26796
26797     memcpy (saved_ordered_free_space_indices, 
26798             ordered_free_space_indices,
26799             sizeof(ordered_free_space_indices));
26800 }
26801
26802 // We fit as many plugs as we can and update the number of plugs left and the number
26803 // of free spaces left.
26804 BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
26805 {
26806     assert (small_index <= big_index);
26807     assert (big_index < MAX_NUM_BUCKETS);
26808
26809     size_t small_blocks = ordered_blocks[small_index];
26810
26811     if (small_blocks == 0)
26812     {
26813         return TRUE;
26814     }
26815
26816     size_t big_spaces = ordered_spaces[big_index];
26817
26818     if (big_spaces == 0)
26819     {
26820         return FALSE;
26821     }
26822
26823     dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces", 
26824         heap_number,
26825         small_blocks, (small_index + MIN_INDEX_POWER2),
26826         big_spaces, (big_index + MIN_INDEX_POWER2)));
26827
26828     size_t big_to_small = big_spaces << (big_index - small_index);
26829
26830     SSIZE_T extra_small_spaces = big_to_small - small_blocks;
26831     dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks", 
26832         heap_number,
26833         big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
26834     BOOL can_fit = (extra_small_spaces >= 0);
26835
26836     if (can_fit) 
26837     {
26838         dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks", 
26839             heap_number,
26840             extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
26841     }
26842
26843     int i = 0;
26844
26845     dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
26846     ordered_spaces[big_index] = 0;
26847     if (extra_small_spaces > 0)
26848     {
26849         dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
26850         ordered_blocks[small_index] = 0;
26851         for (i = small_index; i < big_index; i++)
26852         {
26853             if (extra_small_spaces & 1)
26854             {
26855                 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d", 
26856                     heap_number,
26857                     (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
26858                 ordered_spaces[i] += 1;
26859             }
26860             extra_small_spaces >>= 1;
26861         }
26862
26863         dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d", 
26864             heap_number,
26865             (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
26866         ordered_spaces[i] += extra_small_spaces;
26867     }
26868     else
26869     {
26870         dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d", 
26871             heap_number,
26872             (small_index + MIN_INDEX_POWER2), 
26873             ordered_blocks[small_index], 
26874             (ordered_blocks[small_index] - big_to_small)));
26875         ordered_blocks[small_index] -= big_to_small;
26876     }
26877
26878 #ifdef SEG_REUSE_STATS
26879     size_t temp;
26880     dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
26881     dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
26882
26883     dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
26884     dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
26885 #endif //SEG_REUSE_STATS
26886
26887     return can_fit;
26888 }
26889
26890 // space_index gets updated to the biggest available space index.
26891 BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
26892 {
26893     assert (*space_index >= block_index);
26894
26895     while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
26896     {
26897         (*space_index)--;
26898         if (*space_index < block_index)
26899         {
26900             return FALSE;
26901         }
26902     }
26903
26904     return TRUE;
26905 }
26906
26907 BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
26908 {
26909 #ifdef FEATURE_STRUCTALIGN
26910     // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
26911     return FALSE;
26912 #endif // FEATURE_STRUCTALIGN
26913     int space_index = count - 1;
26914     for (int block_index = (count - 1); block_index >= 0; block_index--)
26915     {
26916         if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
26917         {
26918             return FALSE;
26919         }
26920     }
26921
26922     return TRUE;
26923 }
26924
26925 void gc_heap::build_ordered_free_spaces (heap_segment* seg)
26926 {
26927     assert (bestfit_seg);
26928
26929     //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2, 
26930     //                    ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets), 
26931     //                    free_space_buckets, 
26932     //                    free_space_items);
26933
26934     bestfit_seg->add_buckets (MIN_INDEX_POWER2, 
26935                         ordered_free_space_indices, 
26936                         MAX_NUM_BUCKETS, 
26937                         free_space_items);
26938
26939     assert (settings.condemned_generation == max_generation);
26940
26941     BYTE* first_address = heap_segment_mem (seg);
26942     BYTE* end_address   = heap_segment_reserved (seg);
26943     //look through the pinned plugs for relevant ones.
26944     //Look for the right pinned plug to start from.
26945     reset_pinned_queue_bos();
26946     mark* m = 0;
26947     // See comment in can_expand_into_p why we need (max_generation + 1).
26948     size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
26949     BOOL has_fit_gen_starts = FALSE;
26950
26951     while (!pinned_plug_que_empty_p())
26952     {
26953         m = oldest_pin();
26954         if ((pinned_plug (m) >= first_address) && 
26955             (pinned_plug (m) < end_address) &&
26956             (pinned_len (m) >= eph_gen_starts))
26957         {
26958
26959             assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
26960             break;
26961         }
26962         else
26963         {
26964             deque_pinned_plug();
26965         }
26966     }
26967
26968     if (!pinned_plug_que_empty_p())
26969     {
26970         bestfit_seg->add ((void*)m, TRUE, TRUE);
26971         deque_pinned_plug();
26972         m = oldest_pin();
26973         has_fit_gen_starts = TRUE;
26974     }
26975
26976     while (!pinned_plug_que_empty_p() &&
26977             ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
26978     {
26979         bestfit_seg->add ((void*)m, TRUE, FALSE);
26980         deque_pinned_plug();
26981         m = oldest_pin();
26982     }
26983
26984     if (commit_end_of_seg)
26985     {
26986         if (!has_fit_gen_starts)
26987         {
26988             assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
26989         }
26990         bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
26991     }
26992
26993 #ifdef _DEBUG
26994     bestfit_seg->check();
26995 #endif //_DEBUG
26996 }
26997
26998 BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
26999 {
27000     if (!end_of_segment_p)
27001     {
27002         trim_free_spaces_indices ();
27003     }
27004
27005     BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices, 
27006                                              ordered_free_space_indices, 
27007                                              MAX_NUM_BUCKETS);
27008
27009     return can_bestfit;
27010 }
27011
27012 BOOL gc_heap::best_fit (size_t free_space, 
27013                         size_t largest_free_space, 
27014                         size_t additional_space, 
27015                         BOOL* use_additional_space)
27016 {
27017     dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
27018
27019     assert (!additional_space || (additional_space && use_additional_space));
27020     if (use_additional_space)
27021     {
27022         *use_additional_space = FALSE;
27023     }
27024
27025     if (ordered_plug_indices_init == FALSE)
27026     {
27027         total_ephemeral_plugs = 0;
27028         build_ordered_plug_indices();
27029         ordered_plug_indices_init = TRUE;
27030     }
27031     else
27032     {
27033         memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
27034     }
27035
27036     if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
27037     {
27038         dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
27039         size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
27040         BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
27041         if (!can_fit_empty_eph)
27042         {
27043             can_fit_empty_eph = (additional_space >= empty_eph);
27044
27045             if (can_fit_empty_eph)
27046             {
27047                 *use_additional_space = TRUE;
27048             }
27049         }
27050
27051         return can_fit_empty_eph;
27052     }
27053
27054     if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
27055     {
27056         dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
27057         return FALSE;
27058     }
27059
27060     if ((free_space + additional_space) == 0)
27061     {
27062         dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
27063         return FALSE;
27064     }
27065
27066 #ifdef SEG_REUSE_STATS
27067     dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
27068     size_t total_free_space_power2 = 0;
27069     size_t total_free_space_items = 
27070         dump_buckets (ordered_free_space_indices, 
27071                       MAX_NUM_BUCKETS,
27072                       &total_free_space_power2);
27073     dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
27074
27075     dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
27076                 total_ephemeral_plugs, 
27077                 free_space, 
27078                 total_free_space_power2, 
27079                 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
27080                 additional_space));
27081
27082     size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
27083     memcpy (saved_all_free_space_indices, 
27084             ordered_free_space_indices, 
27085             sizeof(saved_all_free_space_indices));
27086
27087 #endif // SEG_REUSE_STATS
27088
27089     if (total_ephemeral_plugs > (free_space + additional_space))
27090     {
27091         return FALSE;
27092     }
27093
27094     use_bestfit = try_best_fit(FALSE);
27095
27096     if (!use_bestfit && additional_space)
27097     {
27098         int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
27099
27100         if (relative_free_space_index != -1)
27101         {
27102             int relative_plug_index = 0;
27103             size_t plugs_to_fit = 0;
27104
27105             for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
27106             {
27107                 plugs_to_fit = ordered_plug_indices[relative_plug_index];
27108                 if (plugs_to_fit != 0)
27109                 {
27110                     break;
27111                 }
27112             }
27113
27114             if ((relative_plug_index > relative_free_space_index) ||
27115                 ((relative_plug_index == relative_free_space_index) &&
27116                 (plugs_to_fit > 1)))
27117             {
27118 #ifdef SEG_REUSE_STATS
27119                 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
27120                             (relative_free_space_index + MIN_INDEX_POWER2),
27121                             plugs_to_fit,
27122                             (relative_plug_index + MIN_INDEX_POWER2)));
27123 #endif // SEG_REUSE_STATS
27124                 goto adjust;
27125             }
27126             
27127             dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
27128             ordered_free_space_indices[relative_free_space_index]++;
27129             use_bestfit = try_best_fit(TRUE);
27130             if (use_bestfit)
27131             {
27132                 free_space_items++;
27133                 // Since we might've trimmed away some of the free spaces we had, we should see
27134                 // if we really need to use end of seg space - if it's the same or smaller than
27135                 // the largest space we trimmed we can just add that one back instead of 
27136                 // using end of seg.
27137                 if (relative_free_space_index > trimmed_free_space_index)
27138                 {
27139                     *use_additional_space = TRUE;
27140                 }
27141                 else 
27142                 {
27143                     // If the addition space is <= than the last trimmed space, we
27144                     // should just use that last trimmed space instead.
27145                     saved_ordered_free_space_indices[trimmed_free_space_index]++;
27146                 }
27147             }
27148         }
27149     }
27150
27151 adjust:
27152
27153     if (!use_bestfit)
27154     {
27155         dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
27156
27157 #ifdef SEG_REUSE_STATS
27158         size_t saved_max = max_free_space_items;
27159         BOOL temp_bestfit = FALSE;
27160
27161         dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
27162         dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
27163
27164         // TODO: need to take the end of segment into consideration.
27165         while (max_free_space_items <= total_free_space_items)
27166         {
27167             max_free_space_items += max_free_space_items / 2;
27168             dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
27169             memcpy (ordered_free_space_indices, 
27170                     saved_all_free_space_indices,
27171                     sizeof(ordered_free_space_indices));
27172             if (try_best_fit(FALSE))
27173             {
27174                 temp_bestfit = TRUE;
27175                 break;
27176             }
27177         }
27178
27179         if (temp_bestfit)
27180         {
27181             dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
27182         }
27183         else
27184         {
27185             dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
27186         }
27187
27188         dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
27189         max_free_space_items = saved_max;
27190 #endif // SEG_REUSE_STATS
27191         if (free_space_items)
27192         {
27193             max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
27194             max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
27195         }
27196         else
27197         {
27198             max_free_space_items = MAX_NUM_FREE_SPACES;
27199         }
27200     }
27201
27202     dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
27203     dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
27204
27205     return use_bestfit;
27206 }
27207
27208 BOOL gc_heap::process_free_space (heap_segment* seg, 
27209                          size_t free_space,
27210                          size_t min_free_size, 
27211                          size_t min_cont_size,
27212                          size_t* total_free_space,
27213                          size_t* largest_free_space)
27214 {
27215     *total_free_space += free_space;
27216     *largest_free_space = max (*largest_free_space, free_space);
27217
27218 #ifdef SIMPLE_DPRINTF
27219     dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix", 
27220                 free_space, *total_free_space, *largest_free_space));
27221 #endif //SIMPLE_DPRINTF
27222
27223     if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
27224     {
27225 #ifdef SIMPLE_DPRINTF
27226         dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit", 
27227             settings.condemned_generation,
27228             *total_free_space, min_free_size, *largest_free_space, min_cont_size,
27229             (size_t)seg));
27230 #endif //SIMPLE_DPRINTF
27231         return TRUE;
27232     }
27233
27234     int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
27235     if (free_space_index != -1)
27236     {
27237         ordered_free_space_indices[free_space_index]++;
27238     }
27239     return FALSE;
27240 }
27241
27242 BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
27243                                  allocator* gen_allocator)
27244 {
27245     min_cont_size += END_SPACE_AFTER_GC;
27246     use_bestfit = FALSE;
27247     commit_end_of_seg = FALSE;
27248     bestfit_first_pin = 0;
27249     BYTE* first_address = heap_segment_mem (seg);
27250     BYTE* end_address   = heap_segment_reserved (seg);
27251     size_t end_extra_space = end_space_after_gc();
27252
27253     if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
27254     {
27255         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
27256                                    first_address, end_address, end_extra_space));
27257         return FALSE;
27258     }
27259
27260     end_address -= end_extra_space;
27261
27262     dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix", 
27263         settings.condemned_generation, min_free_size, min_cont_size));
27264     size_t eph_gen_starts = eph_gen_starts_size;
27265
27266     if (settings.condemned_generation == max_generation)
27267     {
27268         size_t free_space = 0;
27269         size_t largest_free_space = free_space;
27270         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
27271         //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from. 
27272         //We are going to allocate the generation starts in the 1st free space,
27273         //so start from the first free space that's big enough for gen starts and a min object size.
27274         // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it - 
27275         // we could use it by allocating the last generation start a bit bigger but 
27276         // the complexity isn't worth the effort (those plugs are from gen2 
27277         // already anyway).
27278         reset_pinned_queue_bos();
27279         mark* m = 0;
27280         BOOL has_fit_gen_starts = FALSE;
27281
27282         init_ordered_free_space_indices ();
27283         while (!pinned_plug_que_empty_p())
27284         {
27285             m = oldest_pin();
27286             if ((pinned_plug (m) >= first_address) && 
27287                 (pinned_plug (m) < end_address) &&
27288                 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
27289             {
27290                 break;
27291             }
27292             else
27293             {
27294                 deque_pinned_plug();
27295             }
27296         }
27297
27298         if (!pinned_plug_que_empty_p())
27299         {
27300             bestfit_first_pin = pinned_plug (m) - pinned_len (m);
27301
27302             if (process_free_space (seg, 
27303                                     pinned_len (m) - eph_gen_starts, 
27304                                     min_free_size, min_cont_size, 
27305                                     &free_space, &largest_free_space))
27306             {
27307                 return TRUE;
27308             }
27309
27310             deque_pinned_plug();
27311             m = oldest_pin();
27312             has_fit_gen_starts = TRUE;
27313         }
27314
27315         dprintf (3, ("first pin is %Ix", pinned_plug (m)));
27316
27317         //tally up free space
27318         while (!pinned_plug_que_empty_p() &&
27319                ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
27320         {
27321             dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
27322             if (process_free_space (seg, 
27323                                     pinned_len (m), 
27324                                     min_free_size, min_cont_size, 
27325                                     &free_space, &largest_free_space))
27326             {
27327                 return TRUE;
27328             }
27329
27330             deque_pinned_plug();
27331             m = oldest_pin();
27332         }
27333
27334         //try to find space at the end of the segment. 
27335         size_t end_space = (end_address - heap_segment_plan_allocated (seg)); 
27336         size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0); 
27337         dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
27338         if (end_space >= additional_space)
27339         {
27340             BOOL can_fit = TRUE;
27341             commit_end_of_seg = TRUE;
27342
27343             if (largest_free_space < min_cont_size)
27344             {
27345                 if (end_space >= min_cont_size)
27346                 {
27347                     additional_space = max (min_cont_size, additional_space);
27348                     dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph", 
27349                         seg));
27350                 }
27351                 else 
27352                 {
27353                     if (settings.concurrent)
27354                     {
27355                         can_fit = FALSE;
27356                         commit_end_of_seg = FALSE;
27357                     }
27358                     else
27359                     {
27360                         size_t additional_space_bestfit = additional_space;
27361                         if (!has_fit_gen_starts)
27362                         {
27363                             if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
27364                             {
27365                                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
27366                                         additional_space_bestfit));
27367                                 return FALSE;
27368                             }
27369
27370                             bestfit_first_pin = heap_segment_plan_allocated (seg);
27371                             additional_space_bestfit -= eph_gen_starts;
27372                         }
27373
27374                         can_fit = best_fit (free_space, 
27375                                             largest_free_space,
27376                                             additional_space_bestfit, 
27377                                             &commit_end_of_seg);
27378
27379                         if (can_fit)
27380                         {
27381                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg", 
27382                                 seg, (commit_end_of_seg ? "with" : "without")));
27383                         }
27384                         else
27385                         {
27386                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
27387                         }
27388                     }
27389                 }
27390             }
27391             else
27392             {
27393                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
27394             }
27395
27396             assert (additional_space <= end_space);
27397             if (commit_end_of_seg)
27398             {
27399                 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
27400                 {
27401                     dprintf (2, ("Couldn't commit end of segment?!"));
27402                     use_bestfit = FALSE;
27403  
27404                     return FALSE;
27405                 }
27406
27407                 if (use_bestfit)
27408                 {
27409                     // We increase the index here because growing heap segment could create a discrepency with 
27410                     // the additional space we used (could be bigger).
27411                     size_t free_space_end_of_seg = 
27412                         heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
27413                     int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
27414                     saved_ordered_free_space_indices[relative_free_space_index]++;
27415                 }
27416             }
27417         
27418             if (use_bestfit)
27419             {
27420                 memcpy (ordered_free_space_indices, 
27421                         saved_ordered_free_space_indices, 
27422                         sizeof(ordered_free_space_indices));
27423                 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
27424                 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
27425                 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
27426             }
27427
27428             return can_fit;
27429         }
27430
27431         dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
27432         return FALSE;
27433     }
27434     else
27435     {
27436         assert (settings.condemned_generation == (max_generation-1));
27437         size_t free_space = (end_address - heap_segment_plan_allocated (seg));
27438         size_t largest_free_space = free_space;
27439         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
27440         //find the first free list in range of the current segment
27441         size_t sz_list = gen_allocator->first_bucket_size();
27442         unsigned int a_l_idx = 0;
27443         BYTE* free_list = 0; 
27444         for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
27445         {
27446             if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
27447             {
27448                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
27449                 while (free_list)
27450                 {
27451                     if ((free_list >= first_address) && 
27452                         (free_list < end_address) && 
27453                         (unused_array_size (free_list) >= eph_gen_starts))
27454                     {
27455                         goto next;
27456                     }
27457                     else
27458                     {
27459                         free_list = free_list_slot (free_list);
27460                     }
27461                 }
27462             }
27463         }
27464 next:
27465         if (free_list)
27466         {
27467             init_ordered_free_space_indices ();
27468             if (process_free_space (seg, 
27469                                     unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size), 
27470                                     min_free_size, min_cont_size, 
27471                                     &free_space, &largest_free_space))
27472             {
27473                 return TRUE;
27474             }
27475
27476             free_list = free_list_slot (free_list);
27477         }
27478         else
27479         {
27480             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
27481             return FALSE;
27482         }
27483
27484        //tally up free space
27485
27486         while (1)
27487         {
27488             while (free_list)
27489             {
27490                 if ((free_list >= first_address) && (free_list < end_address) &&
27491                     process_free_space (seg, 
27492                                         unused_array_size (free_list), 
27493                                         min_free_size, min_cont_size, 
27494                                         &free_space, &largest_free_space))
27495                 {
27496                     return TRUE;
27497                 }
27498
27499                 free_list = free_list_slot (free_list);
27500             }
27501             a_l_idx++;
27502             if (a_l_idx < gen_allocator->number_of_buckets())
27503             {
27504                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
27505             }
27506             else
27507                 break;
27508         } 
27509
27510         dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
27511         return FALSE;
27512
27513         /*
27514         BOOL can_fit = best_fit (free_space, 0, NULL);
27515         if (can_fit)
27516         {
27517             dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
27518         }
27519         else
27520         {
27521             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
27522         }
27523
27524         return can_fit;
27525         */
27526     }
27527 }
27528
27529 void gc_heap::realloc_plug (size_t last_plug_size, BYTE*& last_plug,
27530                             generation* gen, BYTE* start_address,
27531                             unsigned int& active_new_gen_number,
27532                             BYTE*& last_pinned_gap, BOOL& leftp,
27533                             BOOL shortened_p
27534 #ifdef SHORT_PLUGS
27535                             , mark* pinned_plug_entry
27536 #endif //SHORT_PLUGS
27537                             )
27538 {
27539     // detect generation boundaries
27540     // make sure that active_new_gen_number is not the youngest generation.
27541     // because the generation_limit wouldn't return the right thing in this case.
27542     if (!use_bestfit)
27543     {
27544         if ((active_new_gen_number > 1) &&
27545             (last_plug >= generation_limit (active_new_gen_number)))
27546         {
27547             assert (last_plug >= start_address);
27548             active_new_gen_number--;
27549             realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
27550             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
27551             leftp = FALSE;
27552         }
27553     }
27554
27555     // detect pinned plugs
27556     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
27557     {
27558         size_t  entry = deque_pinned_plug();
27559         mark*  m = pinned_plug_of (entry);
27560
27561         size_t saved_pinned_len = pinned_len(m);
27562         pinned_len(m) = last_plug - last_pinned_gap;
27563         //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
27564
27565         if (m->has_post_plug_info())
27566         {
27567             last_plug_size += sizeof (gap_reloc_pair);
27568             dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
27569         }
27570
27571         last_pinned_gap = last_plug + last_plug_size;
27572         dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
27573             pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
27574         leftp = FALSE;
27575
27576         //we are creating a generation fault. set the cards.
27577         {
27578             size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
27579             size_t card = card_of (last_plug);
27580             while (card != end_card)
27581             {
27582                 set_card (card);
27583                 card++;
27584             }
27585         }
27586     }
27587     else if (last_plug >= start_address)
27588     {
27589
27590 #ifdef SHORT_PLUGS
27591         clear_plug_padded (last_plug);
27592 #endif //SHORT_PLUGS
27593
27594 #ifdef FEATURE_STRUCTALIGN
27595         int requiredAlignment;
27596         ptrdiff_t pad;
27597         node_aligninfo (last_plug, requiredAlignment, pad);
27598
27599         // from how we previously aligned the plug's destination address,
27600         // compute the actual alignment offset.
27601         BYTE* reloc_plug = last_plug + node_relocation_distance (last_plug);
27602         ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
27603         if (!alignmentOffset)
27604         {
27605             // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
27606             alignmentOffset = requiredAlignment;
27607         }
27608
27609         //clear the alignment info because we are reallocating
27610         clear_node_aligninfo (last_plug);
27611 #else // FEATURE_STRUCTALIGN
27612         //clear the realignment flag because we are reallocating
27613         clear_node_realigned (last_plug);
27614 #endif // FEATURE_STRUCTALIGN
27615         BOOL adjacentp = FALSE;
27616         BOOL set_padding_on_saved_p = FALSE;
27617
27618         if (shortened_p)
27619         {
27620             assert (pinned_plug_entry != NULL);
27621
27622             last_plug_size += sizeof (gap_reloc_pair);
27623
27624 #ifdef SHORT_PLUGS
27625             if (last_plug_size <= sizeof (plug_and_gap))
27626             {
27627                 set_padding_on_saved_p = TRUE;
27628             }
27629 #endif //SHORT_PLUGS
27630
27631             dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug_size))
27632         }
27633
27634         BYTE* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug, 
27635 #ifdef SHORT_PLUGS
27636                                      set_padding_on_saved_p,
27637                                      pinned_plug_entry,
27638 #endif //SHORT_PLUGS
27639                                      TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
27640
27641         dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
27642         assert (new_address);
27643         set_node_relocation_distance (last_plug, new_address - last_plug);
27644 #ifdef FEATURE_STRUCTALIGN
27645         if (leftp && node_alignpad (last_plug) == 0)
27646 #else // FEATURE_STRUCTALIGN
27647         if (leftp && !node_realigned (last_plug))
27648 #endif // FEATURE_STRUCTALIGN
27649         {
27650             // TODO - temporarily disable L optimization because of a bug in it.
27651             //set_node_left (last_plug);
27652         }
27653         dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
27654         leftp = adjacentp;
27655     }
27656 }
27657
27658 void gc_heap::realloc_in_brick (BYTE* tree, BYTE*& last_plug,
27659                                 BYTE* start_address,
27660                                 generation* gen,
27661                                 unsigned int& active_new_gen_number,
27662                                 BYTE*& last_pinned_gap, BOOL& leftp)
27663 {
27664     assert (tree >= 0);
27665     int   left_node = node_left_child (tree);
27666     int   right_node = node_right_child (tree);
27667
27668     dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d", 
27669         tree, last_pinned_gap, last_plug, left_node, right_node));
27670
27671     if (left_node)
27672     {
27673         dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
27674         realloc_in_brick ((tree + left_node), last_plug, start_address,
27675                           gen, active_new_gen_number, last_pinned_gap,
27676                           leftp);
27677     }
27678
27679     if (last_plug != 0)
27680     {
27681         BYTE*  plug = tree;
27682
27683         BOOL has_pre_plug_info_p = FALSE;
27684         BOOL has_post_plug_info_p = FALSE;
27685         mark* pinned_plug_entry = get_next_pinned_entry (tree, 
27686                                                          &has_pre_plug_info_p,
27687                                                          &has_post_plug_info_p, 
27688                                                          FALSE);
27689
27690         // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
27691         // The pinned plugs are handled in realloc_plug.
27692         size_t gap_size = node_gap_size (plug);
27693         BYTE*   gap = (plug - gap_size);
27694         BYTE*  last_plug_end = gap;
27695         size_t  last_plug_size = (last_plug_end - last_plug);
27696         // Cannot assert this - a plug could be less than that due to the shortened ones.
27697         //assert (last_plug_size >= Align (min_obj_size));
27698         dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
27699             plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
27700         realloc_plug (last_plug_size, last_plug, gen, start_address,
27701                       active_new_gen_number, last_pinned_gap,
27702                       leftp, has_pre_plug_info_p
27703 #ifdef SHORT_PLUGS
27704                       , pinned_plug_entry
27705 #endif //SHORT_PLUGS
27706                       );
27707     }
27708
27709     last_plug = tree;
27710
27711     if (right_node)
27712     {
27713         dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
27714         realloc_in_brick ((tree + right_node), last_plug, start_address,
27715                           gen, active_new_gen_number, last_pinned_gap,
27716                           leftp);
27717     }
27718 }
27719
27720 void
27721 gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
27722                         BYTE* start_address, BYTE* end_address,
27723                         unsigned active_new_gen_number)
27724 {
27725     dprintf (3, ("--- Reallocing ---"));
27726
27727     if (use_bestfit)
27728     {
27729         //make sure that every generation has a planned allocation start
27730         int  gen_number = max_generation - 1;
27731         while (gen_number >= 0)
27732         {
27733             generation* gen = generation_of (gen_number);
27734             if (0 == generation_plan_allocation_start (gen))
27735             {
27736                 generation_plan_allocation_start (gen) = 
27737                     bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
27738                 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
27739                 assert (generation_plan_allocation_start (gen));
27740             }
27741             gen_number--;
27742         }
27743     }
27744
27745     BYTE* first_address = start_address;
27746     //Look for the right pinned plug to start from.
27747     reset_pinned_queue_bos();
27748     BYTE* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
27749     while (!pinned_plug_que_empty_p())
27750     {
27751         mark* m = oldest_pin();
27752         if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
27753         {
27754             if (pinned_plug (m) < first_address)
27755             {
27756                 first_address = pinned_plug (m);
27757             }
27758             break;
27759         }
27760         else
27761             deque_pinned_plug();
27762     }
27763
27764     size_t  current_brick = brick_of (first_address);
27765     size_t  end_brick = brick_of (end_address-1);
27766     BYTE*  last_plug = 0;
27767
27768     BYTE* last_pinned_gap = heap_segment_plan_allocated (seg);
27769     BOOL leftp = FALSE;
27770
27771     dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
27772         start_address, first_address, pinned_plug (oldest_pin())));
27773
27774     while (current_brick <= end_brick)
27775     {
27776         int   brick_entry =  brick_table [ current_brick ];
27777         if (brick_entry >= 0)
27778         {
27779             realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
27780                               last_plug, start_address, consing_gen,
27781                               active_new_gen_number, last_pinned_gap,
27782                               leftp);
27783         }
27784         current_brick++;
27785     }
27786
27787     if (last_plug != 0)
27788     {
27789         realloc_plug (end_address - last_plug, last_plug, consing_gen,
27790                       start_address,
27791                       active_new_gen_number, last_pinned_gap,
27792                       leftp, FALSE
27793 #ifdef SHORT_PLUGS
27794                       , NULL
27795 #endif //SHORT_PLUGS
27796                       );
27797     }
27798
27799     //Fix the old segment allocated size
27800     assert (last_pinned_gap >= heap_segment_mem (seg));
27801     assert (last_pinned_gap <= heap_segment_committed (seg));
27802     heap_segment_plan_allocated (seg) = last_pinned_gap;
27803 }
27804
27805 void gc_heap::verify_no_pins (BYTE* start, BYTE* end)
27806 {
27807 #ifdef VERIFY_HEAP
27808     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
27809     {
27810         BOOL contains_pinned_plugs = FALSE;
27811         size_t mi = 0;
27812         mark* m = 0;
27813         while (mi != mark_stack_tos)
27814         {
27815             m = pinned_plug_of (mi);
27816             if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
27817             {
27818                 contains_pinned_plugs = TRUE;
27819                 break;
27820             }
27821             else
27822                 mi++;
27823         }
27824
27825         if (contains_pinned_plugs)
27826         {
27827             FATAL_GC_ERROR();
27828         }
27829     }
27830 #endif //VERIFY_HEAP
27831 }
27832
27833 void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
27834 {
27835     if (!should_expand_in_full_gc)
27836     {
27837         if ((condemned_gen_number != max_generation) && 
27838             (settings.pause_mode != pause_low_latency) &&
27839             (settings.pause_mode != pause_sustained_low_latency))
27840         {
27841             should_expand_in_full_gc = TRUE;
27842         }
27843     }
27844 }
27845
27846 void gc_heap::save_ephemeral_generation_starts()
27847 {
27848     for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
27849     {
27850         saved_ephemeral_plan_start[ephemeral_generation] = 
27851             generation_plan_allocation_start (generation_of (ephemeral_generation));
27852         saved_ephemeral_plan_start_size[ephemeral_generation] = 
27853             generation_plan_allocation_start_size (generation_of (ephemeral_generation));
27854     }
27855 }
27856
27857 generation* gc_heap::expand_heap (int condemned_generation,
27858                                   generation* consing_gen,
27859                                   heap_segment* new_heap_segment)
27860 {
27861     assert (condemned_generation >= (max_generation -1));
27862     unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
27863     BYTE*  start_address = generation_limit (max_generation);
27864     BYTE*  end_address = heap_segment_allocated (ephemeral_heap_segment);
27865     BOOL should_promote_ephemeral = FALSE;
27866     ptrdiff_t eph_size = total_ephemeral_size;
27867 #ifdef BACKGROUND_GC
27868     dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
27869 #endif //BACKGROUND_GC
27870     settings.heap_expansion = TRUE;
27871
27872 #ifdef BACKGROUND_GC
27873     if (cm_in_progress)
27874     {
27875         if (!expanded_in_fgc)
27876         {
27877             expanded_in_fgc = TRUE;
27878         }
27879     }
27880 #endif //BACKGROUND_GC
27881
27882     //reset the elevation state for next time.
27883     dprintf (2, ("Elevation: elevation = el_none"));
27884     settings.should_lock_elevation = FALSE;
27885
27886     heap_segment* new_seg = new_heap_segment;
27887
27888     if (!new_seg)
27889         return consing_gen;
27890
27891     //copy the card and brick tables
27892     if (g_card_table!= card_table)
27893         copy_brick_card_table (TRUE);
27894
27895     BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
27896     dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
27897
27898     assert (generation_plan_allocation_start (generation_of (max_generation-1)));
27899     assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
27900             heap_segment_mem (ephemeral_heap_segment));
27901     assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
27902             heap_segment_committed (ephemeral_heap_segment));
27903
27904     assert (generation_plan_allocation_start (youngest_generation));
27905     assert (generation_plan_allocation_start (youngest_generation) <
27906             heap_segment_plan_allocated (ephemeral_heap_segment));
27907
27908     if (!use_bestfit)
27909     {
27910         should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
27911     }
27912
27913     if (should_promote_ephemeral)
27914     {
27915         ephemeral_promotion = TRUE;
27916         gc_data_per_heap.clear_mechanism (gc_heap_expand);
27917         gc_data_per_heap.set_mechanism (gc_heap_expand, expand_new_seg_ep);
27918         dprintf (2, ("promoting ephemeral"));
27919         save_ephemeral_generation_starts();
27920     }
27921     else
27922     {
27923         // commit the new ephemeral segment all at once if it is a new one.
27924         if ((eph_size > 0) && new_segment_p)
27925         {
27926 #ifdef FEATURE_STRUCTALIGN
27927             // The destination may require a larger alignment padding than the source.
27928             // Assume the worst possible alignment padding.
27929             eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
27930 #endif // FEATURE_STRUCTALIGN
27931 #ifdef RESPECT_LARGE_ALIGNMENT
27932             //Since the generation start can be larger than min_obj_size
27933             //The alignment could be switched. 
27934             eph_size += switch_alignment_size(FALSE);
27935 #endif //RESPECT_LARGE_ALIGNMENT
27936             //Since the generation start can be larger than min_obj_size
27937             //Compare the alignemnt of the first object in gen1 
27938             if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
27939             {
27940                 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
27941                 return consing_gen;
27942             }
27943             heap_segment_used (new_seg) = heap_segment_committed (new_seg);
27944         }
27945
27946         //Fix the end of the old ephemeral heap segment
27947         heap_segment_plan_allocated (ephemeral_heap_segment) =
27948             generation_plan_allocation_start (generation_of (max_generation-1));
27949
27950         dprintf (3, ("Old ephemeral allocated set to %Ix",
27951                     (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
27952     }
27953
27954     if (new_segment_p)
27955     {
27956         // TODO - Is this really necessary? We should think about it.
27957         //initialize the first brick
27958         size_t first_brick = brick_of (heap_segment_mem (new_seg));
27959         set_brick (first_brick,
27960                 heap_segment_mem (new_seg) - brick_address (first_brick));
27961     }
27962
27963     //From this point on, we cannot run out of memory
27964
27965     //reset the allocation of the consing generation back to the end of the
27966     //old ephemeral segment
27967     generation_allocation_limit (consing_gen) =
27968         heap_segment_plan_allocated (ephemeral_heap_segment);
27969     generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
27970     generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
27971
27972     //clear the generation gap for all of the ephemeral generations
27973     {
27974         int generation_num = max_generation-1;
27975         while (generation_num >= 0)
27976         {
27977             generation* gen = generation_of (generation_num);
27978             generation_plan_allocation_start (gen) = 0;
27979             generation_num--;
27980         }
27981     }
27982
27983     heap_segment* old_seg = ephemeral_heap_segment;
27984     ephemeral_heap_segment = new_seg;
27985
27986     //Note: the ephemeral segment shouldn't be threaded onto the segment chain
27987     //because the relocation and compact phases shouldn't see it
27988
27989     // set the generation members used by allocate_in_expanded_heap
27990     // and switch to ephemeral generation
27991     consing_gen = ensure_ephemeral_heap_segment (consing_gen);
27992
27993     if (!should_promote_ephemeral)
27994     {
27995         realloc_plugs (consing_gen, old_seg, start_address, end_address,
27996                     active_new_gen_number);
27997     }
27998
27999     if (!use_bestfit)
28000     {
28001         repair_allocation_in_expanded_heap (consing_gen);
28002     }
28003
28004     // assert that the generation gap for all of the ephemeral generations were allocated.
28005 #ifdef _DEBUG
28006     {
28007         int generation_num = max_generation-1;
28008         while (generation_num >= 0)
28009         {
28010             generation* gen = generation_of (generation_num);
28011             assert (generation_plan_allocation_start (gen));
28012             generation_num--;
28013         }
28014     }
28015 #endif // _DEBUG
28016
28017     if (!new_segment_p)
28018     {
28019         dprintf (2, ("Demoting ephemeral segment"));
28020         //demote the entire segment.
28021         settings.demotion = TRUE;
28022         demotion_low = heap_segment_mem (ephemeral_heap_segment);
28023         demotion_high = heap_segment_reserved (ephemeral_heap_segment);
28024     }
28025     else
28026     {
28027         demotion_low = MAX_PTR;
28028         demotion_high = 0;
28029 #ifndef MULTIPLE_HEAPS
28030         settings.demotion = FALSE;
28031 #endif //!MULTIPLE_HEAPS
28032     }
28033     ptrdiff_t eph_size1 = total_ephemeral_size;
28034     MAYBE_UNUSED_VAR(eph_size1);
28035
28036     if (!should_promote_ephemeral && new_segment_p)
28037     {
28038         assert (eph_size1 <= eph_size);
28039     }
28040
28041     if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
28042     {
28043         // This is to catch when we accidently delete a segment that has pins.
28044         verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
28045     }
28046
28047     verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
28048
28049     dprintf(2,("---- End of Heap Expansion ----"));
28050     return consing_gen;
28051 }
28052
28053 bool gc_heap::init_dynamic_data()
28054 {
28055     LARGE_INTEGER ts;
28056     if (!QueryPerformanceFrequency(&qpf))
28057     {
28058         FATAL_GC_ERROR();
28059     }
28060
28061     if (!QueryPerformanceCounter(&ts))
28062     {
28063         FATAL_GC_ERROR();
28064     }
28065
28066     DWORD now = (DWORD)(ts.QuadPart/(qpf.QuadPart/1000));
28067
28068     //clear some fields
28069     for (int i = 0; i < max_generation+1; i++)
28070     {
28071         dynamic_data* dd = dynamic_data_of (i);
28072         dd->gc_clock = 0;
28073         dd->time_clock = now;
28074     }
28075
28076     // get the registry setting for generation 0 size
28077     size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
28078
28079     dprintf (2, ("gen 0 size: %Id", gen0size));
28080
28081     dynamic_data* dd = dynamic_data_of (0);
28082     dd->current_size = 0;
28083     dd->promoted_size = 0;
28084     dd->collection_count = 0;
28085 //  dd->limit = 3.0f;
28086 #ifdef MULTIPLE_HEAPS
28087     dd->limit = 20.0f;     // be more aggressive on server gc
28088     dd->max_limit = 40.0f;
28089 #else
28090     dd->limit = 9.0f;
28091 //  dd->max_limit = 15.0f; //10.0f;
28092     dd->max_limit = 20.0f;
28093 #endif //MULTIPLE_HEAPS
28094     dd->min_gc_size = Align(gen0size / 8 * 5);
28095     dd->min_size = dd->min_gc_size;
28096     //dd->max_size = Align (gen0size);
28097 //g_pConfig->GetGCconcurrent() is not necessarily 0 for server builds
28098 #ifdef MULTIPLE_HEAPS
28099     dd->max_size = max (6*1024*1024, min ( Align(get_valid_segment_size()/2), 200*1024*1024));
28100 #else //MULTIPLE_HEAPS
28101   dd->max_size = ((g_pConfig->GetGCconcurrent()!=0) ?
28102                   6*1024*1024 :
28103                   max (6*1024*1024,  min ( Align(get_valid_segment_size()/2), 200*1024*1024)));
28104 #endif //MULTIPLE_HEAPS
28105     dd->new_allocation = dd->min_gc_size;
28106     dd->gc_new_allocation = dd->new_allocation;
28107     dd->desired_allocation = dd->new_allocation;
28108     dd->default_new_allocation = dd->min_gc_size;
28109     dd->fragmentation = 0;
28110     dd->fragmentation_limit = 40000;
28111     dd->fragmentation_burden_limit = 0.5f;
28112
28113     dd =  dynamic_data_of (1);
28114     dd->current_size = 0;
28115     dd->promoted_size = 0;
28116     dd->collection_count = 0;
28117     dd->limit = 2.0f;
28118 //  dd->max_limit = 15.0f;
28119     dd->max_limit = 7.0f;
28120     dd->min_gc_size = 9*32*1024;
28121     dd->min_size = dd->min_gc_size;
28122 //  dd->max_size = 2397152;
28123 #ifdef MULTIPLE_HEAPS
28124     dd->max_size = max (6*1024*1024, Align(get_valid_segment_size()/2));
28125 #else //MULTIPLE_HEAPS
28126   dd->max_size = ((g_pConfig->GetGCconcurrent()!=0) ?
28127                   6*1024*1024 :
28128                   max (6*1024*1024, Align(get_valid_segment_size()/2)));
28129 #endif //MULTIPLE_HEAPS
28130     dd->new_allocation = dd->min_gc_size;
28131     dd->gc_new_allocation = dd->new_allocation;
28132     dd->desired_allocation = dd->new_allocation;
28133     dd->default_new_allocation = dd->min_gc_size;
28134     dd->fragmentation = 0;
28135     dd->fragmentation_limit = 80000;
28136     dd->fragmentation_burden_limit = 0.5f;
28137
28138     dd =  dynamic_data_of (2);
28139     dd->current_size = 0;
28140     dd->promoted_size = 0;
28141     dd->collection_count = 0;
28142     dd->limit = 1.2f;
28143     dd->max_limit = 1.8f;
28144     dd->min_gc_size = 256*1024;
28145     dd->min_size = dd->min_gc_size;
28146     dd->max_size = SSIZE_T_MAX;
28147     dd->new_allocation = dd->min_gc_size;
28148     dd->gc_new_allocation = dd->new_allocation;
28149     dd->desired_allocation = dd->new_allocation;
28150     dd->default_new_allocation = dd->min_gc_size;
28151     dd->fragmentation = 0;
28152     dd->fragmentation_limit = 200000;
28153     dd->fragmentation_burden_limit = 0.25f;
28154
28155     //dynamic data for large objects
28156     dd =  dynamic_data_of (3);
28157     dd->current_size = 0;
28158     dd->promoted_size = 0;
28159     dd->collection_count = 0;
28160     dd->limit = 1.25f;
28161     dd->max_limit = 4.5f;
28162     dd->min_gc_size = 3*1024*1024;
28163     dd->min_size = dd->min_gc_size;
28164     dd->max_size = SSIZE_T_MAX;
28165     dd->new_allocation = dd->min_gc_size;
28166     dd->gc_new_allocation = dd->new_allocation;
28167     dd->desired_allocation = dd->new_allocation;
28168     dd->default_new_allocation = dd->min_gc_size;
28169     dd->fragmentation = 0;
28170     dd->fragmentation_limit = 0;
28171     dd->fragmentation_burden_limit = 0.0f;
28172
28173     return true;
28174 }
28175
28176 // This returns a time stamp in milliseconds that is used throughout GC.
28177 // TODO: Replace all calls to QueryPerformanceCounter with this function.
28178 size_t gc_heap::get_time_now()
28179 {
28180     LARGE_INTEGER ts;
28181     if (!QueryPerformanceCounter(&ts))
28182         FATAL_GC_ERROR();
28183
28184     return (size_t)(ts.QuadPart/(qpf.QuadPart/1000));
28185 }
28186
28187 float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
28188 {
28189     if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
28190         return ((limit - limit*cst) / (1.0f - (cst * limit)));
28191     else
28192         return max_limit;
28193 }
28194
28195
28196 //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may 
28197 //not be correct (collection happened too soon). Correct with a linear estimation based on the previous 
28198 //value of the budget 
28199 static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation, 
28200                                        size_t previous_desired_allocation, size_t collection_count)
28201 {
28202     if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
28203     {
28204         dprintf (2, ("allocation fraction: %d%", (int)(allocation_fraction/100.0)));
28205         new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
28206     }
28207 #if 0 
28208     size_t smoothing = 3; // exponential smoothing factor
28209     if (smoothing  > collection_count)
28210         smoothing  = collection_count;
28211     new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
28212 #endif //0
28213     return new_allocation;
28214 }
28215
28216 size_t gc_heap::desired_new_allocation (dynamic_data* dd,
28217                                         size_t out, int gen_number,
28218                                         int pass)
28219 {
28220     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
28221
28222     if (dd_begin_data_size (dd) == 0)
28223     {
28224         size_t new_allocation = dd_default_new_allocation (dd);
28225         current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;
28226         if ((gen_number == 0) && (pass == 1))
28227         {
28228             current_gc_data_per_heap->gen_data[max_generation+2].new_allocation = new_allocation;
28229         }
28230         
28231         return new_allocation;
28232     }
28233     else
28234     {
28235         float     cst;
28236         size_t    previous_desired_allocation = dd_desired_allocation (dd);
28237         //ptrdiff_t allocation = (previous_desired_allocation - dd_gc_new_allocation (dd));
28238         ptrdiff_t allocation = (previous_desired_allocation - dd_new_allocation (dd));
28239         size_t    current_size = dd_current_size (dd);
28240         float     max_limit = dd_max_limit (dd);
28241         float     limit = dd_limit (dd);
28242         size_t    min_gc_size = dd_min_gc_size (dd);
28243         float     f = 0;
28244         size_t    max_size = dd_max_size (dd);
28245         size_t    new_allocation = 0;
28246         float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
28247         if (gen_number >= max_generation)
28248         {
28249             size_t    new_size = 0;
28250
28251             cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
28252
28253             f = surv_to_growth (cst, limit, max_limit);
28254             size_t max_growth_size = (size_t)(max_size / f);
28255             if (current_size >= max_growth_size)
28256             {
28257                 new_size = max_size;
28258             }
28259             else
28260             {
28261                 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
28262             }
28263
28264             assert ((new_size >= current_size) || (new_size == max_size));
28265
28266             if (gen_number == max_generation)
28267             {
28268                 new_allocation  =  max((new_size - current_size), min_gc_size);
28269
28270                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
28271                                                           dd_desired_allocation (dd), dd_collection_count (dd));
28272
28273                 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
28274                 {
28275                     //reducing allocation in case of fragmentation
28276                     size_t new_allocation1 = max (min_gc_size,
28277                                                   // CAN OVERFLOW
28278                                                   (size_t)((float)new_allocation * current_size /
28279                                                            ((float)current_size + 2*dd_fragmentation (dd))));
28280                     dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
28281                                  new_allocation, new_allocation1));
28282                     new_allocation = new_allocation1;
28283                 }
28284             }
28285             else //large object heap
28286             {
28287                 MEMORYSTATUSEX ms;
28288                 GetProcessMemoryLoad (&ms);
28289                 ULONGLONG available_ram = ms.ullAvailPhys;
28290
28291                 if (ms.ullAvailPhys > 1024*1024)
28292                     available_ram -= 1024*1024;
28293
28294                 ULONGLONG available_free = available_ram + (ULONGLONG)generation_free_list_space (generation_of (gen_number));
28295                 if (available_free > (ULONGLONG)MAX_PTR)
28296                 {
28297                     available_free = (ULONGLONG)MAX_PTR;
28298                 }
28299
28300                 //try to avoid OOM during large object allocation
28301                 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))), 
28302                                           (size_t)available_free), 
28303                                       max ((current_size/4), min_gc_size));
28304
28305                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
28306                                                           dd_desired_allocation (dd), dd_collection_count (dd));
28307
28308             }
28309         }
28310         else
28311         {
28312             size_t survivors = out;
28313             cst = float (survivors) / float (dd_begin_data_size (dd));
28314             f = surv_to_growth (cst, limit, max_limit);
28315             new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
28316
28317             new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
28318                                                       dd_desired_allocation (dd), dd_collection_count (dd));
28319
28320             if (gen_number == 0)
28321             {
28322                 if (pass == 0)
28323                 {
28324
28325                     //printf ("%f, %Id\n", cst, new_allocation);
28326                     size_t free_space = generation_free_list_space (generation_of (gen_number));
28327                     // DTREVIEW - is min_gc_size really a good choice? 
28328                     // on 64-bit this will almost always be true.
28329                     if (free_space > min_gc_size)
28330                     {
28331                         dprintf (2, ("Detected excessive Fragmentation"));
28332                         settings.gen0_reduction_count = 2;
28333                     }
28334                     else
28335                     {
28336                         if (settings.gen0_reduction_count > 0)
28337                             settings.gen0_reduction_count--;
28338                     }
28339                 }
28340                 if (settings.gen0_reduction_count > 0)
28341                 {
28342                     dprintf (2, ("Reducing new allocation based on fragmentation"));
28343                     new_allocation = min (new_allocation,
28344                                           max (min_gc_size, (max_size/3)));
28345                 }
28346             }
28347
28348         }
28349
28350         size_t new_allocation_ret = 
28351             Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
28352         int gen_data_index = gen_number;
28353         if ((gen_number == 0) && (pass == 1))
28354         {
28355             gen_data_index = max_generation+2;
28356         }
28357         gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
28358         gen_data->surv = (size_t)(cst*100);
28359         gen_data->new_allocation = new_allocation_ret;
28360
28361         dd_surv (dd) = cst;
28362
28363 #ifdef SIMPLE_DPRINTF
28364         dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
28365                      heap_number, gen_number, out, current_size, allocation,
28366                      (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
28367 #else
28368         dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
28369         dprintf (1,("current: %Id alloc: %Id ", current_size, allocation));
28370         dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
28371                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
28372 #endif //SIMPLE_DPRINTF
28373
28374         return new_allocation_ret;
28375     }
28376 }
28377
28378 //returns the planned size of a generation (including free list element)
28379 size_t gc_heap::generation_plan_size (int gen_number)
28380 {
28381     if (0 == gen_number)
28382         return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
28383                     generation_plan_allocation_start (generation_of (gen_number))),
28384                    (int)Align (min_obj_size));
28385     else
28386     {
28387         generation* gen = generation_of (gen_number);
28388         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
28389             return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
28390                     generation_plan_allocation_start (generation_of (gen_number)));
28391         else
28392         {
28393             size_t gensize = 0;
28394             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
28395
28396             PREFIX_ASSUME(seg != NULL);
28397
28398             while (seg && (seg != ephemeral_heap_segment))
28399             {
28400                 gensize += heap_segment_plan_allocated (seg) -
28401                            heap_segment_mem (seg);
28402                 seg = heap_segment_next_rw (seg);
28403             }
28404             if (seg)
28405             {
28406                 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
28407                             heap_segment_mem (ephemeral_heap_segment));
28408             }
28409             return gensize;
28410         }
28411     }
28412
28413 }
28414
28415 //returns the size of a generation (including free list element)
28416 size_t gc_heap::generation_size (int gen_number)
28417 {
28418     if (0 == gen_number)
28419         return max((heap_segment_allocated (ephemeral_heap_segment) -
28420                     generation_allocation_start (generation_of (gen_number))),
28421                    (int)Align (min_obj_size));
28422     else
28423     {
28424         generation* gen = generation_of (gen_number);
28425         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
28426             return (generation_allocation_start (generation_of (gen_number - 1)) -
28427                     generation_allocation_start (generation_of (gen_number)));
28428         else
28429         {
28430             size_t gensize = 0;
28431             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
28432
28433             PREFIX_ASSUME(seg != NULL);
28434
28435             while (seg && (seg != ephemeral_heap_segment))
28436             {
28437                 gensize += heap_segment_allocated (seg) -
28438                            heap_segment_mem (seg);
28439                 seg = heap_segment_next_rw (seg);
28440             }
28441             if (seg)
28442             {
28443                 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
28444                             heap_segment_mem (ephemeral_heap_segment));
28445             }
28446
28447             return gensize;
28448         }
28449     }
28450
28451 }
28452
28453 size_t  gc_heap::compute_in (int gen_number)
28454 {
28455     assert (gen_number != 0);
28456     dynamic_data* dd = dynamic_data_of (gen_number);
28457
28458     size_t in = generation_allocation_size (generation_of (gen_number));
28459
28460     if (gen_number == max_generation && ephemeral_promotion)
28461     {
28462         in = 0;
28463         for (int i = 0; i <= max_generation; i++)
28464         {
28465             dynamic_data* dd = dynamic_data_of (i);
28466             in += dd_survived_size (dd);
28467             if (i != max_generation)
28468             {
28469                 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
28470             }
28471         }
28472     }
28473
28474     dd_gc_new_allocation (dd) -= in;
28475
28476     generation_allocation_size (generation_of (gen_number)) = 0;
28477     return in;
28478 }
28479
28480 void  gc_heap::compute_promoted_allocation (int gen_number)
28481 {
28482     compute_in (gen_number);
28483 }
28484
28485 #ifdef _WIN64
28486 inline
28487 size_t gc_heap::trim_youngest_desired (DWORD memory_load, 
28488                                        size_t total_new_allocation,
28489                                        size_t total_min_allocation)
28490 {
28491     if (memory_load < MAX_ALLOWED_MEM_LOAD)
28492     {
28493         // If the total of memory load and gen0 budget exceeds 
28494         // our max memory load limit, trim the gen0 budget so the total 
28495         // is the max memory load limit.
28496         size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
28497         return min (total_new_allocation, remain_memory_load);
28498     }
28499     else
28500     {
28501         return max (mem_one_percent, total_min_allocation);
28502     }
28503 }
28504
28505 size_t gc_heap::joined_youngest_desired (size_t new_allocation)
28506 {
28507     dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
28508
28509     size_t final_new_allocation = new_allocation;
28510     if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
28511     {
28512         DWORD num_heaps = 1;
28513
28514 #ifdef MULTIPLE_HEAPS
28515         num_heaps = gc_heap::n_heaps;
28516 #endif //MULTIPLE_HEAPS
28517
28518         size_t total_new_allocation = new_allocation * num_heaps;
28519         size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
28520
28521         if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
28522             (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
28523         {
28524             DWORD dwMemoryLoad = 0;
28525             MEMORYSTATUSEX ms;
28526             GetProcessMemoryLoad(&ms);
28527             dprintf (2, ("Current memory load: %d", ms.dwMemoryLoad));
28528             dwMemoryLoad = ms.dwMemoryLoad;
28529
28530             size_t final_total = 
28531                 trim_youngest_desired (dwMemoryLoad, total_new_allocation, total_min_allocation);
28532             final_new_allocation  = Align ((final_total / num_heaps), get_alignment_constant (TRUE));
28533         }
28534     }
28535
28536     if (final_new_allocation < new_allocation)
28537     {
28538         settings.gen0_reduction_count = 2;
28539     }
28540
28541     return final_new_allocation;
28542 }
28543 #endif //_WIN64 
28544
28545 inline
28546 gc_history_per_heap* gc_heap::get_gc_data_per_heap()
28547 {
28548 #ifdef BACKGROUND_GC
28549     return (settings.concurrent ? 
28550                 (bgc_data_saved_p ? &saved_bgc_data_per_heap : &gc_data_per_heap) :
28551                 &gc_data_per_heap);
28552 #else
28553     return &gc_data_per_heap;
28554 #endif //BACKGROUND_GC
28555 }
28556
28557 void gc_heap::compute_new_dynamic_data (int gen_number)
28558 {
28559     PREFIX_ASSUME(gen_number >= 0);
28560     PREFIX_ASSUME(gen_number <= max_generation);
28561
28562     dynamic_data* dd = dynamic_data_of (gen_number);
28563     generation*   gen = generation_of (gen_number);
28564     size_t        in = (gen_number==0) ? 0 : compute_in (gen_number);
28565
28566     size_t total_gen_size = generation_size (gen_number);
28567     //keep track of fragmentation
28568     dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
28569     dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
28570
28571     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
28572
28573     size_t out = dd_survived_size (dd);
28574
28575     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
28576     gen_data->size_after = total_gen_size;
28577     gen_data->free_list_space_after = generation_free_list_space (gen);
28578     gen_data->free_obj_space_after = generation_free_obj_space (gen);
28579     gen_data->in = in;
28580
28581     if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
28582     {
28583         // When we are in the low latency mode, we can still be
28584         // condemning more than gen1's 'cause of induced GCs.
28585         dd_desired_allocation (dd) = low_latency_alloc;
28586     }
28587     else
28588     {
28589         if (gen_number == 0)
28590         {
28591             //compensate for dead finalizable objects promotion.
28592             //they shoudn't be counted for growth.
28593             size_t final_promoted = 0;
28594             final_promoted = min (promoted_bytes (heap_number), out);
28595             // Prefast: this is clear from above but prefast needs to be told explicitly
28596             PREFIX_ASSUME(final_promoted <= out);
28597
28598             dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
28599             dd_freach_previous_promotion (dd) = final_promoted;
28600             size_t lower_bound = desired_new_allocation  (dd, out-final_promoted, gen_number, 0);
28601             gen_data->out = out - final_promoted;
28602
28603             if (settings.condemned_generation == 0)
28604             {
28605                 //there is no noise.
28606                 dd_desired_allocation (dd) = lower_bound;
28607             }
28608             else
28609             {
28610                 current_gc_data_per_heap->gen_data[max_generation+2] = *gen_data;
28611                 current_gc_data_per_heap->gen_data[max_generation+2].out = out;
28612
28613                 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
28614
28615                 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
28616                 //assert ( lower_bound <= higher_bound);
28617
28618                 //discount the noise. Change the desired allocation
28619                 //only if the previous value is outside of the range.
28620                 if (dd_desired_allocation (dd) < lower_bound)
28621                 {
28622                     dd_desired_allocation (dd) = lower_bound;
28623                 }
28624                 else if (dd_desired_allocation (dd) > higher_bound)
28625                 {
28626                     dd_desired_allocation (dd) = higher_bound;
28627                 }
28628 #if defined (_WIN64) && !defined (MULTIPLE_HEAPS)
28629                 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
28630 #endif //_WIN64 && !MULTIPLE_HEAPS
28631                 trim_youngest_desired_low_memory();
28632                 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
28633             }
28634         }
28635         else
28636         {
28637             gen_data->out = out;
28638             dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
28639         }
28640     }
28641
28642     dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
28643     //update counter
28644     dd_promoted_size (dd) = out;
28645     if (gen_number == max_generation)
28646     {
28647         dd = dynamic_data_of (max_generation+1);
28648         total_gen_size = generation_size (max_generation + 1);
28649         dd_fragmentation (dd) = generation_free_list_space (large_object_generation) + 
28650                                 generation_free_obj_space (large_object_generation);
28651         dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
28652         dd_survived_size (dd) = dd_current_size (dd);
28653         in = 0;
28654         out = dd_current_size (dd);
28655         dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
28656         dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
28657                                            get_alignment_constant (FALSE));
28658
28659         gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
28660         gen_data->size_after = total_gen_size;
28661         gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
28662         gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
28663         gen_data->in = in;
28664         gen_data->out = out;
28665 #ifdef BACKGROUND_GC
28666         end_loh_size = total_gen_size;
28667 #endif //BACKGROUND_GC
28668         //update counter
28669         dd_promoted_size (dd) = out;
28670     }
28671 }
28672
28673 void gc_heap::trim_youngest_desired_low_memory()
28674 {
28675     if (g_low_memory_status)
28676     {
28677         size_t committed_mem = 0;
28678         heap_segment* seg = generation_start_segment (generation_of (max_generation));
28679         while (seg)
28680         {
28681             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
28682             seg = heap_segment_next (seg);
28683         }
28684         seg = generation_start_segment (generation_of (max_generation + 1));
28685         while (seg)
28686         {
28687             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
28688             seg = heap_segment_next (seg);
28689         }
28690
28691         dynamic_data* dd = dynamic_data_of (0);
28692         size_t current = dd_desired_allocation (dd);
28693         size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_gc_size (dd));
28694
28695         dd_desired_allocation (dd) = min (current, candidate);
28696     }
28697 }
28698
28699 void gc_heap::decommit_ephemeral_segment_pages()
28700 {
28701     if (settings.concurrent)
28702     {
28703         return;
28704     }
28705
28706     BOOL trim_p = FALSE;
28707     size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
28708     dynamic_data* dd = dynamic_data_of (0);
28709
28710     if (settings.condemned_generation >= (max_generation-1))
28711     {
28712         trim_p = TRUE;
28713         size_t new_slack_space = 
28714 #ifdef _WIN64
28715                     max(min(min(get_valid_segment_size()/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
28716 #else
28717 #ifdef FEATURE_CORECLR
28718                     dd_desired_allocation (dd);
28719 #else
28720                     dd_max_size (dd);
28721 #endif //FEATURE_CORECLR                                    
28722 #endif //_WIN64
28723
28724         slack_space = min (slack_space, new_slack_space);
28725     }
28726
28727 #ifndef MULTIPLE_HEAPS
28728     size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
28729     size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
28730     size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
28731
28732     if (dd_desired_allocation (dynamic_data_of(0)) > gc_gen0_desired_high)
28733     {
28734         gc_gen0_desired_high = dd_desired_allocation (dynamic_data_of(0)) + extra_space;
28735     }
28736
28737     if (ephemeral_elapsed >= decommit_timeout)
28738     {
28739         slack_space = min (slack_space, gc_gen0_desired_high);
28740
28741         gc_last_ephemeral_decommit_time = dd_time_clock(dynamic_data_of(0));
28742         gc_gen0_desired_high = 0;
28743     }
28744 #endif //!MULTIPLE_HEAPS
28745
28746     size_t saved_slack_space = slack_space;
28747     size_t current_slack_space = ((slack_space < gen0_big_free_spaces) ? 0 : (slack_space - gen0_big_free_spaces));
28748     slack_space = current_slack_space;
28749
28750     dprintf (1, ("ss: %Id->%Id", saved_slack_space, slack_space));
28751     decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);    
28752
28753     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
28754     current_gc_data_per_heap->extra_gen0_committed = (ULONGLONG)(heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment));
28755 }
28756
28757 size_t gc_heap::new_allocation_limit (size_t size, size_t free_size, int gen_number)
28758 {
28759     dynamic_data* dd        = dynamic_data_of (gen_number);
28760     ptrdiff_t           new_alloc = dd_new_allocation (dd);
28761     assert (new_alloc == (ptrdiff_t)Align (new_alloc,
28762                                            get_alignment_constant (!(gen_number == (max_generation+1)))));
28763     size_t        limit     = min (max (new_alloc, (SSIZE_T)size), (SSIZE_T)free_size);
28764     assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
28765     dd_new_allocation (dd) = (new_alloc - limit );
28766     return limit;
28767 }
28768
28769 //This is meant to be called by decide_on_compacting.
28770
28771 size_t gc_heap::generation_fragmentation (generation* gen,
28772                                           generation* consing_gen,
28773                                           BYTE* end)
28774 {
28775     size_t frag;
28776     BYTE* alloc = generation_allocation_pointer (consing_gen);
28777     // If the allocation pointer has reached the ephemeral segment
28778     // fine, otherwise the whole ephemeral segment is considered
28779     // fragmentation
28780     if (in_range_for_segment (alloc, ephemeral_heap_segment))
28781         {
28782             if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
28783                 frag = end - alloc;
28784             else
28785             {
28786                 // case when no survivors, allocated set to beginning
28787                 frag = 0;
28788             }
28789             dprintf (3, ("ephemeral frag: %Id", frag));
28790         }
28791     else
28792         frag = (heap_segment_allocated (ephemeral_heap_segment) -
28793                 heap_segment_mem (ephemeral_heap_segment));
28794     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
28795
28796     PREFIX_ASSUME(seg != NULL);
28797
28798     while (seg != ephemeral_heap_segment)
28799     {
28800         frag += (heap_segment_allocated (seg) -
28801                  heap_segment_plan_allocated (seg));
28802         dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
28803                      (heap_segment_allocated (seg) -
28804                       heap_segment_plan_allocated (seg))));
28805
28806         seg = heap_segment_next_rw (seg);
28807         assert (seg);
28808     }
28809     dprintf (3, ("frag: %Id discounting pinned plugs", frag));
28810     //add the length of the dequeued plug free space
28811     size_t bos = 0;
28812     while (bos < mark_stack_bos)
28813     {
28814         frag += (pinned_len (pinned_plug_of (bos)));
28815         bos++;
28816     }
28817
28818     return frag;
28819 }
28820
28821 // for SOH this returns the total sizes of the generation and its 
28822 // younger generation(s).
28823 // for LOH this returns just LOH size.
28824 size_t gc_heap::generation_sizes (generation* gen)
28825 {
28826     size_t result = 0;
28827     if (generation_start_segment (gen ) == ephemeral_heap_segment)
28828         result = (heap_segment_allocated (ephemeral_heap_segment) -
28829                   generation_allocation_start (gen));
28830     else
28831     {
28832         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
28833
28834         PREFIX_ASSUME(seg != NULL);
28835
28836         while (seg)
28837         {
28838             result += (heap_segment_allocated (seg) -
28839                        heap_segment_mem (seg));
28840             seg = heap_segment_next_in_range (seg);
28841         }
28842     }
28843
28844     return result;
28845 }
28846
28847 BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
28848                                     size_t fragmentation,
28849                                     BOOL& should_expand)
28850 {
28851     BOOL should_compact = FALSE;
28852     should_expand = FALSE;
28853     generation*   gen = generation_of (condemned_gen_number);
28854     dynamic_data* dd = dynamic_data_of (condemned_gen_number);
28855     size_t gen_sizes     = generation_sizes(gen);
28856     float  fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
28857                                     (float (fragmentation) / gen_sizes) );
28858
28859 #ifdef STRESS_HEAP
28860     // for pure GC stress runs we need compaction, for GC stress "mix"
28861     // we need to ensure a better mix of compacting and sweeping collections
28862     if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
28863         && !g_pConfig->IsGCStressMix())
28864         should_compact = TRUE;
28865
28866 #ifdef GC_STATS
28867     // in GC stress "mix" mode, for stress induced collections make sure we 
28868     // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
28869     // against the GC's determination, as it may lead to premature OOMs.
28870     if (g_pConfig->IsGCStressMix() && settings.stress_induced)
28871     {
28872         int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
28873         int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
28874         if (compactions < sweeps / 10)
28875         {
28876             should_compact = TRUE;
28877         }
28878     }
28879 #endif // GC_STATS
28880 #endif //STRESS_HEAP
28881
28882     if (g_pConfig->GetGCForceCompact())
28883         should_compact = TRUE;
28884
28885     if ((condemned_gen_number == max_generation) && last_gc_before_oom)
28886     {
28887         should_compact = TRUE;
28888         last_gc_before_oom = FALSE;
28889     }
28890
28891     if (settings.reason == reason_induced_compacting)
28892     {
28893         dprintf (2, ("induced compacting GC"));
28894         should_compact = TRUE;
28895     }
28896
28897     dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
28898                 fragmentation, (int) (100*fragmentation_burden)));
28899
28900     if (!should_compact)
28901     {
28902         if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
28903         {
28904             dprintf(GTC_LOG, ("compacting due to low ephemeral"));
28905             should_compact = TRUE;
28906             gc_data_per_heap.set_mechanism (gc_compact, compact_low_ephemeral);
28907         }
28908     }
28909
28910     if (should_compact)
28911     {
28912         if ((condemned_gen_number >= (max_generation - 1)))
28913         {
28914             if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
28915             {
28916                 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
28917                 should_expand = TRUE;
28918             }
28919         }
28920     }
28921
28922 #ifdef _WIN64
28923     BOOL high_memory = FALSE;
28924 #endif // _WIN64
28925
28926     if (!should_compact)
28927     {
28928         // We are not putting this in dt_high_frag_p because it's not exactly
28929         // high fragmentation - it's just enough planned fragmentation for us to 
28930         // want to compact. Also the "fragmentation" we are talking about here
28931         // is different from anywhere else.
28932         BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
28933                                 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
28934
28935         if (frag_exceeded)
28936         {
28937 #ifdef BACKGROUND_GC
28938             // do not force compaction if this was a stress-induced GC
28939             IN_STRESS_HEAP(if (!settings.stress_induced))
28940             {
28941 #endif // BACKGROUND_GC
28942             assert (settings.concurrent == FALSE);
28943             dprintf(GTC_LOG,("compacting due to fragmentation"));
28944             should_compact = TRUE;
28945 #ifdef BACKGROUND_GC
28946             }
28947 #endif // BACKGROUND_GC
28948         }
28949
28950 #ifdef _WIN64
28951         // check for high memory situation
28952         if(!should_compact)
28953         {
28954             DWORD num_heaps = 1;
28955 #ifdef MULTIPLE_HEAPS
28956             num_heaps = gc_heap::n_heaps;
28957 #endif // MULTIPLE_HEAPS
28958             
28959             SSIZE_T reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
28960             if((settings.entry_memory_load >= 90) && (settings.entry_memory_load < 97))
28961             {
28962                 if(reclaim_space > (LONGLONG)(min_high_fragmentation_threshold(available_physical_mem, num_heaps)))
28963                 {
28964                     dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
28965                     should_compact = TRUE;
28966                 }
28967                 high_memory = TRUE;
28968             }
28969             else if(settings.entry_memory_load >= 97)
28970             {
28971                 if(reclaim_space > (SSIZE_T)(min_reclaim_fragmentation_threshold(total_physical_mem, num_heaps)))
28972                 {
28973                     dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
28974                     should_compact = TRUE;
28975                 }
28976                 high_memory = TRUE;
28977             }
28978         }
28979 #endif // _WIN64
28980     }
28981
28982     // The purpose of calling ensure_gap_allocation here is to make sure
28983     // that we actually are able to commit the memory to allocate generation
28984     // starts.
28985     if ((should_compact == FALSE) &&
28986         (ensure_gap_allocation (condemned_gen_number) == FALSE))
28987     {
28988         should_compact = TRUE;
28989         gc_data_per_heap.set_mechanism (gc_compact, compact_no_gaps);
28990     }
28991
28992     if (settings.condemned_generation == max_generation)
28993     {
28994         //check the progress
28995         if (
28996 #ifdef _WIN64
28997             (high_memory && !should_compact) ||
28998 #endif // _WIN64
28999             generation_size (max_generation) <= generation_plan_size (max_generation))
29000         {
29001             dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked",
29002                      generation_size (max_generation),
29003                      generation_plan_size (max_generation)));
29004             //no progress -> lock
29005             settings.should_lock_elevation = TRUE;
29006         }
29007     }
29008
29009     dprintf (2, ("will %s", (should_compact ? "compact" : "sweep")));
29010     return should_compact;
29011 }
29012
29013 size_t align_lower_good_size_allocation (size_t size)
29014 {
29015     return (size/64)*64;
29016 }
29017
29018 size_t gc_heap::approximate_new_allocation()
29019 {
29020     dynamic_data* dd0 = dynamic_data_of (0);
29021     return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
29022 }
29023
29024 // After we did a GC we expect to have at least this 
29025 // much space at the end of the segment to satisfy
29026 // a reasonable amount of allocation requests.
29027 size_t gc_heap::end_space_after_gc()
29028 {
29029     return max ((dd_min_gc_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
29030 }
29031
29032 BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
29033 {
29034     BYTE* start = 0;
29035     
29036     if ((tp == tuning_deciding_condemned_gen) ||
29037         (tp == tuning_deciding_compaction))
29038     {
29039         start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
29040         if (settings.concurrent)
29041         {
29042             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)", 
29043                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
29044         }
29045         else
29046         {
29047             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)", 
29048                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
29049         }
29050     }
29051     else if (tp == tuning_deciding_expansion)
29052     {
29053         start = heap_segment_plan_allocated (ephemeral_heap_segment);
29054         dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan", 
29055             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
29056     }
29057     else
29058     {
29059         assert (tp == tuning_deciding_full_gc);
29060         dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)", 
29061             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
29062         start = alloc_allocated;
29063     }
29064     
29065     if (start == 0) // empty ephemeral generations
29066     {
29067         assert (tp == tuning_deciding_expansion);
29068         // if there are no survivors in the ephemeral segment, 
29069         // this should be the beginning of ephemeral segment.
29070         start = generation_allocation_pointer (generation_of (max_generation));
29071         assert (start == heap_segment_mem (ephemeral_heap_segment));
29072     }
29073
29074     if (tp == tuning_deciding_expansion)
29075     {
29076         assert (settings.condemned_generation >= (max_generation-1));
29077         size_t gen0size = approximate_new_allocation();
29078         size_t eph_size = gen0size;
29079
29080         for (int j = 1; j <= max_generation-1; j++)
29081         {
29082             eph_size += 2*dd_min_size (dynamic_data_of(j));
29083         }
29084         
29085         // We must find room for one large object and enough room for gen0size
29086         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
29087         {
29088             dprintf (3, ("Enough room before end of segment"));
29089             return TRUE;
29090         }
29091         else
29092         {
29093             size_t room = align_lower_good_size_allocation
29094                 (heap_segment_reserved (ephemeral_heap_segment) - start);
29095             size_t end_seg = room;
29096
29097             //look at the plug free space
29098             size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
29099             bool large_chunk_found = FALSE;
29100             size_t bos = 0;
29101             BYTE* gen0start = generation_plan_allocation_start (youngest_generation);
29102             dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
29103             if (gen0start == 0)
29104                 return FALSE;
29105             dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
29106                          room, gen0size));
29107             while ((bos < mark_stack_bos) &&
29108                    !((room >= gen0size) && large_chunk_found))
29109             {
29110                 BYTE* plug = pinned_plug (pinned_plug_of (bos));
29111                 if (in_range_for_segment (plug, ephemeral_heap_segment))
29112                 {
29113                     if (plug >= gen0start)
29114                     {
29115                         size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
29116                         room += chunk;
29117                         if (!large_chunk_found)
29118                         {
29119                             large_chunk_found = (chunk >= largest_alloc);
29120                         }
29121                         dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
29122                                      room, large_chunk_found));
29123                     }
29124                 }
29125                 bos++;
29126             }
29127
29128             if (room >= gen0size)
29129             {
29130                 if (large_chunk_found)
29131                 {
29132                     dprintf (3, ("Enough room"));
29133                     return TRUE;
29134                 }
29135                 else
29136                 {
29137                     // now we need to find largest_alloc at the end of the segment.
29138                     if (end_seg >= end_space_after_gc())
29139                     {
29140                         dprintf (3, ("Enough room (may need end of seg)"));
29141                         return TRUE;
29142                     }
29143                 }
29144             }
29145
29146             dprintf (3, ("Not enough room"));
29147                 return FALSE;
29148         }
29149     }
29150     else
29151     {
29152         size_t end_space = 0;
29153         dynamic_data* dd = dynamic_data_of (0);
29154         if ((tp == tuning_deciding_condemned_gen) ||
29155             (tp == tuning_deciding_full_gc))
29156         {
29157             end_space = 2*dd_min_size (dd);
29158         }
29159         else
29160         {
29161             assert (tp == tuning_deciding_compaction);
29162             end_space = approximate_new_allocation();
29163         }
29164
29165         if (!((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space))
29166         {
29167             dprintf (GTC_LOG, ("ephemeral_gen_fit_p: does not fit without compaction"));
29168         }
29169         return ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space);
29170     }
29171 }
29172
29173 CObjectHeader* gc_heap::allocate_large_object (size_t jsize, __int64& alloc_bytes)
29174 {
29175     //create a new alloc context because gen3context is shared.
29176     alloc_context acontext;
29177     acontext.alloc_ptr = 0;
29178     acontext.alloc_limit = 0;
29179     acontext.alloc_bytes = 0;
29180 #ifdef MULTIPLE_HEAPS
29181     acontext.alloc_heap = vm_heap;
29182 #endif //MULTIPLE_HEAPS
29183
29184 #ifdef MARK_ARRAY
29185     BYTE* current_lowest_address = lowest_address;
29186     BYTE* current_highest_address = highest_address;
29187 #ifdef BACKGROUND_GC
29188     if (recursive_gc_sync::background_running_p())
29189     {
29190         current_lowest_address = background_saved_lowest_address;
29191         current_highest_address = background_saved_highest_address;
29192     }
29193 #endif //BACKGROUND_GC
29194 #endif // MARK_ARRAY
29195
29196     SIZE_T maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
29197
29198 #ifdef _WIN64
29199     if (g_pConfig->GetGCAllowVeryLargeObjects())
29200     {
29201         maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
29202     }
29203 #endif
29204
29205     if (jsize >= maxObjectSize)
29206     {
29207         if (g_pConfig->IsGCBreakOnOOMEnabled())
29208         {
29209             DebugBreak();
29210         }
29211
29212 #ifndef FEATURE_REDHAWK
29213         ThrowOutOfMemoryDimensionsExceeded();
29214 #else
29215         return 0;
29216 #endif
29217     }
29218
29219     size_t size = AlignQword (jsize);
29220     int align_const = get_alignment_constant (FALSE);
29221 #ifdef FEATURE_LOH_COMPACTION
29222     size_t pad = Align (loh_padding_obj_size, align_const);
29223 #else
29224     size_t pad = 0;
29225 #endif //FEATURE_LOH_COMPACTION
29226
29227     assert (size >= Align (min_obj_size, align_const));
29228 #ifdef _MSC_VER
29229 #pragma inline_depth(0)
29230 #endif //_MSC_VER
29231     if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
29232     {
29233         return 0;
29234     }
29235
29236 #ifdef _MSC_VER
29237 #pragma inline_depth(20)
29238 #endif //_MSC_VER
29239
29240 #ifdef FEATURE_LOH_COMPACTION
29241     // The GC allocator made a free object already in this alloc context and
29242     // adjusted the alloc_ptr accordingly.
29243 #endif //FEATURE_LOH_COMPACTION
29244
29245     BYTE*  result = acontext.alloc_ptr;
29246
29247     assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
29248
29249     CObjectHeader* obj = (CObjectHeader*)result;
29250
29251 #ifdef MARK_ARRAY
29252     if (recursive_gc_sync::background_running_p())
29253     {
29254         if ((result < current_highest_address) && (result >= current_lowest_address))
29255         {
29256             dprintf (3, ("Clearing mark bit at address %Ix",
29257                      (size_t)(&mark_array [mark_word_of (result)])));
29258
29259             mark_array_clear_marked (result);
29260         }
29261 #ifdef BACKGROUND_GC
29262         //the object has to cover one full mark DWORD
29263         assert (size > mark_word_size);
29264         if (current_c_gc_state == c_gc_state_marking)
29265         {
29266             dprintf (3, ("Concurrent allocation of a large object %Ix",
29267                         (size_t)obj));
29268             //mark the new block specially so we know it is a new object
29269             if ((result < current_highest_address) && (result >= current_lowest_address))
29270             {
29271                 dprintf (3, ("Setting mark bit at address %Ix",
29272                             (size_t)(&mark_array [mark_word_of (result)])));
29273     
29274                 mark_array_set_marked (result);
29275             }
29276         }
29277 #endif //BACKGROUND_GC
29278     }
29279 #endif //MARK_ARRAY
29280
29281     assert (obj != 0);
29282     assert ((size_t)obj == Align ((size_t)obj, align_const));
29283
29284     alloc_bytes += acontext.alloc_bytes;
29285     return obj;
29286 }
29287
29288 void reset_memory (BYTE* o, size_t sizeo)
29289 {
29290 #ifndef FEATURE_PAL
29291     if (sizeo > 128 * 1024)
29292     {
29293         // We cannot reset the memory for the useful part of a free object.
29294         size_t size_to_skip = min_free_list - plug_skew;
29295
29296         size_t page_start = align_on_page ((size_t)(o + size_to_skip));
29297         size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
29298         VirtualAlloc ((char*)page_start, size, MEM_RESET, PAGE_READWRITE);
29299         VirtualUnlock ((char*)page_start, size);
29300     }
29301 #endif //!FEATURE_PAL
29302 }
29303
29304 void gc_heap::reset_large_object (BYTE* o)
29305 {
29306     // If it's a large object, allow the O/S to discard the backing store for these pages.
29307     reset_memory (o, size(o));
29308 }
29309
29310 BOOL gc_heap::large_object_marked (BYTE* o, BOOL clearp)
29311 {
29312     BOOL m = FALSE;
29313     // It shouldn't be necessary to do these comparisons because this is only used for blocking
29314     // GCs and LOH segments cannot be out of range.
29315     if ((o >= lowest_address) && (o < highest_address))
29316     {
29317         if (marked (o))
29318         {
29319             if (clearp)
29320             {
29321                 clear_marked (o);
29322                 if (pinned (o))
29323                     clear_pinned(o);
29324             }
29325             m = TRUE;
29326         }
29327         else
29328             m = FALSE;
29329     }
29330     else
29331         m = TRUE;
29332     return m;
29333 }
29334
29335 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
29336 void gc_heap::record_survived_for_profiler(int condemned_gen_number, BYTE * start_address)
29337 {
29338     size_t profiling_context = 0;
29339
29340     ETW::GCLog::BeginMovedReferences(&profiling_context);
29341
29342     // Now walk the portion of memory that is actually being relocated.
29343     walk_relocation(condemned_gen_number, start_address, profiling_context);
29344
29345 #ifdef FEATURE_LOH_COMPACTION
29346     if (loh_compacted_p)
29347     {
29348         walk_relocation_loh (profiling_context);
29349     }
29350 #endif //FEATURE_LOH_COMPACTION
29351
29352     // Notify the EE-side profiling code that all the references have been traced for
29353     // this heap, and that it needs to flush all cached data it hasn't sent to the
29354     // profiler and release resources it no longer needs.
29355     ETW::GCLog::EndMovedReferences(profiling_context);
29356 }
29357
29358 void gc_heap::notify_profiler_of_surviving_large_objects ()
29359 {
29360     size_t profiling_context = 0;
29361
29362     ETW::GCLog::BeginMovedReferences(&profiling_context);
29363
29364     generation* gen        = large_object_generation;
29365     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));;
29366
29367     PREFIX_ASSUME(seg != NULL);
29368
29369     BYTE* o                = generation_allocation_start (gen);
29370     BYTE* plug_end         = o;
29371     BYTE* plug_start       = o;
29372
29373     // Generally, we can only get here if this is TRUE:
29374     // (CORProfilerTrackGC() || ETW::GCLog::ShouldTrackMovementForEtw())
29375     // But we can't always assert that, as races could theoretically cause GC profiling
29376     // or ETW to turn off just before we get here.  This is harmless (we do checks later
29377     // on, under appropriate locks, before actually calling into profilers), though it's
29378     // a slowdown to determine these plugs for nothing.
29379
29380     while (1)
29381     {
29382         if (o >= heap_segment_allocated (seg))
29383         {
29384             seg = heap_segment_next (seg);
29385             if (seg == 0)
29386                 break;
29387             else
29388                 o = heap_segment_mem (seg);
29389         }
29390         if (large_object_marked(o, FALSE))
29391         {
29392             plug_start = o;
29393
29394             BOOL m = TRUE;
29395             while (m)
29396             {
29397                 o = o + AlignQword (size (o));
29398                 if (o >= heap_segment_allocated (seg))
29399                 {
29400                     break;
29401                 }
29402                 m = large_object_marked (o, FALSE);
29403             }
29404
29405             plug_end = o;
29406
29407             ETW::GCLog::MovedReference(
29408                 plug_start,
29409                 plug_end,
29410                 0,
29411                 profiling_context,
29412                 FALSE);
29413         }
29414         else
29415         {
29416             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
29417             {
29418                 o = o + AlignQword (size (o));
29419             }
29420         }
29421     }
29422     ETW::GCLog::EndMovedReferences(profiling_context);
29423 }
29424 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
29425
29426 #ifdef BACKGROUND_GC
29427
29428 BOOL gc_heap::background_object_marked (BYTE* o, BOOL clearp)
29429 {
29430     BOOL m = FALSE;
29431     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
29432     {
29433         if (mark_array_marked (o))
29434         {
29435             if (clearp)
29436             {
29437                 mark_array_clear_marked (o);
29438                 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
29439                 dprintf (3, ("CM: %Ix", o));
29440             }
29441             m = TRUE;
29442         }
29443         else
29444             m = FALSE;
29445     }
29446     else
29447         m = TRUE;
29448
29449     dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
29450     return m;
29451 }
29452
29453 BYTE* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
29454 {
29455     return
29456         (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
29457 }
29458
29459 void gc_heap::set_mem_verify (BYTE* start, BYTE* end, BYTE b)
29460 {
29461 #ifdef VERIFY_HEAP
29462     if (end > start)
29463     {
29464         if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC) &&
29465            !(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_NO_MEM_FILL))
29466         {
29467             dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
29468             memset (start, b, (end - start));
29469         }
29470     }
29471 #endif //VERIFY_HEAP
29472 }
29473
29474 void gc_heap::generation_delete_heap_segment (generation* gen, 
29475                                               heap_segment* seg,
29476                                               heap_segment* prev_seg,
29477                                               heap_segment* next_seg)
29478 {
29479     dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
29480     if (gen == large_object_generation)
29481     {
29482         heap_segment_next (prev_seg) = next_seg;
29483
29484         dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
29485
29486         heap_segment_next (seg) = freeable_large_heap_segment;
29487         freeable_large_heap_segment = seg;
29488     }
29489     else
29490     {
29491         if (seg == ephemeral_heap_segment)
29492         {
29493             FATAL_GC_ERROR();
29494         }
29495
29496         heap_segment_next (next_seg) = prev_seg;
29497
29498         dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
29499         heap_segment_next (seg) = freeable_small_heap_segment;
29500         freeable_small_heap_segment = seg;
29501     }
29502
29503     decommit_heap_segment (seg);
29504     seg->flags |= heap_segment_flags_decommitted;
29505
29506     set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
29507 }
29508
29509 void gc_heap::process_background_segment_end (heap_segment* seg, 
29510                                           generation* gen,
29511                                           BYTE* last_plug_end,
29512                                           heap_segment* start_seg,
29513                                           BOOL* delete_p)
29514 {
29515     *delete_p = FALSE;
29516     BYTE* allocated = heap_segment_allocated (seg);
29517     BYTE* background_allocated = heap_segment_background_allocated (seg);
29518
29519     dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)", 
29520                 (size_t)heap_segment_mem (seg), background_allocated, allocated));
29521
29522
29523     if (allocated != background_allocated)
29524     {
29525         if (gen == large_object_generation)
29526         {
29527             FATAL_GC_ERROR();
29528         }
29529
29530         dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[", 
29531                     (size_t)last_plug_end, background_allocated));
29532         thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
29533
29534         fix_brick_to_highest (last_plug_end, background_allocated);
29535
29536         // When we allowed fgc's during going through gaps, we could have erased the brick
29537         // that corresponds to bgc_allocated 'cause we had to update the brick there, 
29538         // recover it here.
29539         fix_brick_to_highest (background_allocated, background_allocated);
29540     }
29541     else
29542     {
29543         // by default, if allocated == background_allocated, it can't
29544         // be the ephemeral segment.
29545         if (seg == ephemeral_heap_segment)
29546         {
29547             FATAL_GC_ERROR();
29548         }
29549
29550         if (allocated == heap_segment_mem (seg))
29551         {
29552             // this can happen with LOH segments when multiple threads
29553             // allocate new segments and not all of them were needed to
29554             // satisfy allocation requests.
29555             assert (gen == large_object_generation);
29556         }
29557
29558         if (last_plug_end == heap_segment_mem (seg))
29559         {
29560             dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
29561                         (size_t)allocated, (*delete_p ? "should" : "should not")));
29562
29563             if (seg != start_seg)
29564             {
29565                 *delete_p = TRUE;
29566             }
29567         }
29568         else
29569         {
29570             dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
29571             heap_segment_allocated (seg) = last_plug_end;
29572             set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
29573
29574             decommit_heap_segment_pages (seg, 0);
29575         }
29576     }
29577
29578     dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
29579     bgc_verify_mark_array_cleared (seg);
29580 }
29581
29582 void gc_heap::process_n_background_segments (heap_segment* seg, 
29583                                              heap_segment* prev_seg,
29584                                              generation* gen)
29585 {
29586     assert (gen != large_object_generation);
29587
29588     while (seg)
29589     {
29590         dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
29591         heap_segment* next_seg = heap_segment_next (seg);
29592
29593         if (heap_segment_read_only_p (seg))
29594         {
29595             prev_seg = seg;
29596         }
29597         else
29598         {
29599             if (heap_segment_allocated (seg) == heap_segment_mem (seg))
29600             {
29601                 // This can happen - if we have a LOH segment where nothing survived
29602                 // or a SOH segment allocated by a gen1 GC when BGC was going where 
29603                 // nothing survived last time we did a gen1 GC.
29604                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
29605             }
29606             else
29607             {
29608                 prev_seg = seg;
29609             }
29610         }
29611
29612         verify_soh_segment_list();
29613         seg = next_seg;
29614     }
29615 }
29616
29617 inline
29618 BOOL gc_heap::fgc_should_consider_object (BYTE* o, 
29619                                           heap_segment* seg,
29620                                           BOOL consider_bgc_mark_p, 
29621                                           BOOL check_current_sweep_p, 
29622                                           BOOL check_saved_sweep_p)
29623 {
29624     // the logic for this function must be kept in sync with the analogous function
29625     // in ToolBox\SOS\Strike\gc.cpp
29626
29627     // TRUE means we don't need to check the bgc mark bit
29628     // FALSE means we do.
29629     BOOL no_bgc_mark_p = FALSE;
29630
29631     if (consider_bgc_mark_p)
29632     {
29633         if (check_current_sweep_p && (o < current_sweep_pos))
29634         {
29635             dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
29636             no_bgc_mark_p = TRUE;
29637         }
29638
29639         if (!no_bgc_mark_p)
29640         {
29641             if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
29642             {
29643                 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
29644                 no_bgc_mark_p = TRUE;
29645             }
29646
29647             if (!check_saved_sweep_p)
29648             {
29649                 BYTE* background_allocated = heap_segment_background_allocated (seg);
29650                 // if this was the saved ephemeral segment, check_saved_sweep_p 
29651                 // would've been true.
29652                 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
29653                 // background_allocated could be 0 for the new segments acquired during bgc
29654                 // sweep and we still want no_bgc_mark_p to be true.
29655                 if (o >= background_allocated)
29656                 {
29657                     dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
29658                     no_bgc_mark_p = TRUE;
29659                 }
29660             }
29661         }
29662     }
29663     else
29664     {
29665         no_bgc_mark_p = TRUE;
29666     }
29667
29668     dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
29669     return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
29670 }
29671
29672 // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
29673 // if it's TRUE, check_current_sweep_p tells you if you should consider the
29674 // current sweep position or not.
29675 void gc_heap::should_check_bgc_mark (heap_segment* seg, 
29676                                      BOOL* consider_bgc_mark_p, 
29677                                      BOOL* check_current_sweep_p,
29678                                      BOOL* check_saved_sweep_p)
29679 {
29680     // the logic for this function must be kept in sync with the analogous function
29681     // in ToolBox\SOS\Strike\gc.cpp
29682     *consider_bgc_mark_p = FALSE;
29683     *check_current_sweep_p = FALSE;
29684     *check_saved_sweep_p = FALSE;
29685
29686     if (current_c_gc_state == c_gc_state_planning)
29687     {
29688         // We are doing the current_sweep_pos comparison here because we have yet to 
29689         // turn on the swept flag for the segment but in_range_for_segment will return
29690         // FALSE if the address is the same as reserved.
29691         if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
29692         {
29693             dprintf (3, ("seg %Ix is already swept by bgc"));
29694         }
29695         else
29696         {
29697             *consider_bgc_mark_p = TRUE;
29698
29699             dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
29700
29701             if (seg == saved_sweep_ephemeral_seg)
29702             {
29703                 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
29704                 *check_saved_sweep_p = TRUE;
29705             }
29706
29707             if (in_range_for_segment (current_sweep_pos, seg))
29708             {
29709                 dprintf (3, ("current sweep pos is %Ix and within seg %Ix", 
29710                               current_sweep_pos, seg));
29711                 *check_current_sweep_p = TRUE;
29712             }
29713         }
29714     }
29715 }
29716
29717 void gc_heap::background_ephemeral_sweep()
29718 {
29719     dprintf (3, ("bgc ephemeral sweep"));
29720
29721     int align_const = get_alignment_constant (TRUE);
29722
29723     saved_sweep_ephemeral_seg = ephemeral_heap_segment;
29724     saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
29725
29726     // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
29727     // we thread onto a list first then publish it when we are done.
29728     allocator youngest_free_list;
29729     size_t youngest_free_list_space = 0;
29730     size_t youngest_free_obj_space = 0;
29731
29732     youngest_free_list.clear();
29733
29734     for (int i = 0; i <= (max_generation - 1); i++)
29735     {
29736         generation* gen_to_reset = generation_of (i);
29737         assert (generation_free_list_space (gen_to_reset) == 0);
29738         assert (generation_free_obj_space (gen_to_reset) == 0);
29739     }
29740
29741     for (int i = (max_generation - 1); i >= 0; i--)
29742     {
29743         generation* current_gen = generation_of (i);
29744         BYTE* o = generation_allocation_start (current_gen);
29745         //Skip the generation gap object
29746         o = o + Align(size (o), align_const);
29747         BYTE* end = ((i > 0) ? 
29748                      generation_allocation_start (generation_of (i - 1)) : 
29749                      heap_segment_allocated (ephemeral_heap_segment));
29750
29751         BYTE* plug_end = o;
29752         BYTE* plug_start = o;
29753         BOOL marked_p = FALSE;
29754
29755         while (o < end)
29756         {
29757             marked_p = background_object_marked (o, TRUE);
29758             if (marked_p)
29759             {
29760                 plug_start = o;
29761                 size_t plug_size = plug_start - plug_end;
29762
29763                 if (i >= 1)
29764                 {
29765                     thread_gap (plug_end, plug_size, current_gen);
29766                 }
29767                 else
29768                 {
29769                     if (plug_size > 0)
29770                     {
29771                         make_unused_array (plug_end, plug_size);
29772                         if (plug_size >= min_free_list)
29773                         {
29774                             youngest_free_list_space += plug_size;
29775                             youngest_free_list.thread_item (plug_end, plug_size);
29776                         }
29777                         else
29778                         {
29779                             youngest_free_obj_space += plug_size;
29780                         }
29781                     }
29782                 }
29783
29784                 fix_brick_to_highest (plug_end, plug_start);
29785                 fix_brick_to_highest (plug_start, plug_start);
29786
29787                 BOOL m = TRUE;
29788                 while (m)
29789                 {
29790                     o = o + Align (size (o), align_const);
29791                     if (o >= end)
29792                     {
29793                         break;
29794                     }
29795
29796                     m = background_object_marked (o, TRUE);
29797                 }
29798                 plug_end = o;
29799                 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
29800             }
29801             else
29802             {
29803                 while ((o < end) && !background_object_marked (o, FALSE))
29804                 {
29805                     o = o + Align (size (o), align_const);
29806                 }
29807             }
29808         }
29809
29810         if (plug_end != end)
29811         {
29812             if (i >= 1)
29813             {
29814                 thread_gap (plug_end, end - plug_end, current_gen);
29815                 fix_brick_to_highest (plug_end, end);
29816             }
29817             else
29818             {
29819                 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
29820                 // the following line is temporary.
29821                 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
29822 #ifdef VERIFY_HEAP
29823                 if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
29824                 {
29825                     make_unused_array (plug_end, (end - plug_end));
29826                 }
29827 #endif //VERIFY_HEAP
29828             }
29829         }
29830
29831         dd_fragmentation (dynamic_data_of (i)) = 
29832             generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
29833     }
29834
29835     generation* youngest_gen = generation_of (0);
29836     generation_free_list_space (youngest_gen) = youngest_free_list_space;
29837     generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
29838     dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
29839     generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
29840 }
29841
29842 void gc_heap::background_sweep()
29843 {
29844     Thread* current_thread  = GetThread();
29845     generation* gen         = generation_of (max_generation);
29846     dynamic_data* dd        = dynamic_data_of (max_generation);
29847     // For SOH segments we go backwards.
29848     heap_segment* start_seg = ephemeral_heap_segment;
29849     PREFIX_ASSUME(start_seg != NULL);
29850     heap_segment* fseg      = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
29851     heap_segment* seg       = start_seg;
29852     BYTE* o                 = heap_segment_mem (seg);
29853
29854     heap_segment* prev_seg = heap_segment_next (seg);
29855     int align_const        = get_alignment_constant (TRUE);
29856     if (seg == fseg)
29857     {
29858         assert (o == generation_allocation_start (generation_of (max_generation)));
29859         o = o + Align(size (o), align_const);
29860     }
29861
29862     BYTE* plug_end         = o;
29863     BYTE* plug_start       = o;
29864     next_sweep_obj         = o;
29865     current_sweep_pos      = o;
29866
29867     //BYTE* end              = background_next_end (seg, (gen == large_object_generation));
29868     BYTE* end              = heap_segment_background_allocated (seg);
29869     BOOL delete_p          = FALSE;
29870
29871     //concurrent_print_time_delta ("finished with mark and start with sweep");
29872     concurrent_print_time_delta ("Sw");
29873     dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
29874
29875     //block concurrent allocation for large objects
29876     dprintf (3, ("lh state: planning"));
29877     if (gc_lh_block_event.IsValid())
29878     {
29879         gc_lh_block_event.Reset();
29880     }
29881
29882     for (int i = 0; i <= (max_generation + 1); i++)
29883     {
29884         generation* gen_to_reset = generation_of (i);
29885         generation_allocator (gen_to_reset)->clear();
29886         generation_free_list_space (gen_to_reset) = 0;
29887         generation_free_obj_space (gen_to_reset) = 0;
29888         generation_free_list_allocated (gen_to_reset) = 0;
29889         generation_end_seg_allocated (gen_to_reset) = 0;
29890         generation_condemned_allocated (gen_to_reset) = 0; 
29891         //reset the allocation so foreground gc can allocate into older generation
29892         generation_allocation_pointer (gen_to_reset)= 0;
29893         generation_allocation_limit (gen_to_reset) = 0;
29894         generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
29895     }
29896
29897     fire_bgc_event (BGC2ndNonConEnd);
29898
29899     current_bgc_state = bgc_sweep_soh;
29900     verify_soh_segment_list();
29901
29902     if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
29903         ro_segments_in_range)
29904     {
29905         sweep_ro_segments (generation_start_segment (gen));
29906     }
29907
29908     //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
29909     if (current_c_gc_state != c_gc_state_planning)
29910     {
29911         current_c_gc_state = c_gc_state_planning;
29912     }
29913
29914     concurrent_print_time_delta ("Swe");
29915
29916     heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
29917     PREFIX_ASSUME(loh_seg  != NULL);
29918     while (loh_seg )
29919     {
29920         loh_seg->flags &= ~heap_segment_flags_swept;
29921         heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
29922         loh_seg = heap_segment_next_rw (loh_seg);
29923     }
29924
29925 #ifdef MULTIPLE_HEAPS
29926     bgc_t_join.join(this, gc_join_restart_ee);
29927     if (bgc_t_join.joined())
29928 #endif //MULTIPLE_HEAPS 
29929     {
29930 #ifdef MULTIPLE_HEAPS
29931         dprintf(2, ("Starting BGC threads for resuming EE"));
29932         bgc_t_join.restart();
29933 #endif //MULTIPLE_HEAPS
29934     }
29935
29936     if (heap_number == 0)
29937     {
29938         restart_EE ();
29939     }
29940
29941     fire_bgc_event (BGC2ndConBegin);
29942
29943     background_ephemeral_sweep();
29944
29945 #ifdef MULTIPLE_HEAPS
29946     bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
29947     if (bgc_t_join.joined())
29948 #endif //MULTIPLE_HEAPS
29949     {
29950         leave_spin_lock (&gc_lock);
29951
29952 #ifdef MULTIPLE_HEAPS
29953         dprintf(2, ("Starting BGC threads for BGC sweeping"));
29954         bgc_t_join.restart();
29955 #endif //MULTIPLE_HEAPS
29956     }
29957
29958     disable_preemptive (current_thread, TRUE);
29959
29960     dprintf (2, ("bgs: sweeping gen2 objects"));
29961     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
29962                     (size_t)heap_segment_mem (seg),
29963                     (size_t)heap_segment_allocated (seg),
29964                     (size_t)heap_segment_background_allocated (seg)));
29965
29966     int num_objs = 256;
29967     int current_num_objs = 0;
29968     heap_segment* next_seg = 0;
29969
29970     while (1)
29971     {
29972         if (o >= end)
29973         {
29974             if (gen == large_object_generation)
29975             {
29976                 next_seg = heap_segment_next (seg);
29977             }
29978             else
29979             {
29980                 next_seg = heap_segment_prev (fseg, seg);
29981             }
29982
29983             delete_p = FALSE;
29984
29985             if (!heap_segment_read_only_p (seg))
29986             {
29987                 if (gen == large_object_generation)
29988                 {
29989                     // we can treat all LOH segments as in the bgc domain
29990                     // regardless of whether we saw in bgc mark or not
29991                     // because we don't allow LOH allocations during bgc
29992                     // sweep anyway - the LOH segments can't change.
29993                     process_background_segment_end (seg, gen, plug_end, 
29994                                                     start_seg, &delete_p);
29995                 }
29996                 else
29997                 {
29998                     assert (heap_segment_background_allocated (seg) != 0);
29999                     process_background_segment_end (seg, gen, plug_end, 
30000                                                     start_seg, &delete_p);
30001
30002                     assert (next_seg || !delete_p);
30003                 }
30004             }
30005
30006             if (delete_p)
30007             {
30008                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
30009             }
30010             else
30011             {
30012                 prev_seg = seg;
30013                 dprintf (2, ("seg %Ix has been swept", seg));
30014                 seg->flags |= heap_segment_flags_swept;
30015             }
30016
30017             verify_soh_segment_list();
30018
30019             seg = next_seg;
30020
30021             dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
30022             
30023             if (seg == 0)
30024             {
30025                 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
30026
30027                 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
30028
30029                 if (gen != large_object_generation)
30030                 {
30031                     dprintf (2, ("bgs: sweeping gen3 objects"));
30032                     current_bgc_state = bgc_sweep_loh;
30033                     gen = generation_of (max_generation+1);
30034                     start_seg = heap_segment_rw (generation_start_segment (gen));
30035
30036                     PREFIX_ASSUME(start_seg != NULL);
30037
30038                     seg = start_seg;
30039                     prev_seg = 0;
30040                     o = generation_allocation_start (gen);
30041                     assert (method_table (o) == g_pFreeObjectMethodTable);
30042                     align_const = get_alignment_constant (FALSE);
30043                     o = o + Align(size (o), align_const);
30044                     plug_end = o;
30045                     end = heap_segment_allocated (seg);
30046                     dprintf (2, ("sweeping gen3 objects"));
30047                     generation_free_obj_space (gen) = 0;
30048                     generation_allocator (gen)->clear();
30049                     generation_free_list_space (gen) = 0;
30050
30051                     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
30052                                     (size_t)heap_segment_mem (seg),
30053                                     (size_t)heap_segment_allocated (seg),
30054                                     (size_t)heap_segment_background_allocated (seg)));
30055                 }
30056                 else
30057                     break;
30058             }
30059             else
30060             {
30061                 o = heap_segment_mem (seg);
30062                 if (seg == fseg)
30063                 {
30064                     assert (gen != large_object_generation);
30065                     assert (o == generation_allocation_start (generation_of (max_generation)));
30066                     align_const = get_alignment_constant (TRUE);
30067                     o = o + Align(size (o), align_const);
30068                 }
30069
30070                 plug_end = o;
30071                 current_sweep_pos = o;
30072                 next_sweep_obj = o;
30073                 
30074                 allow_fgc();
30075                 end = background_next_end (seg, (gen == large_object_generation));
30076                 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
30077                                 (size_t)heap_segment_mem (seg),
30078                                 (size_t)heap_segment_allocated (seg),
30079                                 (size_t)heap_segment_background_allocated (seg)));
30080             }
30081         }
30082
30083         if ((o < end) && background_object_marked (o, TRUE))
30084         {
30085             plug_start = o;
30086             if (gen == large_object_generation)
30087             {
30088                 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
30089             }
30090
30091             thread_gap (plug_end, plug_start-plug_end, gen);
30092             if (gen != large_object_generation)
30093             {
30094                 add_gen_free (max_generation, plug_start-plug_end);
30095                 fix_brick_to_highest (plug_end, plug_start);
30096                 // we need to fix the brick for the next plug here 'cause an FGC can
30097                 // happen and can't read a stale brick.
30098                 fix_brick_to_highest (plug_start, plug_start);
30099             }
30100
30101             BOOL m = TRUE;
30102
30103             while (m)
30104             {
30105                 next_sweep_obj = o + Align(size (o), align_const);
30106                 current_num_objs++;
30107                 if (current_num_objs >= num_objs)
30108                 {
30109                     current_sweep_pos = next_sweep_obj;
30110
30111                     allow_fgc();
30112                     current_num_objs = 0;
30113                 }
30114
30115                 o = next_sweep_obj;
30116                 if (o >= end)
30117                 {
30118                     break;
30119                 }
30120
30121                 m = background_object_marked (o, TRUE);
30122             }
30123             plug_end = o;
30124             if (gen != large_object_generation)
30125             {
30126                 add_gen_plug (max_generation, plug_end-plug_start);
30127                 dd_survived_size (dd) += (plug_end - plug_start);
30128             }
30129             dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
30130         }
30131         else
30132         {
30133             while ((o < end) && !background_object_marked (o, FALSE))
30134             {
30135                 next_sweep_obj = o + Align(size (o), align_const);;
30136                 current_num_objs++;
30137                 if (current_num_objs >= num_objs)
30138                 {
30139                     current_sweep_pos = plug_end;
30140                     dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
30141                     allow_fgc();
30142                     current_num_objs = 0;
30143                 }
30144
30145                 o = next_sweep_obj;
30146             }
30147         }
30148     }
30149
30150     size_t total_loh_size = generation_size (max_generation + 1);
30151     size_t total_soh_size = generation_sizes (generation_of (max_generation));
30152
30153     dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
30154
30155     dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id", 
30156         generation_free_list_space (generation_of (max_generation)),
30157         generation_free_obj_space (generation_of (max_generation))));
30158     dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id", 
30159         heap_number,
30160         generation_free_list_space (generation_of (max_generation + 1)),
30161         generation_free_obj_space (generation_of (max_generation + 1))));
30162
30163     fire_bgc_event (BGC2ndConEnd);
30164     concurrent_print_time_delta ("background sweep");
30165     
30166     heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
30167     PREFIX_ASSUME(reset_seg != NULL);
30168
30169     while (reset_seg)
30170     {
30171         heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
30172         heap_segment_background_allocated (reset_seg) = 0;
30173         reset_seg = heap_segment_next_rw (reset_seg);
30174     }
30175
30176     // We calculate dynamic data here because if we wait till we signal the lh event, 
30177     // the allocation thread can change the fragmentation and we may read an intermediate
30178     // value (which can be greater than the generation size). Plus by that time it won't 
30179     // be accurate.
30180     compute_new_dynamic_data (max_generation);
30181
30182     enable_preemptive (current_thread);
30183
30184 #ifdef MULTIPLE_HEAPS
30185     bgc_t_join.join(this, gc_join_set_state_free);
30186     if (bgc_t_join.joined())
30187 #endif //MULTIPLE_HEAPS
30188     {
30189         // TODO: We are using this join just to set the state. Should
30190         // look into eliminating it - check to make sure things that use 
30191         // this state can live with per heap state like should_check_bgc_mark.
30192         current_c_gc_state = c_gc_state_free;
30193
30194 #ifdef MULTIPLE_HEAPS
30195         dprintf(2, ("Starting BGC threads after background sweep phase"));
30196         bgc_t_join.restart();
30197 #endif //MULTIPLE_HEAPS
30198     }
30199
30200     disable_preemptive (current_thread, TRUE);
30201
30202     if (gc_lh_block_event.IsValid())
30203     {
30204         gc_lh_block_event.Set();
30205     }
30206
30207     //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
30208     dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
30209 }
30210 #endif //BACKGROUND_GC
30211
30212 void gc_heap::sweep_large_objects ()
30213 {
30214     //this min value is for the sake of the dynamic tuning.
30215     //so we know that we are not starting even if we have no
30216     //survivors.
30217     generation* gen        = large_object_generation;
30218     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
30219
30220     PREFIX_ASSUME(start_seg != NULL);
30221
30222     heap_segment* seg      = start_seg;
30223     heap_segment* prev_seg = 0;
30224     BYTE* o                = generation_allocation_start (gen);
30225     int align_const        = get_alignment_constant (FALSE);
30226
30227     //Skip the generation gap object
30228     o = o + Align(size (o), align_const);
30229
30230     BYTE* plug_end         = o;
30231     BYTE* plug_start       = o;
30232
30233     generation_allocator (gen)->clear();
30234     generation_free_list_space (gen) = 0;
30235     generation_free_obj_space (gen) = 0;
30236
30237
30238     dprintf (3, ("sweeping large objects"));
30239     dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix", 
30240                  (size_t)seg,
30241                  (size_t)heap_segment_mem (seg),
30242                  (size_t)heap_segment_allocated (seg),
30243                  o));
30244
30245     while (1)
30246     {
30247         if (o >= heap_segment_allocated (seg))
30248         {
30249             heap_segment* next_seg = heap_segment_next (seg);
30250             //delete the empty segment if not the only one
30251             if ((plug_end == heap_segment_mem (seg)) &&
30252                 (seg != start_seg) && !heap_segment_read_only_p (seg))
30253             {
30254                 //prepare for deletion
30255                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
30256                 assert (prev_seg);
30257                 heap_segment_next (prev_seg) = next_seg;
30258                 heap_segment_next (seg) = freeable_large_heap_segment;
30259                 freeable_large_heap_segment = seg;
30260             }
30261             else
30262             {
30263                 if (!heap_segment_read_only_p (seg))
30264                 {
30265                     dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
30266                     heap_segment_allocated (seg) = plug_end;
30267                     decommit_heap_segment_pages (seg, 0);
30268                 }
30269                 prev_seg = seg;
30270             }
30271             seg = next_seg;
30272             if (seg == 0)
30273                 break;
30274             else
30275             {
30276                 o = heap_segment_mem (seg);
30277                 plug_end = o;
30278                 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
30279                              (size_t)heap_segment_mem (seg),
30280                              (size_t)heap_segment_allocated (seg)));
30281             }
30282         }
30283         if (large_object_marked(o, TRUE))
30284         {
30285             plug_start = o;
30286             //everything between plug_end and plug_start is free
30287             thread_gap (plug_end, plug_start-plug_end, gen);
30288
30289             BOOL m = TRUE;
30290             while (m)
30291             {
30292                 o = o + AlignQword (size (o));
30293                 if (o >= heap_segment_allocated (seg))
30294                 {
30295                     break;
30296                 }
30297                 m = large_object_marked (o, TRUE);
30298             }
30299             plug_end = o;
30300             dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
30301         }
30302         else
30303         {
30304             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
30305             {
30306                 o = o + AlignQword (size (o));
30307             }
30308         }
30309     }
30310
30311     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
30312
30313     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
30314 }
30315
30316 void gc_heap::relocate_in_large_objects ()
30317 {
30318     relocate_args args;
30319     args.low = gc_low;
30320     args.high = gc_high;
30321     args.last_plug = 0;
30322
30323     generation* gen = large_object_generation;
30324
30325     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30326
30327     PREFIX_ASSUME(seg != NULL);
30328
30329     BYTE* o = generation_allocation_start (gen);
30330
30331     while (1)
30332     {
30333         if (o >= heap_segment_allocated (seg))
30334         {
30335             seg = heap_segment_next_rw (seg);
30336             if (seg == 0)
30337                 break;
30338             else
30339             {
30340                 o = heap_segment_mem (seg);
30341             }
30342         }
30343         while (o < heap_segment_allocated (seg))
30344         {
30345             check_class_object_demotion (o);
30346             if (contain_pointers (o))
30347             {
30348                 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
30349                 go_through_object_nostart (method_table (o), o, size(o), pval,
30350                         {
30351                             reloc_survivor_helper (pval);
30352                         });
30353             }
30354             o = o + AlignQword (size (o));
30355         }
30356     }
30357 }
30358
30359 void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
30360                                                     BOOL relocating)
30361 {
30362     BYTE*         low               = gc_low;
30363     size_t        end_card          = 0;
30364     generation*   oldest_gen        = generation_of (max_generation+1);
30365     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
30366
30367     PREFIX_ASSUME(seg != NULL);
30368
30369     BYTE*         beg               = generation_allocation_start (oldest_gen);
30370     BYTE*         end               = heap_segment_allocated (seg);
30371
30372     size_t  cg_pointers_found = 0;
30373
30374     size_t  card_word_end = (card_of (align_on_card_word (end)) /
30375                              card_word_width);
30376
30377     size_t      n_eph             = 0;
30378     size_t      n_gen             = 0;
30379     size_t        n_card_set        = 0;
30380     BYTE*    next_boundary = (relocating ?
30381                               generation_plan_allocation_start (generation_of (max_generation -1)) :
30382                               ephemeral_low);
30383
30384     BYTE*    nhigh         = (relocating ?
30385                               heap_segment_plan_allocated (ephemeral_heap_segment) :
30386                               ephemeral_high);
30387
30388     BOOL          foundp            = FALSE;
30389     BYTE*         start_address     = 0;
30390     BYTE*         limit             = 0;
30391     size_t        card              = card_of (beg);
30392     BYTE*         o                 = beg;
30393 #ifdef BACKGROUND_GC
30394     BOOL consider_bgc_mark_p        = FALSE;
30395     BOOL check_current_sweep_p      = FALSE;
30396     BOOL check_saved_sweep_p        = FALSE;
30397     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
30398 #endif //BACKGROUND_GC
30399
30400     size_t total_cards_cleared = 0;
30401
30402     //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
30403     dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
30404     while (1)
30405     {
30406         if ((o < end) && (card_of(o) > card))
30407         {
30408             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
30409             if (cg_pointers_found == 0)
30410             {
30411                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
30412                 clear_cards (card, card_of((BYTE*)o));
30413                 total_cards_cleared += (card_of((BYTE*)o) - card);
30414             }
30415             n_eph +=cg_pointers_found;
30416             cg_pointers_found = 0;
30417             card = card_of ((BYTE*)o);
30418         }
30419         if ((o < end) &&(card >= end_card))
30420         {
30421             foundp = find_card (card_table, card, card_word_end, end_card);
30422             if (foundp)
30423             {
30424                 n_card_set+= end_card - card;
30425                 start_address = max (beg, card_address (card));
30426             }
30427             limit = min (end, card_address (end_card));
30428         }
30429         if ((!foundp) || (o >= end) || (card_address (card) >= end))
30430         {
30431             if ((foundp) && (cg_pointers_found == 0))
30432             {
30433                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
30434                            (size_t)card_address(card+1)));
30435                 clear_cards (card, card+1);
30436                 total_cards_cleared += 1;
30437             }
30438             n_eph +=cg_pointers_found;
30439             cg_pointers_found = 0;
30440             if ((seg = heap_segment_next_rw (seg)) != 0)
30441             {
30442 #ifdef BACKGROUND_GC
30443                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
30444 #endif //BACKGROUND_GC
30445                 beg = heap_segment_mem (seg);
30446                 end = compute_next_end (seg, low);
30447                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
30448                 card = card_of (beg);
30449                 o  = beg;
30450                 end_card = 0;
30451                 continue;
30452             }
30453             else
30454             {
30455                 break;
30456             }
30457         }
30458
30459         assert (card_set_p (card));
30460         {
30461             dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
30462                        card, (size_t)o, (size_t)limit));
30463
30464             assert (Align (size (o)) >= Align (min_obj_size));
30465             size_t s = size (o);
30466             BYTE* next_o =  o + AlignQword (s);
30467             Prefetch (next_o);
30468
30469             while (o < limit)
30470             {
30471                 s = size (o);
30472                 assert (Align (s) >= Align (min_obj_size));
30473                 next_o =  o + AlignQword (s);
30474                 Prefetch (next_o);
30475
30476                 dprintf (4, ("|%Ix|", (size_t)o));
30477                 if (next_o < start_address)
30478                 {
30479                     goto end_object;
30480                 }
30481
30482 #ifdef BACKGROUND_GC
30483                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
30484                 {
30485                     goto end_object;
30486                 }
30487 #endif //BACKGROUND_GC
30488
30489 #ifdef COLLECTIBLE_CLASS
30490                 if (is_collectible(o))
30491                 {
30492                     BOOL passed_end_card_p = FALSE;
30493
30494                     if (card_of (o) > card)
30495                     {
30496                         passed_end_card_p = card_transition (o, end, card_word_end,
30497                             cg_pointers_found, 
30498                             n_eph, n_card_set,
30499                             card, end_card,
30500                             foundp, start_address,
30501                             limit, total_cards_cleared);
30502                     }
30503
30504                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
30505                     {
30506                         // card is valid and it covers the head of the object
30507                         if (fn == &gc_heap::relocate_address)
30508                         {
30509                             keep_card_live (o, n_gen, cg_pointers_found);
30510                         }
30511                         else
30512                         {
30513                             BYTE* class_obj = get_class_object (o);
30514                             mark_through_cards_helper (&class_obj, n_gen,
30515                                                     cg_pointers_found, fn,
30516                                                     nhigh, next_boundary);
30517                         }
30518                     }
30519
30520                     if (passed_end_card_p)
30521                     {
30522                         if (foundp && (card_address (card) < next_o))
30523                         {
30524                             goto go_through_refs;
30525                         }
30526                         else 
30527                         {
30528                             goto end_object;
30529                         }
30530                     }
30531                 }
30532
30533 go_through_refs:
30534 #endif //COLLECTIBLE_CLASS
30535
30536                 if (contain_pointers (o))
30537                 {
30538                     dprintf(3,("Going through %Ix", (size_t)o));
30539
30540                     go_through_object (method_table(o), o, s, poo,
30541                                        start_address, use_start, (o + s),
30542                        {
30543                            if (card_of ((BYTE*)poo) > card)
30544                            {
30545                                 BOOL passed_end_card_p  = card_transition ((BYTE*)poo, end,
30546                                         card_word_end,
30547                                         cg_pointers_found, 
30548                                         n_eph, n_card_set,
30549                                         card, end_card,
30550                                         foundp, start_address,
30551                                         limit, total_cards_cleared);
30552
30553                                 if (passed_end_card_p)
30554                                 {
30555                                     if (foundp && (card_address (card) < next_o))
30556                                     {
30557                                         //new_start();
30558                                         {
30559                                             if (ppstop <= (BYTE**)start_address)
30560                                             {break;}
30561                                             else if (poo < (BYTE**)start_address)
30562                                             {poo = (BYTE**)start_address;}
30563                                         }
30564                                     }
30565                                     else
30566                                     {
30567                                         goto end_object;
30568                                     }
30569                                 }
30570                             }
30571
30572                            mark_through_cards_helper (poo, n_gen,
30573                                                       cg_pointers_found, fn,
30574                                                       nhigh, next_boundary);
30575                        }
30576                         );
30577                 }
30578
30579             end_object:
30580                 o = next_o;
30581             }
30582
30583         }
30584     }
30585
30586     // compute the efficiency ratio of the card table
30587     if (!relocating)
30588     {
30589         generation_skip_ratio = min (((n_eph > 800) ?
30590                                       (int)(((float)n_gen / (float)n_eph) * 100) : 100),
30591                                      generation_skip_ratio);
30592
30593         dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d", 
30594              n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
30595     }
30596     else
30597     {
30598         dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d", 
30599              n_eph, n_gen, n_card_set, generation_skip_ratio));
30600     }
30601 }
30602
30603 void gc_heap::descr_segment (heap_segment* seg )
30604 {
30605
30606 #ifdef TRACE_GC
30607     BYTE*  x = heap_segment_mem (seg);
30608     while (x < heap_segment_allocated (seg))
30609     {
30610         dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
30611         x = x + Align(size (x));
30612     }
30613 #endif //TRACE_GC
30614 }
30615
30616 void gc_heap::descr_card_table ()
30617 {
30618 #ifdef TRACE_GC
30619     if (trace_gc && (print_level >= 4))
30620     {
30621         ptrdiff_t  min = -1;
30622         dprintf(3,("Card Table set at: "));
30623         for (size_t i = card_of (lowest_address); i < card_of (highest_address); i++)
30624         {
30625             if (card_set_p (i))
30626             {
30627                 if ((min == -1))
30628                 {
30629                     min = i;
30630                 }
30631             }
30632             else
30633             {
30634                 if (! ((min == -1)))
30635                 {
30636                     dprintf (3,("[%Ix %Ix[, ",
30637                             (size_t)card_address (min), (size_t)card_address (i)));
30638                     min = -1;
30639                 }
30640             }
30641         }
30642     }
30643 #endif //TRACE_GC
30644 }
30645
30646 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
30647 void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
30648 {
30649 #ifdef MULTIPLE_HEAPS
30650     int n_heaps = GCHeap::GetGCHeap()->GetNumberOfHeaps ();
30651     for (int i = 0; i < n_heaps; i++)
30652     {
30653         gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
30654 #else //MULTIPLE_HEAPS
30655     {
30656         gc_heap* hp = NULL;
30657 #ifdef _PREFAST_
30658         // prefix complains about us dereferencing hp in wks build even though we only access static members
30659         // this way. not sure how to shut it up except for this ugly workaround:
30660         PREFIX_ASSUME(hp != NULL);
30661 #endif // _PREFAST_
30662 #endif //MULTIPLE_HEAPS
30663
30664         int curr_gen_number0 = max_generation+1;
30665         while (curr_gen_number0 >= 0)
30666         {
30667             generation* gen = hp->generation_of (curr_gen_number0);
30668             heap_segment* seg = generation_start_segment (gen);
30669             while (seg && (seg != hp->ephemeral_heap_segment))
30670             {
30671                 assert (curr_gen_number0 > 0);
30672
30673                 // report bounds from heap_segment_mem (seg) to
30674                 // heap_segment_allocated (seg);
30675                 // for generation # curr_gen_number0
30676                 // for heap # heap_no
30677
30678                 fn(context, curr_gen_number0, heap_segment_mem (seg),
30679                                               heap_segment_allocated (seg),
30680                                               curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
30681
30682                 seg = heap_segment_next (seg);
30683             }
30684             if (seg)
30685             {
30686                 assert (seg == hp->ephemeral_heap_segment);
30687                 assert (curr_gen_number0 <= max_generation);
30688                 //
30689                 if ((curr_gen_number0 == max_generation))
30690                 {
30691                     if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
30692                     {
30693                         // report bounds from heap_segment_mem (seg) to
30694                         // generation_allocation_start (generation_of (max_generation-1))
30695                         // for heap # heap_number
30696
30697                         fn(context, curr_gen_number0, heap_segment_mem (seg),
30698                                                       generation_allocation_start (hp->generation_of (max_generation-1)),
30699                                                       generation_allocation_start (hp->generation_of (max_generation-1)) );
30700                     }
30701                 }
30702                 else if (curr_gen_number0 != 0)
30703                 {
30704                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
30705                     // to generation_allocation_start (generation_of (curr_gen_number0-1))
30706                     // for heap # heap_number
30707
30708                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
30709                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
30710                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
30711                 }
30712                 else
30713                 {
30714                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
30715                     // to heap_segment_allocated (ephemeral_heap_segment);
30716                     // for heap # heap_number
30717
30718                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
30719                                                   heap_segment_allocated (hp->ephemeral_heap_segment),
30720                                                   heap_segment_reserved (hp->ephemeral_heap_segment) );
30721                 }
30722             }
30723             curr_gen_number0--;
30724         }
30725     }
30726 }
30727 #endif // defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
30728
30729 #ifdef TRACE_GC
30730 // Note that when logging is on it can take a long time to go through the free items.
30731 void gc_heap::print_free_list (int gen, heap_segment* seg)
30732 {
30733 /*
30734     if (settings.concurrent == FALSE)
30735     {
30736         BYTE* seg_start = heap_segment_mem (seg);
30737         BYTE* seg_end = heap_segment_allocated (seg);
30738
30739         dprintf (3, ("Free list in seg %Ix:", seg_start));
30740
30741         size_t total_free_item = 0;
30742
30743         allocator* gen_allocator = generation_allocator (generation_of (gen));
30744         for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
30745         {
30746             BYTE* fo = gen_allocator->alloc_list_head_of (b);
30747             while (fo)
30748             {
30749                 if (fo >= seg_start && fo < seg_end)
30750                 {
30751                     total_free_item++;
30752
30753                     size_t free_item_len = size(fo);
30754
30755                     dprintf (3, ("[%Ix, %Ix[:%Id",
30756                                  (size_t)fo,
30757                                  (size_t)(fo + free_item_len),
30758                                  free_item_len));
30759                 }
30760
30761                 fo = free_list_slot (fo);
30762             }
30763         }
30764
30765         dprintf (3, ("total %Id free items", total_free_item));
30766     }
30767 */
30768 }
30769 #endif //TRACE_GC
30770
30771 void gc_heap::descr_generations (BOOL begin_gc_p)
30772 {
30773 #ifdef STRESS_LOG
30774     if (StressLog::StressLogOn(LF_GC, LL_INFO10))
30775     {
30776         gc_heap* hp = 0;
30777 #ifdef MULTIPLE_HEAPS
30778         hp= this;
30779 #endif //MULTIPLE_HEAPS
30780
30781         STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
30782         for (int n = max_generation; n >= 0; --n)
30783         {
30784             STRESS_LOG4(LF_GC, LL_INFO10, "    Generation %d [%p, %p] cur = %p\n",
30785                     n,
30786                     generation_allocation_start(generation_of(n)),
30787                     generation_allocation_limit(generation_of(n)),
30788                     generation_allocation_pointer(generation_of(n)));
30789
30790             heap_segment* seg = generation_start_segment(generation_of(n));
30791             while (seg)
30792             {
30793                 STRESS_LOG4(LF_GC, LL_INFO10, "        Segment mem %p alloc = %p used %p committed %p\n",
30794                         heap_segment_mem(seg),
30795                         heap_segment_allocated(seg),
30796                         heap_segment_used(seg),
30797                         heap_segment_committed(seg));
30798                 seg = heap_segment_next(seg);
30799             }
30800         }
30801     }
30802 #endif  // STRESS_LOG
30803
30804 #ifdef TRACE_GC
30805     dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
30806              (size_t) lowest_address, (size_t) highest_address));
30807 #ifdef BACKGROUND_GC
30808     dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
30809              (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
30810 #endif //BACKGROUND_GC
30811
30812     if (heap_number == 0)
30813     {
30814         dprintf (1, ("soh size: %Id", get_total_heap_size()));
30815     }
30816
30817     int curr_gen_number = max_generation+1;
30818     while (curr_gen_number >= 0)
30819     {
30820         size_t total_gen_size = generation_size (curr_gen_number);
30821 #ifdef SIMPLE_DPRINTF
30822         dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
30823                       (begin_gc_p ? "BEG" : "END"),
30824                       settings.condemned_generation,
30825                       curr_gen_number,
30826                       total_gen_size,
30827                       dd_fragmentation (dynamic_data_of (curr_gen_number)),
30828                       generation_free_list_space (generation_of (curr_gen_number)),
30829                       generation_free_obj_space (generation_of (curr_gen_number)),
30830                       (total_gen_size ? 
30831                         (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
30832                         0),
30833                       (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
30834                       (settings.heap_expansion ? "(EX)" : " "),
30835                       (settings.promotion ? "Promotion" : "NoPromotion")));
30836 #else
30837         dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
30838                       curr_gen_number,
30839                       size (generation_allocation_start (generation_of (curr_gen_number))),
30840                       total_gen_size,
30841                       dd_fragmentation (dynamic_data_of (curr_gen_number))));
30842 #endif //SIMPLE_DPRINTF
30843
30844         generation* gen = generation_of (curr_gen_number);
30845         heap_segment* seg = generation_start_segment (gen);
30846         while (seg && (seg != ephemeral_heap_segment))
30847         {
30848             dprintf (GTC_LOG,("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
30849                          curr_gen_number,
30850                          (size_t)heap_segment_mem (seg),
30851                         (size_t)heap_segment_allocated (seg),
30852                         (size_t)heap_segment_committed (seg),
30853                         (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
30854                         (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
30855             print_free_list (curr_gen_number, seg);
30856             seg = heap_segment_next (seg);
30857         }
30858         if (seg && (seg != generation_start_segment (gen)))
30859         {
30860             dprintf (GTC_LOG,("g%d: [%Ix %Ix[",
30861                          curr_gen_number,
30862                          (size_t)heap_segment_mem (seg),
30863                          (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
30864             print_free_list (curr_gen_number, seg);
30865
30866         }
30867         else if (seg)
30868         {
30869             dprintf (GTC_LOG,("g%d: [%Ix %Ix[",
30870                          curr_gen_number,
30871                          (size_t)generation_allocation_start (generation_of (curr_gen_number)),
30872                          (size_t)(((curr_gen_number == 0)) ?
30873                                   (heap_segment_allocated
30874                                    (generation_start_segment
30875                                     (generation_of (curr_gen_number)))) :
30876                                   (generation_allocation_start
30877                                    (generation_of (curr_gen_number - 1))))
30878                          ));
30879             print_free_list (curr_gen_number, seg);
30880         }
30881         curr_gen_number--;
30882     }
30883
30884 #endif //TRACE_GC
30885 }
30886
30887 #undef TRACE_GC
30888
30889 //#define TRACE_GC
30890
30891 //-----------------------------------------------------------------------------
30892 //
30893 //                                  VM Specific support
30894 //
30895 //-----------------------------------------------------------------------------
30896
30897
30898 #ifdef TRACE_GC
30899
30900  unsigned int PromotedObjectCount  = 0;
30901  unsigned int CreatedObjectCount       = 0;
30902  unsigned int AllocDuration            = 0;
30903  unsigned int AllocCount               = 0;
30904  unsigned int AllocBigCount            = 0;
30905  unsigned int AllocSmallCount      = 0;
30906  unsigned int AllocStart             = 0;
30907 #endif //TRACE_GC
30908
30909 //Static member variables.
30910 VOLATILE(BOOL)    GCHeap::GcInProgress            = FALSE;
30911 //GCTODO
30912 //CMCSafeLock*      GCHeap::fGcLock;
30913 CLREvent            *GCHeap::WaitForGCEvent         = NULL;
30914 //GCTODO
30915 #ifdef TRACE_GC
30916 unsigned int       GCHeap::GcDuration;
30917 #endif //TRACE_GC
30918 unsigned            GCHeap::GcCondemnedGeneration   = 0;
30919 size_t              GCHeap::totalSurvivedSize       = 0;
30920 #ifdef FEATURE_PREMORTEM_FINALIZATION
30921 CFinalize*          GCHeap::m_Finalize              = 0;
30922 BOOL                GCHeap::GcCollectClasses        = FALSE;
30923 VOLATILE(LONG)      GCHeap::m_GCFLock               = 0;
30924
30925 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
30926 #ifdef STRESS_HEAP
30927 #ifdef BACKGROUND_GC
30928 int                 GCHeap::gc_stress_fgcs_in_bgc   = 0;
30929 #endif // BACKGROUND_GC
30930 #ifndef MULTIPLE_HEAPS
30931 OBJECTHANDLE        GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
30932 int                 GCHeap::m_CurStressObj          = 0;
30933 #endif // !MULTIPLE_HEAPS
30934 #endif // STRESS_HEAP
30935 #endif // FEATURE_REDHAWK
30936
30937 #endif //FEATURE_PREMORTEM_FINALIZATION
30938 inline
30939 static void spin_lock ()
30940 {
30941     enter_spin_lock_noinstru (&m_GCLock);
30942 }
30943
30944 inline
30945 void EnterAllocLock()
30946 {
30947 #if defined(_TARGET_X86_)
30948     __asm {
30949         inc dword ptr m_GCLock
30950         jz gotit
30951         call spin_lock
30952             gotit:
30953     }
30954 #else //_TARGET_X86_
30955     spin_lock();
30956 #endif //_TARGET_X86_
30957 }
30958
30959 inline
30960 void LeaveAllocLock()
30961 {
30962     // Trick this out
30963     leave_spin_lock_noinstru (&m_GCLock);
30964 }
30965
30966 class AllocLockHolder
30967 {
30968 public:
30969     AllocLockHolder()
30970     {
30971         EnterAllocLock();
30972     }
30973
30974     ~AllocLockHolder()
30975     {
30976         LeaveAllocLock();
30977     }
30978 };
30979
30980 // An explanation of locking for finalization:
30981 //
30982 // Multiple threads allocate objects.  During the allocation, they are serialized by
30983 // the AllocLock above.  But they release that lock before they register the object
30984 // for finalization.  That's because there is much contention for the alloc lock, but
30985 // finalization is presumed to be a rare case.
30986 //
30987 // So registering an object for finalization must be protected by the FinalizeLock.
30988 //
30989 // There is another logical queue that involves finalization.  When objects registered
30990 // for finalization become unreachable, they are moved from the "registered" queue to
30991 // the "unreachable" queue.  Note that this only happens inside a GC, so no other
30992 // threads can be manipulating either queue at that time.  Once the GC is over and
30993 // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
30994 // queue and call their finalizers.  This dequeue operation is also protected with
30995 // the finalize lock.
30996 //
30997 // At first, this seems unnecessary.  Only one thread is ever enqueuing or dequeuing
30998 // on the unreachable queue (either the GC thread during a GC or the finalizer thread
30999 // when a GC is not in progress).  The reason we share a lock with threads enqueuing
31000 // on the "registered" queue is that the "registered" and "unreachable" queues are
31001 // interrelated.
31002 //
31003 // They are actually two regions of a longer list, which can only grow at one end.
31004 // So to enqueue an object to the "registered" list, you actually rotate an unreachable
31005 // object at the boundary between the logical queues, out to the other end of the
31006 // unreachable queue -- where all growing takes place.  Then you move the boundary
31007 // pointer so that the gap we created at the boundary is now on the "registered"
31008 // side rather than the "unreachable" side.  Now the object can be placed into the
31009 // "registered" side at that point.  This is much more efficient than doing moves
31010 // of arbitrarily long regions, but it causes the two queues to require a shared lock.
31011 //
31012 // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock.  Instead, it relies
31013 // on the fact that the lock will only be taken for a brief period and that it will
31014 // never provoke or allow a GC while the lock is held.  This is critical.  If the
31015 // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
31016 // allow a GC), then the Alloc client would have to GC protect a finalizable object
31017 // to protect against that eventuality.  That is too slow!
31018
31019
31020
31021 BOOL IsValidObject99(BYTE *pObject)
31022 {
31023 #ifdef VERIFY_HEAP
31024     if (!((CObjectHeader*)pObject)->IsFree())
31025         ((CObjectHeader *) pObject)->Validate();
31026 #endif //VERIFY_HEAP
31027     return(TRUE);
31028 }
31029
31030 #ifdef BACKGROUND_GC 
31031 BOOL gc_heap::bgc_mark_array_range (heap_segment* seg, 
31032                                     BOOL whole_seg_p,
31033                                     BYTE** range_beg,
31034                                     BYTE** range_end)
31035 {
31036     BYTE* seg_start = heap_segment_mem (seg);
31037     BYTE* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
31038
31039     if ((seg_start < background_saved_highest_address) &&
31040         (seg_end > background_saved_lowest_address))
31041     {
31042         *range_beg = max (seg_start, background_saved_lowest_address);
31043         *range_end = min (seg_end, background_saved_highest_address);
31044         return TRUE;
31045     }
31046     else
31047     {
31048         return FALSE;
31049     }
31050 }
31051
31052 void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
31053 {
31054 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31055     if (recursive_gc_sync::background_running_p() && g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
31056     {
31057         BYTE* range_beg = 0;
31058         BYTE* range_end = 0;
31059
31060         if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
31061         {
31062             size_t  markw = mark_word_of (range_beg);
31063             size_t  markw_end = mark_word_of (range_end);
31064             while (markw < markw_end)
31065             {
31066                 if (mark_array [markw])
31067                 {
31068                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
31069                                     markw, mark_array [markw], mark_word_address (markw)));
31070                     FATAL_GC_ERROR();
31071                 }
31072                 markw++;
31073             }
31074             BYTE* p = mark_word_address (markw_end);
31075             while (p < range_end)
31076             {
31077                 assert (!(mark_array_marked (p)));
31078                 p++;
31079             }
31080         }
31081     }
31082 #endif //VERIFY_HEAP && MARK_ARRAY
31083 }
31084
31085 void gc_heap::verify_mark_bits_cleared (BYTE* obj, size_t s)
31086 {
31087 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31088     size_t start_mark_bit = mark_bit_of (obj) + 1;
31089     size_t end_mark_bit = mark_bit_of (obj + s);
31090     unsigned int startbit = mark_bit_bit (start_mark_bit);
31091     unsigned int endbit = mark_bit_bit (end_mark_bit);
31092     size_t startwrd = mark_bit_word (start_mark_bit);
31093     size_t endwrd = mark_bit_word (end_mark_bit);
31094     unsigned int result = 0;
31095
31096     unsigned int firstwrd = ~(lowbits (~0, startbit));
31097     unsigned int lastwrd = ~(highbits (~0, endbit));
31098
31099     if (startwrd == endwrd)
31100     {
31101         unsigned int wrd = firstwrd & lastwrd;
31102         result = mark_array[startwrd] & wrd;
31103         if (result)
31104         {
31105             FATAL_GC_ERROR();
31106         }
31107         return;
31108     }
31109
31110     // verify the first mark word is cleared.
31111     if (startbit)
31112     {
31113         result = mark_array[startwrd] & firstwrd;
31114         if (result)
31115         {
31116             FATAL_GC_ERROR();
31117         }
31118         startwrd++;
31119     }
31120
31121     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
31122     {
31123         result = mark_array[wrdtmp];
31124         if (result)
31125         {
31126             FATAL_GC_ERROR();
31127         }
31128     }
31129
31130     // set the last mark word.
31131     if (endbit)
31132     {
31133         result = mark_array[endwrd] & lastwrd;
31134         if (result)
31135         {
31136             FATAL_GC_ERROR();
31137         }
31138     }
31139 #endif //VERIFY_HEAP && MARK_ARRAY
31140 }
31141
31142 void gc_heap::clear_all_mark_array()
31143 {
31144 #ifdef MARK_ARRAY
31145     //size_t num_dwords_written = 0;
31146     //LARGE_INTEGER ts;
31147     //if (!QueryPerformanceCounter(&ts))
31148     //    FATAL_GC_ERROR();
31149     //
31150     //size_t begin_time = (size_t) (ts.QuadPart/(qpf.QuadPart/1000));
31151
31152     generation* gen = generation_of (max_generation);
31153     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31154     
31155     while (1)
31156     {
31157         if (seg == 0)
31158         {
31159             if (gen != large_object_generation)
31160             {
31161                 gen = generation_of (max_generation+1);
31162                 seg = heap_segment_rw (generation_start_segment (gen));
31163             }
31164             else
31165             {
31166                 break;
31167             }
31168         }
31169
31170         BYTE* range_beg = 0;
31171         BYTE* range_end = 0;
31172
31173         if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
31174         { 
31175             size_t markw = mark_word_of (range_beg);
31176             size_t markw_end = mark_word_of (range_end);
31177             size_t size_total = (markw_end - markw) * sizeof (DWORD);
31178             //num_dwords_written = markw_end - markw;
31179             size_t size = 0;
31180             size_t size_left = 0;
31181
31182             assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
31183
31184             if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
31185             {
31186                 size = (size_total & ~(sizeof(PTR_PTR) - 1));
31187                 size_left = size_total - size;
31188                 assert ((size_left & (sizeof (DWORD) - 1)) == 0);
31189             }
31190             else
31191             {
31192                 size = size_total;
31193             }
31194
31195             memclr ((BYTE*)&mark_array[markw], size);
31196
31197             if (size_left != 0)
31198             {
31199                 DWORD* markw_to_clear = &mark_array[markw + size / sizeof (DWORD)];
31200                 for (size_t i = 0; i < (size_left / sizeof (DWORD)); i++)
31201                 {
31202                     *markw_to_clear = 0;
31203                     markw_to_clear++;
31204                 }
31205             }
31206         }
31207
31208         seg = heap_segment_next_rw (seg);
31209     }
31210
31211     //if (!QueryPerformanceCounter(&ts))
31212     //    FATAL_GC_ERROR();
31213     //
31214     //size_t end_time = (size_t) (ts.QuadPart/(qpf.QuadPart/1000)) - begin_time; 
31215
31216     //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(DWORD));
31217
31218 #endif //MARK_ARRAY
31219 }
31220
31221 #endif //BACKGROUND_GC 
31222
31223 void gc_heap::verify_mark_array_cleared (heap_segment* seg)
31224 {
31225 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31226     assert (card_table == g_card_table);
31227     size_t  markw = mark_word_of (heap_segment_mem (seg));
31228     size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
31229
31230     while (markw < markw_end)
31231     {
31232         if (mark_array [markw])
31233         {
31234             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
31235                             markw, mark_array [markw], mark_word_address (markw)));
31236             FATAL_GC_ERROR();
31237         }
31238         markw++;
31239     }
31240 #endif //VERIFY_HEAP && MARK_ARRAY
31241 }
31242
31243 void gc_heap::verify_mark_array_cleared ()
31244 {
31245 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31246     if (recursive_gc_sync::background_running_p() && g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
31247     {
31248         generation* gen = generation_of (max_generation);
31249         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31250         
31251         while (1)
31252         {
31253             if (seg == 0)
31254             {
31255                 if (gen != large_object_generation)
31256                 {
31257                     gen = generation_of (max_generation+1);
31258                     seg = heap_segment_rw (generation_start_segment (gen));
31259                 }
31260                 else
31261                 {
31262                     break;
31263                 }
31264             }
31265
31266             bgc_verify_mark_array_cleared (seg);
31267             seg = heap_segment_next_rw (seg);
31268         }
31269     }
31270 #endif //VERIFY_HEAP && MARK_ARRAY
31271 }
31272
31273 void gc_heap::verify_seg_end_mark_array_cleared()
31274 {
31275 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
31276     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
31277     {
31278         generation* gen = generation_of (max_generation);
31279         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31280         
31281         while (1)
31282         {
31283             if (seg == 0)
31284             {
31285                 if (gen != large_object_generation)
31286                 {
31287                     gen = generation_of (max_generation+1);
31288                     seg = heap_segment_rw (generation_start_segment (gen));
31289                 }
31290                 else
31291                 {
31292                     break;
31293                 }
31294             }
31295
31296             // We already cleared all mark array bits for ephemeral generations
31297             // at the beginning of bgc sweep
31298             BYTE* from = ((seg == ephemeral_heap_segment) ? 
31299                           generation_allocation_start (generation_of (max_generation - 1)) :
31300                           heap_segment_allocated (seg));
31301             size_t  markw = mark_word_of (align_on_mark_word (from));
31302             size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
31303
31304             while (from < mark_word_address (markw))
31305             {
31306                 if (is_mark_bit_set (from))
31307                 {
31308                     dprintf (3, ("mark bit for %Ix was not cleared", from));
31309                     FATAL_GC_ERROR();
31310                 }
31311
31312                 from += mark_bit_pitch;
31313             }
31314
31315             while (markw < markw_end)
31316             {
31317                 if (mark_array [markw])
31318                 {
31319                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
31320                                     markw, mark_array [markw], mark_word_address (markw)));
31321                     FATAL_GC_ERROR();
31322                 }
31323                 markw++;
31324             }
31325             seg = heap_segment_next_rw (seg);
31326         }
31327     }
31328 #endif //VERIFY_HEAP && MARK_ARRAY
31329 }
31330
31331 // This function is called to make sure we don't mess up the segment list
31332 // in SOH. It's called by:
31333 // 1) begin and end of ephemeral GCs
31334 // 2) during bgc sweep when we switch segments.
31335 void gc_heap::verify_soh_segment_list()
31336 {
31337 #ifdef VERIFY_HEAP
31338     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
31339     {
31340         generation* gen = generation_of (max_generation);
31341         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31342         heap_segment* last_seg = 0;
31343         while (seg)
31344         {
31345             last_seg = seg;
31346             seg = heap_segment_next_rw (seg);
31347         }
31348         if (last_seg != ephemeral_heap_segment)
31349         {
31350             FATAL_GC_ERROR();
31351         }
31352     }
31353 #endif //VERIFY_HEAP
31354 }
31355
31356 // This function can be called at any foreground GCs or blocking GCs. For background GCs,
31357 // it can be called at the end of the final marking; and at any point during background
31358 // sweep.
31359 // NOTE - to be able to call this function during background sweep, we need to temporarily 
31360 // NOT clear the mark array bits as we go.
31361 void gc_heap::verify_partial ()
31362 {
31363 #ifdef BACKGROUND_GC
31364     //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
31365     //generation* gen = large_object_generation;
31366     generation* gen = generation_of (max_generation);
31367     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31368     int align_const = get_alignment_constant (gen != large_object_generation);
31369
31370     BYTE* o = 0;
31371     BYTE* end = 0;
31372     size_t s = 0;
31373
31374     // Different ways to fail.
31375     BOOL mark_missed_p = FALSE;
31376     BOOL bad_ref_p = FALSE;
31377     BOOL free_ref_p = FALSE;
31378
31379     while (1)
31380     {
31381         if (seg == 0)
31382         {
31383             if (gen != large_object_generation)
31384             {
31385                 //switch to LOH
31386                 gen = large_object_generation;
31387                 align_const = get_alignment_constant (gen != large_object_generation);
31388                 seg = heap_segment_rw (generation_start_segment (gen));
31389                 continue;
31390             }
31391             else
31392             {
31393                 break;
31394             }
31395         }
31396
31397         o = heap_segment_mem (seg);
31398         end  = heap_segment_allocated (seg);
31399         //printf ("validating [%Ix-[%Ix\n", o, end);
31400         while (o < end)
31401         {
31402             s = size (o);
31403
31404             BOOL marked_p = background_object_marked (o, FALSE);
31405
31406             if (marked_p)
31407             {
31408                 go_through_object_cl (method_table (o), o, s, oo,
31409                     {
31410                         if (*oo)
31411                         {
31412                             //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
31413                             MethodTable *pMT = method_table (*oo);
31414
31415                             if (pMT == g_pFreeObjectMethodTable)
31416                             {
31417                                 free_ref_p = TRUE;
31418                                 FATAL_GC_ERROR();
31419                             }
31420
31421                             if (!pMT->SanityCheck()) 
31422                             {
31423                                 bad_ref_p = TRUE;
31424                                 dprintf (3, ("Bad member of %Ix %Ix",
31425                                             (size_t)oo, (size_t)*oo));
31426                                 FATAL_GC_ERROR();
31427                             }
31428
31429                             if (current_bgc_state == bgc_final_marking)
31430                             {
31431                                 if (marked_p && !background_object_marked (*oo, FALSE))
31432                                 {
31433                                     mark_missed_p = TRUE;
31434                                     FATAL_GC_ERROR();
31435                                 }
31436                             }
31437                         }
31438                     }
31439                                     );
31440             }
31441
31442             o = o + Align(s, align_const);
31443         }
31444         seg = heap_segment_next_rw (seg);
31445     }
31446
31447     //printf ("didn't find any large object large enough...\n");
31448     //printf ("finished verifying loh\n");
31449 #endif //BACKGROUND_GC 
31450 }
31451
31452 #ifdef VERIFY_HEAP
31453
31454 void 
31455 gc_heap::verify_free_lists ()
31456 {
31457     for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
31458     {
31459         dprintf (3, ("Verifying free list for gen:%d", gen_num));
31460         allocator* gen_alloc = generation_allocator (generation_of (gen_num));
31461         size_t sz = gen_alloc->first_bucket_size();
31462         bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
31463
31464         for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
31465         {
31466             BYTE* free_list = gen_alloc->alloc_list_head_of (a_l_number);
31467             BYTE* prev = 0;
31468             while (free_list)
31469             {
31470                 if (!((CObjectHeader*)free_list)->IsFree())
31471                 {
31472                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
31473                                  (size_t)free_list));
31474                     FATAL_GC_ERROR();
31475                 }
31476                 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
31477                     || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
31478                 {
31479                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
31480                                  (size_t)free_list));
31481                     FATAL_GC_ERROR();
31482                 }
31483                 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
31484                 {
31485                     dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
31486                                  (size_t)free_list));
31487                     FATAL_GC_ERROR();
31488                 }
31489                 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
31490                 {
31491                     dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
31492                                  (size_t)free_list));
31493                     FATAL_GC_ERROR();
31494                 }
31495                     
31496                 prev = free_list;
31497                 free_list = free_list_slot (free_list);
31498             }
31499             //verify the sanity of the tail 
31500             BYTE* tail = gen_alloc->alloc_list_tail_of (a_l_number);
31501             if (!((tail == 0) || (tail == prev)))
31502             {
31503                 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
31504                 FATAL_GC_ERROR();
31505             }
31506             if (tail == 0)
31507             {
31508                 BYTE* head = gen_alloc->alloc_list_head_of (a_l_number);
31509                 if ((head != 0) && (free_list_slot (head) != 0))
31510                 {
31511                     dprintf (3, ("Verifying Heap: tail of free list is not correct"));
31512                     FATAL_GC_ERROR();
31513                 }
31514             }
31515
31516             sz *=2;
31517         }
31518     }
31519 }
31520
31521 void
31522 gc_heap::verify_heap (BOOL begin_gc_p)
31523 {
31524     int             heap_verify_level = g_pConfig->GetHeapVerifyLevel();
31525     size_t          last_valid_brick = 0;
31526     BOOL            bCurrentBrickInvalid = FALSE;
31527     BOOL            large_brick_p = TRUE;
31528     size_t          curr_brick = 0;
31529     size_t          prev_brick = (size_t)-1;
31530     int             curr_gen_num = max_generation+1;    
31531     heap_segment*   seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
31532
31533     PREFIX_ASSUME(seg != NULL);
31534
31535     BYTE*           curr_object = heap_segment_mem (seg);
31536     BYTE*           prev_object = 0;
31537     BYTE*           begin_youngest = generation_allocation_start(generation_of(0));
31538     BYTE*           end_youngest = heap_segment_allocated (ephemeral_heap_segment);
31539     BYTE*           next_boundary = generation_allocation_start (generation_of (max_generation - 1));
31540     int             align_const = get_alignment_constant (FALSE);
31541     size_t          total_objects_verified = 0;
31542     size_t          total_objects_verified_deep = 0;
31543
31544 #ifdef BACKGROUND_GC
31545     BOOL consider_bgc_mark_p    = FALSE;
31546     BOOL check_current_sweep_p  = FALSE;
31547     BOOL check_saved_sweep_p    = FALSE;
31548     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31549 #endif //BACKGROUND_GC
31550
31551 #ifdef MULTIPLE_HEAPS
31552     t_join* current_join = &gc_t_join;
31553 #ifdef BACKGROUND_GC
31554     if (settings.concurrent && (GetCurrentThreadId() == bgc_thread_id))
31555     {
31556         // We always call verify_heap on entry of GC on the SVR GC threads.
31557         current_join = &bgc_t_join;
31558     }
31559 #endif //BACKGROUND_GC
31560 #endif //MULTIPLE_HEAPS
31561
31562 #ifdef BACKGROUND_GC 
31563     dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin", 
31564         (begin_gc_p ? "BEG" : "END"),
31565         VolatileLoad(&settings.gc_index), 
31566         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
31567 #else
31568     dprintf (2,("[%s]GC#%d: Verifying heap - begin", 
31569                 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
31570 #endif //BACKGROUND_GC 
31571
31572 #ifndef MULTIPLE_HEAPS
31573     if ((g_ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
31574         (g_ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
31575     {
31576         FATAL_GC_ERROR();
31577     }
31578 #endif //MULTIPLE_HEAPS
31579
31580 #ifdef BACKGROUND_GC
31581     //don't touch the memory because the program is allocating from it.
31582     if (!settings.concurrent)
31583 #endif //BACKGROUND_GC
31584     {
31585         if (!(heap_verify_level & EEConfig::HEAPVERIFY_NO_MEM_FILL))
31586         {
31587             //uninit the unused portions of segments.
31588             generation* gen1 = large_object_generation;
31589             heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
31590             PREFIX_ASSUME(seg1 != NULL);
31591
31592             while (1)
31593             {
31594                 if (seg1)
31595                 {
31596                     BYTE* clear_start = heap_segment_allocated (seg1) - plug_skew;
31597                     if (heap_segment_used (seg1) > clear_start)
31598                     {
31599                         dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa", 
31600                                     heap_segment_mem (seg1),
31601                                     clear_start ,
31602                                     heap_segment_used (seg1)));
31603                         memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
31604                             (heap_segment_used (seg1) - clear_start));
31605                     }
31606                     seg1 = heap_segment_next_rw (seg1);
31607                 }
31608                 else
31609                 {
31610                     if (gen1 == large_object_generation)
31611                     {
31612                         gen1 = generation_of (max_generation);
31613                         seg1 = heap_segment_rw (generation_start_segment (gen1));
31614                         PREFIX_ASSUME(seg1 != NULL);
31615                     }
31616                     else
31617                     {
31618                         break;
31619                     }
31620                 }
31621             }
31622         }
31623     }
31624
31625 #ifdef MULTIPLE_HEAPS
31626     current_join->join(this, gc_join_verify_copy_table);
31627     if (current_join->joined())
31628     {
31629         // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
31630         for (int i = 0; i < n_heaps; i++)
31631         {
31632             //copy the card and brick tables
31633             if (g_card_table != g_heaps[i]->card_table)
31634             {
31635                 g_heaps[i]->copy_brick_card_table (FALSE);
31636             }
31637         }
31638
31639         current_join->restart();
31640     }
31641 #else
31642         if (g_card_table != card_table)
31643             copy_brick_card_table (FALSE);
31644 #endif //MULTIPLE_HEAPS
31645
31646     //verify that the generation structures makes sense
31647     {
31648         generation* gen = generation_of (max_generation);
31649
31650         assert (generation_allocation_start (gen) ==
31651                 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
31652         int gen_num = max_generation-1;
31653         generation* prev_gen = gen;
31654         while (gen_num >= 0)
31655         {
31656             gen = generation_of (gen_num);
31657             assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
31658             assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
31659             assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
31660
31661             if (generation_start_segment (prev_gen ) ==
31662                 generation_start_segment (gen))
31663             {
31664                 assert (generation_allocation_start (prev_gen) <
31665                         generation_allocation_start (gen));
31666             }
31667             prev_gen = gen;
31668             gen_num--;
31669         }
31670     }
31671
31672     while (1)
31673     {
31674         // Handle segment transitions
31675         if (curr_object >= heap_segment_allocated (seg))
31676         {
31677             if (curr_object > heap_segment_allocated(seg))
31678             {
31679                 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
31680                         (size_t)curr_object, (size_t)seg));
31681                 FATAL_GC_ERROR();
31682             }
31683             seg = heap_segment_next_in_range (seg);
31684             if (seg)
31685             {
31686 #ifdef BACKGROUND_GC
31687                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31688 #endif //BACKGROUND_GC
31689                 curr_object = heap_segment_mem(seg);
31690                 prev_object = 0;
31691                 continue;
31692             }
31693             else
31694             {
31695                 if (curr_gen_num == (max_generation+1))
31696                 {
31697                     curr_gen_num--;
31698                     seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
31699
31700                     PREFIX_ASSUME(seg != NULL);
31701
31702 #ifdef BACKGROUND_GC
31703                     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31704 #endif //BACKGROUND_GC
31705                     curr_object = heap_segment_mem (seg);
31706                     prev_object = 0;
31707                     large_brick_p = FALSE;
31708                     align_const = get_alignment_constant (TRUE);
31709                 }
31710                 else
31711                     break;  // Done Verifying Heap -- no more segments
31712             }
31713         }
31714
31715         // Are we at the end of the youngest_generation?
31716         if ((seg == ephemeral_heap_segment))
31717         {
31718             if (curr_object >= end_youngest)
31719             {
31720                 // prev_object length is too long if we hit this int3
31721                 if (curr_object > end_youngest)
31722                 {
31723                     dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
31724                             (size_t)curr_object, (size_t)end_youngest));
31725                     FATAL_GC_ERROR();
31726                 }
31727                 break;
31728             }
31729             
31730             if ((curr_object >= next_boundary) && (curr_gen_num > 0))
31731             {
31732                 curr_gen_num--;
31733                 if (curr_gen_num > 0)
31734                 {
31735                     next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
31736                 }
31737             }
31738         }
31739
31740          //if (is_mark_set (curr_object))
31741          //{
31742          //        printf ("curr_object: %Ix is marked!",(size_t)curr_object);
31743          //        FATAL_GC_ERROR();
31744          //}
31745
31746         size_t s = size (curr_object);
31747         dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
31748         if (s == 0)
31749         {
31750             dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
31751             FATAL_GC_ERROR();
31752         }
31753
31754         // If object is not in the youngest generation, then lets
31755         // verify that the brick table is correct....
31756         if (((seg != ephemeral_heap_segment) ||
31757              (brick_of(curr_object) < brick_of(begin_youngest))))
31758         {
31759             curr_brick = brick_of(curr_object);
31760
31761             // Brick Table Verification...
31762             //
31763             // On brick transition
31764             //     if brick is negative
31765             //          verify that brick indirects to previous valid brick
31766             //     else
31767             //          set current brick invalid flag to be flipped if we
31768             //          encounter an object at the correct place
31769             //
31770             if (curr_brick != prev_brick)
31771             {
31772                 // If the last brick we were examining had positive
31773                 // entry but we never found the matching object, then
31774                 // we have a problem
31775                 // If prev_brick was the last one of the segment
31776                 // it's ok for it to be invalid because it is never looked at
31777                 if (bCurrentBrickInvalid &&
31778                     (curr_brick != brick_of (heap_segment_mem (seg))) &&
31779                     !heap_segment_read_only_p (seg))
31780                 {
31781                     dprintf (3, ("curr brick %Ix invalid", curr_brick));
31782                     FATAL_GC_ERROR();
31783                 }
31784
31785                 if (large_brick_p)
31786                 {
31787                     //large objects verify the table only if they are in
31788                     //range.
31789                     if ((heap_segment_reserved (seg) <= highest_address) &&
31790                         (heap_segment_mem (seg) >= lowest_address) &&
31791                         brick_table [curr_brick] != 0)
31792                     {
31793                         dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
31794                                 curr_brick, (size_t)curr_object));
31795                         FATAL_GC_ERROR();
31796                     }
31797                     else
31798                     {
31799                         bCurrentBrickInvalid = FALSE;
31800                     }
31801                 }
31802                 else
31803                 {
31804                     // If the current brick contains a negative value make sure
31805                     // that the indirection terminates at the last  valid brick
31806                     if (brick_table [curr_brick] < 0)
31807                     {
31808                         if (brick_table [curr_brick] == 0)
31809                         {
31810                             dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
31811                                     curr_brick, (size_t)curr_object));
31812                             FATAL_GC_ERROR();
31813                         }
31814                         ptrdiff_t i = curr_brick;
31815                         while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
31816                                (brick_table[i] < 0))
31817                         {
31818                             i = i + brick_table[i];
31819                         }
31820                         if (i <  ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
31821                         {
31822                             dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
31823                                     i, brick_of (heap_segment_mem (seg)),
31824                                     curr_brick));
31825                             FATAL_GC_ERROR();
31826                         }
31827                         // if (i != last_valid_brick)
31828                         //  FATAL_GC_ERROR();
31829                         bCurrentBrickInvalid = FALSE;
31830                     }
31831                     else if (!heap_segment_read_only_p (seg))
31832                     {
31833                         bCurrentBrickInvalid = TRUE;
31834                     }
31835                 }
31836             }
31837
31838             if (bCurrentBrickInvalid)
31839             {
31840                 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
31841                 {
31842                     bCurrentBrickInvalid = FALSE;
31843                     last_valid_brick = curr_brick;
31844                 }
31845             }
31846         }
31847
31848         if (*((BYTE**)curr_object) != (BYTE *) g_pFreeObjectMethodTable)
31849         {
31850 #ifdef FEATURE_LOH_COMPACTION
31851             if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
31852             {
31853                 assert (method_table (prev_object) == g_pFreeObjectMethodTable);
31854             }
31855 #endif //FEATURE_LOH_COMPACTION
31856
31857             total_objects_verified++;
31858
31859             BOOL can_verify_deep = TRUE;
31860 #ifdef BACKGROUND_GC
31861             can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
31862 #endif //BACKGROUND_GC
31863
31864             BOOL deep_verify_obj = can_verify_deep;
31865             if ((heap_verify_level & EEConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
31866                 deep_verify_obj = FALSE;
31867
31868             ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
31869
31870             if (can_verify_deep)
31871             {
31872                 if (curr_gen_num > 0)
31873                 {
31874                     BOOL need_card_p = FALSE;
31875                     if (contain_pointers_or_collectible (curr_object))
31876                     {
31877                         dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
31878                         size_t crd = card_of (curr_object);
31879                         BOOL found_card_p = card_set_p (crd);
31880
31881 #ifdef COLLECTIBLE_CLASS
31882                         if (is_collectible(curr_object))
31883                         {
31884                             BYTE* class_obj = get_class_object (curr_object);
31885                             if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
31886                             {
31887                                 if (!found_card_p)
31888                                 {
31889                                     dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
31890                                                 card_of (curr_object), (size_t)curr_object, class_obj));
31891
31892                                     FATAL_GC_ERROR();
31893                                 }
31894                             }
31895                         }
31896 #endif //COLLECTIBLE_CLASS
31897
31898                         if (contain_pointers(curr_object))
31899                         {
31900                             go_through_object_nostart
31901                                 (method_table(curr_object), curr_object, s, oo,
31902                                 {
31903                                     if ((crd != card_of ((BYTE*)oo)) && !found_card_p)
31904                                     {
31905                                         crd = card_of ((BYTE*)oo);
31906                                         found_card_p = card_set_p (crd);
31907                                         need_card_p = FALSE;
31908                                     }
31909                                     if ((*oo < ephemeral_high) && (*oo >= next_boundary))
31910                                     {
31911                                         need_card_p = TRUE;
31912                                     }
31913
31914                                 if (need_card_p && !found_card_p)
31915                                 {
31916
31917                                         dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
31918                                                     card_of (curr_object), (size_t)curr_object,
31919                                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
31920                                         FATAL_GC_ERROR();
31921                                     }
31922                                 }
31923                                     );
31924                         }
31925                         if (need_card_p && !found_card_p)
31926                         {
31927                             dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
31928                                     card_of (curr_object), (size_t)curr_object,
31929                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
31930                             FATAL_GC_ERROR();
31931                         }
31932                     }
31933                 }
31934                 total_objects_verified_deep++;
31935             }
31936         }
31937
31938         prev_object = curr_object;
31939         prev_brick = curr_brick;
31940         curr_object = curr_object + Align(s, align_const);
31941         if (curr_object < prev_object)
31942         {
31943             dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
31944             FATAL_GC_ERROR();
31945         }
31946     }
31947
31948 #ifdef BACKGROUND_GC
31949     dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id", 
31950                  (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
31951                  (begin_gc_p ? "BEG" : "END"),
31952                  ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
31953                  total_objects_verified, total_objects_verified_deep));
31954     if (current_c_gc_state != c_gc_state_planning)
31955     {
31956         assert (total_objects_verified == total_objects_verified_deep);
31957     }
31958 #endif //BACKGROUND_GC
31959     
31960     verify_free_lists();
31961
31962 #ifdef FEATURE_PREMORTEM_FINALIZATION
31963     finalize_queue->CheckFinalizerObjects();
31964 #endif // FEATURE_PREMORTEM_FINALIZATION
31965
31966     {
31967         // to be consistent with handle table APIs pass a ScanContext*
31968         // to provide the heap number.  the SC isn't complete though so
31969         // limit its scope to handle table verification.
31970         ScanContext sc;
31971         sc.thread_number = heap_number;
31972         CNameSpace::VerifyHandleTable(max_generation, max_generation, &sc);
31973     }
31974
31975 #ifdef MULTIPLE_HEAPS
31976     current_join->join(this, gc_join_verify_objects_done);
31977     if (current_join->joined())
31978 #endif //MULTIPLE_HEAPS
31979     {
31980         SyncBlockCache::GetSyncBlockCache()->VerifySyncTableEntry();
31981 #ifdef MULTIPLE_HEAPS
31982         current_join->restart();
31983 #endif //MULTIPLE_HEAPS
31984     }
31985
31986 #ifdef BACKGROUND_GC 
31987     if (!settings.concurrent)
31988     {
31989         if (current_c_gc_state == c_gc_state_planning)
31990         {
31991             // temporarily commenting this out 'cause an FGC
31992             // could be triggered before we sweep ephemeral.
31993             //verify_seg_end_mark_array_cleared();
31994         }
31995     }
31996
31997     if (settings.concurrent)
31998     {
31999         verify_mark_array_cleared();
32000     }
32001     dprintf (2,("GC%d(%s): Verifying heap - end", 
32002         VolatileLoad(&settings.gc_index), 
32003         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
32004 #else
32005     dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
32006 #endif //BACKGROUND_GC 
32007 }
32008
32009 void GCHeap::ValidateObjectMember (Object* obj)
32010 {
32011     size_t s = size (obj);
32012     BYTE* o = (BYTE*)obj;
32013
32014     go_through_object_cl (method_table (obj), o, s, oo,
32015                                 {
32016                                     BYTE* child_o = *oo;
32017                                     if (child_o)
32018                                     {
32019                                         dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
32020                                         MethodTable *pMT = method_table (child_o);
32021                                         if (!pMT->SanityCheck()) {
32022                                             dprintf (3, ("Bad member of %Ix %Ix",
32023                                                         (size_t)oo, (size_t)child_o));
32024                                             FATAL_GC_ERROR();
32025                                         }
32026                                     }
32027                                 } );
32028
32029 }
32030 #endif  //VERIFY_HEAP
32031
32032 void DestructObject (CObjectHeader* hdr)
32033 {
32034     hdr->~CObjectHeader();
32035 }
32036
32037 HRESULT GCHeap::Shutdown ()
32038 {
32039     deleteGCShadow();
32040
32041     CNameSpace::GcRuntimeStructuresValid (FALSE);
32042
32043     // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
32044     // threads except the one performing the shutdown.
32045     // ASSERT( !GcInProgress );
32046
32047     // Guard against any more GC occurring and against any threads blocking
32048     // for GC to complete when the GC heap is gone.  This fixes a race condition
32049     // where a thread in GC is destroyed as part of process destruction and
32050     // the remaining threads block for GC complete.
32051
32052     //GCTODO
32053     //EnterAllocLock();
32054     //Enter();
32055     //EnterFinalizeLock();
32056     //SetGCDone();
32057
32058     // during shutdown lot of threads are suspended
32059     // on this even, we don't want to wake them up just yet
32060     //CloseHandle (WaitForGCEvent);
32061
32062     //find out if the global card table hasn't been used yet
32063     DWORD* ct = &g_card_table[card_word (gcard_of (g_lowest_address))];
32064     if (card_table_refcount (ct) == 0)
32065     {
32066         destroy_card_table (ct);
32067         g_card_table = 0;
32068     }
32069
32070     //destroy all segments on the standby list
32071     while(gc_heap::segment_standby_list != 0)
32072     {
32073         heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
32074 #ifdef MULTIPLE_HEAPS
32075         (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
32076 #else //MULTIPLE_HEAPS
32077         pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
32078 #endif //MULTIPLE_HEAPS
32079         gc_heap::segment_standby_list = next_seg;
32080     }
32081
32082
32083 #ifdef MULTIPLE_HEAPS
32084
32085     for (int i = 0; i < gc_heap::n_heaps; i ++)
32086     {
32087         delete gc_heap::g_heaps[i]->vm_heap;
32088         //destroy pure GC stuff
32089         gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
32090     }
32091 #else
32092     gc_heap::destroy_gc_heap (pGenGCHeap);
32093
32094 #endif //MULTIPLE_HEAPS
32095     gc_heap::shutdown_gc();
32096
32097     return S_OK;
32098 }
32099
32100 //used by static variable implementation
32101 void CGCDescGcScan(LPVOID pvCGCDesc, promote_func* fn, ScanContext* sc)
32102 {
32103     CGCDesc* map = (CGCDesc*)pvCGCDesc;
32104
32105     CGCDescSeries *last = map->GetLowestSeries();
32106     CGCDescSeries *cur = map->GetHighestSeries();
32107
32108     assert (cur >= last);
32109     do
32110     {
32111         BYTE** ppslot = (BYTE**)((PBYTE)pvCGCDesc + cur->GetSeriesOffset());
32112         BYTE**ppstop = (BYTE**)((PBYTE)ppslot + cur->GetSeriesSize());
32113
32114         while (ppslot < ppstop)
32115         {
32116             if (*ppslot)
32117             {
32118                 (fn) ((Object**)ppslot, sc, 0);
32119             }
32120
32121             ppslot++;
32122         }
32123
32124         cur--;
32125     }
32126     while (cur >= last);
32127 }
32128
32129 // Wait until a garbage collection is complete
32130 // returns NOERROR if wait was OK, other error code if failure.
32131 // WARNING: This will not undo the must complete state. If you are
32132 // in a must complete when you call this, you'd better know what you're
32133 // doing.
32134
32135 #ifdef FEATURE_PREMORTEM_FINALIZATION
32136 static
32137 HRESULT AllocateCFinalize(CFinalize **pCFinalize)
32138 {
32139     *pCFinalize = new (nothrow) CFinalize();
32140     if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
32141         return E_OUTOFMEMORY;
32142
32143     return S_OK;
32144 }
32145 #endif // FEATURE_PREMORTEM_FINALIZATION
32146
32147 // init the instance heap
32148 HRESULT GCHeap::Init(size_t hn)
32149 {
32150     HRESULT hres = S_OK;
32151
32152     //Initialize all of the instance members.
32153
32154 #ifdef MULTIPLE_HEAPS
32155     m_GCLock                = -1;
32156 #endif //MULTIPLE_HEAPS
32157
32158     // Rest of the initialization
32159
32160 #ifdef MULTIPLE_HEAPS
32161     if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
32162         hres = E_OUTOFMEMORY;
32163 #else
32164     if (!gc_heap::make_gc_heap())
32165         hres = E_OUTOFMEMORY;
32166 #endif //MULTIPLE_HEAPS
32167
32168     // Failed.
32169     return hres;
32170 }
32171
32172 //System wide initialization
32173 HRESULT GCHeap::Initialize ()
32174 {
32175
32176     HRESULT hr = S_OK;
32177
32178 //Initialize the static members.
32179 #ifdef TRACE_GC
32180     GcDuration = 0;
32181     CreatedObjectCount = 0;
32182 #endif //TRACE_GC
32183
32184     size_t seg_size = get_valid_segment_size();
32185     size_t large_seg_size = get_valid_segment_size(TRUE);
32186     gc_heap::min_segment_size = min (seg_size, large_seg_size);
32187
32188 #ifdef MULTIPLE_HEAPS
32189     // GetGCProcessCpuCount only returns up to 64 procs.
32190     unsigned nhp = CPUGroupInfo::CanEnableGCCPUGroups() ? CPUGroupInfo::GetNumActiveProcessors(): 
32191                                                           GetCurrentProcessCpuCount();
32192     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
32193 #else
32194     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
32195 #endif //MULTIPLE_HEAPS
32196
32197     if (hr != S_OK)
32198         return hr;
32199
32200 #if defined(_WIN64)
32201     MEMORYSTATUSEX ms;
32202     GetProcessMemoryLoad (&ms);
32203     gc_heap::total_physical_mem = ms.ullTotalPhys;
32204     gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
32205     gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
32206 #endif // _WIN64
32207
32208     WaitForGCEvent = new (nothrow) CLREvent;
32209
32210     if (!WaitForGCEvent)
32211     {
32212         return E_OUTOFMEMORY;
32213     }
32214
32215     WaitForGCEvent->CreateManualEvent(TRUE);
32216
32217     StompWriteBarrierResize(FALSE);
32218
32219 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32220 #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
32221     if (GCStress<cfg_any>::IsEnabled())  {
32222         for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
32223             m_StressObjs[i] = CreateGlobalHandle(0);
32224         m_CurStressObj = 0;
32225     }
32226 #endif //STRESS_HEAP && !MULTIPLE_HEAPS
32227 #endif // FEATURE_REDHAWK
32228
32229     initGCShadow();         // If we are debugging write barriers, initialize heap shadow
32230
32231 #ifdef MULTIPLE_HEAPS
32232
32233     for (unsigned i = 0; i < nhp; i++)
32234     {
32235         GCHeap* Hp = new (nothrow) GCHeap();
32236         if (!Hp)
32237             return E_OUTOFMEMORY;
32238
32239         if ((hr = Hp->Init (i))!= S_OK)
32240         {
32241             return hr;
32242         }
32243     }
32244     // initialize numa node to heap map
32245     heap_select::init_numa_node_to_heap_map(nhp);
32246 #else
32247     hr = Init (0);
32248 #endif //MULTIPLE_HEAPS
32249
32250     if (hr == S_OK)
32251     {
32252         CNameSpace::GcRuntimeStructuresValid (TRUE);
32253
32254 #ifdef GC_PROFILING
32255         if (CORProfilerTrackGC())
32256             UpdateGenerationBounds();
32257 #endif // GC_PROFILING
32258     }
32259
32260     return hr;
32261 };
32262
32263 ////
32264 // GC callback functions
32265 BOOL GCHeap::IsPromoted(Object* object)
32266 {
32267 #ifdef _DEBUG
32268     ((CObjectHeader*)object)->Validate();
32269 #endif //_DEBUG
32270
32271     BYTE* o = (BYTE*)object;
32272
32273     if (gc_heap::settings.condemned_generation == max_generation)
32274     {
32275 #ifdef MULTIPLE_HEAPS
32276         gc_heap* hp = gc_heap::g_heaps[0];
32277 #else
32278         gc_heap* hp = pGenGCHeap;
32279 #endif //MULTIPLE_HEAPS
32280
32281 #ifdef BACKGROUND_GC
32282         if (gc_heap::settings.concurrent)
32283         {
32284             BOOL is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
32285                             hp->background_marked (o));
32286             return is_marked;
32287         }
32288         else
32289 #endif //BACKGROUND_GC
32290         {
32291             return (!((o < hp->highest_address) && (o >= hp->lowest_address))
32292                     || hp->is_mark_set (o));
32293         }
32294     }
32295     else
32296     {
32297         gc_heap* hp = gc_heap::heap_of (o);
32298         return (!((o < hp->gc_high) && (o >= hp->gc_low))
32299                 || hp->is_mark_set (o));
32300     }
32301 }
32302
32303 size_t GCHeap::GetPromotedBytes(int heap_index)
32304 {
32305 #ifdef BACKGROUND_GC
32306     if (gc_heap::settings.concurrent)
32307     {
32308         return gc_heap::bpromoted_bytes (heap_index);
32309     }
32310     else
32311 #endif //BACKGROUND_GC
32312     {
32313         return gc_heap::promoted_bytes (heap_index);
32314     }
32315 }
32316
32317 unsigned int GCHeap::WhichGeneration (Object* object)
32318 {
32319     gc_heap* hp = gc_heap::heap_of ((BYTE*)object);
32320     unsigned int g = hp->object_gennum ((BYTE*)object);
32321     dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
32322     return g;
32323 }
32324
32325 BOOL    GCHeap::IsEphemeral (Object* object)
32326 {
32327     BYTE* o = (BYTE*)object;
32328     gc_heap* hp = gc_heap::heap_of (o);
32329     return hp->ephemeral_pointer_p (o);
32330 }
32331
32332 #ifdef VERIFY_HEAP
32333 // Return NULL if can't find next object. When EE is not suspended,
32334 // the result is not accurate: if the input arg is in gen0, the function could 
32335 // return zeroed out memory as next object
32336 Object * GCHeap::NextObj (Object * object)
32337 {
32338     BYTE* o = (BYTE*)object;
32339     heap_segment * hs = gc_heap::find_segment (o, FALSE);
32340     if (!hs)
32341     {
32342         return NULL;
32343     }
32344
32345     BOOL large_object_p = heap_segment_loh_p (hs);
32346     if (large_object_p)
32347         return NULL; //could be racing with another core allocating. 
32348 #ifdef MULTIPLE_HEAPS
32349     gc_heap* hp = heap_segment_heap (hs);
32350 #else //MULTIPLE_HEAPS
32351     gc_heap* hp = 0;
32352 #endif //MULTIPLE_HEAPS
32353     unsigned int g = hp->object_gennum ((BYTE*)object);
32354     if ((g == 0) && hp->settings.demotion)
32355         return NULL;//could be racing with another core allocating. 
32356     int align_const = get_alignment_constant (!large_object_p);
32357     BYTE* nextobj = o + Align (size (o), align_const);
32358     if (nextobj <= o) // either overflow or 0 sized object.
32359     {
32360         return NULL;
32361     }
32362
32363     if ((nextobj < heap_segment_mem(hs)) || 
32364         (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) || 
32365         (nextobj >= hp->alloc_allocated))
32366     {
32367         return NULL;
32368     }
32369
32370     return (Object *)nextobj;
32371 }
32372
32373 #ifdef FEATURE_BASICFREEZE
32374 BOOL GCHeap::IsInFrozenSegment (Object * object)
32375 {
32376     BYTE* o = (BYTE*)object;
32377     heap_segment * hs = gc_heap::find_segment (o, FALSE);
32378     //We create a frozen object for each frozen segment before the segment is inserted
32379     //to segment list; during ngen, we could also create frozen objects in segments which
32380     //don't belong to current GC heap.
32381     //So we return true if hs is NULL. It might create a hole about detecting invalidate 
32382     //object. But given all other checks present, the hole should be very small
32383     return !hs || heap_segment_read_only_p (hs);
32384 }
32385 #endif //FEATURE_BASICFREEZE
32386
32387 #endif //VERIFY_HEAP
32388
32389 // returns TRUE if the pointer is in one of the GC heaps.
32390 BOOL GCHeap::IsHeapPointer (void* vpObject, BOOL small_heap_only)
32391 {
32392     STATIC_CONTRACT_SO_TOLERANT;
32393
32394     // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment 
32395     // no longer calls CLREvent::Wait which eventually takes a lock.
32396
32397     BYTE* object = (BYTE*) vpObject;
32398 #ifndef FEATURE_BASICFREEZE
32399     if (!((object < g_highest_address) && (object >= g_lowest_address)))
32400         return FALSE;
32401 #endif //!FEATURE_BASICFREEZE
32402
32403     heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
32404     return !!hs;
32405 }
32406
32407 #ifdef STRESS_PINNING
32408 static n_promote = 0;
32409 #endif //STRESS_PINNING
32410 // promote an object
32411 void GCHeap::Promote(Object** ppObject, ScanContext* sc, DWORD flags)
32412 {
32413     THREAD_NUMBER_FROM_CONTEXT;
32414 #ifndef MULTIPLE_HEAPS
32415     const int thread = 0;
32416 #endif //!MULTIPLE_HEAPS
32417
32418     BYTE* o = (BYTE*)*ppObject;
32419
32420     if (o == 0)
32421         return;
32422
32423 #ifdef DEBUG_DestroyedHandleValue
32424     // we can race with destroy handle during concurrent scan
32425     if (o == (BYTE*)DEBUG_DestroyedHandleValue)
32426         return;
32427 #endif //DEBUG_DestroyedHandleValue
32428
32429     HEAP_FROM_THREAD;
32430
32431     gc_heap* hp = gc_heap::heap_of (o);
32432
32433     dprintf (3, ("Promote %Ix", (size_t)o));
32434
32435 #ifdef INTERIOR_POINTERS
32436     if (flags & GC_CALL_INTERIOR)
32437     {
32438         if ((o < hp->gc_low) || (o >= hp->gc_high))
32439         {
32440             return;
32441         }
32442         if ( (o = hp->find_object (o, hp->gc_low)) == 0)
32443         {
32444             return;
32445         }
32446
32447     }
32448 #endif //INTERIOR_POINTERS
32449
32450 #ifdef FEATURE_CONSERVATIVE_GC
32451     // For conservative GC, a value on stack may point to middle of a free object.
32452     // In this case, we don't need to promote the pointer.
32453     if (g_pConfig->GetGCConservative()
32454         && ((CObjectHeader*)o)->IsFree())
32455     {
32456         return;
32457     }
32458 #endif
32459
32460 #ifdef _DEBUG
32461     ((CObjectHeader*)o)->ValidatePromote(sc, flags);
32462 #endif //_DEBUG
32463
32464     if (flags & GC_CALL_PINNED)
32465         hp->pin_object (o, (BYTE**) ppObject, hp->gc_low, hp->gc_high);
32466
32467 #ifdef STRESS_PINNING
32468     if ((++n_promote % 20) == 1)
32469             hp->pin_object (o, (BYTE**) ppObject, hp->gc_low, hp->gc_high);
32470 #endif //STRESS_PINNING
32471
32472 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
32473     size_t promoted_size_begin = hp->promoted_bytes (thread);
32474 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
32475
32476     if ((o >= hp->gc_low) && (o < hp->gc_high))
32477     {
32478         hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
32479     }
32480
32481 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
32482     size_t promoted_size_end = hp->promoted_bytes (thread);
32483     if (g_fEnableARM)
32484     {
32485         if (sc->pCurrentDomain)
32486         {
32487             sc->pCurrentDomain->RecordSurvivedBytes ((promoted_size_end - promoted_size_begin), thread);
32488         }
32489     }
32490 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
32491
32492     STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
32493 }
32494
32495 void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
32496                        DWORD flags)
32497 {
32498     BYTE* object = (BYTE*)(Object*)(*ppObject);
32499     
32500     THREAD_NUMBER_FROM_CONTEXT;
32501
32502     //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
32503     dprintf (3, ("R: %Ix", (size_t)ppObject));
32504     
32505     if (object == 0)
32506         return;
32507
32508     gc_heap* hp = gc_heap::heap_of (object);
32509
32510 #ifdef _DEBUG
32511     if (!(flags & GC_CALL_INTERIOR))
32512     {
32513         // We cannot validate this object if it's in the condemned gen because it could 
32514         // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
32515         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
32516         {
32517             ((CObjectHeader*)object)->Validate(FALSE);
32518         }
32519     }
32520 #endif //_DEBUG
32521
32522     dprintf (3, ("Relocate %Ix\n", (size_t)object));
32523
32524     BYTE* pheader;
32525
32526     if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
32527     {
32528         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
32529         {
32530             return;
32531         }
32532
32533         if (gc_heap::loh_object_p (object))
32534         {
32535             pheader = hp->find_object (object, 0);
32536             if (pheader == 0)
32537             {
32538                 return;
32539             }
32540
32541             ptrdiff_t ref_offset = object - pheader;
32542             hp->relocate_address(&pheader THREAD_NUMBER_ARG);
32543             *ppObject = (Object*)(pheader + ref_offset);
32544             return;
32545         }
32546     }
32547
32548     {
32549         pheader = object;
32550         hp->relocate_address(&pheader THREAD_NUMBER_ARG);
32551         *ppObject = (Object*)pheader;
32552     }
32553
32554     STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
32555 }
32556
32557 /*static*/ BOOL GCHeap::IsLargeObject(MethodTable *mt)
32558 {
32559     return mt->GetBaseSize() >= LARGE_OBJECT_SIZE;
32560 }
32561
32562 /*static*/ BOOL GCHeap::IsObjectInFixedHeap(Object *pObj)
32563 {
32564     // For now we simply look at the size of the object to determine if it in the
32565     // fixed heap or not. If the bit indicating this gets set at some point
32566     // we should key off that instead.
32567     return size( pObj ) >= LARGE_OBJECT_SIZE;
32568 }
32569
32570 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32571 #ifdef STRESS_HEAP
32572
32573 void StressHeapDummy ();
32574
32575 static LONG GCStressStartCount = -1;
32576 static LONG GCStressCurCount = 0;
32577 static LONG GCStressStartAtJit = -1;
32578
32579 // the maximum number of foreground GCs we'll induce during one BGC
32580 // (this number does not include "naturally" occuring GCs).
32581 static LONG GCStressMaxFGCsPerBGC = -1;
32582
32583 // CLRRandom implementation can produce FPU exceptions if 
32584 // the test/application run by CLR is enabling any FPU exceptions. 
32585 // We want to avoid any unexpected exception coming from stress 
32586 // infrastructure, so CLRRandom is not an option.
32587 // The code below is a replicate of CRT rand() implementation.
32588 // Using CRT rand() is not an option because we will interfere with the user application
32589 // that may also use it. 
32590 int StressRNG(int iMaxValue)
32591 {
32592     static BOOL bisRandInit = FALSE;
32593     static int lHoldrand = 1L;
32594
32595     if (!bisRandInit)
32596     {
32597         lHoldrand = (int)time(NULL);
32598         bisRandInit = TRUE;
32599     }
32600     int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
32601     return randValue % iMaxValue;
32602 }
32603
32604 // free up object so that things will move and then do a GC
32605 //return TRUE if GC actually happens, otherwise FALSE
32606 BOOL GCHeap::StressHeap(alloc_context * acontext)
32607 {
32608     // if GC stress was dynamically disabled during this run we return FALSE
32609     if (!GCStressPolicy::IsEnabled())
32610         return FALSE;
32611
32612 #ifdef _DEBUG
32613     if (g_pConfig->FastGCStressLevel() && !GetThread()->StressHeapIsEnabled()) {
32614         return FALSE;
32615     }
32616
32617 #endif //_DEBUG
32618
32619     if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
32620 #ifdef _DEBUG
32621         || g_pConfig->FastGCStressLevel() > 1
32622 #endif //_DEBUG
32623         ) {
32624         if (!Thread::UniqueStack(&acontext)) {
32625             return FALSE;
32626         }
32627     }
32628
32629 #ifdef BACKGROUND_GC
32630         // don't trigger a GC from the GC threads but still trigger GCs from user threads.
32631         if (IsGCSpecialThread())
32632         {
32633             return FALSE;
32634         }
32635 #endif //BACKGROUND_GC
32636
32637         if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
32638         {
32639             GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
32640             GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
32641         }
32642
32643         if (GCStressMaxFGCsPerBGC == -1)
32644         {
32645             GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
32646             if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
32647                 GCStressMaxFGCsPerBGC = 6;
32648         }
32649
32650 #ifdef _DEBUG
32651         if (g_JitCount < GCStressStartAtJit)
32652             return FALSE;
32653 #endif //_DEBUG
32654
32655         // Allow programmer to skip the first N Stress GCs so that you can
32656         // get to the interesting ones faster.
32657         FastInterlockIncrement(&GCStressCurCount);
32658         if (GCStressCurCount < GCStressStartCount)
32659             return FALSE;
32660
32661         // throttle the number of stress-induced GCs by a factor given by GCStressStep
32662         if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
32663         {
32664             return FALSE;
32665         }
32666
32667 #ifdef BACKGROUND_GC
32668         if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
32669         {
32670             // allow a maximum number of stress induced FGCs during one BGC
32671             if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
32672                 return FALSE;
32673             ++gc_stress_fgcs_in_bgc;
32674         }
32675 #endif // BACKGROUND_GC
32676
32677     if (g_pStringClass == 0)
32678     {
32679         // If the String class has not been loaded, dont do any stressing. This should
32680         // be kept to a minimum to get as complete coverage as possible.
32681         _ASSERTE(g_fEEInit);
32682         return FALSE;
32683     }
32684
32685     EX_TRY
32686     {
32687
32688 #ifndef MULTIPLE_HEAPS
32689         static LONG OneAtATime = -1;
32690
32691         if (acontext == 0)
32692             acontext = generation_alloc_context (pGenGCHeap->generation_of(0));
32693
32694         // Only bother with this if the stress level is big enough and if nobody else is
32695         // doing it right now.  Note that some callers are inside the AllocLock and are
32696         // guaranteed synchronized.  But others are using AllocationContexts and have no
32697         // particular synchronization.
32698         //
32699         // For this latter case, we want a very high-speed way of limiting this to one
32700         // at a time.  A secondary advantage is that we release part of our StressObjs
32701         // buffer sparingly but just as effectively.
32702
32703         if (FastInterlockIncrement((LONG *) &OneAtATime) == 0 &&
32704             !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
32705         {
32706             StringObject* str;
32707
32708             // If the current string is used up
32709             if (ObjectFromHandle(m_StressObjs[m_CurStressObj]) == 0)
32710             {
32711                 // Populate handles with strings
32712                 int i = m_CurStressObj;
32713                 while(ObjectFromHandle(m_StressObjs[i]) == 0)
32714                 {
32715                     _ASSERTE(m_StressObjs[i] != 0);
32716                     unsigned strLen = (LARGE_OBJECT_SIZE - 32) / sizeof(WCHAR);
32717                     unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
32718                     
32719                     // update the cached type handle before allocating
32720                     SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
32721                     str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
32722                     if (str)
32723                     {
32724                         str->SetMethodTable (g_pStringClass);
32725                         str->SetStringLength (strLen);
32726
32727 #if CHECK_APP_DOMAIN_LEAKS
32728                         if (g_pConfig->AppDomainLeaks())
32729                             str->SetAppDomain();
32730 #endif
32731                         StoreObjectInHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
32732                     }
32733                     i = (i + 1) % NUM_HEAP_STRESS_OBJS;
32734                     if (i == m_CurStressObj) break;
32735                 }
32736
32737                 // advance the current handle to the next string
32738                 m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
32739             }
32740
32741             // Get the current string
32742             str = (StringObject*) OBJECTREFToObject(ObjectFromHandle(m_StressObjs[m_CurStressObj]));
32743             if (str)
32744             {
32745                 // Chop off the end of the string and form a new object out of it.
32746                 // This will 'free' an object at the begining of the heap, which will
32747                 // force data movement.  Note that we can only do this so many times.
32748                 // before we have to move on to the next string.
32749                 unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
32750                 if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
32751                 {
32752                     unsigned sizeToNextObj = (unsigned)Align(size(str));
32753                     BYTE* freeObj = ((BYTE*) str) + sizeToNextObj - sizeOfNewObj;
32754                     pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);                    
32755                     str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
32756                 }
32757                 else
32758                 {
32759                     // Let the string itself become garbage.
32760                     // will be realloced next time around
32761                     StoreObjectInHandle(m_StressObjs[m_CurStressObj], 0);
32762                 }
32763             }
32764         }
32765         FastInterlockDecrement((LONG *) &OneAtATime);
32766 #endif // !MULTIPLE_HEAPS
32767         if (IsConcurrentGCEnabled())
32768         {
32769             int rgen = StressRNG(10);
32770
32771             // gen0:gen1:gen2 distribution: 40:40:20
32772             if (rgen >= 8)
32773                 rgen = 2;
32774             else if (rgen >= 4)
32775                 rgen = 1;
32776         else
32777                 rgen = 0;
32778
32779             GarbageCollectTry (rgen, FALSE, collection_gcstress);
32780         }
32781         else
32782         {
32783             GarbageCollect(max_generation, FALSE, collection_gcstress);
32784         }
32785     }
32786     EX_CATCH
32787     {
32788         _ASSERTE (!"Exception happens during StressHeap");
32789     }
32790     EX_END_CATCH(RethrowTerminalExceptions)
32791
32792     return TRUE;
32793 }
32794
32795 #endif // STRESS_HEAP
32796 #endif // FEATURE_REDHAWK
32797
32798
32799 #ifdef FEATURE_PREMORTEM_FINALIZATION
32800 #define REGISTER_FOR_FINALIZATION(_object, _size) \
32801     hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
32802 #else // FEATURE_PREMORTEM_FINALIZATION
32803 #define REGISTER_FOR_FINALIZATION(_object, _size) true
32804 #endif // FEATURE_PREMORTEM_FINALIZATION
32805
32806 #ifdef FEATURE_REDHAWK
32807 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {  \
32808     if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size)))   \
32809     {                                                                                       \
32810         STRESS_LOG_OOM_STACK(_size);                                                        \
32811         return NULL;                                                                        \
32812     }                                                                                       \
32813 } while (false)
32814 #else // FEATURE_REDHAWK
32815 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {  \
32816     if ((_object) == NULL)                                                                  \
32817     {                                                                                       \
32818         STRESS_LOG_OOM_STACK(_size);                                                        \
32819         ThrowOutOfMemory();                                                                 \
32820     }                                                                                       \
32821     if (_register)                                                                          \
32822     {                                                                                       \
32823         REGISTER_FOR_FINALIZATION(_object, _size);                                          \
32824     }                                                                                       \
32825 } while (false)
32826 #endif // FEATURE_REDHAWK
32827
32828 //
32829 // Small Object Allocator
32830 //
32831 //
32832 Object *
32833 GCHeap::Alloc( size_t size, DWORD flags REQD_ALIGN_DCL)
32834 {
32835     CONTRACTL {
32836 #ifdef FEATURE_REDHAWK
32837         // Under Redhawk NULL is returned on failure.
32838         NOTHROW;
32839 #else
32840         THROWS;
32841 #endif
32842         GC_TRIGGERS;
32843     } CONTRACTL_END;
32844
32845 #if defined(_DEBUG) && !defined(FEATURE_REDHAWK)
32846     if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
32847     {
32848         char *a = new char;
32849         delete a;
32850     }
32851 #endif //_DEBUG && !FEATURE_REDHAWK
32852
32853     TRIGGERSGC();
32854
32855     assert (!GCHeap::UseAllocationContexts());
32856
32857     Object* newAlloc = NULL;
32858
32859 #ifdef TRACE_GC
32860 #ifdef COUNT_CYCLES
32861     AllocStart = GetCycleCount32();
32862     unsigned finish;
32863 #elif defined(ENABLE_INSTRUMENTATION)
32864     unsigned AllocStart = GetInstLogTime();
32865     unsigned finish;
32866 #endif //COUNT_CYCLES
32867 #endif //TRACE_GC
32868
32869 #ifdef MULTIPLE_HEAPS
32870     //take the first heap....
32871     gc_heap* hp = gc_heap::g_heaps[0];
32872 #else
32873     gc_heap* hp = pGenGCHeap;
32874 #ifdef _PREFAST_
32875     // prefix complains about us dereferencing hp in wks build even though we only access static members
32876     // this way. not sure how to shut it up except for this ugly workaround:
32877     PREFIX_ASSUME(hp != NULL);
32878 #endif //_PREFAST_
32879 #endif //MULTIPLE_HEAPS
32880
32881     {
32882         AllocLockHolder lh;
32883
32884 #ifndef FEATURE_REDHAWK
32885         GCStress<gc_on_alloc>::MaybeTrigger(generation_alloc_context(hp->generation_of(0)));
32886 #endif // FEATURE_REDHAWK
32887
32888         alloc_context* acontext = 0;
32889
32890         if (size < LARGE_OBJECT_SIZE)
32891         {
32892             acontext = generation_alloc_context (hp->generation_of (0));
32893
32894 #ifdef TRACE_GC
32895             AllocSmallCount++;
32896 #endif //TRACE_GC
32897             newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
32898 #ifdef FEATURE_STRUCTALIGN
32899             newAlloc = (Object*) hp->pad_for_alignment ((BYTE*) newAlloc, requiredAlignment, size, acontext);
32900 #endif // FEATURE_STRUCTALIGN
32901             // ASSERT (newAlloc);
32902         }
32903         else
32904         {
32905             acontext = generation_alloc_context (hp->generation_of (max_generation+1));
32906
32907             newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
32908 #ifdef FEATURE_STRUCTALIGN
32909             newAlloc = (Object*) hp->pad_for_alignment_large ((BYTE*) newAlloc, requiredAlignment, size);
32910 #endif // FEATURE_STRUCTALIGN
32911         }
32912     }
32913
32914     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
32915
32916 #ifdef TRACE_GC
32917 #ifdef COUNT_CYCLES
32918     finish = GetCycleCount32();
32919 #elif defined(ENABLE_INSTRUMENTATION)
32920     finish = GetInstLogTime();
32921 #endif //COUNT_CYCLES
32922     AllocDuration += finish - AllocStart;
32923     AllocCount++;
32924 #endif //TRACE_GC
32925     return newAlloc;
32926 }
32927
32928 #ifdef FEATURE_64BIT_ALIGNMENT
32929 // Allocate small object with an alignment requirement of 8-bytes. Non allocation context version.
32930 Object *
32931 GCHeap::AllocAlign8( size_t size, DWORD flags)
32932 {
32933     CONTRACTL {
32934 #ifdef FEATURE_REDHAWK
32935         // Under Redhawk NULL is returned on failure.
32936         NOTHROW;
32937 #else
32938         THROWS;
32939 #endif
32940         GC_TRIGGERS;
32941     } CONTRACTL_END;
32942
32943     assert (!GCHeap::UseAllocationContexts());
32944
32945     Object* newAlloc = NULL;
32946
32947     {
32948         AllocLockHolder lh;
32949
32950 #ifdef MULTIPLE_HEAPS
32951         //take the first heap....
32952         gc_heap* hp = gc_heap::g_heaps[0];
32953 #else
32954         gc_heap* hp = pGenGCHeap;
32955 #endif //MULTIPLE_HEAPS
32956
32957         newAlloc = AllocAlign8Common(hp, generation_alloc_context (hp->generation_of (0)), size, flags);
32958     }
32959
32960     return newAlloc;
32961 }
32962
32963 // Allocate small object with an alignment requirement of 8-bytes. Allocation context version.
32964 Object*
32965 GCHeap::AllocAlign8(alloc_context* acontext, size_t size, DWORD flags )
32966 {
32967     CONTRACTL {
32968 #ifdef FEATURE_REDHAWK
32969         // Under Redhawk NULL is returned on failure.
32970         NOTHROW;
32971 #else
32972         THROWS;
32973 #endif
32974         GC_TRIGGERS;
32975     } CONTRACTL_END;
32976
32977 #ifdef MULTIPLE_HEAPS
32978     if (acontext->alloc_heap == 0)
32979     {
32980         AssignHeap (acontext);
32981         assert (acontext->alloc_heap);
32982     }
32983
32984     gc_heap* hp = acontext->alloc_heap->pGenGCHeap;
32985 #else
32986     gc_heap* hp = pGenGCHeap;
32987 #endif //MULTIPLE_HEAPS
32988
32989     return AllocAlign8Common(hp, acontext, size, flags);
32990 }
32991
32992 // Common code used by both variants of AllocAlign8 above.
32993 Object*
32994 GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, DWORD flags)
32995 {
32996     CONTRACTL {
32997 #ifdef FEATURE_REDHAWK
32998         // Under Redhawk NULL is returned on failure.
32999         NOTHROW;
33000 #else
33001         THROWS;
33002 #endif
33003         GC_TRIGGERS;
33004     } CONTRACTL_END;
33005
33006     gc_heap* hp = (gc_heap*)_hp;
33007
33008 #if defined(_DEBUG) && !defined(FEATURE_REDHAWK)
33009     if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
33010     {
33011         char *a = new char;
33012         delete a;
33013     }
33014 #endif //_DEBUG && !FEATURE_REDHAWK
33015
33016     TRIGGERSGC();
33017
33018     Object* newAlloc = NULL;
33019
33020 #ifdef TRACE_GC
33021 #ifdef COUNT_CYCLES
33022     AllocStart = GetCycleCount32();
33023     unsigned finish;
33024 #elif defined(ENABLE_INSTRUMENTATION)
33025     unsigned AllocStart = GetInstLogTime();
33026     unsigned finish;
33027 #endif //COUNT_CYCLES
33028 #endif //TRACE_GC
33029
33030 #ifndef FEATURE_REDHAWK
33031     GCStress<gc_on_alloc>::MaybeTrigger(acontext);
33032 #endif // FEATURE_REDHAWK
33033
33034     if (size < LARGE_OBJECT_SIZE)
33035     {
33036 #ifdef TRACE_GC
33037         AllocSmallCount++;
33038 #endif //TRACE_GC
33039
33040         // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
33041         // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
33042         // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
33043         size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
33044
33045         // Retrieve the address of the next allocation from the context (note that we're inside the alloc
33046         // lock at this point).
33047         BYTE*  result = acontext->alloc_ptr;
33048
33049         // Will an allocation at this point yield the correct alignment and fit into the remainder of the
33050         // context?
33051         if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
33052         {
33053             // Yes, we can just go ahead and make the allocation.
33054             newAlloc = (Object*) hp->allocate (size, acontext);
33055             ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
33056         }
33057         else
33058         {
33059             // No, either the next available address is not aligned in the way we require it or there's
33060             // not enough space to allocate an object of the required size. In both cases we allocate a
33061             // padding object (marked as a free object). This object's size is such that it will reverse
33062             // the alignment of the next header (asserted below).
33063             //
33064             // We allocate both together then decide based on the result whether we'll format the space as
33065             // free object + real object or real object + free object.
33066             ASSERT((Align(min_obj_size) & 7) == 4);
33067             CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
33068             if (freeobj)
33069             {
33070                 if (((size_t)freeobj & 7) == desiredAlignment)
33071                 {
33072                     // New allocation has desired alignment, return this one and place the free object at the
33073                     // end of the allocated space.
33074                     newAlloc = (Object*)freeobj;
33075                     freeobj = (CObjectHeader*)((BYTE*)freeobj + Align(size));
33076                 }
33077                 else
33078                 {
33079                     // New allocation is still mis-aligned, format the initial space as a free object and the
33080                     // rest of the space should be correctly aligned for the real object.
33081                     newAlloc = (Object*)((BYTE*)freeobj + Align(min_obj_size));
33082                     ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
33083                 }
33084                 freeobj->SetFree(min_obj_size);
33085             }
33086         }
33087     }
33088     else
33089     {
33090         // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
33091         // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
33092         // we've managed to arrange things so the only case where we see a bias is for boxed value types and
33093         // these can never get large enough to be allocated on the LOH.
33094         ASSERT(65536 < LARGE_OBJECT_SIZE);
33095         ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
33096
33097         alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
33098
33099         newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
33100         ASSERT(((size_t)newAlloc & 7) == 0);
33101     }
33102
33103     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
33104
33105 #ifdef TRACE_GC
33106 #ifdef COUNT_CYCLES
33107     finish = GetCycleCount32();
33108 #elif defined(ENABLE_INSTRUMENTATION)
33109     finish = GetInstLogTime();
33110 #endif //COUNT_CYCLES
33111     AllocDuration += finish - AllocStart;
33112     AllocCount++;
33113 #endif //TRACE_GC
33114     return newAlloc;
33115 }
33116 #endif // FEATURE_64BIT_ALIGNMENT
33117
33118 Object *
33119 GCHeap::AllocLHeap( size_t size, DWORD flags REQD_ALIGN_DCL)
33120 {
33121     CONTRACTL {
33122 #ifdef FEATURE_REDHAWK
33123         // Under Redhawk NULL is returned on failure.
33124         NOTHROW;
33125 #else
33126         THROWS;
33127 #endif
33128         GC_TRIGGERS;
33129     } CONTRACTL_END;
33130
33131 #if defined(_DEBUG) && !defined(FEATURE_REDHAWK)
33132     if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
33133     {
33134         char *a = new char;
33135         delete a;
33136     }
33137 #endif //_DEBUG && !FEATURE_REDHAWK
33138
33139     TRIGGERSGC();
33140
33141     Object* newAlloc = NULL;
33142
33143 #ifdef TRACE_GC
33144 #ifdef COUNT_CYCLES
33145     AllocStart = GetCycleCount32();
33146     unsigned finish;
33147 #elif defined(ENABLE_INSTRUMENTATION)
33148     unsigned AllocStart = GetInstLogTime();
33149     unsigned finish;
33150 #endif //COUNT_CYCLES
33151 #endif //TRACE_GC
33152
33153 #ifdef MULTIPLE_HEAPS
33154     //take the first heap....
33155     gc_heap* hp = gc_heap::g_heaps[0];
33156 #else
33157     gc_heap* hp = pGenGCHeap;
33158 #ifdef _PREFAST_
33159     // prefix complains about us dereferencing hp in wks build even though we only access static members
33160     // this way. not sure how to shut it up except for this ugly workaround:
33161     PREFIX_ASSUME(hp != NULL);
33162 #endif //_PREFAST_
33163 #endif //MULTIPLE_HEAPS
33164
33165 #ifndef FEATURE_REDHAWK
33166     GCStress<gc_on_alloc>::MaybeTrigger(generation_alloc_context(hp->generation_of(0)));
33167 #endif // FEATURE_REDHAWK
33168
33169     alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
33170
33171     newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
33172 #ifdef FEATURE_STRUCTALIGN
33173     newAlloc = (Object*) hp->pad_for_alignment_large ((BYTE*) newAlloc, requiredAlignment, size);
33174 #endif // FEATURE_STRUCTALIGN
33175     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
33176
33177 #ifdef TRACE_GC
33178 #ifdef COUNT_CYCLES
33179     finish = GetCycleCount32();
33180 #elif defined(ENABLE_INSTRUMENTATION)
33181     finish = GetInstLogTime();
33182 #endif //COUNT_CYCLES
33183     AllocDuration += finish - AllocStart;
33184     AllocCount++;
33185 #endif //TRACE_GC
33186     return newAlloc;
33187 }
33188
33189 Object*
33190 GCHeap::Alloc(alloc_context* acontext, size_t size, DWORD flags REQD_ALIGN_DCL)
33191 {
33192     CONTRACTL {
33193 #ifdef FEATURE_REDHAWK
33194         // Under Redhawk NULL is returned on failure.
33195         NOTHROW;
33196 #else
33197         THROWS;
33198 #endif
33199         GC_TRIGGERS;
33200     } CONTRACTL_END;
33201
33202 #if defined(_DEBUG) && !defined(FEATURE_REDHAWK)
33203     if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
33204     {
33205         char *a = new char;
33206         delete a;
33207     }
33208 #endif //_DEBUG && !FEATURE_REDHAWK
33209
33210     TRIGGERSGC();
33211
33212     Object* newAlloc = NULL;
33213
33214 #ifdef TRACE_GC
33215 #ifdef COUNT_CYCLES
33216     AllocStart = GetCycleCount32();
33217     unsigned finish;
33218 #elif defined(ENABLE_INSTRUMENTATION)
33219     unsigned AllocStart = GetInstLogTime();
33220     unsigned finish;
33221 #endif //COUNT_CYCLES
33222 #endif //TRACE_GC
33223
33224 #ifdef MULTIPLE_HEAPS
33225     if (acontext->alloc_heap == 0)
33226     {
33227         AssignHeap (acontext);
33228         assert (acontext->alloc_heap);
33229     }
33230 #endif //MULTIPLE_HEAPS
33231
33232 #ifndef FEATURE_REDHAWK
33233     GCStress<gc_on_alloc>::MaybeTrigger(acontext);
33234 #endif // FEATURE_REDHAWK
33235
33236 #ifdef MULTIPLE_HEAPS
33237     gc_heap* hp = acontext->alloc_heap->pGenGCHeap;
33238 #else
33239     gc_heap* hp = pGenGCHeap;
33240 #ifdef _PREFAST_
33241     // prefix complains about us dereferencing hp in wks build even though we only access static members
33242     // this way. not sure how to shut it up except for this ugly workaround:
33243     PREFIX_ASSUME(hp != NULL);
33244 #endif //_PREFAST_
33245 #endif //MULTIPLE_HEAPS
33246
33247     if (size < LARGE_OBJECT_SIZE)
33248     {
33249
33250 #ifdef TRACE_GC
33251         AllocSmallCount++;
33252 #endif //TRACE_GC
33253         newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
33254 #ifdef FEATURE_STRUCTALIGN
33255         newAlloc = (Object*) hp->pad_for_alignment ((BYTE*) newAlloc, requiredAlignment, size, acontext);
33256 #endif // FEATURE_STRUCTALIGN
33257 //        ASSERT (newAlloc);
33258     }
33259     else 
33260     {
33261         newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
33262 #ifdef FEATURE_STRUCTALIGN
33263         newAlloc = (Object*) hp->pad_for_alignment_large ((BYTE*) newAlloc, requiredAlignment, size);
33264 #endif // FEATURE_STRUCTALIGN
33265     }
33266
33267     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
33268
33269 #ifdef TRACE_GC
33270 #ifdef COUNT_CYCLES
33271     finish = GetCycleCount32();
33272 #elif defined(ENABLE_INSTRUMENTATION)
33273     finish = GetInstLogTime();
33274 #endif //COUNT_CYCLES
33275     AllocDuration += finish - AllocStart;
33276     AllocCount++;
33277 #endif //TRACE_GC
33278     return newAlloc;
33279 }
33280
33281 void
33282 GCHeap::FixAllocContext (alloc_context* acontext, BOOL lockp, void* arg, void *heap)
33283 {
33284 #ifdef MULTIPLE_HEAPS
33285
33286     if (arg != 0)
33287         acontext->alloc_count = 0;
33288
33289     BYTE * alloc_ptr = acontext->alloc_ptr;
33290
33291     if (!alloc_ptr)
33292         return;
33293
33294     // The acontext->alloc_heap can be out of sync with the ptrs because
33295     // of heap re-assignment in allocate
33296     gc_heap* hp = gc_heap::heap_of (alloc_ptr);
33297 #else
33298     gc_heap* hp = pGenGCHeap;
33299 #endif //MULTIPLE_HEAPS
33300
33301     if (heap == NULL || heap == hp)
33302     {
33303         if (lockp)
33304         {
33305             enter_spin_lock (&hp->more_space_lock);
33306         }
33307         hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
33308                                 get_alignment_constant(TRUE));
33309         if (lockp)
33310         {
33311             leave_spin_lock (&hp->more_space_lock);
33312         }
33313     }
33314 }
33315
33316 Object*
33317 GCHeap::GetContainingObject (void *pInteriorPtr)
33318 {
33319     BYTE *o = (BYTE*)pInteriorPtr;
33320
33321     gc_heap* hp = gc_heap::heap_of (o);
33322     if (o >= hp->lowest_address && o < hp->highest_address)
33323     {
33324         o = hp->find_object (o, hp->gc_low);
33325     }
33326     else
33327     {
33328         o = NULL;
33329     }
33330     
33331     return (Object *)o;
33332 }
33333
33334 BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
33335 {
33336     if (dd_new_allocation (dd) < 0)
33337     {
33338         return TRUE;
33339     }
33340
33341     if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
33342     {
33343         return TRUE;
33344     }
33345
33346     return FALSE;
33347 }
33348
33349 //----------------------------------------------------------------------------
33350 // #GarbageCollector
33351 //
33352 //  API to ensure that a complete new garbage collection takes place
33353 //
33354 HRESULT
33355 GCHeap::GarbageCollect (int generation, BOOL low_memory_p, int mode)
33356 {
33357 #if defined(_WIN64) 
33358     if (low_memory_p)
33359     {
33360         size_t total_allocated = 0;
33361         size_t total_desired = 0;
33362 #ifdef MULTIPLE_HEAPS
33363         int hn = 0;
33364         for (hn = 0; hn < gc_heap::n_heaps; hn++)
33365         {
33366             gc_heap* hp = gc_heap::g_heaps [hn];
33367             total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
33368             total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
33369                 dd_new_allocation (hp->dynamic_data_of (0));
33370         }
33371 #else
33372         gc_heap* hp = pGenGCHeap;
33373         total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
33374         total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
33375             dd_new_allocation (hp->dynamic_data_of (0));
33376 #endif //MULTIPLE_HEAPS
33377
33378         if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
33379         {
33380             dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
33381                          total_allocated, total_desired));
33382
33383             return S_OK;
33384         }
33385     }
33386 #endif //_WIN64 
33387
33388 #ifdef MULTIPLE_HEAPS
33389     gc_heap* hpt = gc_heap::g_heaps[0];
33390 #else
33391     gc_heap* hpt = 0;
33392 #endif //MULTIPLE_HEAPS
33393
33394     generation = (generation < 0) ? max_generation : min (generation, max_generation);
33395     dynamic_data* dd = hpt->dynamic_data_of (generation);
33396
33397 #ifdef BACKGROUND_GC
33398     if (recursive_gc_sync::background_running_p())
33399     {
33400         if ((mode == collection_optimized) || (mode & collection_non_blocking))
33401         {
33402             return S_OK;
33403         }
33404         if (mode & collection_blocking)
33405         {
33406             pGenGCHeap->background_gc_wait();
33407             if (mode & collection_optimized)
33408             {
33409                 return S_OK;
33410             }
33411         }
33412     }
33413 #endif //BACKGROUND_GC
33414
33415     if (mode & collection_optimized)
33416     {
33417         if (pGenGCHeap->gc_started)
33418         {
33419             return S_OK;
33420         }
33421         else 
33422         {
33423             BOOL should_collect = FALSE;
33424             BOOL should_check_loh = (generation == max_generation);
33425 #ifdef MULTIPLE_HEAPS
33426             for (int i = 0; i < gc_heap::n_heaps; i++)
33427             {
33428                 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
33429                 dynamic_data* dd2 = (should_check_loh ? 
33430                                      (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
33431                                      0);
33432
33433                 if (should_collect_optimized (dd1, low_memory_p))
33434                 {
33435                     should_collect = TRUE;
33436                     break;
33437                 }
33438                 if (dd2 && should_collect_optimized (dd2, low_memory_p))
33439                 {
33440                     should_collect = TRUE;
33441                     break;
33442                 }
33443             }
33444 #else
33445             should_collect = should_collect_optimized (dd, low_memory_p);
33446             if (!should_collect && should_check_loh)
33447             {
33448                 should_collect = 
33449                     should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
33450             }
33451 #endif //MULTIPLE_HEAPS
33452             if (!should_collect)
33453             {
33454                 return S_OK;
33455             }
33456         }
33457     }
33458
33459     size_t CollectionCountAtEntry = dd_collection_count (dd);
33460     size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
33461     size_t CurrentCollectionCount = 0;
33462
33463 retry:
33464
33465     CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
33466     
33467     if ((mode & collection_blocking) && 
33468         (generation == max_generation) && 
33469         (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
33470     {
33471 #ifdef BACKGROUND_GC
33472         if (recursive_gc_sync::background_running_p())
33473         {
33474             pGenGCHeap->background_gc_wait();
33475         }
33476 #endif //BACKGROUND_GC
33477
33478         goto retry;
33479     }
33480
33481     if (CollectionCountAtEntry == CurrentCollectionCount)
33482     {
33483         goto retry;
33484     }
33485
33486     return S_OK;
33487 }
33488
33489 size_t
33490 GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
33491 {
33492     int gen = (generation < 0) ? 
33493                max_generation : min (generation, max_generation);
33494
33495     gc_reason reason = reason_empty;
33496     
33497     if (low_memory_p ) 
33498     {
33499         if (mode & collection_blocking)
33500             reason = reason_lowmemory_blocking;
33501         else
33502             reason = reason_lowmemory;
33503     }
33504     else
33505         reason = reason_induced;
33506
33507     if (reason == reason_induced)
33508     {
33509         if (mode & collection_compacting)
33510         {
33511             reason = reason_induced_compacting;
33512         }
33513         else if (mode & collection_non_blocking)
33514         {
33515             reason = reason_induced_noforce;
33516         }
33517 #ifdef STRESS_HEAP
33518         else if (mode & collection_gcstress)
33519         {
33520             reason = reason_gcstress;
33521         }
33522 #endif
33523     }
33524
33525     return GarbageCollectGeneration (gen, reason);
33526 }
33527
33528 void gc_heap::do_pre_gc()
33529 {
33530     STRESS_LOG_GC_STACK;
33531
33532 #ifdef STRESS_LOG
33533     STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
33534                         (ULONG)settings.condemned_generation,
33535                         (ULONG)settings.reason);
33536 #endif // STRESS_LOG
33537
33538 #ifdef BACKGROUND_GC
33539     settings.b_state = current_bgc_state;
33540 #endif //BACKGROUND_GC
33541
33542 #ifdef BACKGROUND_GC
33543     dprintf (1, ("*GC* %d(gen0:%d)(%d)(%s)(%d)", 
33544         VolatileLoad(&settings.gc_index), 
33545         dd_collection_count (dynamic_data_of (0)),
33546         settings.condemned_generation,
33547         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
33548         VolatileLoad(&current_bgc_state)));
33549 #else
33550     dprintf (1, ("*GC* %d(gen0:%d)(%d)", 
33551         VolatileLoad(&settings.gc_index), 
33552         dd_collection_count (dynamic_data_of (0)),
33553         settings.condemned_generation));
33554 #endif //BACKGROUND_GC
33555
33556     // TODO: this can happen...it's because of the way we are calling
33557     // do_pre_gc, will fix later.
33558     //if (last_gc_index > VolatileLoad(&settings.gc_index))
33559     //{
33560     //    FATAL_GC_ERROR();
33561     //}
33562
33563     last_gc_index = VolatileLoad(&settings.gc_index);
33564     GCHeap::UpdatePreGCCounters();
33565
33566     if (settings.concurrent)
33567     {
33568 #ifdef BACKGROUND_GC
33569         full_gc_counts[gc_type_background]++;
33570 #ifdef STRESS_HEAP
33571         GCHeap::gc_stress_fgcs_in_bgc = 0;
33572 #endif // STRESS_HEAP
33573 #endif // BACKGROUND_GC
33574     }
33575     else
33576     {
33577         if (settings.condemned_generation == max_generation)
33578         {
33579             full_gc_counts[gc_type_blocking]++;
33580         }
33581         else
33582         {
33583 #ifdef BACKGROUND_GC
33584             if (settings.background_p)
33585             {
33586                 ephemeral_fgc_counts[settings.condemned_generation]++;
33587             }
33588 #endif //BACKGROUND_GC
33589         }
33590     }
33591
33592 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33593     if (g_fEnableARM)
33594     {
33595         SystemDomain::ResetADSurvivedBytes();
33596     }
33597 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33598 }
33599
33600 void gc_heap::do_post_gc()
33601 {
33602     if (!settings.concurrent)
33603     {
33604         GCProfileWalkHeap();
33605         initGCShadow();
33606     }
33607
33608 #ifdef TRACE_GC
33609 #ifdef COUNT_CYCLES
33610     AllocStart = GetCycleCount32();
33611 #else
33612     AllocStart = clock();
33613 #endif //COUNT_CYCLES
33614 #endif //TRACE_GC
33615
33616     GCToEEInterface::GcDone(settings.condemned_generation);
33617
33618 #ifdef GC_PROFILING
33619     if (!settings.concurrent)
33620     {
33621         UpdateGenerationBounds();
33622         GarbageCollectionFinishedCallback();
33623     }
33624 #endif // GC_PROFILING
33625
33626
33627     //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)", 
33628     dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)", 
33629         VolatileLoad(&settings.gc_index), 
33630         dd_collection_count (dynamic_data_of (0)),
33631         settings.condemned_generation,
33632         (settings.concurrent ? "BGC" : "GC")));
33633
33634     GCHeap::UpdatePostGCCounters();
33635 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33636     //if (g_fEnableARM)
33637     //{
33638     //    SystemDomain::GetADSurvivedBytes();
33639     //}
33640 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33641
33642 #ifdef STRESS_LOG
33643     STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
33644                       (ULONG)settings.condemned_generation,
33645                       (ULONG)settings.reason);
33646 #endif // STRESS_LOG
33647 }
33648
33649 unsigned GCHeap::GetGcCount()
33650 {
33651     return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
33652 }
33653
33654 size_t
33655 GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
33656 {
33657     dprintf (2, ("triggered a GC!"));
33658
33659 #ifdef MULTIPLE_HEAPS
33660     gc_heap* hpt = gc_heap::g_heaps[0];
33661 #else
33662     gc_heap* hpt = 0;
33663 #endif //MULTIPLE_HEAPS
33664     Thread* current_thread = GetThread();
33665     BOOL cooperative_mode = TRUE;
33666     dynamic_data* dd = hpt->dynamic_data_of (gen);
33667     size_t localCount = dd_collection_count (dd);
33668
33669     enter_spin_lock (&gc_heap::gc_lock);
33670     dprintf (SPINLOCK_LOG, ("GC Egc"));
33671     ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
33672
33673     //don't trigger another GC if one was already in progress
33674     //while waiting for the lock
33675     {
33676         size_t col_count = dd_collection_count (dd);
33677
33678         if (localCount != col_count)
33679         {
33680 #ifdef SYNCHRONIZATION_STATS
33681             gc_lock_contended++;
33682 #endif //SYNCHRONIZATION_STATS
33683             dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
33684             leave_spin_lock (&gc_heap::gc_lock);
33685
33686             // We don't need to release msl here 'cause this means a GC
33687             // has happened and would have release all msl's.
33688             return col_count;
33689          }
33690     }
33691
33692 #ifdef COUNT_CYCLES
33693     int gc_start = GetCycleCount32();
33694 #endif //COUNT_CYCLES
33695
33696 #if defined ( _DEBUG) && defined (CATCH_GC)
33697     __try
33698 #endif // _DEBUG && CATCH_GC
33699     {
33700 #ifdef TRACE_GC
33701 #ifdef COUNT_CYCLES
33702         AllocDuration += GetCycleCount32() - AllocStart;
33703 #else
33704         AllocDuration += clock() - AllocStart;
33705 #endif //COUNT_CYCLES
33706 #endif //TRACE_GC
33707
33708         gc_heap::g_low_memory_status = (reason == reason_lowmemory) || 
33709                                         (reason == reason_lowmemory_blocking) ||
33710                                         g_bLowMemoryFromHost;
33711         gc_trigger_reason = reason;
33712
33713 #ifdef MULTIPLE_HEAPS
33714         for (int i = 0; i < gc_heap::n_heaps; i++)
33715         {
33716             gc_heap::g_heaps[i]->reset_gc_done();
33717         }
33718 #else
33719         gc_heap::reset_gc_done();
33720 #endif //MULTIPLE_HEAPS
33721
33722         gc_heap::gc_started = TRUE;
33723
33724         {
33725             init_sync_log_stats();
33726
33727 #ifndef MULTIPLE_HEAPS
33728             cooperative_mode = gc_heap::enable_preemptive (current_thread);
33729
33730             dprintf (2, ("Suspending EE"));
33731             BEGIN_TIMING(suspend_ee_during_log);
33732             GCToEEInterface::SuspendEE(GCToEEInterface::SUSPEND_FOR_GC);
33733             END_TIMING(suspend_ee_during_log);
33734             pGenGCHeap->settings.init_mechanisms();
33735             gc_heap::disable_preemptive (current_thread, cooperative_mode);
33736 #endif //!MULTIPLE_HEAPS
33737         }
33738
33739     // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
33740
33741 #ifdef TRACE_GC
33742 #ifdef COUNT_CYCLES
33743         unsigned start;
33744         unsigned finish;
33745         start = GetCycleCount32();
33746 #else
33747         clock_t start;
33748         clock_t finish;
33749         start = clock();
33750 #endif //COUNT_CYCLES
33751         PromotedObjectCount = 0;
33752 #endif //TRACE_GC
33753
33754         unsigned int condemned_generation_number = gen;
33755
33756         // We want to get a stack from the user thread that triggered the GC
33757         // instead of on the GC thread which is the case for Server GC.
33758         // But we are doing it for Workstation GC as well to be uniform.
33759         FireEtwGCTriggered((int) reason, GetClrInstanceId());
33760
33761 #ifdef MULTIPLE_HEAPS
33762         GcCondemnedGeneration = condemned_generation_number;
33763  
33764         cooperative_mode = gc_heap::enable_preemptive (current_thread);
33765
33766         BEGIN_TIMING(gc_during_log);
33767         gc_heap::ee_suspend_event.Set();
33768         gc_heap::wait_for_gc_done();
33769         END_TIMING(gc_during_log);
33770
33771         gc_heap::disable_preemptive (current_thread, cooperative_mode);
33772
33773         condemned_generation_number = GcCondemnedGeneration;
33774 #else
33775         BEGIN_TIMING(gc_during_log);
33776         pGenGCHeap->garbage_collect (condemned_generation_number);
33777         END_TIMING(gc_during_log);
33778 #endif //MULTIPLE_HEAPS
33779
33780 #ifdef TRACE_GC
33781 #ifdef COUNT_CYCLES
33782         finish = GetCycleCount32();
33783 #else
33784         finish = clock();
33785 #endif //COUNT_CYCLES
33786         GcDuration += finish - start;
33787         dprintf (3,
33788                  ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
33789                   VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
33790                   finish - start, GcDuration,
33791                   AllocCount ? (AllocDuration / AllocCount) : 0,
33792                   AllocSmallCount, AllocBigCount));
33793         AllocCount = 0;
33794         AllocDuration = 0;
33795 #endif // TRACE_GC
33796
33797 #ifdef BACKGROUND_GC
33798         // We are deciding whether we should fire the alloc wait end event here
33799         // because in begin_foreground we could be calling end_foreground 
33800         // if we need to retry.
33801         if (gc_heap::alloc_wait_event_p)
33802         {
33803             hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
33804             gc_heap::alloc_wait_event_p = FALSE;
33805         }
33806 #endif //BACKGROUND_GC
33807
33808 #ifndef MULTIPLE_HEAPS
33809 #ifdef BACKGROUND_GC
33810         if (!gc_heap::dont_restart_ee_p)
33811         {
33812 #endif //BACKGROUND_GC
33813             BEGIN_TIMING(restart_ee_during_log);
33814             GCToEEInterface::RestartEE(TRUE);
33815             END_TIMING(restart_ee_during_log);
33816 #ifdef BACKGROUND_GC
33817         }
33818 #endif //BACKGROUND_GC
33819 #endif //!MULTIPLE_HEAPS
33820
33821     }
33822 #if defined (_DEBUG) && defined (CATCH_GC)
33823     __except (CheckException(GetExceptionInformation(), NULL))
33824     {
33825         _ASSERTE(!"Exception during GarbageCollectGeneration()");
33826     }
33827 #endif // _DEBUG && CATCH_GC
33828
33829 #ifdef COUNT_CYCLES
33830     printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
33831             GetCycleCount32() - gc_start);
33832 #endif //COUNT_CYCLES
33833
33834 #ifndef MULTIPLE_HEAPS
33835     process_sync_log_stats();
33836     gc_heap::gc_started = FALSE;
33837     gc_heap::set_gc_done();
33838     dprintf (SPINLOCK_LOG, ("GC Lgc"));
33839     leave_spin_lock (&gc_heap::gc_lock);    
33840 #endif //!MULTIPLE_HEAPS
33841
33842 #ifdef FEATURE_PREMORTEM_FINALIZATION
33843     if (!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers || 
33844         FinalizerThread::HaveExtraWorkForFinalizer())
33845     {
33846         FinalizerThread::EnableFinalization();
33847     }
33848 #endif // FEATURE_PREMORTEM_FINALIZATION
33849
33850     return dd_collection_count (dd);
33851 }
33852
33853 size_t      GCHeap::GetTotalBytesInUse ()
33854 {
33855 #ifdef MULTIPLE_HEAPS
33856     //enumarate all the heaps and get their size.
33857     size_t tot_size = 0;
33858     for (int i = 0; i < gc_heap::n_heaps; i++)
33859     {
33860         GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
33861         tot_size += Hp->ApproxTotalBytesInUse (FALSE);
33862     }
33863     return tot_size;
33864 #else
33865     return ApproxTotalBytesInUse ();
33866 #endif //MULTIPLE_HEAPS
33867 }
33868
33869 int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
33870 {
33871     if (get_bgc_fgc_count != 0)
33872     {
33873 #ifdef BACKGROUND_GC
33874         if (generation == max_generation)
33875         {
33876             return (int)(gc_heap::full_gc_counts[gc_type_background]);
33877         }
33878         else
33879         {
33880             return (int)(gc_heap::ephemeral_fgc_counts[generation]);
33881         }
33882 #else
33883         return 0;
33884 #endif //BACKGROUND_GC
33885     }
33886
33887 #ifdef MULTIPLE_HEAPS
33888     gc_heap* hp = gc_heap::g_heaps [0];
33889 #else  //MULTIPLE_HEAPS
33890     gc_heap* hp = pGenGCHeap;
33891 #endif //MULTIPLE_HEAPS
33892     if (generation > max_generation)
33893         return 0;
33894     else
33895         return (int)dd_collection_count (hp->dynamic_data_of (generation));
33896 }
33897
33898 size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
33899 {
33900     size_t totsize = 0;
33901     //GCTODO
33902     //ASSERT(InMustComplete());
33903     enter_spin_lock (&pGenGCHeap->gc_lock);
33904
33905     heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
33906     // Get small block heap size info
33907     totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
33908     heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
33909     while (seg1 != eph_seg)
33910     {
33911         totsize += heap_segment_allocated (seg1) -
33912             heap_segment_mem (seg1);
33913         seg1 = heap_segment_next (seg1);
33914     }
33915
33916     //discount the fragmentation
33917     for (int i = 0; i <= max_generation; i++)
33918     {
33919         generation* gen = pGenGCHeap->generation_of (i);
33920         totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
33921     }
33922
33923     if (!small_heap_only)
33924     {
33925         heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
33926
33927         while (seg2 != 0)
33928         {
33929             totsize += heap_segment_allocated (seg2) -
33930                 heap_segment_mem (seg2);
33931             seg2 = heap_segment_next (seg2);
33932         }
33933
33934         //discount the fragmentation
33935         generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
33936         size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
33937         totsize -= frag;
33938     }
33939     leave_spin_lock (&pGenGCHeap->gc_lock);
33940     return totsize;
33941 }
33942
33943 #ifdef MULTIPLE_HEAPS
33944 void GCHeap::AssignHeap (alloc_context* acontext)
33945 {
33946     // Assign heap based on processor
33947     acontext->alloc_heap = GetHeap(heap_select::select_heap(acontext, 0));
33948     acontext->home_heap = acontext->alloc_heap;
33949 }
33950 GCHeap* GCHeap::GetHeap (int n)
33951 {
33952     assert (n < gc_heap::n_heaps);
33953     return gc_heap::g_heaps [n]->vm_heap;
33954 }
33955 #endif //MULTIPLE_HEAPS
33956
33957 bool GCHeap::IsThreadUsingAllocationContextHeap(alloc_context* acontext, int thread_number)
33958 {
33959 #ifdef MULTIPLE_HEAPS
33960     return ((acontext->home_heap == GetHeap(thread_number)) ||
33961             (acontext->home_heap == 0) && (thread_number == 0));
33962 #else
33963     return true;
33964 #endif //MULTIPLE_HEAPS
33965 }
33966
33967 // Returns the number of processors required to trigger the use of thread based allocation contexts
33968 int GCHeap::GetNumberOfHeaps ()
33969 {
33970 #ifdef MULTIPLE_HEAPS
33971     return gc_heap::n_heaps;
33972 #else
33973     return 1;
33974 #endif //MULTIPLE_HEAPS
33975 }
33976
33977 /*
33978   in this way we spend extra time cycling through all the heaps while create the handle
33979   it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
33980 */
33981 int GCHeap::GetHomeHeapNumber ()
33982 {
33983 #ifdef MULTIPLE_HEAPS
33984     Thread *pThread = GetThread();
33985     for (int i = 0; i < gc_heap::n_heaps; i++)
33986     {
33987         if (pThread)
33988         {
33989             GCHeap *hp = pThread->GetAllocContext()->home_heap;
33990             if (hp == gc_heap::g_heaps[i]->vm_heap) return i;
33991         }
33992     }
33993     return 0;
33994 #else
33995     return 0;
33996 #endif //MULTIPLE_HEAPS
33997 }
33998
33999 unsigned int GCHeap::GetCondemnedGeneration()
34000
34001     return gc_heap::settings.condemned_generation;
34002 }
34003
34004 int GCHeap::GetGcLatencyMode()
34005 {
34006     return (int)(pGenGCHeap->settings.pause_mode);
34007 }
34008
34009 int GCHeap::SetGcLatencyMode (int newLatencyMode)
34010 {
34011     gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
34012
34013     if (new_mode == pause_low_latency)
34014     {
34015 #ifndef MULTIPLE_HEAPS
34016         pGenGCHeap->settings.pause_mode = new_mode;
34017 #endif //!MULTIPLE_HEAPS
34018     }
34019     else if (new_mode == pause_sustained_low_latency)
34020     {
34021 #ifdef BACKGROUND_GC
34022         if (gc_heap::gc_can_use_concurrent)
34023         {
34024             pGenGCHeap->settings.pause_mode = new_mode;
34025         }
34026 #endif //BACKGROUND_GC
34027     }
34028     else
34029     {
34030         pGenGCHeap->settings.pause_mode = new_mode;
34031     }
34032
34033 #ifdef BACKGROUND_GC
34034     if (recursive_gc_sync::background_running_p())
34035     {
34036         // If we get here, it means we are doing an FGC. If the pause
34037         // mode was altered we will need to save it in the BGC settings.
34038         if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
34039         {
34040             gc_heap::saved_bgc_settings.pause_mode = new_mode;
34041         }
34042     }
34043 #endif //BACKGROUND_GC
34044
34045     return (int)set_pause_mode_success;
34046 }
34047
34048 int GCHeap::GetLOHCompactionMode()
34049 {
34050     return pGenGCHeap->loh_compaction_mode;
34051 }
34052
34053 void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
34054 {
34055 #ifdef FEATURE_LOH_COMPACTION
34056     pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
34057 #endif //FEATURE_LOH_COMPACTION
34058 }
34059
34060 BOOL GCHeap::RegisterForFullGCNotification(DWORD gen2Percentage,
34061                                            DWORD lohPercentage)
34062 {
34063 #ifdef MULTIPLE_HEAPS
34064     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34065     {
34066         gc_heap* hp = gc_heap::g_heaps [hn];
34067         hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
34068     }
34069 #else //MULTIPLE_HEAPS
34070     pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
34071 #endif //MULTIPLE_HEAPS
34072
34073     pGenGCHeap->full_gc_approach_event.Reset();
34074     pGenGCHeap->full_gc_end_event.Reset();
34075     pGenGCHeap->full_gc_approach_event_set = false;
34076
34077     pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
34078     pGenGCHeap->fgn_loh_percent = lohPercentage;
34079
34080     return TRUE;
34081 }
34082
34083 BOOL GCHeap::CancelFullGCNotification()
34084 {
34085     pGenGCHeap->fgn_maxgen_percent = 0;
34086     pGenGCHeap->fgn_loh_percent = 0;
34087
34088     pGenGCHeap->full_gc_approach_event.Set();
34089     pGenGCHeap->full_gc_end_event.Set();
34090     
34091     return TRUE;
34092 }
34093
34094 int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
34095 {
34096     dprintf (2, ("WFGA: Begin wait"));
34097     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
34098     dprintf (2, ("WFGA: End wait"));
34099     return result;
34100 }
34101
34102 int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
34103 {
34104     dprintf (2, ("WFGE: Begin wait"));
34105     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
34106     dprintf (2, ("WFGE: End wait"));
34107     return result;
34108 }
34109
34110 void GCHeap::PublishObject (BYTE* Obj)
34111 {
34112 #ifdef BACKGROUND_GC
34113     gc_heap* hp = gc_heap::heap_of (Obj);
34114     hp->bgc_alloc_lock->loh_alloc_done (Obj);
34115 #endif //BACKGROUND_GC
34116 }
34117
34118 // The spec for this one isn't clear. This function
34119 // returns the size that can be allocated without
34120 // triggering a GC of any kind.
34121 size_t GCHeap::ApproxFreeBytes()
34122 {
34123     //GCTODO
34124     //ASSERT(InMustComplete());
34125     enter_spin_lock (&pGenGCHeap->gc_lock);
34126
34127     generation* gen = pGenGCHeap->generation_of (0);
34128     size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
34129
34130     leave_spin_lock (&pGenGCHeap->gc_lock);
34131
34132     return res;
34133 }
34134
34135 HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
34136 {
34137     if ((gen < 0) || (gen > max_generation))
34138         return E_FAIL;
34139 #ifdef MULTIPLE_HEAPS
34140     counters->current_size = 0;
34141     counters->promoted_size = 0;
34142     counters->collection_count = 0;
34143
34144     //enumarate all the heaps and get their counters.
34145     for (int i = 0; i < gc_heap::n_heaps; i++)
34146     {
34147         dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
34148
34149         counters->current_size += dd_current_size (dd);
34150         counters->promoted_size += dd_promoted_size (dd);
34151         if (i == 0)
34152         counters->collection_count += dd_collection_count (dd);
34153     }
34154 #else
34155     dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
34156     counters->current_size = dd_current_size (dd);
34157     counters->promoted_size = dd_promoted_size (dd);
34158     counters->collection_count = dd_collection_count (dd);
34159 #endif //MULTIPLE_HEAPS
34160     return S_OK;
34161 }
34162
34163 // Get the segment size to use, making sure it conforms.
34164 size_t GCHeap::GetValidSegmentSize(BOOL large_seg)
34165 {
34166     return get_valid_segment_size (large_seg);
34167 }
34168
34169 // Get the max gen0 heap size, making sure it conforms.
34170 size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
34171 {
34172     size_t gen0size = g_pConfig->GetGCgen0size();
34173
34174     if ((gen0size == 0) || !GCHeap::IsValidGen0MaxSize(gen0size))
34175     {
34176 #if !defined(FEATURE_REDHAWK)
34177 #ifdef SERVER_GC
34178         // performance data seems to indicate halving the size results
34179         // in optimal perf.  Ask for adjusted gen0 size.
34180         gen0size = max(GetLargestOnDieCacheSize(FALSE)/GetLogicalCpuCount(),(256*1024));
34181 #if (defined(_TARGET_AMD64_))
34182         // if gen0 size is too large given the available memory, reduce it.
34183         // Get true cache size, as we don't want to reduce below this.
34184         size_t trueSize = max(GetLargestOnDieCacheSize(TRUE)/GetLogicalCpuCount(),(256*1024));
34185         dprintf (2, ("cache: %Id-%Id, cpu: %Id", 
34186             GetLargestOnDieCacheSize(FALSE),
34187             GetLargestOnDieCacheSize(TRUE),
34188             GetLogicalCpuCount()));
34189
34190         MEMORYSTATUSEX ms;
34191         GetProcessMemoryLoad (&ms);
34192         // if the total min GC across heaps will exceed 1/6th of available memory,
34193         // then reduce the min GC size until it either fits or has been reduced to cache size.
34194         while ((gen0size * gc_heap::n_heaps) > (ms.ullAvailPhys / 6))
34195         {
34196             gen0size = gen0size / 2;
34197             if (gen0size <= trueSize)
34198             {
34199                 gen0size = trueSize;
34200                 break;
34201             }
34202         }
34203 #endif //_TARGET_AMD64_
34204
34205 #else //SERVER_GC
34206         gen0size = max((4*GetLargestOnDieCacheSize(TRUE)/5),(256*1024));
34207 #endif //SERVER_GC
34208 #else //!FEATURE_REDHAWK
34209         gen0size = (256*1024);
34210 #endif //!FEATURE_REDHAWK
34211     }
34212
34213     // Generation 0 must never be more than 1/2 the segment size.
34214     if (gen0size >= (seg_size / 2))
34215         gen0size = seg_size / 2;
34216
34217     return (gen0size);
34218 }
34219
34220 void GCHeap::SetReservedVMLimit (size_t vmlimit)
34221 {
34222     gc_heap::reserved_memory_limit = vmlimit;
34223 }
34224
34225
34226 //versions of same method on each heap
34227
34228 #ifdef FEATURE_PREMORTEM_FINALIZATION
34229
34230 Object* GCHeap::GetNextFinalizableObject()
34231 {
34232
34233 #ifdef MULTIPLE_HEAPS
34234
34235     //return the first non critical one in the first queue.
34236     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34237     {
34238         gc_heap* hp = gc_heap::g_heaps [hn];
34239         Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
34240         if (O)
34241             return O;
34242     }
34243     //return the first non crtitical/critical one in the first queue.
34244     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34245     {
34246         gc_heap* hp = gc_heap::g_heaps [hn];
34247         Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
34248         if (O)
34249             return O;
34250     }
34251     return 0;
34252
34253
34254 #else //MULTIPLE_HEAPS
34255     return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
34256 #endif //MULTIPLE_HEAPS
34257
34258 }
34259
34260 size_t GCHeap::GetNumberFinalizableObjects()
34261 {
34262 #ifdef MULTIPLE_HEAPS
34263     size_t cnt = 0;
34264     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34265     {
34266         gc_heap* hp = gc_heap::g_heaps [hn];
34267         cnt += hp->finalize_queue->GetNumberFinalizableObjects();
34268     }
34269     return cnt;
34270
34271
34272 #else //MULTIPLE_HEAPS
34273     return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
34274 #endif //MULTIPLE_HEAPS
34275 }
34276
34277 size_t GCHeap::GetFinalizablePromotedCount()
34278 {
34279 #ifdef MULTIPLE_HEAPS
34280     size_t cnt = 0;
34281
34282     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34283     {
34284         gc_heap* hp = gc_heap::g_heaps [hn];
34285         cnt += hp->finalize_queue->GetPromotedCount();
34286     }
34287     return cnt;
34288
34289 #else //MULTIPLE_HEAPS
34290     return pGenGCHeap->finalize_queue->GetPromotedCount();
34291 #endif //MULTIPLE_HEAPS
34292 }
34293
34294 BOOL GCHeap::FinalizeAppDomain(AppDomain *pDomain, BOOL fRunFinalizers)
34295 {
34296 #ifdef MULTIPLE_HEAPS
34297     BOOL foundp = FALSE;
34298     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34299     {
34300         gc_heap* hp = gc_heap::g_heaps [hn];
34301         if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
34302             foundp = TRUE;
34303     }
34304     return foundp;
34305
34306 #else //MULTIPLE_HEAPS
34307     return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
34308 #endif //MULTIPLE_HEAPS
34309 }
34310
34311 BOOL GCHeap::ShouldRestartFinalizerWatchDog()
34312 {
34313     // This condition was historically used as part of the condition to detect finalizer thread timeouts
34314     return gc_heap::gc_lock.lock != -1;
34315 }
34316
34317 void GCHeap::SetFinalizeQueueForShutdown(BOOL fHasLock)
34318 {
34319 #ifdef MULTIPLE_HEAPS
34320     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
34321     {
34322         gc_heap* hp = gc_heap::g_heaps [hn];
34323         hp->finalize_queue->SetSegForShutDown(fHasLock);
34324     }
34325
34326 #else //MULTIPLE_HEAPS
34327     pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
34328 #endif //MULTIPLE_HEAPS
34329 }
34330
34331 //---------------------------------------------------------------------------
34332 // Finalized class tracking
34333 //---------------------------------------------------------------------------
34334
34335 bool GCHeap::RegisterForFinalization (int gen, Object* obj)
34336 {
34337     if (gen == -1)
34338         gen = 0;
34339     if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
34340     {
34341         //just reset the bit
34342         ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
34343         return true;
34344     }
34345     else
34346     {
34347         gc_heap* hp = gc_heap::heap_of ((BYTE*)obj);
34348         return hp->finalize_queue->RegisterForFinalization (gen, obj);
34349     }
34350 }
34351
34352 void GCHeap::SetFinalizationRun (Object* obj)
34353 {
34354     ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
34355 }
34356
34357 #endif // FEATURE_PREMORTEM_FINALIZATION
34358
34359 //----------------------------------------------------------------------------
34360 //
34361 // Write Barrier Support for bulk copy ("Clone") operations
34362 //
34363 // StartPoint is the target bulk copy start point
34364 // len is the length of the bulk copy (in bytes)
34365 //
34366 //
34367 // Performance Note:
34368 //
34369 // This is implemented somewhat "conservatively", that is we
34370 // assume that all the contents of the bulk copy are object
34371 // references.  If they are not, and the value lies in the
34372 // ephemeral range, we will set false positives in the card table.
34373 //
34374 // We could use the pointer maps and do this more accurately if necessary
34375
34376 #if defined(_MSC_VER) && defined(_TARGET_X86_)
34377 #pragma optimize("y", on)        // Small critical routines, don't put in EBP frame 
34378 #endif //_MSC_VER && _TARGET_X86_
34379
34380 VOID
34381 GCHeap::SetCardsAfterBulkCopy( Object **StartPoint, size_t len )
34382 {
34383     Object **rover;
34384     Object **end;
34385
34386     // Target should aligned
34387     assert(Aligned ((size_t)StartPoint));
34388
34389
34390     // Don't optimize the Generation 0 case if we are checking for write barrier voilations
34391     // since we need to update the shadow heap even in the generation 0 case.
34392 #if defined (WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
34393     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)
34394         for(unsigned i=0; i < len / sizeof(Object*); i++)
34395             updateGCShadow(&StartPoint[i], StartPoint[i]);
34396 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
34397
34398     // If destination is in Gen 0 don't bother
34399     if (
34400 #ifdef BACKGROUND_GC
34401         (!gc_heap::settings.concurrent) &&
34402 #endif //BACKGROUND_GC
34403         (GCHeap::GetGCHeap()->WhichGeneration( (Object*) StartPoint ) == 0))
34404         return;
34405
34406     rover = StartPoint;
34407     end = StartPoint + (len/sizeof(Object*));
34408     while (rover < end)
34409     {
34410         if ( (((BYTE*)*rover) >= g_ephemeral_low) && (((BYTE*)*rover) < g_ephemeral_high) )
34411         {
34412             // Set Bit For Card and advance to next card
34413             size_t card = gcard_of ((BYTE*)rover);
34414
34415             FastInterlockOr ((DWORD RAW_KEYWORD(volatile) *)&g_card_table[card/card_word_width],
34416                              (1 << (DWORD)(card % card_word_width)));
34417             // Skip to next card for the object
34418             rover = (Object**)align_on_card ((BYTE*)(rover+1));
34419         }
34420         else
34421         {
34422             rover++;
34423         }
34424     }
34425 }
34426
34427 #if defined(_MSC_VER) && defined(_TARGET_X86_)
34428 #pragma optimize("", on)        // Go back to command line default optimizations
34429 #endif //_MSC_VER && _TARGET_X86_
34430
34431
34432 #ifdef FEATURE_PREMORTEM_FINALIZATION
34433
34434 //--------------------------------------------------------------------
34435 //
34436 //          Support for finalization
34437 //
34438 //--------------------------------------------------------------------
34439
34440 inline
34441 unsigned int gen_segment (int gen)
34442 {
34443     assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
34444     return (NUMBERGENERATIONS - gen - 1);
34445 }
34446
34447 bool CFinalize::Initialize()
34448 {
34449     CONTRACTL {
34450         NOTHROW;
34451         GC_NOTRIGGER;
34452     } CONTRACTL_END;
34453
34454     m_Array = new (nothrow)(Object*[100]);
34455
34456     if (!m_Array)
34457     {
34458         ASSERT (m_Array);
34459         STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
34460         if (g_pConfig->IsGCBreakOnOOMEnabled())
34461         {
34462             DebugBreak();
34463         }
34464         return false;
34465     }
34466     m_EndArray = &m_Array[100];
34467
34468     for (int i =0; i < FreeList; i++)
34469     {
34470         SegQueueLimit (i) = m_Array;
34471     }
34472     m_PromotedCount = 0;
34473     lock = -1;
34474 #ifdef _DEBUG
34475     lockowner_threadid = (DWORD) -1;
34476 #endif // _DEBUG
34477
34478     return true;
34479 }
34480
34481 CFinalize::~CFinalize()
34482 {
34483     delete m_Array;
34484 }
34485
34486 size_t CFinalize::GetPromotedCount ()
34487 {
34488     return m_PromotedCount;
34489 }
34490
34491 inline
34492 void CFinalize::EnterFinalizeLock()
34493 {
34494     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
34495              GetThread() == 0 ||
34496              GetThread()->PreemptiveGCDisabled());
34497
34498 retry:
34499     if (FastInterlockExchange (&lock, 0) >= 0)
34500     {
34501         unsigned int i = 0;
34502         while (lock >= 0)
34503         {
34504             YieldProcessor();           // indicate to the processor that we are spining
34505             if (++i & 7)
34506                 __SwitchToThread (0, CALLER_LIMITS_SPINNING);
34507             else
34508                 __SwitchToThread (5, CALLER_LIMITS_SPINNING);
34509         }
34510         goto retry;
34511     }
34512
34513 #ifdef _DEBUG
34514     lockowner_threadid = ::GetCurrentThreadId();
34515 #endif // _DEBUG
34516 }
34517
34518 inline
34519 void CFinalize::LeaveFinalizeLock()
34520 {
34521     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
34522              GetThread() == 0 ||
34523              GetThread()->PreemptiveGCDisabled());
34524
34525 #ifdef _DEBUG
34526     lockowner_threadid = (DWORD) -1;
34527 #endif // _DEBUG
34528     lock = -1;
34529 }
34530
34531 bool
34532 CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
34533 {
34534     CONTRACTL {
34535 #ifdef FEATURE_REDHAWK
34536         // Under Redhawk false is returned on failure.
34537         NOTHROW;
34538 #else
34539         THROWS;
34540 #endif
34541         GC_NOTRIGGER;
34542     } CONTRACTL_END;
34543
34544     EnterFinalizeLock();
34545     // Adjust gen
34546     unsigned int dest = 0;
34547
34548     if (g_fFinalizerRunOnShutDown)
34549     {
34550         //no method table available yet,
34551         //put it in the finalizer queue and sort out when
34552         //dequeueing
34553         dest = FinalizerListSeg;
34554     }
34555
34556     else
34557         dest = gen_segment (gen);
34558
34559     // Adjust boundary for segments so that GC will keep objects alive.
34560     Object*** s_i = &SegQueue (FreeList);
34561     if ((*s_i) == m_EndArray)
34562     {
34563         if (!GrowArray())
34564         {
34565             LeaveFinalizeLock();
34566             if (method_table(obj) == NULL)
34567             {
34568                 // If the object is uninitialized, a valid size should have been passed.
34569                 assert (size >= Align (min_obj_size));
34570                 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
34571                 ((CObjectHeader*)obj)->SetFree(size);
34572             }
34573             STRESS_LOG_OOM_STACK(0);
34574             if (g_pConfig->IsGCBreakOnOOMEnabled())
34575             {
34576                 DebugBreak();
34577             }
34578 #ifdef FEATURE_REDHAWK
34579             return false;
34580 #else
34581             ThrowOutOfMemory();
34582 #endif
34583         }
34584     }
34585     Object*** end_si = &SegQueueLimit (dest);
34586     do
34587     {
34588         //is the segment empty?
34589         if (!(*s_i == *(s_i-1)))
34590         {
34591             //no, swap the end elements.
34592             *(*s_i) = *(*(s_i-1));
34593         }
34594         //increment the fill pointer
34595         (*s_i)++;
34596         //go to the next segment.
34597         s_i--;
34598     } while (s_i > end_si);
34599
34600     // We have reached the destination segment
34601     // store the object
34602     **s_i = obj;
34603     // increment the fill pointer
34604     (*s_i)++;
34605
34606     LeaveFinalizeLock();
34607
34608     return true;
34609 }
34610
34611 Object*
34612 CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
34613 {
34614     Object* obj = 0;
34615     //serialize
34616     EnterFinalizeLock();
34617
34618 retry:
34619     if (!IsSegEmpty(FinalizerListSeg))
34620     {
34621         if (g_fFinalizerRunOnShutDown)
34622         {
34623             obj = *(SegQueueLimit (FinalizerListSeg)-1);
34624             if (method_table(obj)->HasCriticalFinalizer())
34625             {
34626                 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
34627                           FinalizerListSeg, CriticalFinalizerListSeg);
34628                 goto retry;
34629             }
34630             else
34631                 --SegQueueLimit (FinalizerListSeg);
34632         }
34633         else
34634             obj =  *(--SegQueueLimit (FinalizerListSeg));
34635
34636     }
34637     else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
34638     {
34639         //the FinalizerList is empty, we can adjust both
34640         // limit instead of moving the object to the free list
34641         obj =  *(--SegQueueLimit (CriticalFinalizerListSeg));
34642         --SegQueueLimit (FinalizerListSeg);
34643     }
34644     if (obj)
34645     {
34646         dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
34647     }
34648     LeaveFinalizeLock();
34649     return obj;
34650 }
34651
34652 void
34653 CFinalize::SetSegForShutDown(BOOL fHasLock)
34654 {
34655     int i;
34656
34657     if (!fHasLock)
34658         EnterFinalizeLock();
34659     for (i = 0; i <= max_generation; i++)
34660     {
34661         unsigned int seg = gen_segment (i);
34662         Object** startIndex = SegQueueLimit (seg)-1;
34663         Object** stopIndex  = SegQueue (seg);
34664         for (Object** po = startIndex; po >= stopIndex; po--)
34665         {
34666             Object* obj = *po;
34667             if (method_table(obj)->HasCriticalFinalizer())
34668             {
34669                 MoveItem (po, seg, CriticalFinalizerListSeg);
34670             }
34671             else
34672             {
34673                 MoveItem (po, seg, FinalizerListSeg);
34674             }
34675         }
34676     }
34677     if (!fHasLock)
34678         LeaveFinalizeLock();
34679 }
34680
34681 void
34682 CFinalize::DiscardNonCriticalObjects()
34683 {
34684     //empty the finalization queue
34685     Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
34686     Object** stopIndex  = SegQueue (FinalizerListSeg);
34687     for (Object** po = startIndex; po >= stopIndex; po--)
34688     {
34689         MoveItem (po, FinalizerListSeg, FreeList);
34690     }
34691 }
34692
34693 size_t
34694 CFinalize::GetNumberFinalizableObjects()
34695 {
34696     return SegQueueLimit (FinalizerListSeg) -
34697         (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
34698 }
34699
34700 BOOL
34701 CFinalize::FinalizeSegForAppDomain (AppDomain *pDomain, 
34702                                     BOOL fRunFinalizers, 
34703                                     unsigned int Seg)
34704 {
34705     BOOL finalizedFound = FALSE;
34706     Object** endIndex = SegQueue (Seg);
34707     for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
34708     {
34709         CObjectHeader* obj = (CObjectHeader*)*i;
34710
34711         // Objects are put into the finalization queue before they are complete (ie their methodtable
34712         // may be null) so we must check that the object we found has a method table before checking
34713         // if it has the index we are looking for. If the methodtable is null, it can't be from the
34714         // unloading domain, so skip it.
34715         if (method_table(obj) == NULL)
34716             continue;
34717
34718         // eagerly finalize all objects except those that may be agile.
34719         if (obj->GetAppDomainIndex() != pDomain->GetIndex())
34720             continue;
34721
34722 #ifndef FEATURE_REDHAWK
34723         if (method_table(obj)->IsAgileAndFinalizable())
34724         {
34725             // If an object is both agile & finalizable, we leave it in the
34726             // finalization queue during unload.  This is OK, since it's agile.
34727             // Right now only threads can be this way, so if that ever changes, change
34728             // the assert to just continue if not a thread.
34729             _ASSERTE(method_table(obj) == g_pThreadClass);
34730
34731             if (method_table(obj) == g_pThreadClass)
34732             {
34733                 // However, an unstarted thread should be finalized. It could be holding a delegate
34734                 // in the domain we want to unload. Once the thread has been started, its
34735                 // delegate is cleared so only unstarted threads are a problem.
34736                 Thread *pThread = ((THREADBASEREF)ObjectToOBJECTREF(obj))->GetInternal();
34737                 if (! pThread || ! pThread->IsUnstarted())
34738                 {
34739                     // This appdomain is going to be gone soon so let us assign
34740                     // it the appdomain that's guaranteed to exist
34741                     // The object is agile and the delegate should be null so we can do it
34742                     obj->GetHeader()->ResetAppDomainIndexNoFailure(SystemDomain::System()->DefaultDomain()->GetIndex());
34743                     continue;
34744                 }
34745             }
34746             else
34747             {
34748                 obj->GetHeader()->ResetAppDomainIndexNoFailure(SystemDomain::System()->DefaultDomain()->GetIndex());
34749                 continue;
34750             }
34751         }
34752 #endif //!FEATURE_REDHAWK
34753
34754         if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
34755         {
34756             //remove the object because we don't want to
34757             //run the finalizer
34758             MoveItem (i, Seg, FreeList);
34759             //Reset the bit so it will be put back on the queue
34760             //if resurrected and re-registered.
34761             obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
34762         }
34763         else
34764         {
34765             if (method_table(obj)->HasCriticalFinalizer())
34766             {
34767                 finalizedFound = TRUE;
34768                 MoveItem (i, Seg, CriticalFinalizerListSeg);
34769             }
34770             else
34771             {
34772                 if (pDomain->IsRudeUnload())
34773                 {
34774                     MoveItem (i, Seg, FreeList);
34775                 }
34776                 else
34777                 {
34778                     finalizedFound = TRUE;
34779                     MoveItem (i, Seg, FinalizerListSeg);
34780                 }
34781             }
34782         }
34783     }
34784
34785     return finalizedFound;
34786 }
34787
34788 BOOL
34789 CFinalize::FinalizeAppDomain (AppDomain *pDomain, BOOL fRunFinalizers)
34790 {
34791     BOOL finalizedFound = FALSE;
34792
34793     unsigned int startSeg = gen_segment (max_generation);
34794
34795     EnterFinalizeLock();
34796
34797     for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
34798     {
34799         if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
34800         {
34801             finalizedFound = TRUE;
34802         }
34803     }
34804
34805     LeaveFinalizeLock();
34806
34807     return finalizedFound;
34808 }
34809
34810 void
34811 CFinalize::MoveItem (Object** fromIndex,
34812                      unsigned int fromSeg,
34813                      unsigned int toSeg)
34814 {
34815
34816     int step;
34817     ASSERT (fromSeg != toSeg);
34818     if (fromSeg > toSeg)
34819         step = -1;
34820     else
34821         step = +1;
34822     // Place the element at the boundary closest to dest
34823     Object** srcIndex = fromIndex;
34824     for (unsigned int i = fromSeg; i != toSeg; i+= step)
34825     {
34826         Object**& destFill = m_FillPointers[i+(step - 1 )/2];
34827         Object** destIndex = destFill - (step + 1)/2;
34828         if (srcIndex != destIndex)
34829         {
34830             Object* tmp = *srcIndex;
34831             *srcIndex = *destIndex;
34832             *destIndex = tmp;
34833         }
34834         destFill -= step;
34835         srcIndex = destIndex;
34836     }
34837 }
34838
34839 void
34840 CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
34841 {
34842     ScanContext sc;
34843     if (pSC == 0)
34844         pSC = &sc;
34845
34846     pSC->thread_number = hn;
34847
34848     //scan the finalization queue
34849     Object** startIndex  = SegQueue (CriticalFinalizerListSeg);
34850     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
34851
34852     for (Object** po = startIndex; po < stopIndex; po++)
34853     {
34854         Object* o = *po;
34855         //dprintf (3, ("scan freacheable %Ix", (size_t)o));
34856         dprintf (3, ("scan f %Ix", (size_t)o));
34857 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34858         if (g_fEnableARM)
34859         {
34860             pSC->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(o->GetAppDomainIndex());
34861         }
34862 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34863
34864         (*fn)(po, pSC, 0);
34865     }
34866 }
34867
34868 #ifdef GC_PROFILING
34869 void CFinalize::WalkFReachableObjects (gc_heap* hp)
34870 {
34871     BEGIN_PIN_PROFILER(CORProfilerPresent());
34872     Object** startIndex = SegQueue (CriticalFinalizerListSeg);
34873     Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
34874     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
34875     for (Object** po = startIndex; po < stopIndex; po++)
34876     {
34877         //report *po
34878         g_profControlBlock.pProfInterface->FinalizeableObjectQueued(po < stopCriticalIndex, (ObjectID)*po);
34879     }
34880     END_PIN_PROFILER();
34881 }
34882 #endif //GC_PROFILING
34883
34884 BOOL
34885 CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
34886                                 gc_heap* hp)
34887 {
34888     ScanContext sc;
34889     sc.promotion = TRUE;
34890 #ifdef MULTIPLE_HEAPS
34891     sc.thread_number = hp->heap_number;
34892 #endif //MULTIPLE_HEAPS
34893
34894     BOOL finalizedFound = FALSE;
34895
34896     //start with gen and explore all the younger generations.
34897     unsigned int startSeg = gen_segment (gen);
34898     {
34899         m_PromotedCount = 0;
34900         for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
34901         {
34902             Object** endIndex = SegQueue (Seg);
34903             for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
34904             {
34905                 CObjectHeader* obj = (CObjectHeader*)*i;
34906                 dprintf (3, ("scanning: %Ix", (size_t)obj));
34907                 if (!GCHeap::GetGCHeap()->IsPromoted (obj))
34908                 {
34909                     dprintf (3, ("freacheable: %Ix", (size_t)obj));
34910
34911                     assert (method_table(obj)->HasFinalizer());
34912
34913 #ifndef FEATURE_REDHAWK
34914                     if (method_table(obj) == pWeakReferenceMT || method_table(obj)->GetCanonicalMethodTable() == pWeakReferenceOfTCanonMT)
34915                     {
34916                         //destruct the handle right there.
34917                         FinalizeWeakReference (obj);
34918                         MoveItem (i, Seg, FreeList);
34919                     }
34920                     else
34921 #endif //!FEATURE_REDHAWK
34922                     if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
34923                     {
34924                         //remove the object because we don't want to
34925                         //run the finalizer
34926
34927                         MoveItem (i, Seg, FreeList);
34928
34929                         //Reset the bit so it will be put back on the queue
34930                         //if resurrected and re-registered.
34931                         obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
34932
34933                     }
34934                     else
34935                     {
34936                         m_PromotedCount++;
34937
34938                         if (method_table(obj)->HasCriticalFinalizer())
34939                         {
34940                             MoveItem (i, Seg, CriticalFinalizerListSeg);
34941                         }
34942                         else
34943                         {
34944                             MoveItem (i, Seg, FinalizerListSeg);
34945                         }
34946                     }
34947                 }
34948 #ifdef BACKGROUND_GC
34949                 else
34950                 {
34951                     if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
34952                     {
34953                         // TODO - fix the following line.
34954                         //assert (gc_heap::background_object_marked ((BYTE*)obj, FALSE));
34955                         dprintf (3, ("%Ix is marked", (size_t)obj));
34956                     }
34957                 }
34958 #endif //BACKGROUND_GC
34959             }
34960         }
34961     }
34962     finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
34963                      !IsSegEmpty(CriticalFinalizerListSeg);
34964                     
34965     if (finalizedFound)
34966     {
34967         //Promote the f-reachable objects
34968         GcScanRoots (pfn,
34969 #ifdef MULTIPLE_HEAPS
34970                      hp->heap_number
34971 #else
34972                      0
34973 #endif //MULTIPLE_HEAPS
34974                      , 0);
34975
34976         hp->settings.found_finalizers = TRUE;
34977
34978 #ifdef BACKGROUND_GC
34979         if (hp->settings.concurrent)
34980         {
34981             hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
34982         }
34983 #endif //BACKGROUND_GC
34984         if (hp->settings.concurrent && hp->settings.found_finalizers)
34985         {
34986             if (!mark_only_p)
34987                 FinalizerThread::EnableFinalization();
34988         }
34989     }
34990
34991     return finalizedFound;
34992 }
34993
34994 //Relocates all of the objects in the finalization array
34995 void
34996 CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
34997 {
34998     ScanContext sc;
34999     sc.promotion = FALSE;
35000 #ifdef MULTIPLE_HEAPS
35001     sc.thread_number = hp->heap_number;
35002 #endif //MULTIPLE_HEAPS
35003
35004     unsigned int Seg = gen_segment (gen);
35005
35006     Object** startIndex = SegQueue (Seg);
35007     for (Object** po = startIndex; po < SegQueue (FreeList);po++)
35008     {
35009         GCHeap::Relocate (po, &sc);
35010     }
35011 }
35012
35013 void
35014 CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
35015 {
35016     // update the generation fill pointers.
35017     // if gen_0_empty is FALSE, test each object to find out if
35018     // it was promoted or not
35019     if (gen_0_empty_p)
35020     {
35021         for (int i = min (gen+1, max_generation); i > 0; i--)
35022         {
35023             m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
35024         }
35025     }
35026     else
35027     {
35028         //Look for demoted or promoted plugs
35029
35030         for (int i = gen; i >= 0; i--)
35031         {
35032             unsigned int Seg = gen_segment (i);
35033             Object** startIndex = SegQueue (Seg);
35034
35035             for (Object** po = startIndex;
35036                  po < SegQueueLimit (gen_segment(i)); po++)
35037             {
35038                 int new_gen = GCHeap::GetGCHeap()->WhichGeneration (*po);
35039                 if (new_gen != i)
35040                 {
35041                     if (new_gen > i)
35042                     {
35043                         //promotion
35044                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
35045                     }
35046                     else
35047                     {
35048                         //demotion
35049                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
35050                         //back down in order to see all objects.
35051                         po--;
35052                     }
35053                 }
35054
35055             }
35056         }
35057     }
35058 }
35059
35060 BOOL
35061 CFinalize::GrowArray()
35062 {
35063     size_t oldArraySize = (m_EndArray - m_Array);
35064     size_t newArraySize =  (size_t)(((float)oldArraySize / 10) * 12);
35065
35066     Object** newArray = new (nothrow)(Object*[newArraySize]);
35067     if (!newArray)
35068     {
35069         // It's not safe to throw here, because of the FinalizeLock.  Tell our caller
35070         // to throw for us.
35071 //        ASSERT (newArray);
35072         return FALSE;
35073     }
35074     memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
35075
35076     //adjust the fill pointers
35077     for (int i = 0; i < FreeList; i++)
35078     {
35079         m_FillPointers [i] += (newArray - m_Array);
35080     }
35081     delete m_Array;
35082     m_Array = newArray;
35083     m_EndArray = &m_Array [newArraySize];
35084
35085     return TRUE;
35086 }
35087
35088 #ifdef VERIFY_HEAP
35089 void CFinalize::CheckFinalizerObjects()
35090 {
35091     for (int i = 0; i <= max_generation; i++)
35092     {
35093         Object **startIndex = SegQueue (gen_segment (i));
35094         Object **stopIndex  = SegQueueLimit (gen_segment (i));
35095
35096         for (Object **po = startIndex; po < stopIndex; po++)
35097         {
35098             if ((int)GCHeap::GetGCHeap()->WhichGeneration (*po) < i)
35099                 FATAL_GC_ERROR ();
35100             ((CObjectHeader*)*po)->Validate();
35101         }
35102     }
35103 }
35104 #endif //VERIFY_HEAP
35105
35106 #endif // FEATURE_PREMORTEM_FINALIZATION
35107
35108
35109 //------------------------------------------------------------------------------
35110 //
35111 //                      End of VM specific support
35112 //
35113 //------------------------------------------------------------------------------
35114
35115 void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
35116 {
35117     generation* gen = gc_heap::generation_of (gen_number);
35118     heap_segment*    seg = generation_start_segment (gen);
35119     BYTE*       x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
35120                      generation_allocation_start (gen));
35121
35122     BYTE*       end = heap_segment_allocated (seg);
35123     BOOL small_object_segments = TRUE;
35124     int align_const = get_alignment_constant (small_object_segments);
35125
35126     while (1)
35127
35128     {
35129         if (x >= end)
35130         {
35131             if ((seg = heap_segment_next (seg)) != 0)
35132             {
35133                 x = heap_segment_mem (seg);
35134                 end = heap_segment_allocated (seg);
35135                 continue;
35136             }
35137             else
35138             {
35139                 if (small_object_segments && walk_large_object_heap_p)
35140
35141                 {
35142                     small_object_segments = FALSE;
35143                     align_const = get_alignment_constant (small_object_segments);
35144                     seg = generation_start_segment (large_object_generation);
35145                     x = heap_segment_mem (seg);
35146                     end = heap_segment_allocated (seg);
35147                     continue;
35148                 }
35149                 else
35150                 {
35151                     break;
35152                 }
35153             }
35154         }
35155
35156         size_t s = size (x);
35157         CObjectHeader* o = (CObjectHeader*)x;
35158
35159         if (!o->IsFree())
35160
35161         {
35162             _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
35163
35164             if (!fn (o->GetObjectBase(), context))
35165                 return;
35166         }
35167         x = x + Align (s, align_const);
35168     }
35169 }
35170
35171 #if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
35172 void GCHeap::WalkObject (Object* obj, walk_fn fn, void* context)
35173 {
35174     BYTE* o = (BYTE*)obj;
35175     if (o)
35176     {
35177         go_through_object_cl (method_table (o), o, size(o), oo,
35178                                     {
35179                                         if (*oo)
35180                                         {
35181                                             Object *oh = (Object*)*oo;
35182                                             if (!fn (oh, context))
35183                                                 return;
35184                                         }
35185                                     }
35186             );
35187     }
35188 }
35189 #endif //defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
35190
35191 // Go through and touch (read) each page straddled by a memory block.
35192 void TouchPages(LPVOID pStart, UINT cb)
35193 {
35194     const UINT pagesize = OS_PAGE_SIZE;
35195     _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
35196     if (cb)
35197     {
35198         VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
35199         VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) -  (((size_t)pStart) & (pagesize-1)));
35200         while (p < pEnd)
35201         {
35202             char a;
35203             a = VolatileLoad(p);
35204             //printf("Touching page %lxh\n", (ULONG)p);
35205             p += pagesize;
35206         }
35207     }
35208 }
35209
35210 #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
35211     // This code is designed to catch the failure to update the write barrier
35212     // The way it works is to copy the whole heap right after every GC.  The write
35213     // barrier code has been modified so that it updates the shadow as well as the
35214     // real GC heap.  Before doing the next GC, we walk the heap, looking for pointers
35215     // that were updated in the real heap, but not the shadow.  A mismatch indicates
35216     // an error.  The offending code can be found by breaking after the correct GC,
35217     // and then placing a data breakpoint on the Heap location that was updated without
35218     // going through the write barrier.
35219
35220     // Called at process shutdown
35221 void deleteGCShadow()
35222 {
35223     if (g_GCShadow != 0)
35224         VirtualFree (g_GCShadow, 0, MEM_RELEASE);
35225     g_GCShadow = 0;
35226     g_GCShadowEnd = 0;
35227 }
35228
35229     // Called at startup and right after a GC, get a snapshot of the GC Heap
35230 void initGCShadow()
35231 {
35232     if (!(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK))
35233         return;
35234
35235     size_t len = g_highest_address - g_lowest_address;
35236     if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) 
35237     {
35238         deleteGCShadow();
35239         g_GCShadowEnd = g_GCShadow = (BYTE*) VirtualAlloc(0, len, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
35240         if (g_GCShadow)
35241         {
35242             g_GCShadowEnd += len;
35243         }
35244         else
35245         {
35246             _ASSERTE(!"Not enough memory to run HeapVerify level 2");
35247             // If after the assert we decide to allow the program to continue 
35248             // running we need to be in a state that will not trigger any 
35249             // additional AVs while we fail to allocate a shadow segment, i.e. 
35250             // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
35251             return;
35252         }
35253         }
35254
35255     // save the value of g_lowest_address at this time.  If this value changes before
35256     // the next call to checkGCWriteBarrier() it means we extended the heap (with a
35257     // large object segment most probably), and the whole shadow segment is inconsistent.
35258     g_shadow_lowest_address = g_lowest_address;
35259
35260         //****** Copy the whole GC heap ******
35261     //
35262     // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
35263     // can produce a NULL result.  This is because the initialization has not completed.
35264     //
35265     generation* gen = gc_heap::generation_of (max_generation);
35266     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
35267
35268     ptrdiff_t delta = g_GCShadow - g_lowest_address;
35269     BOOL small_object_segments = TRUE;
35270     while(1)
35271     {
35272         if (!seg)
35273         {
35274             if (small_object_segments)
35275             {
35276                 small_object_segments = FALSE;
35277                 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
35278                 continue;
35279             }
35280             else
35281                 break;
35282         }
35283             // Copy the segment
35284         BYTE* start = heap_segment_mem(seg);
35285         BYTE* end = heap_segment_allocated (seg);
35286         memcpy(start + delta, start, end - start);
35287         seg = heap_segment_next_rw (seg);
35288     }
35289 }
35290
35291 #define INVALIDGCVALUE (LPVOID)((size_t)0xcccccccd)
35292
35293     // test to see if 'ptr' was only updated via the write barrier.
35294 inline void testGCShadow(Object** ptr)
35295 {
35296     Object** shadow = (Object**) &g_GCShadow[((BYTE*) ptr - g_lowest_address)];
35297     if (*ptr != 0 && (BYTE*) shadow < g_GCShadowEnd && *ptr != *shadow) 
35298     {
35299
35300         // If you get this assertion, someone updated a GC poitner in the heap without
35301         // using the write barrier.  To find out who, check the value of 
35302         // dd_collection_count (dynamic_data_of (0)). Also
35303         // note the value of 'ptr'.  Rerun the App that the previous GC just occured.
35304         // Then put a data breakpoint for the value of 'ptr'  Then check every write
35305         // to pointer between the two GCs.  The last one is not using the write barrier.
35306
35307         // If the memory of interest does not exist at system startup,
35308         // you need to set the data breakpoint right after the memory gets committed
35309         // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
35310         // in the memory window.  run until the memory gets mapped. Then you can set
35311         // your breakpoint
35312
35313         // Note a recent change, we've identified race conditions when updating the gc shadow.
35314         // Throughout the runtime, code will update an address in the gc heap, then erect the
35315         // write barrier, which calls updateGCShadow. With an app that pounds one heap location
35316         // from multiple threads, you can hit this assert even though all involved are using the
35317         // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
35318         // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
35319         // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
35320         // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
35321         // TODO: erroneous asserts in here.
35322
35323         if(*shadow!=INVALIDGCVALUE)
35324         {
35325         _ASSERTE(!"Pointer updated without using write barrier");
35326         }
35327         /*
35328         else
35329         {
35330              printf("saw a INVALIDGCVALUE. (just to let you know)\n");
35331         }
35332         */
35333     }
35334 }
35335
35336 void testGCShadowHelper (BYTE* x)
35337 {
35338     size_t s = size (x);
35339     if (contain_pointers (x))
35340     {
35341         go_through_object_nostart (method_table(x), x, s, oo,
35342                            { testGCShadow((Object**) oo); });
35343     }
35344 }
35345
35346     // Walk the whole heap, looking for pointers that were not updated with the write barrier.
35347 void checkGCWriteBarrier()
35348 {
35349     // g_shadow_lowest_address != g_lowest_address means the GC heap was extended by a segment
35350     // and the GC shadow segment did not track that change!
35351     if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_lowest_address)
35352     {
35353         // No shadow stack, nothing to check.
35354         return;
35355     }
35356
35357     {
35358         generation* gen = gc_heap::generation_of (max_generation);
35359         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
35360
35361         PREFIX_ASSUME(seg != NULL);
35362
35363         while(seg)
35364         {
35365             BYTE* x = heap_segment_mem(seg);
35366             while (x < heap_segment_allocated (seg))
35367             {
35368                 size_t s = size (x);
35369                 testGCShadowHelper (x);
35370                 x = x + Align (s);
35371             }
35372             seg = heap_segment_next_rw (seg);
35373         }
35374     }
35375
35376     {
35377         // go through large object heap
35378         int alignment = get_alignment_constant(FALSE);
35379         generation* gen = gc_heap::generation_of (max_generation+1);
35380         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
35381
35382         PREFIX_ASSUME(seg != NULL);
35383
35384         while(seg)
35385         {
35386             BYTE* x = heap_segment_mem(seg);
35387             while (x < heap_segment_allocated (seg))
35388             {
35389                 size_t s = size (x);
35390                 testGCShadowHelper (x);
35391                 x = x + Align (s, alignment);
35392             }
35393             seg = heap_segment_next_rw (seg);
35394         }
35395     }
35396 }
35397 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
35398
35399 #endif // !DACCESS_COMPILE
35400
35401 #ifdef FEATURE_BASICFREEZE
35402 void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
35403 {
35404 #ifndef DACCESS_COMPILE
35405     BYTE *o = heap_segment_mem(seg);
35406
35407     // small heap alignment constant
35408     int alignment = get_alignment_constant(TRUE);
35409
35410     while (o < heap_segment_allocated(seg))
35411     {
35412         pfnMethodTable(pvContext, o);
35413
35414         if (contain_pointers (o))
35415         {
35416             go_through_object_nostart (method_table (o), o, size(o), oo,
35417                    {
35418                        if (*oo)
35419                            pfnObjRef(pvContext, oo);
35420                    }
35421             );
35422         }
35423
35424         o += Align(size(o), alignment);
35425     }
35426 #endif //!DACCESS_COMPILE
35427 }
35428 #endif // FEATURE_BASICFREEZE
35429
35430 #ifndef DACCESS_COMPILE
35431 HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
35432 {
35433 #ifdef BACKGROUND_GC
35434     if (recursive_gc_sync::background_running_p())
35435     {
35436         DWORD dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
35437         if (dwRet == WAIT_OBJECT_0)
35438             return S_OK;
35439         else if (dwRet == WAIT_TIMEOUT)
35440             return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
35441         else
35442             return E_FAIL;      // It is not clear if what the last error would be if the wait failed,
35443                                 // as there are too many layers in between. The best we can do is to return E_FAIL;
35444     }
35445 #endif
35446
35447     return S_OK;
35448 }
35449 #endif // !DACCESS_COMPILE
35450
35451 void GCHeap::TemporaryEnableConcurrentGC()
35452 {
35453 #ifdef BACKGROUND_GC
35454     gc_heap::temp_disable_concurrent_p = FALSE;
35455 #endif //BACKGROUND_GC
35456 }
35457
35458 void GCHeap::TemporaryDisableConcurrentGC()
35459 {
35460 #ifdef BACKGROUND_GC
35461     gc_heap::temp_disable_concurrent_p = TRUE;
35462 #endif //BACKGROUND_GC
35463 }
35464
35465 BOOL GCHeap::IsConcurrentGCEnabled()
35466 {
35467 #ifdef BACKGROUND_GC
35468     return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
35469 #else
35470     return FALSE;
35471 #endif //BACKGROUND_GC
35472 }