1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 
6 //
7 // #Overview
8 //
9 // GC automatically manages memory allocated by managed code.
10 // The design doc for GC can be found at Documentation/botr/garbage-collection.md
11 //
12 // This file includes both the code for GC and the allocator. The most common
13 // case for a GC to be triggered is from the allocator code. See
14 // code:#try_allocate_more_space where it calls GarbageCollectGeneration.
15 //
16 // Entry points for the allocator are GCHeap::Alloc* which are called by the
17 // allocation helpers in gcscan.cpp
18 //
19 
20 #include "gcpriv.h"
21 
22 #define USE_INTROSORT
23 
24 
25 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
26 BOOL bgc_heap_walk_for_etw_p = FALSE;
27 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
28 
29 #if defined(FEATURE_REDHAWK)
30 #define MAYBE_UNUSED_VAR(v) v = v
31 #else
32 #define MAYBE_UNUSED_VAR(v)
33 #endif // FEATURE_REDHAWK
34 
35 #define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0))
36 
37 #ifdef SERVER_GC
38 #define partial_size_th 100
39 #define num_partial_refs 64
40 #else //SERVER_GC
41 #define partial_size_th 100
42 #define num_partial_refs 32
43 #endif //SERVER_GC
44 
45 #define demotion_plug_len_th (6*1024*1024)
46 
47 #ifdef BIT64
48 #define MARK_STACK_INITIAL_LENGTH 1024
49 #else
50 #define MARK_STACK_INITIAL_LENGTH 128
51 #endif // BIT64
52 
53 #define LOH_PIN_QUEUE_LENGTH 100
54 #define LOH_PIN_DECAY 10
55 
56 // Right now we support maximum 256 procs - meaning that we will create at most
57 // 256 GC threads and 256 GC heaps.
58 #define MAX_SUPPORTED_CPUS 256
59 
60 #ifdef GC_CONFIG_DRIVEN
61 int compact_ratio = 0;
62 #endif //GC_CONFIG_DRIVEN
63 
64 #if defined(FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP) && defined(NO_WRITE_BARRIER)
65 #error Software write watch requires write barriers.
66 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP && NO_WRITE_BARRIER
67 
68 // See comments in reset_memory.
69 BOOL reset_mm_p = TRUE;
70 
71 #if defined (TRACE_GC) && !defined (DACCESS_COMPILE)
72 const char * const allocation_state_str[] = {
73     "start",
74     "can_allocate",
75     "cant_allocate",
76     "try_fit",
77     "try_fit_new_seg",
78     "try_fit_new_seg_after_cg",
79     "try_fit_no_seg",
80     "try_fit_after_cg",
81     "try_fit_after_bgc",
82     "try_free_full_seg_in_bgc",
83     "try_free_after_bgc",
84     "try_seg_end",
85     "acquire_seg",
86     "acquire_seg_after_cg",
87     "acquire_seg_after_bgc",
88     "check_and_wait_for_bgc",
89     "trigger_full_compact_gc",
90     "trigger_ephemeral_gc",
91     "trigger_2nd_ephemeral_gc",
92     "check_retry_seg"
93 };
94 #endif //TRACE_GC && !DACCESS_COMPILE
95 
96 
97 // Keep this in sync with the definition of gc_reason
98 #if (defined(DT_LOG) || defined(TRACE_GC)) && !defined (DACCESS_COMPILE)
99 static const char* const str_gc_reasons[] =
100 {
101     "alloc_soh",
102     "induced",
103     "lowmem",
104     "empty",
105     "alloc_loh",
106     "oos_soh",
107     "oos_loh",
108     "induced_noforce",
109     "gcstress",
110     "induced_lowmem",
111     "induced_compacting"
112 };
113 
114 static const char* const str_gc_pause_modes[] =
115 {
116     "batch",
117     "interactive",
118     "low_latency",
119     "sustained_low_latency",
120     "no_gc"
121 };
122 #endif // defined(DT_LOG) || defined(TRACE_GC)
123 
124 inline
is_induced(gc_reason reason)125 BOOL is_induced (gc_reason reason)
126 {
127     return ((reason == reason_induced) ||
128             (reason == reason_induced_noforce) ||
129             (reason == reason_lowmemory) ||
130             (reason == reason_lowmemory_blocking) ||
131             (reason == reason_induced_compacting));
132 }
133 
134 inline
is_induced_blocking(gc_reason reason)135 BOOL is_induced_blocking (gc_reason reason)
136 {
137     return ((reason == reason_induced) ||
138             (reason == reason_lowmemory_blocking) ||
139             (reason == reason_induced_compacting));
140 }
141 
142 #ifndef DACCESS_COMPILE
143 int64_t qpf;
144 
GetHighPrecisionTimeStamp()145 size_t GetHighPrecisionTimeStamp()
146 {
147     int64_t ts = GCToOSInterface::QueryPerformanceCounter();
148 
149     return (size_t)(ts / (qpf / 1000));
150 }
151 #endif
152 
153 #ifdef GC_STATS
154 // There is a current and a prior copy of the statistics.  This allows us to display deltas per reporting
155 // interval, as well as running totals.  The 'min' and 'max' values require special treatment.  They are
156 // Reset (zeroed) in the current statistics when we begin a new interval and they are updated via a
157 // comparison with the global min/max.
158 GCStatistics g_GCStatistics;
159 GCStatistics g_LastGCStatistics;
160 
161 TCHAR* GCStatistics::logFileName = NULL;
162 FILE*  GCStatistics::logFile = NULL;
163 
AddGCStats(const gc_mechanisms & settings,size_t timeInMSec)164 void GCStatistics::AddGCStats(const gc_mechanisms& settings, size_t timeInMSec)
165 {
166 #ifdef BACKGROUND_GC
167     if (settings.concurrent)
168     {
169         bgc.Accumulate((uint32_t)timeInMSec*1000);
170         cntBGC++;
171     }
172     else if (settings.background_p)
173     {
174         fgc.Accumulate((uint32_t)timeInMSec*1000);
175         cntFGC++;
176         if (settings.compaction)
177             cntCompactFGC++;
178         assert(settings.condemned_generation < max_generation);
179         cntFGCGen[settings.condemned_generation]++;
180     }
181     else
182 #endif // BACKGROUND_GC
183     {
184         ngc.Accumulate((uint32_t)timeInMSec*1000);
185         cntNGC++;
186         if (settings.compaction)
187             cntCompactNGC++;
188         cntNGCGen[settings.condemned_generation]++;
189     }
190 
191     if (is_induced (settings.reason))
192         cntReasons[(int)reason_induced]++;
193     else if (settings.stress_induced)
194         cntReasons[(int)reason_gcstress]++;
195     else
196         cntReasons[(int)settings.reason]++;
197 
198 #ifdef BACKGROUND_GC
199     if (settings.concurrent || !settings.background_p)
200     {
201 #endif // BACKGROUND_GC
202         RollOverIfNeeded();
203 #ifdef BACKGROUND_GC
204     }
205 #endif // BACKGROUND_GC
206 }
207 
Initialize()208 void GCStatistics::Initialize()
209 {
210     LIMITED_METHOD_CONTRACT;
211     // for efficiency sake we're taking a dependency on the layout of a C++ object
212     // with a vtable. protect against violations of our premise:
213     static_assert(offsetof(GCStatistics, cntDisplay) == sizeof(void*),
214             "The first field of GCStatistics follows the pointer sized vtable");
215 
216     int podOffs = offsetof(GCStatistics, cntDisplay);       // offset of the first POD field
217     memset((uint8_t*)(&g_GCStatistics)+podOffs, 0, sizeof(g_GCStatistics)-podOffs);
218     memset((uint8_t*)(&g_LastGCStatistics)+podOffs, 0, sizeof(g_LastGCStatistics)-podOffs);
219 }
220 
DisplayAndUpdate()221 void GCStatistics::DisplayAndUpdate()
222 {
223     LIMITED_METHOD_CONTRACT;
224 
225     if (logFileName == NULL || logFile == NULL)
226         return;
227 
228     {
229         if (cntDisplay == 0)
230             fprintf(logFile, "\nGCMix **** Initialize *****\n\n");
231 
232         fprintf(logFile, "GCMix **** Summary ***** %d\n", cntDisplay);
233 
234         // NGC summary (total, timing info)
235         ngc.DisplayAndUpdate(logFile, "NGC ", &g_LastGCStatistics.ngc, cntNGC, g_LastGCStatistics.cntNGC, msec);
236 
237         // FGC summary (total, timing info)
238         fgc.DisplayAndUpdate(logFile, "FGC ", &g_LastGCStatistics.fgc, cntFGC, g_LastGCStatistics.cntFGC, msec);
239 
240         // BGC summary
241         bgc.DisplayAndUpdate(logFile, "BGC ", &g_LastGCStatistics.bgc, cntBGC, g_LastGCStatistics.cntBGC, msec);
242 
243         // NGC/FGC break out by generation & compacting vs. sweeping
244         fprintf(logFile, "NGC   ");
245         for (int i = max_generation; i >= 0; --i)
246             fprintf(logFile, "gen%d %d (%d). ", i, cntNGCGen[i]-g_LastGCStatistics.cntNGCGen[i], cntNGCGen[i]);
247         fprintf(logFile, "\n");
248 
249         fprintf(logFile, "FGC   ");
250         for (int i = max_generation-1; i >= 0; --i)
251             fprintf(logFile, "gen%d %d (%d). ", i, cntFGCGen[i]-g_LastGCStatistics.cntFGCGen[i], cntFGCGen[i]);
252         fprintf(logFile, "\n");
253 
254         // Compacting vs. Sweeping break out
255         int _cntSweep = cntNGC-cntCompactNGC;
256         int _cntLastSweep = g_LastGCStatistics.cntNGC-g_LastGCStatistics.cntCompactNGC;
257         fprintf(logFile, "NGC   Sweeping %d (%d) Compacting %d (%d)\n",
258                _cntSweep - _cntLastSweep, _cntSweep,
259                cntCompactNGC - g_LastGCStatistics.cntCompactNGC, cntCompactNGC);
260 
261         _cntSweep = cntFGC-cntCompactFGC;
262         _cntLastSweep = g_LastGCStatistics.cntFGC-g_LastGCStatistics.cntCompactFGC;
263         fprintf(logFile, "FGC   Sweeping %d (%d) Compacting %d (%d)\n",
264                _cntSweep - _cntLastSweep, _cntSweep,
265                cntCompactFGC - g_LastGCStatistics.cntCompactFGC, cntCompactFGC);
266 
267 #ifdef TRACE_GC
268         // GC reasons...
269         for (int reason=(int)reason_alloc_soh; reason <= (int)reason_gcstress; ++reason)
270         {
271             if (cntReasons[reason] != 0)
272                 fprintf(logFile, "%s %d (%d). ", str_gc_reasons[reason],
273                     cntReasons[reason]-g_LastGCStatistics.cntReasons[reason], cntReasons[reason]);
274         }
275 #endif // TRACE_GC
276         fprintf(logFile, "\n\n");
277 
278         // flush the log file...
279         fflush(logFile);
280     }
281 
282     g_LastGCStatistics = *this;
283 
284     ngc.Reset();
285     fgc.Reset();
286     bgc.Reset();
287 }
288 
289 #endif // GC_STATS
290 
291 #ifdef BACKGROUND_GC
292 uint32_t bgc_alloc_spin_count = 140;
293 uint32_t bgc_alloc_spin_count_loh = 16;
294 uint32_t bgc_alloc_spin = 2;
295 
296 
297 inline
c_write(uint32_t & place,uint32_t value)298 void c_write (uint32_t& place, uint32_t value)
299 {
300     Interlocked::Exchange (&place, value);
301     //place = value;
302 }
303 
304 #ifndef DACCESS_COMPILE
305 // If every heap's gen2 or gen3 size is less than this threshold we will do a blocking GC.
306 const size_t bgc_min_per_heap = 4*1024*1024;
307 
308 int gc_heap::gchist_index = 0;
309 gc_mechanisms_store gc_heap::gchist[max_history_count];
310 
311 #ifndef MULTIPLE_HEAPS
312 size_t gc_heap::total_promoted_bytes = 0;
313 VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process;
314 int gc_heap::gchist_index_per_heap = 0;
315 gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count];
316 #endif //MULTIPLE_HEAPS
317 
add_to_history_per_heap()318 void gc_heap::add_to_history_per_heap()
319 {
320 #ifdef GC_HISTORY
321     gc_history* current_hist = &gchist_per_heap[gchist_index_per_heap];
322     current_hist->gc_index = settings.gc_index;
323     current_hist->current_bgc_state = current_bgc_state;
324     size_t elapsed = dd_gc_elapsed_time (dynamic_data_of (0));
325     current_hist->gc_time_ms = (uint32_t)elapsed;
326     current_hist->gc_efficiency = (elapsed ? (total_promoted_bytes / elapsed) : total_promoted_bytes);
327     current_hist->eph_low = generation_allocation_start (generation_of (max_generation-1));
328     current_hist->gen0_start = generation_allocation_start (generation_of (0));
329     current_hist->eph_high = heap_segment_allocated (ephemeral_heap_segment);
330 #ifdef BACKGROUND_GC
331     current_hist->bgc_lowest = background_saved_lowest_address;
332     current_hist->bgc_highest = background_saved_highest_address;
333 #endif //BACKGROUND_GC
334     current_hist->fgc_lowest = lowest_address;
335     current_hist->fgc_highest = highest_address;
336     current_hist->g_lowest = g_gc_lowest_address;
337     current_hist->g_highest = g_gc_highest_address;
338 
339     gchist_index_per_heap++;
340     if (gchist_index_per_heap == max_history_count)
341     {
342         gchist_index_per_heap = 0;
343     }
344 #endif //GC_HISTORY
345 }
346 
add_to_history()347 void gc_heap::add_to_history()
348 {
349 #ifdef GC_HISTORY
350     gc_mechanisms_store* current_settings = &gchist[gchist_index];
351     current_settings->store (&settings);
352 
353     gchist_index++;
354     if (gchist_index == max_history_count)
355     {
356         gchist_index = 0;
357     }
358 #endif //GC_HISTORY
359 }
360 
361 #endif //DACCESS_COMPILE
362 #endif //BACKGROUND_GC
363 
364 #if defined(TRACE_GC) && !defined(DACCESS_COMPILE)
365 BOOL   gc_log_on = TRUE;
366 FILE* gc_log = NULL;
367 size_t gc_log_file_size = 0;
368 
369 size_t gc_buffer_index = 0;
370 size_t max_gc_buffers = 0;
371 
372 static CLRCriticalSection gc_log_lock;
373 
374 // we keep this much in a buffer and only flush when the buffer is full
375 #define gc_log_buffer_size (1024*1024)
376 uint8_t* gc_log_buffer = 0;
377 size_t gc_log_buffer_offset = 0;
378 
log_va_msg(const char * fmt,va_list args)379 void log_va_msg(const char *fmt, va_list args)
380 {
381     gc_log_lock.Enter();
382 
383     const int BUFFERSIZE = 512;
384     static char rgchBuffer[BUFFERSIZE];
385     char *  pBuffer  = &rgchBuffer[0];
386 
387     pBuffer[0] = '\n';
388     int buffer_start = 1;
389     int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging());
390     buffer_start += pid_len;
391     memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start);
392     int msg_len = _vsnprintf_s(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
393     if (msg_len == -1)
394     {
395         msg_len = BUFFERSIZE - buffer_start;
396     }
397 
398     msg_len += buffer_start;
399 
400     if ((gc_log_buffer_offset + msg_len) > (gc_log_buffer_size - 12))
401     {
402         char index_str[8];
403         memset (index_str, '-', 8);
404         sprintf_s (index_str, _countof(index_str), "%d", (int)gc_buffer_index);
405         gc_log_buffer[gc_log_buffer_offset] = '\n';
406         memcpy (gc_log_buffer + (gc_log_buffer_offset + 1), index_str, 8);
407 
408         gc_buffer_index++;
409         if (gc_buffer_index > max_gc_buffers)
410         {
411             fseek (gc_log, 0, SEEK_SET);
412             gc_buffer_index = 0;
413         }
414         fwrite(gc_log_buffer, gc_log_buffer_size, 1, gc_log);
415         fflush(gc_log);
416         memset (gc_log_buffer, '*', gc_log_buffer_size);
417         gc_log_buffer_offset = 0;
418     }
419 
420     memcpy (gc_log_buffer + gc_log_buffer_offset, pBuffer, msg_len);
421     gc_log_buffer_offset += msg_len;
422 
423     gc_log_lock.Leave();
424 }
425 
GCLog(const char * fmt,...)426 void GCLog (const char *fmt, ... )
427 {
428     if (gc_log_on && (gc_log != NULL))
429     {
430         va_list     args;
431         va_start(args, fmt);
432         log_va_msg (fmt, args);
433         va_end(args);
434     }
435 }
436 #endif // TRACE_GC && !DACCESS_COMPILE
437 
438 #if defined(GC_CONFIG_DRIVEN) && !defined(DACCESS_COMPILE)
439 
440 BOOL   gc_config_log_on = FALSE;
441 FILE* gc_config_log = NULL;
442 
443 // we keep this much in a buffer and only flush when the buffer is full
444 #define gc_config_log_buffer_size (1*1024) // TEMP
445 uint8_t* gc_config_log_buffer = 0;
446 size_t gc_config_log_buffer_offset = 0;
447 
448 // For config since we log so little we keep the whole history. Also it's only
449 // ever logged by one thread so no need to synchronize.
log_va_msg_config(const char * fmt,va_list args)450 void log_va_msg_config(const char *fmt, va_list args)
451 {
452     const int BUFFERSIZE = 256;
453     static char rgchBuffer[BUFFERSIZE];
454     char *  pBuffer  = &rgchBuffer[0];
455 
456     pBuffer[0] = '\n';
457     int buffer_start = 1;
458     int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
459     assert (msg_len != -1);
460     msg_len += buffer_start;
461 
462     if ((gc_config_log_buffer_offset + msg_len) > gc_config_log_buffer_size)
463     {
464         fwrite(gc_config_log_buffer, gc_config_log_buffer_offset, 1, gc_config_log);
465         fflush(gc_config_log);
466         gc_config_log_buffer_offset = 0;
467     }
468 
469     memcpy (gc_config_log_buffer + gc_config_log_buffer_offset, pBuffer, msg_len);
470     gc_config_log_buffer_offset += msg_len;
471 }
472 
GCLogConfig(const char * fmt,...)473 void GCLogConfig (const char *fmt, ... )
474 {
475     if (gc_config_log_on && (gc_config_log != NULL))
476     {
477         va_list     args;
478         va_start( args, fmt );
479         log_va_msg_config (fmt, args);
480     }
481 }
482 #endif // GC_CONFIG_DRIVEN && !DACCESS_COMPILE
483 
484 #ifdef SYNCHRONIZATION_STATS
485 
486 // Number of GCs have we done since we last logged.
487 static unsigned int         gc_count_during_log;
488  // In ms. This is how often we print out stats.
489 static const unsigned int   log_interval = 5000;
490 // Time (in ms) when we start a new log interval.
491 static unsigned int         log_start_tick;
492 static unsigned int         gc_lock_contended;
493 // Cycles accumulated in SuspendEE during log_interval.
494 static uint64_t             suspend_ee_during_log;
495 // Cycles accumulated in RestartEE during log_interval.
496 static uint64_t             restart_ee_during_log;
497 static uint64_t             gc_during_log;
498 
499 #endif //SYNCHRONIZATION_STATS
500 
501 void
init_sync_log_stats()502 init_sync_log_stats()
503 {
504 #ifdef SYNCHRONIZATION_STATS
505     if (gc_count_during_log == 0)
506     {
507         gc_heap::init_sync_stats();
508         suspend_ee_during_log = 0;
509         restart_ee_during_log = 0;
510         gc_during_log = 0;
511         gc_lock_contended = 0;
512 
513         log_start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
514     }
515     gc_count_during_log++;
516 #endif //SYNCHRONIZATION_STATS
517 }
518 
519 void
process_sync_log_stats()520 process_sync_log_stats()
521 {
522 #ifdef SYNCHRONIZATION_STATS
523 
524     unsigned int log_elapsed = GCToOSInterface::GetLowPrecisionTimeStamp() - log_start_tick;
525 
526     if (log_elapsed > log_interval)
527     {
528         // Print out the cycles we spent on average in each suspend and restart.
529         printf("\n_________________________________________________________________________________\n"
530             "Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n"
531             "SuspendEE: %8u; RestartEE: %8u\n",
532             log_interval / 1000,
533             gc_count_during_log,
534             gc_lock_contended,
535             (unsigned int)(gc_during_log / gc_count_during_log),
536             (unsigned int)(suspend_ee_during_log / gc_count_during_log),
537             (unsigned int)(restart_ee_during_log / gc_count_during_log));
538         gc_heap::print_sync_stats(gc_count_during_log);
539 
540         gc_count_during_log = 0;
541     }
542 #endif //SYNCHRONIZATION_STATS
543 }
544 
545 #ifdef MULTIPLE_HEAPS
546 #ifndef DACCESS_COMPILE
547 
548 enum gc_join_stage
549 {
550     gc_join_init_cpu_mapping = 0,
551     gc_join_done = 1,
552     gc_join_generation_determined = 2,
553     gc_join_begin_mark_phase = 3,
554     gc_join_scan_dependent_handles = 4,
555     gc_join_rescan_dependent_handles = 5,
556     gc_join_scan_sizedref_done = 6,
557     gc_join_null_dead_short_weak = 7,
558     gc_join_scan_finalization = 8,
559     gc_join_null_dead_long_weak = 9,
560     gc_join_null_dead_syncblk = 10,
561     gc_join_decide_on_compaction = 11,
562     gc_join_rearrange_segs_compaction = 12,
563     gc_join_adjust_handle_age_compact = 13,
564     gc_join_adjust_handle_age_sweep = 14,
565     gc_join_begin_relocate_phase = 15,
566     gc_join_relocate_phase_done = 16,
567     gc_join_verify_objects_done = 17,
568     gc_join_start_bgc = 18,
569     gc_join_restart_ee = 19,
570     gc_join_concurrent_overflow = 20,
571     gc_join_suspend_ee = 21,
572     gc_join_bgc_after_ephemeral = 22,
573     gc_join_allow_fgc = 23,
574     gc_join_bgc_sweep = 24,
575     gc_join_suspend_ee_verify = 25,
576     gc_join_restart_ee_verify = 26,
577     gc_join_set_state_free = 27,
578     gc_r_join_update_card_bundle = 28,
579     gc_join_after_absorb = 29,
580     gc_join_verify_copy_table = 30,
581     gc_join_after_reset = 31,
582     gc_join_after_ephemeral_sweep = 32,
583     gc_join_after_profiler_heap_walk = 33,
584     gc_join_minimal_gc = 34,
585     gc_join_after_commit_soh_no_gc = 35,
586     gc_join_expand_loh_no_gc = 36,
587     gc_join_final_no_gc = 37,
588     gc_join_disable_software_write_watch = 38,
589     gc_join_max = 39
590 };
591 
592 enum gc_join_flavor
593 {
594     join_flavor_server_gc = 0,
595     join_flavor_bgc = 1
596 };
597 
598 #define first_thread_arrived 2
599 struct join_structure
600 {
601     CLREvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
602     VOLATILE(int32_t) join_lock;
603     VOLATILE(int32_t) r_join_lock;
604     VOLATILE(int32_t) join_restart;
605     VOLATILE(int32_t) r_join_restart; // only used by get_here_first and friends.
606     int n_threads;
607     VOLATILE(BOOL) joined_p;
608     // avoid lock_color and join_lock being on same cache line
609     // make sure to modify this if adding/removing variables to layout
610     char cache_line_separator[HS_CACHE_LINE_SIZE - (3*sizeof(int) + sizeof(int) + sizeof(BOOL))];
611     VOLATILE(int) lock_color;
612     VOLATILE(BOOL) wait_done;
613 };
614 
615 enum join_type
616 {
617     type_last_join = 0,
618     type_join = 1,
619     type_restart = 2,
620     type_first_r_join = 3,
621     type_r_join = 4
622 };
623 
624 enum join_time
625 {
626     time_start = 0,
627     time_end = 1
628 };
629 
630 enum join_heap_index
631 {
632     join_heap_restart = 100,
633     join_heap_r_restart = 200
634 };
635 
636 struct join_event
637 {
638     uint32_t heap;
639     join_time time;
640     join_type type;
641 };
642 
643 class t_join
644 {
645     join_structure join_struct;
646 
647     int id;
648     gc_join_flavor flavor;
649 
650 #ifdef JOIN_STATS
651     unsigned int start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
652     // remember join id and last thread to arrive so restart can use these
653     int thd;
654     // we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval
655     uint32_t start_tick;
656     // counters for joins, in 1000's of clock cycles
657     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];
658 #endif //JOIN_STATS
659 
660 public:
init(int n_th,gc_join_flavor f)661     BOOL init (int n_th, gc_join_flavor f)
662     {
663         dprintf (JOIN_LOG, ("Initializing join structure"));
664         join_struct.n_threads = n_th;
665         join_struct.lock_color = 0;
666         for (int i = 0; i < 3; i++)
667         {
668             if (!join_struct.joined_event[i].IsValid())
669             {
670                 join_struct.joined_p = FALSE;
671                 dprintf (JOIN_LOG, ("Creating join event %d", i));
672                 // TODO - changing this to a non OS event
673                 // because this is also used by BGC threads which are
674                 // managed threads and WaitEx does not allow you to wait
675                 // for an OS event on a managed thread.
676                 // But we are not sure if this plays well in the hosting
677                 // environment.
678                 //join_struct.joined_event[i].CreateOSManualEventNoThrow(FALSE);
679                 if (!join_struct.joined_event[i].CreateManualEventNoThrow(FALSE))
680                     return FALSE;
681             }
682         }
683         join_struct.join_lock = join_struct.n_threads;
684         join_struct.join_restart = join_struct.n_threads - 1;
685         join_struct.r_join_lock = join_struct.n_threads;
686         join_struct.r_join_restart = join_struct.n_threads - 1;
687         join_struct.wait_done = FALSE;
688         flavor = f;
689 
690 #ifdef JOIN_STATS
691         start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
692 #endif //JOIN_STATS
693 
694         return TRUE;
695     }
696 
destroy()697     void destroy ()
698     {
699         dprintf (JOIN_LOG, ("Destroying join structure"));
700         for (int i = 0; i < 3; i++)
701         {
702             if (join_struct.joined_event[i].IsValid())
703                 join_struct.joined_event[i].CloseEvent();
704         }
705     }
706 
fire_event(int heap,join_time time,join_type type,int join_id)707     inline void fire_event (int heap, join_time time, join_type type, int join_id)
708     {
709         FireEtwGCJoin_V2(heap, time, type, GetClrInstanceId(), join_id);
710     }
711 
join(gc_heap * gch,int join_id)712     void join (gc_heap* gch, int join_id)
713     {
714 #ifdef JOIN_STATS
715         // parallel execution ends here
716         end[gch->heap_number] = GetCycleCount32();
717 #endif //JOIN_STATS
718 
719         assert (!join_struct.joined_p);
720         int color = join_struct.lock_color;
721 
722         if (Interlocked::Decrement(&join_struct.join_lock) != 0)
723         {
724             dprintf (JOIN_LOG, ("join%d(%d): Join() Waiting...join_lock is now %d",
725                 flavor, join_id, (int32_t)(join_struct.join_lock)));
726 
727             fire_event (gch->heap_number, time_start, type_join, join_id);
728 
729             //busy wait around the color
730             if (color == join_struct.lock_color)
731             {
732 respin:
733                 int spin_count = 4096 * g_SystemInfo.dwNumberOfProcessors;
734                 for (int j = 0; j < spin_count; j++)
735                 {
736                     if (color != join_struct.lock_color)
737                     {
738                         break;
739                     }
740                     YieldProcessor();           // indicate to the processor that we are spinning
741                 }
742 
743                 // we've spun, and if color still hasn't changed, fall into hard wait
744                 if (color == join_struct.lock_color)
745                 {
746                     dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d",
747                         flavor, join_id, color, (int32_t)(join_struct.join_lock)));
748 
749                     //Thread* current_thread = GetThread();
750                     //BOOL cooperative_mode = gc_heap::enable_preemptive (current_thread);
751                     uint32_t dwJoinWait = join_struct.joined_event[color].Wait(INFINITE, FALSE);
752                     //gc_heap::disable_preemptive (current_thread, cooperative_mode);
753 
754                     if (dwJoinWait != WAIT_OBJECT_0)
755                     {
756                         STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
757                         FATAL_GC_ERROR ();
758                     }
759                 }
760 
761                 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
762                 if (color == join_struct.lock_color)
763                 {
764                     goto respin;
765                 }
766 
767                 dprintf (JOIN_LOG, ("join%d(%d): Join() done, join_lock is %d",
768                     flavor, join_id, (int32_t)(join_struct.join_lock)));
769             }
770 
771             fire_event (gch->heap_number, time_end, type_join, join_id);
772 
773             // last thread out should reset event
774             if (Interlocked::Decrement(&join_struct.join_restart) == 0)
775             {
776                 // the joined event must be set at this point, because the restarting must have done this
777                 join_struct.join_restart = join_struct.n_threads - 1;
778 //                printf("Reset joined_event %d\n", color);
779             }
780 
781 #ifdef JOIN_STATS
782             // parallel execution starts here
783             start[gch->heap_number] = GetCycleCount32();
784             Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])/1000);
785 #endif //JOIN_STATS
786         }
787         else
788         {
789             fire_event (gch->heap_number, time_start, type_last_join, join_id);
790 
791             join_struct.joined_p = TRUE;
792             dprintf (JOIN_LOG, ("join%d(%d): Last thread to complete the join, setting id", flavor, join_id));
793             join_struct.joined_event[!color].Reset();
794             id = join_id;
795             // this one is alone so it can proceed
796 #ifdef JOIN_STATS
797             // remember the join id, the last thread arriving, the start of the sequential phase,
798             // and keep track of the cycles spent waiting in the join
799             thd = gch->heap_number;
800             start_seq = GetCycleCount32();
801             Interlocked::ExchangeAdd(&in_join_total[join_id], (start_seq - end[gch->heap_number])/1000);
802 #endif //JOIN_STATS
803         }
804     }
805 
806     // Reverse join - first thread gets here does the work; other threads will only proceed
807     // after the work is done.
808     // Note that you cannot call this twice in a row on the same thread. Plus there's no
809     // need to call it twice in row - you should just merge the work.
r_join(gc_heap * gch,int join_id)810     BOOL r_join (gc_heap* gch, int join_id)
811     {
812 #ifdef JOIN_STATS
813         // parallel execution ends here
814         end[gch->heap_number] = GetCycleCount32();
815 #endif //JOIN_STATS
816 
817         if (join_struct.n_threads == 1)
818         {
819             return TRUE;
820         }
821 
822         if (Interlocked::Decrement(&join_struct.r_join_lock) != (join_struct.n_threads - 1))
823         {
824             if (!join_struct.wait_done)
825             {
826                 dprintf (JOIN_LOG, ("r_join() Waiting..."));
827 
828                 fire_event (gch->heap_number, time_start, type_join, join_id);
829 
830                 //busy wait around the color
831                 if (!join_struct.wait_done)
832                 {
833         respin:
834                     int spin_count = 2 * 4096 * g_SystemInfo.dwNumberOfProcessors;
835                     for (int j = 0; j < spin_count; j++)
836                     {
837                         if (join_struct.wait_done)
838                         {
839                             break;
840                         }
841                         YieldProcessor();           // indicate to the processor that we are spinning
842                     }
843 
844                     // we've spun, and if color still hasn't changed, fall into hard wait
845                     if (!join_struct.wait_done)
846                     {
847                         dprintf (JOIN_LOG, ("Join() hard wait on reset event %d", first_thread_arrived));
848                         uint32_t dwJoinWait = join_struct.joined_event[first_thread_arrived].Wait(INFINITE, FALSE);
849                         if (dwJoinWait != WAIT_OBJECT_0)
850                         {
851                             STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
852                             FATAL_GC_ERROR ();
853                         }
854                     }
855 
856                     // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
857                     if (!join_struct.wait_done)
858                     {
859                         goto respin;
860                     }
861 
862                     dprintf (JOIN_LOG, ("r_join() done"));
863                 }
864 
865                 fire_event (gch->heap_number, time_end, type_join, join_id);
866 
867 #ifdef JOIN_STATS
868                 // parallel execution starts here
869                 start[gch->heap_number] = GetCycleCount32();
870                 Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number])/1000);
871 #endif //JOIN_STATS
872             }
873 
874             return FALSE;
875         }
876         else
877         {
878             fire_event (gch->heap_number, time_start, type_first_r_join, join_id);
879             return TRUE;
880         }
881     }
882 
restart()883     void restart()
884     {
885 #ifdef JOIN_STATS
886         unsigned int elapsed_seq = GetCycleCount32() - start_seq;
887         unsigned int max = 0, sum = 0;
888         for (int i = 0; i < join_struct.n_threads; i++)
889         {
890             unsigned int elapsed = end[i] - start[i];
891             if (max < elapsed)
892                 max = elapsed;
893             sum += elapsed;
894         }
895         unsigned int seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
896         unsigned int par_loss = join_struct.n_threads*max - sum;
897         double efficiency = 0.0;
898         if (max > 0)
899             efficiency = sum*100.0/(join_struct.n_threads*max);
900 
901         // enable this printf to get statistics on each individual join as it occurs
902 //      printf("join #%3d  seq_loss = %5d   par_loss = %5d  efficiency = %3.0f%%\n", join_id, seq_loss/1000, par_loss/1000, efficiency);
903 
904         elapsed_total[join_id] += sum/1000;
905         seq_loss_total[join_id] += seq_loss/1000;
906         par_loss_total[join_id] += par_loss/1000;
907 
908         // every 10 seconds, print a summary of the time spent in each type of join, in 1000's of clock cycles
909         if (GCToOSInterface::GetLowPrecisionTimeStamp() - start_tick > 10*1000)
910         {
911             printf("**** summary *****\n");
912             for (int i = 0; i < 16; i++)
913             {
914                 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]);
915                 elapsed_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
916             }
917             start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
918         }
919 #endif //JOIN_STATS
920 
921         fire_event (join_heap_restart, time_start, type_restart, -1);
922         assert (join_struct.joined_p);
923         join_struct.joined_p = FALSE;
924         join_struct.join_lock = join_struct.n_threads;
925         dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
926 //        printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start);
927         int color = join_struct.lock_color;
928         join_struct.lock_color = !color;
929         join_struct.joined_event[color].Set();
930 
931 //        printf("Set joined_event %d\n", !join_struct.lock_color);
932 
933         fire_event (join_heap_restart, time_end, type_restart, -1);
934 
935 #ifdef JOIN_STATS
936         start[thd] = GetCycleCount32();
937 #endif //JOIN_STATS
938     }
939 
joined()940     BOOL joined()
941     {
942         dprintf (JOIN_LOG, ("join%d(%d): joined, join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
943         return join_struct.joined_p;
944     }
945 
r_restart()946     void r_restart()
947     {
948         if (join_struct.n_threads != 1)
949         {
950             fire_event (join_heap_r_restart, time_start, type_restart, -1);
951             join_struct.wait_done = TRUE;
952             join_struct.joined_event[first_thread_arrived].Set();
953             fire_event (join_heap_r_restart, time_end, type_restart, -1);
954         }
955     }
956 
r_init()957     void r_init()
958     {
959         if (join_struct.n_threads != 1)
960         {
961             join_struct.r_join_lock = join_struct.n_threads;
962             join_struct.r_join_restart = join_struct.n_threads - 1;
963             join_struct.wait_done = FALSE;
964             join_struct.joined_event[first_thread_arrived].Reset();
965         }
966     }
967 };
968 
969 t_join gc_t_join;
970 
971 #ifdef BACKGROUND_GC
972 t_join bgc_t_join;
973 #endif //BACKGROUND_GC
974 
975 #endif // DACCESS_COMPILE
976 
977 #endif //MULTIPLE_HEAPS
978 
979 #define spin_and_switch(count_to_spin, expr) \
980 { \
981     for (int j = 0; j < count_to_spin; j++) \
982     { \
983         if (expr) \
984         { \
985             break;\
986         } \
987         YieldProcessor(); \
988     } \
989     if (!(expr)) \
990     { \
991         GCToOSInterface::YieldThread(0); \
992     } \
993 }
994 
995 #ifndef DACCESS_COMPILE
996 #ifdef BACKGROUND_GC
997 
998 #define max_pending_allocs 64
999 
1000 class exclusive_sync
1001 {
1002     // TODO - verify that this is the right syntax for Volatile.
1003     VOLATILE(uint8_t*) rwp_object;
1004     VOLATILE(int32_t) needs_checking;
1005 
1006     int spin_count;
1007 
1008     uint8_t cache_separator[HS_CACHE_LINE_SIZE - sizeof (int) - sizeof (int32_t)];
1009 
1010     // TODO - perhaps each object should be on its own cache line...
1011     VOLATILE(uint8_t*) alloc_objects[max_pending_allocs];
1012 
find_free_index()1013     int find_free_index ()
1014     {
1015         for (int i = 0; i < max_pending_allocs; i++)
1016         {
1017             if (alloc_objects [i] == (uint8_t*)0)
1018             {
1019                 return i;
1020             }
1021         }
1022 
1023         return -1;
1024     }
1025 
1026 public:
init()1027     void init()
1028     {
1029         spin_count = 32 * (g_SystemInfo.dwNumberOfProcessors - 1);
1030         rwp_object = 0;
1031         needs_checking = 0;
1032         for (int i = 0; i < max_pending_allocs; i++)
1033         {
1034             alloc_objects [i] = (uint8_t*)0;
1035         }
1036     }
1037 
check()1038     void check()
1039     {
1040         for (int i = 0; i < max_pending_allocs; i++)
1041         {
1042             if (alloc_objects [i] != (uint8_t*)0)
1043             {
1044                 GCToOSInterface::DebugBreak();
1045             }
1046         }
1047     }
1048 
bgc_mark_set(uint8_t * obj)1049     void bgc_mark_set (uint8_t* obj)
1050     {
1051         dprintf (3, ("cm: probing %Ix", obj));
1052 retry:
1053         if (Interlocked::Exchange (&needs_checking, 1) == 0)
1054         {
1055             // If we spend too much time spending all the allocs,
1056             // consider adding a high water mark and scan up
1057             // to that; we'll need to interlock in done when
1058             // we update the high watermark.
1059             for (int i = 0; i < max_pending_allocs; i++)
1060             {
1061                 if (obj == alloc_objects[i])
1062                 {
1063                     needs_checking = 0;
1064                     dprintf (3, ("cm: will spin"));
1065                     spin_and_switch (spin_count, (obj != alloc_objects[i]));
1066                     goto retry;
1067                 }
1068             }
1069 
1070             rwp_object = obj;
1071             needs_checking = 0;
1072             dprintf (3, ("cm: set %Ix", obj));
1073             return;
1074         }
1075         else
1076         {
1077             spin_and_switch (spin_count, (needs_checking == 0));
1078             goto retry;
1079         }
1080     }
1081 
loh_alloc_set(uint8_t * obj)1082     int loh_alloc_set (uint8_t* obj)
1083     {
1084         if (!gc_heap::cm_in_progress)
1085         {
1086             return -1;
1087         }
1088 
1089 retry:
1090         dprintf (3, ("loh alloc: probing %Ix", obj));
1091 
1092         if (Interlocked::Exchange (&needs_checking, 1) == 0)
1093         {
1094             if (obj == rwp_object)
1095             {
1096                 needs_checking = 0;
1097                 spin_and_switch (spin_count, (obj != rwp_object));
1098                 goto retry;
1099             }
1100             else
1101             {
1102                 int cookie = find_free_index();
1103 
1104                 if (cookie != -1)
1105                 {
1106                     alloc_objects[cookie] = obj;
1107                     needs_checking = 0;
1108                     //if (cookie >= 4)
1109                     //{
1110                     //    GCToOSInterface::DebugBreak();
1111                     //}
1112 
1113                     dprintf (3, ("loh alloc: set %Ix at %d", obj, cookie));
1114                     return cookie;
1115                 }
1116                 else
1117                 {
1118                     needs_checking = 0;
1119                     dprintf (3, ("loh alloc: setting %Ix will spin to acquire a free index", obj));
1120                     spin_and_switch (spin_count, (find_free_index () != -1));
1121                     goto retry;
1122                 }
1123             }
1124         }
1125         else
1126         {
1127             dprintf (3, ("loh alloc: will spin on checking %Ix", obj));
1128             spin_and_switch (spin_count, (needs_checking == 0));
1129             goto retry;
1130         }
1131     }
1132 
bgc_mark_done()1133     void bgc_mark_done ()
1134     {
1135         dprintf (3, ("cm: release lock on %Ix", (uint8_t *)rwp_object));
1136         rwp_object = 0;
1137     }
1138 
loh_alloc_done_with_index(int index)1139     void loh_alloc_done_with_index (int index)
1140     {
1141         dprintf (3, ("loh alloc: release lock on %Ix based on %d", (uint8_t *)alloc_objects[index], index));
1142         assert ((index >= 0) && (index < max_pending_allocs));
1143         alloc_objects[index] = (uint8_t*)0;
1144     }
1145 
loh_alloc_done(uint8_t * obj)1146     void loh_alloc_done (uint8_t* obj)
1147     {
1148 #ifdef BACKGROUND_GC
1149         if (!gc_heap::cm_in_progress)
1150         {
1151             return;
1152         }
1153 
1154         for (int i = 0; i < max_pending_allocs; i++)
1155         {
1156             if (alloc_objects [i] == obj)
1157             {
1158                 dprintf (3, ("loh alloc: release lock on %Ix at %d", (uint8_t *)alloc_objects[i], i));
1159                 alloc_objects[i] = (uint8_t*)0;
1160                 return;
1161             }
1162         }
1163 #endif //BACKGROUND_GC
1164     }
1165 };
1166 
1167 // Note that this class was written assuming just synchronization between
1168 // one background GC thread and multiple user threads that might request
1169 // an FGC - it does not take into account what kind of locks the multiple
1170 // user threads might be holding at the time (eg, there could only be one
1171 // user thread requesting an FGC because it needs to take gc_lock first)
1172 // so you'll see checks that may not be necessary if you take those conditions
1173 // into consideration.
1174 //
1175 // With the introduction of Server Background GC we no longer use this
1176 // class to do synchronization between FGCs and BGC.
1177 class recursive_gc_sync
1178 {
1179     static VOLATILE(int32_t) foreground_request_count;//initial state 0
1180     static VOLATILE(BOOL) gc_background_running; //initial state FALSE
1181     static VOLATILE(int32_t) foreground_count; // initial state 0;
1182     static VOLATILE(uint32_t) foreground_gate; // initial state FALSE;
1183     static CLREvent foreground_complete;//Auto Reset
1184     static CLREvent foreground_allowed;//Auto Reset
1185 public:
1186     static void begin_background();
1187     static void end_background();
1188     static void begin_foreground();
1189     static void end_foreground();
1190     BOOL allow_foreground ();
1191     static BOOL init();
1192     static void shutdown();
background_running_p()1193     static BOOL background_running_p() {return gc_background_running;}
1194 };
1195 
1196 VOLATILE(int32_t) recursive_gc_sync::foreground_request_count = 0;//initial state 0
1197 VOLATILE(int32_t) recursive_gc_sync::foreground_count = 0; // initial state 0;
1198 VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE
1199 VOLATILE(uint32_t) recursive_gc_sync::foreground_gate = 0;
1200 CLREvent recursive_gc_sync::foreground_complete;//Auto Reset
1201 CLREvent recursive_gc_sync::foreground_allowed;//Manual Reset
1202 
init()1203 BOOL recursive_gc_sync::init ()
1204 {
1205     foreground_request_count = 0;
1206     foreground_count = 0;
1207     gc_background_running = FALSE;
1208     foreground_gate = 0;
1209 
1210     if (!foreground_complete.CreateOSAutoEventNoThrow(FALSE))
1211     {
1212         goto error;
1213     }
1214     if (!foreground_allowed.CreateManualEventNoThrow(FALSE))
1215     {
1216         goto error;
1217     }
1218     return TRUE;
1219 
1220 error:
1221     shutdown();
1222     return FALSE;
1223 
1224 }
1225 
shutdown()1226 void recursive_gc_sync::shutdown()
1227 {
1228     if (foreground_complete.IsValid())
1229         foreground_complete.CloseEvent();
1230     if (foreground_allowed.IsValid())
1231         foreground_allowed.CloseEvent();
1232 }
1233 
begin_background()1234 void recursive_gc_sync::begin_background()
1235 {
1236     dprintf (2, ("begin background"));
1237     foreground_request_count = 1;
1238     foreground_count = 1;
1239     foreground_allowed.Reset();
1240     gc_background_running = TRUE;
1241 }
end_background()1242 void recursive_gc_sync::end_background()
1243 {
1244     dprintf (2, ("end background"));
1245     gc_background_running = FALSE;
1246     foreground_gate = 1;
1247     foreground_allowed.Set();
1248 }
1249 
begin_foreground()1250 void recursive_gc_sync::begin_foreground()
1251 {
1252     dprintf (2, ("begin_foreground"));
1253 
1254     BOOL cooperative_mode = FALSE;
1255     Thread* current_thread = 0;
1256 
1257     if (gc_background_running)
1258     {
1259         gc_heap::fire_alloc_wait_event_begin (awr_fgc_wait_for_bgc);
1260         gc_heap::alloc_wait_event_p = TRUE;
1261 
1262 try_again_top:
1263 
1264         Interlocked::Increment (&foreground_request_count);
1265 
1266 try_again_no_inc:
1267         dprintf(2, ("Waiting sync gc point"));
1268         assert (foreground_allowed.IsValid());
1269         assert (foreground_complete.IsValid());
1270 
1271         current_thread = GetThread();
1272         cooperative_mode = gc_heap::enable_preemptive (current_thread);
1273 
1274         foreground_allowed.Wait(INFINITE, FALSE);
1275 
1276         dprintf(2, ("Waiting sync gc point is done"));
1277 
1278         gc_heap::disable_preemptive (current_thread, cooperative_mode);
1279 
1280         if (foreground_gate)
1281         {
1282             Interlocked::Increment (&foreground_count);
1283             dprintf (2, ("foreground_count: %d", (int32_t)foreground_count));
1284             if (foreground_gate)
1285             {
1286                 gc_heap::settings.concurrent = FALSE;
1287                 return;
1288             }
1289             else
1290             {
1291                 end_foreground();
1292                 goto try_again_top;
1293             }
1294         }
1295         else
1296         {
1297             goto try_again_no_inc;
1298         }
1299     }
1300 }
1301 
end_foreground()1302 void recursive_gc_sync::end_foreground()
1303 {
1304     dprintf (2, ("end_foreground"));
1305     if (gc_background_running)
1306     {
1307         Interlocked::Decrement (&foreground_request_count);
1308         dprintf (2, ("foreground_count before decrement: %d", (int32_t)foreground_count));
1309         if (Interlocked::Decrement (&foreground_count) == 0)
1310         {
1311             //c_write ((BOOL*)&foreground_gate, 0);
1312             // TODO - couldn't make the syntax work with Volatile<T>
1313             foreground_gate = 0;
1314             if (foreground_count == 0)
1315             {
1316                 foreground_allowed.Reset ();
1317                 dprintf(2, ("setting foreground complete event"));
1318                 foreground_complete.Set();
1319             }
1320         }
1321     }
1322 }
1323 
1324 inline
allow_foreground()1325 BOOL recursive_gc_sync::allow_foreground()
1326 {
1327     assert (gc_heap::settings.concurrent);
1328     dprintf (100, ("enter allow_foreground, f_req_count: %d, f_count: %d",
1329                    (int32_t)foreground_request_count, (int32_t)foreground_count));
1330 
1331     BOOL did_fgc = FALSE;
1332 
1333     //if we have suspended the EE, just return because
1334     //some thread could be waiting on this to proceed.
1335     if (!GCHeap::GcInProgress)
1336     {
1337         //TODO BACKGROUND_GC This is to stress the concurrency between
1338         //background and foreground
1339 //        gc_heap::disallow_new_allocation (0);
1340 
1341         //GCToOSInterface::YieldThread(0);
1342 
1343         //END of TODO
1344         if (foreground_request_count != 0)
1345         {
1346             //foreground wants to run
1347             //save the important settings
1348             //TODO BACKGROUND_GC be more selective about the important settings.
1349             gc_mechanisms saved_settings = gc_heap::settings;
1350             do
1351             {
1352                 did_fgc = TRUE;
1353                 //c_write ((BOOL*)&foreground_gate, 1);
1354                 // TODO - couldn't make the syntax work with Volatile<T>
1355                 foreground_gate = 1;
1356                 foreground_allowed.Set ();
1357                 foreground_complete.Wait (INFINITE, FALSE);
1358             }while (/*foreground_request_count ||*/ foreground_gate);
1359 
1360             assert (!foreground_gate);
1361 
1362             //restore the important settings
1363             gc_heap::settings = saved_settings;
1364             GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
1365             //the background GC shouldn't be using gc_high and gc_low
1366             //gc_low = lowest_address;
1367             //gc_high = highest_address;
1368         }
1369 
1370         //TODO BACKGROUND_GC This is to stress the concurrency between
1371         //background and foreground
1372 //        gc_heap::allow_new_allocation (0);
1373         //END of TODO
1374     }
1375 
1376     dprintf (100, ("leave allow_foreground"));
1377     assert (gc_heap::settings.concurrent);
1378     return did_fgc;
1379 }
1380 
1381 #endif //BACKGROUND_GC
1382 #endif //DACCESS_COMPILE
1383 
1384 #if  defined(COUNT_CYCLES) || defined(JOIN_STATS) || defined(SYNCHRONIZATION_STATS)
1385 #ifdef _MSC_VER
1386 #pragma warning(disable:4035)
1387 #endif //_MSC_VER
1388 
1389 static
GetCycleCount32()1390 unsigned        GetCycleCount32()        // enough for about 40 seconds
1391 {
1392 __asm   push    EDX
1393 __asm   _emit   0x0F
1394 __asm   _emit   0x31
1395 __asm   pop     EDX
1396 };
1397 
1398 #pragma warning(default:4035)
1399 
1400 #endif //COUNT_CYCLES || JOIN_STATS || SYNCHRONIZATION_STATS
1401 
1402 #ifdef TIME_GC
1403 int mark_time, plan_time, sweep_time, reloc_time, compact_time;
1404 #endif //TIME_GC
1405 
1406 #ifndef MULTIPLE_HEAPS
1407 
1408 #endif // MULTIPLE_HEAPS
1409 
1410 #ifdef TRACE_GC
1411 
1412 int     print_level     = DEFAULT_GC_PRN_LVL;  //level of detail of the debug trace
1413 BOOL    trace_gc        = FALSE;
1414 int       gc_trace_fac = 0;
1415 hlet* hlet::bindings = 0;
1416 
1417 #endif //TRACE_GC
1418 
1419 void reset_memory (uint8_t* o, size_t sizeo);
1420 
1421 #ifdef WRITE_WATCH
1422 
1423 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1424 static bool virtual_alloc_hardware_write_watch = false;
1425 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1426 
1427 static bool hardware_write_watch_capability = false;
1428 
1429 #ifndef DACCESS_COMPILE
1430 
1431 //check if the write watch APIs are supported.
1432 
hardware_write_watch_api_supported()1433 void hardware_write_watch_api_supported()
1434 {
1435     if (GCToOSInterface::SupportsWriteWatch())
1436     {
1437         hardware_write_watch_capability = true;
1438         dprintf (2, ("WriteWatch supported"));
1439     }
1440     else
1441     {
1442         dprintf (2,("WriteWatch not supported"));
1443     }
1444 }
1445 
1446 #endif //!DACCESS_COMPILE
1447 
can_use_hardware_write_watch()1448 inline bool can_use_hardware_write_watch()
1449 {
1450     return hardware_write_watch_capability;
1451 }
1452 
can_use_write_watch_for_gc_heap()1453 inline bool can_use_write_watch_for_gc_heap()
1454 {
1455 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1456     return true;
1457 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1458     return can_use_hardware_write_watch();
1459 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1460 }
1461 
can_use_write_watch_for_card_table()1462 inline bool can_use_write_watch_for_card_table()
1463 {
1464     return can_use_hardware_write_watch();
1465 }
1466 
1467 #else
1468 #define mem_reserve (MEM_RESERVE)
1469 #endif //WRITE_WATCH
1470 
1471 //check if the low memory notification is supported
1472 
1473 #ifndef DACCESS_COMPILE
1474 
WaitLongerNoInstru(int i)1475 void WaitLongerNoInstru (int i)
1476 {
1477     // every 8th attempt:
1478     Thread *pCurThread = GetThread();
1479     bool bToggleGC = false;
1480     if (pCurThread)
1481     {
1482         bToggleGC = GCToEEInterface::IsPreemptiveGCDisabled(pCurThread);
1483         if (bToggleGC)
1484             GCToEEInterface::EnablePreemptiveGC(pCurThread);
1485     }
1486 
1487     // if we're waiting for gc to finish, we should block immediately
1488     if (!g_TrapReturningThreads)
1489     {
1490         if  (g_SystemInfo.dwNumberOfProcessors > 1)
1491         {
1492             YieldProcessor();           // indicate to the processor that we are spining
1493             if  (i & 0x01f)
1494                 GCToOSInterface::YieldThread (0);
1495             else
1496                 GCToOSInterface::Sleep (5);
1497         }
1498         else
1499             GCToOSInterface::Sleep (5);
1500     }
1501 
1502     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1503     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1504     // It is important that the thread is going to wait for GC.  Otherwise the thread
1505     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD.
1506     if (pCurThread)
1507     {
1508         if (bToggleGC || g_TrapReturningThreads)
1509         {
1510 #ifdef _DEBUG
1511             // In debug builds, all enter_spin_lock operations go through this code.  If a GC has
1512             // started, it is important to block until the GC thread calls set_gc_done (since it is
1513             // guaranteed to have cleared g_TrapReturningThreads by this point).  This avoids livelock
1514             // conditions which can otherwise occur if threads are allowed to spin in this function
1515             // (and therefore starve the GC thread) between the point when the GC thread sets the
1516             // WaitForGC event and the point when the GC thread clears g_TrapReturningThreads.
1517             if (gc_heap::gc_started)
1518             {
1519                 gc_heap::wait_for_gc_done();
1520             }
1521 #endif // _DEBUG
1522             GCToEEInterface::DisablePreemptiveGC(pCurThread);
1523             if (!bToggleGC)
1524             {
1525                 GCToEEInterface::EnablePreemptiveGC(pCurThread);
1526             }
1527         }
1528     }
1529     else if (g_TrapReturningThreads)
1530     {
1531         g_theGCHeap->WaitUntilGCComplete();
1532     }
1533 }
1534 
1535 inline
safe_switch_to_thread()1536 static void safe_switch_to_thread()
1537 {
1538     Thread* current_thread = GetThread();
1539     BOOL cooperative_mode = gc_heap::enable_preemptive(current_thread);
1540 
1541     GCToOSInterface::YieldThread(0);
1542 
1543     gc_heap::disable_preemptive(current_thread, cooperative_mode);
1544 }
1545 
1546 //
1547 // We need the following methods to have volatile arguments, so that they can accept
1548 // raw pointers in addition to the results of the & operator on Volatile<T>.
1549 //
1550 inline
enter_spin_lock_noinstru(RAW_KEYWORD (volatile)int32_t * lock)1551 static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1552 {
1553 retry:
1554 
1555     if (Interlocked::Exchange (lock, 0) >= 0)
1556     {
1557         unsigned int i = 0;
1558         while (VolatileLoad(lock) >= 0)
1559         {
1560             if ((++i & 7) && !IsGCInProgress())
1561             {
1562                 if  (g_SystemInfo.dwNumberOfProcessors > 1)
1563                 {
1564 #ifndef MULTIPLE_HEAPS
1565                     int spin_count = 1024 * g_SystemInfo.dwNumberOfProcessors;
1566 #else //!MULTIPLE_HEAPS
1567                     int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
1568 #endif //!MULTIPLE_HEAPS
1569                     for (int j = 0; j < spin_count; j++)
1570                     {
1571                         if  (VolatileLoad(lock) < 0 || IsGCInProgress())
1572                             break;
1573                         YieldProcessor();           // indicate to the processor that we are spining
1574                     }
1575                     if  (VolatileLoad(lock) >= 0 && !IsGCInProgress())
1576                     {
1577                         safe_switch_to_thread();
1578                     }
1579                 }
1580                 else
1581                 {
1582                     safe_switch_to_thread();
1583                 }
1584             }
1585             else
1586             {
1587                 WaitLongerNoInstru(i);
1588             }
1589         }
1590         goto retry;
1591     }
1592 }
1593 
1594 inline
try_enter_spin_lock_noinstru(RAW_KEYWORD (volatile)int32_t * lock)1595 static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) int32_t* lock)
1596 {
1597     return (Interlocked::Exchange (&*lock, 0) < 0);
1598 }
1599 
1600 inline
leave_spin_lock_noinstru(RAW_KEYWORD (volatile)int32_t * lock)1601 static void leave_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1602 {
1603     VolatileStore<int32_t>((int32_t*)lock, -1);
1604 }
1605 
1606 #ifdef _DEBUG
1607 
1608 inline
enter_spin_lock(GCSpinLock * pSpinLock)1609 static void enter_spin_lock(GCSpinLock *pSpinLock)
1610 {
1611     enter_spin_lock_noinstru(&pSpinLock->lock);
1612     assert (pSpinLock->holding_thread == (Thread*)-1);
1613     pSpinLock->holding_thread = GetThread();
1614 }
1615 
1616 inline
try_enter_spin_lock(GCSpinLock * pSpinLock)1617 static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock)
1618 {
1619     BOOL ret = try_enter_spin_lock_noinstru(&pSpinLock->lock);
1620     if (ret)
1621         pSpinLock->holding_thread = GetThread();
1622     return ret;
1623 }
1624 
1625 inline
leave_spin_lock(GCSpinLock * pSpinLock)1626 static void leave_spin_lock(GCSpinLock *pSpinLock)
1627 {
1628     BOOL gc_thread_p = IsGCSpecialThread();
1629 //    _ASSERTE((pSpinLock->holding_thread == GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p);
1630     pSpinLock->released_by_gc_p = gc_thread_p;
1631     pSpinLock->holding_thread = (Thread*) -1;
1632     if (pSpinLock->lock != -1)
1633         leave_spin_lock_noinstru(&pSpinLock->lock);
1634 }
1635 
1636 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) \
1637     _ASSERTE((pSpinLock)->holding_thread == GetThread());
1638 
1639 #define ASSERT_NOT_HOLDING_SPIN_LOCK(pSpinLock) \
1640     _ASSERTE((pSpinLock)->holding_thread != GetThread());
1641 
1642 #else //_DEBUG
1643 
1644 //In the concurrent version, the Enable/DisablePreemptiveGC is optional because
1645 //the gc thread call WaitLonger.
WaitLonger(int i,VOLATILE (GCSpinLock)* spin_lock)1646 void WaitLonger (int i
1647 #ifdef SYNCHRONIZATION_STATS
1648     , VOLATILE(GCSpinLock)* spin_lock
1649 #endif //SYNCHRONIZATION_STATS
1650     )
1651 {
1652 #ifdef SYNCHRONIZATION_STATS
1653     (spin_lock->num_wait_longer)++;
1654 #endif //SYNCHRONIZATION_STATS
1655 
1656     // every 8th attempt:
1657     Thread *pCurThread = GetThread();
1658     bool bToggleGC = false;
1659     if (pCurThread)
1660     {
1661         bToggleGC = GCToEEInterface::IsPreemptiveGCDisabled(pCurThread);
1662         if (bToggleGC)
1663         {
1664             GCToEEInterface::EnablePreemptiveGC(pCurThread);
1665         }
1666         else
1667         {
1668             assert (!"bToggleGC == TRUE");
1669         }
1670     }
1671 
1672     // if we're waiting for gc to finish, we should block immediately
1673     if (!gc_heap::gc_started)
1674     {
1675 #ifdef SYNCHRONIZATION_STATS
1676         (spin_lock->num_switch_thread_w)++;
1677 #endif //SYNCHRONIZATION_STATS
1678         if  (g_SystemInfo.dwNumberOfProcessors > 1)
1679         {
1680             YieldProcessor();           // indicate to the processor that we are spining
1681             if  (i & 0x01f)
1682                 GCToOSInterface::YieldThread (0);
1683             else
1684                 GCToOSInterface::Sleep (5);
1685         }
1686         else
1687             GCToOSInterface::Sleep (5);
1688     }
1689 
1690     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1691     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1692     // It is important that the thread is going to wait for GC.  Otherwise the thread
1693     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD.
1694     if (pCurThread)
1695     {
1696         if (bToggleGC || gc_heap::gc_started)
1697         {
1698             if (gc_heap::gc_started)
1699             {
1700                 gc_heap::wait_for_gc_done();
1701             }
1702 
1703 #ifdef SYNCHRONIZATION_STATS
1704             (spin_lock->num_disable_preemptive_w)++;
1705 #endif //SYNCHRONIZATION_STATS
1706             GCToEEInterface::DisablePreemptiveGC(pCurThread);
1707         }
1708     }
1709 }
1710 
1711 inline
enter_spin_lock(GCSpinLock * spin_lock)1712 static void enter_spin_lock (GCSpinLock* spin_lock)
1713 {
1714 retry:
1715 
1716     if (Interlocked::Exchange (&spin_lock->lock, 0) >= 0)
1717     {
1718         unsigned int i = 0;
1719         while (spin_lock->lock >= 0)
1720         {
1721             if ((++i & 7) && !gc_heap::gc_started)
1722             {
1723                 if  (g_SystemInfo.dwNumberOfProcessors > 1)
1724                 {
1725 #ifndef MULTIPLE_HEAPS
1726                     int spin_count = 1024 * g_SystemInfo.dwNumberOfProcessors;
1727 #else //!MULTIPLE_HEAPS
1728                     int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
1729 #endif //!MULTIPLE_HEAPS
1730                     for (int j = 0; j < spin_count; j++)
1731                     {
1732                         if  (spin_lock->lock < 0 || gc_heap::gc_started)
1733                             break;
1734                         YieldProcessor();           // indicate to the processor that we are spining
1735                     }
1736                     if  (spin_lock->lock >= 0 && !gc_heap::gc_started)
1737                     {
1738 #ifdef SYNCHRONIZATION_STATS
1739                         (spin_lock->num_switch_thread)++;
1740 #endif //SYNCHRONIZATION_STATS
1741                         Thread* current_thread = GetThread();
1742                         BOOL cooperative_mode = gc_heap::enable_preemptive (current_thread);
1743 
1744                         GCToOSInterface::YieldThread(0);
1745 
1746                         gc_heap::disable_preemptive (current_thread, cooperative_mode);
1747                     }
1748                 }
1749                 else
1750                     GCToOSInterface::YieldThread(0);
1751             }
1752             else
1753             {
1754                 WaitLonger(i
1755 #ifdef SYNCHRONIZATION_STATS
1756                         , spin_lock
1757 #endif //SYNCHRONIZATION_STATS
1758                     );
1759             }
1760         }
1761         goto retry;
1762     }
1763 }
1764 
try_enter_spin_lock(GCSpinLock * spin_lock)1765 inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock)
1766 {
1767     return (Interlocked::Exchange (&spin_lock->lock, 0) < 0);
1768 }
1769 
1770 inline
leave_spin_lock(GCSpinLock * spin_lock)1771 static void leave_spin_lock (GCSpinLock * spin_lock)
1772 {
1773     spin_lock->lock = -1;
1774 }
1775 
1776 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock)
1777 
1778 #endif //_DEBUG
1779 
enable_preemptive(Thread * current_thread)1780 BOOL gc_heap::enable_preemptive (Thread* current_thread)
1781 {
1782     bool cooperative_mode = false;
1783     if (current_thread)
1784     {
1785         cooperative_mode = GCToEEInterface::IsPreemptiveGCDisabled(current_thread);
1786         if (cooperative_mode)
1787         {
1788             GCToEEInterface::EnablePreemptiveGC(current_thread);
1789         }
1790     }
1791 
1792     return cooperative_mode;
1793 }
1794 
disable_preemptive(Thread * current_thread,BOOL restore_cooperative)1795 void gc_heap::disable_preemptive (Thread* current_thread, BOOL restore_cooperative)
1796 {
1797     if (current_thread)
1798     {
1799         if (restore_cooperative)
1800         {
1801             GCToEEInterface::DisablePreemptiveGC(current_thread);
1802         }
1803     }
1804 }
1805 
1806 #endif // !DACCESS_COMPILE
1807 
1808 typedef void **  PTR_PTR;
1809 //This function clears a piece of memory
1810 // size has to be Dword aligned
1811 
1812 inline
memclr(uint8_t * mem,size_t size)1813 void memclr ( uint8_t* mem, size_t size)
1814 {
1815     dprintf (3, ("MEMCLR: %Ix, %d", mem, size));
1816     assert ((size & (sizeof(PTR_PTR)-1)) == 0);
1817     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1818 
1819 #if 0
1820     // The compiler will recognize this pattern and replace it with memset call. We can as well just call
1821     // memset directly to make it obvious what's going on.
1822     PTR_PTR m = (PTR_PTR) mem;
1823     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
1824         *(m++) = 0;
1825 #endif
1826 
1827     memset (mem, 0, size);
1828 }
1829 
memcopy(uint8_t * dmem,uint8_t * smem,size_t size)1830 void memcopy (uint8_t* dmem, uint8_t* smem, size_t size)
1831 {
1832     const size_t sz4ptr = sizeof(PTR_PTR)*4;
1833     const size_t sz2ptr = sizeof(PTR_PTR)*2;
1834     const size_t sz1ptr = sizeof(PTR_PTR)*1;
1835 
1836     // size must be a multiple of the pointer size
1837     assert ((size & (sizeof (PTR_PTR)-1)) == 0);
1838     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1839 
1840     // copy in groups of four pointer sized things at a time
1841     if (size >= sz4ptr)
1842     {
1843         do
1844         {
1845             ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1846             ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1847             ((PTR_PTR)dmem)[2] = ((PTR_PTR)smem)[2];
1848             ((PTR_PTR)dmem)[3] = ((PTR_PTR)smem)[3];
1849             dmem += sz4ptr;
1850             smem += sz4ptr;
1851         }
1852         while ((size -= sz4ptr) >= sz4ptr);
1853     }
1854 
1855     // still two pointer sized things or more left to copy?
1856     if (size & sz2ptr)
1857     {
1858         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1859         ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1860         dmem += sz2ptr;
1861         smem += sz2ptr;
1862     }
1863 
1864     // still one pointer sized thing left to copy?
1865     if (size & sz1ptr)
1866     {
1867         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1868         // dmem += sz1ptr;
1869         // smem += sz1ptr;
1870     }
1871 
1872 }
1873 
1874 inline
round_down(ptrdiff_t add,int pitch)1875 ptrdiff_t round_down (ptrdiff_t add, int pitch)
1876 {
1877     return ((add / pitch) * pitch);
1878 }
1879 
1880 #if defined(FEATURE_STRUCTALIGN) && defined(RESPECT_LARGE_ALIGNMENT)
1881 // FEATURE_STRUCTALIGN allows the compiler to dictate the alignment,
1882 // i.e, if a larger alignment matters or is beneficial, the compiler
1883 // generated info tells us so.  RESPECT_LARGE_ALIGNMENT is just the
1884 // converse - it's a heuristic for the GC to use a larger alignment.
1885 #error FEATURE_STRUCTALIGN should imply !RESPECT_LARGE_ALIGNMENT
1886 #endif
1887 
1888 #if defined(FEATURE_STRUCTALIGN) && defined(FEATURE_LOH_COMPACTION)
1889 #error FEATURE_STRUCTALIGN and FEATURE_LOH_COMPACTION are mutually exclusive
1890 #endif
1891 
1892 #if defined(GROWABLE_SEG_MAPPING_TABLE) && !defined(SEG_MAPPING_TABLE)
1893 #error if GROWABLE_SEG_MAPPING_TABLE is defined, SEG_MAPPING_TABLE must be defined
1894 #endif
1895 
1896 inline
same_large_alignment_p(uint8_t * p1,uint8_t * p2)1897 BOOL same_large_alignment_p (uint8_t* p1, uint8_t* p2)
1898 {
1899 #ifdef RESPECT_LARGE_ALIGNMENT
1900     return ((((size_t)p1 ^ (size_t)p2) & 7) == 0);
1901 #else
1902     UNREFERENCED_PARAMETER(p1);
1903     UNREFERENCED_PARAMETER(p2);
1904     return TRUE;
1905 #endif //RESPECT_LARGE_ALIGNMENT
1906 }
1907 
1908 inline
switch_alignment_size(BOOL already_padded_p)1909 size_t switch_alignment_size (BOOL already_padded_p)
1910 {
1911     if (already_padded_p)
1912         return DATA_ALIGNMENT;
1913     else
1914         return (Align (min_obj_size) +((Align (min_obj_size)&DATA_ALIGNMENT)^DATA_ALIGNMENT));
1915 }
1916 
1917 
1918 #ifdef FEATURE_STRUCTALIGN
1919 void set_node_aligninfo (uint8_t *node, int requiredAlignment, ptrdiff_t pad);
1920 void clear_node_aligninfo (uint8_t *node);
1921 #else // FEATURE_STRUCTALIGN
1922 #define node_realigned(node)    (((plug_and_reloc*)(node))[-1].reloc & 1)
1923 void set_node_realigned (uint8_t* node);
1924 void clear_node_realigned(uint8_t* node);
1925 #endif // FEATURE_STRUCTALIGN
1926 
1927 inline
AlignQword(size_t nbytes)1928 size_t AlignQword (size_t nbytes)
1929 {
1930 #ifdef FEATURE_STRUCTALIGN
1931     // This function is used to align everything on the large object
1932     // heap to an 8-byte boundary, to reduce the number of unaligned
1933     // accesses to (say) arrays of doubles.  With FEATURE_STRUCTALIGN,
1934     // the compiler dictates the optimal alignment instead of having
1935     // a heuristic in the GC.
1936     return Align (nbytes);
1937 #else // FEATURE_STRUCTALIGN
1938     return (nbytes + 7) & ~7;
1939 #endif // FEATURE_STRUCTALIGN
1940 }
1941 
1942 inline
Aligned(size_t n)1943 BOOL Aligned (size_t n)
1944 {
1945     return (n & ALIGNCONST) == 0;
1946 }
1947 
1948 #define OBJECT_ALIGNMENT_OFFSET (sizeof(MethodTable *))
1949 
1950 #ifdef FEATURE_STRUCTALIGN
1951 #define MAX_STRUCTALIGN OS_PAGE_SIZE
1952 #else // FEATURE_STRUCTALIGN
1953 #define MAX_STRUCTALIGN 0
1954 #endif // FEATURE_STRUCTALIGN
1955 
1956 #ifdef FEATURE_STRUCTALIGN
1957 inline
AdjustmentForMinPadSize(ptrdiff_t pad,int requiredAlignment)1958 ptrdiff_t AdjustmentForMinPadSize(ptrdiff_t pad, int requiredAlignment)
1959 {
1960     // The resulting alignpad must be either 0 or at least min_obj_size.
1961     // Note that by computing the following difference on unsigned types,
1962     // we can do the range check 0 < alignpad < min_obj_size with a
1963     // single conditional branch.
1964     if ((size_t)(pad - DATA_ALIGNMENT) < Align (min_obj_size) - DATA_ALIGNMENT)
1965     {
1966         return requiredAlignment;
1967     }
1968     return 0;
1969 }
1970 
1971 inline
StructAlign(uint8_t * origPtr,int requiredAlignment,ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)1972 uint8_t* StructAlign (uint8_t* origPtr, int requiredAlignment, ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
1973 {
1974     // required alignment must be a power of two
1975     _ASSERTE(((size_t)origPtr & ALIGNCONST) == 0);
1976     _ASSERTE(((requiredAlignment - 1) & requiredAlignment) == 0);
1977     _ASSERTE(requiredAlignment >= sizeof(void *));
1978     _ASSERTE(requiredAlignment <= MAX_STRUCTALIGN);
1979 
1980     // When this method is invoked for individual objects (i.e., alignmentOffset
1981     // is just the size of the PostHeader), what needs to be aligned when
1982     // we're done is the pointer to the payload of the object (which means
1983     // the actual resulting object pointer is typically not aligned).
1984 
1985     uint8_t* result = (uint8_t*)Align ((size_t)origPtr + alignmentOffset, requiredAlignment-1) - alignmentOffset;
1986     ptrdiff_t alignpad = result - origPtr;
1987 
1988     return result + AdjustmentForMinPadSize (alignpad, requiredAlignment);
1989 }
1990 
1991 inline
ComputeStructAlignPad(uint8_t * plug,int requiredAlignment,size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)1992 ptrdiff_t ComputeStructAlignPad (uint8_t* plug, int requiredAlignment, size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
1993 {
1994     return StructAlign (plug, requiredAlignment, alignmentOffset) - plug;
1995 }
1996 
IsStructAligned(uint8_t * ptr,int requiredAlignment)1997 BOOL IsStructAligned (uint8_t *ptr, int requiredAlignment)
1998 {
1999     return StructAlign (ptr, requiredAlignment) == ptr;
2000 }
2001 
2002 inline
ComputeMaxStructAlignPad(int requiredAlignment)2003 ptrdiff_t ComputeMaxStructAlignPad (int requiredAlignment)
2004 {
2005     if (requiredAlignment == DATA_ALIGNMENT)
2006         return 0;
2007     // Since a non-zero alignment padding cannot be less than min_obj_size (so we can fit the
2008     // alignment padding object), the worst-case alignment padding is correspondingly larger
2009     // than the required alignment.
2010     return requiredAlignment + Align (min_obj_size) - DATA_ALIGNMENT;
2011 }
2012 
2013 inline
ComputeMaxStructAlignPadLarge(int requiredAlignment)2014 ptrdiff_t ComputeMaxStructAlignPadLarge (int requiredAlignment)
2015 {
2016     if (requiredAlignment <= get_alignment_constant (TRUE)+1)
2017         return 0;
2018     // This is the same as ComputeMaxStructAlignPad, except that in addition to leaving space
2019     // for padding before the actual object, it also leaves space for filling a gap after the
2020     // actual object.  This is needed on the large object heap, as the outer allocation functions
2021     // don't operate on an allocation context (which would have left space for the final gap).
2022     return requiredAlignment + Align (min_obj_size) * 2 - DATA_ALIGNMENT;
2023 }
2024 
pad_for_alignment(uint8_t * newAlloc,int requiredAlignment,size_t size,alloc_context * acontext)2025 uint8_t* gc_heap::pad_for_alignment (uint8_t* newAlloc, int requiredAlignment, size_t size, alloc_context* acontext)
2026 {
2027     uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2028     if (alignedPtr != newAlloc) {
2029         make_unused_array (newAlloc, alignedPtr - newAlloc);
2030     }
2031     acontext->alloc_ptr = alignedPtr + Align (size);
2032     return alignedPtr;
2033 }
2034 
pad_for_alignment_large(uint8_t * newAlloc,int requiredAlignment,size_t size)2035 uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignment, size_t size)
2036 {
2037     uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2038     if (alignedPtr != newAlloc) {
2039         make_unused_array (newAlloc, alignedPtr - newAlloc);
2040     }
2041     if (alignedPtr < newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment)) {
2042         make_unused_array (alignedPtr + AlignQword (size), newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment) - alignedPtr);
2043     }
2044     return alignedPtr;
2045 }
2046 #else // FEATURE_STRUCTALIGN
2047 #define ComputeMaxStructAlignPad(requiredAlignment) 0
2048 #define ComputeMaxStructAlignPadLarge(requiredAlignment) 0
2049 #endif // FEATURE_STRUCTALIGN
2050 
2051 //CLR_SIZE  is the max amount of bytes from gen0 that is set to 0 in one chunk
2052 #ifdef SERVER_GC
2053 #define CLR_SIZE ((size_t)(8*1024))
2054 #else //SERVER_GC
2055 #define CLR_SIZE ((size_t)(8*1024))
2056 #endif //SERVER_GC
2057 
2058 #define END_SPACE_AFTER_GC (LARGE_OBJECT_SIZE + MAX_STRUCTALIGN)
2059 
2060 #ifdef BACKGROUND_GC
2061 #define SEGMENT_INITIAL_COMMIT (2*OS_PAGE_SIZE)
2062 #else
2063 #define SEGMENT_INITIAL_COMMIT (OS_PAGE_SIZE)
2064 #endif //BACKGROUND_GC
2065 
2066 #ifdef SERVER_GC
2067 
2068 #ifdef BIT64
2069 
2070 #define INITIAL_ALLOC ((size_t)((size_t)4*1024*1024*1024))
2071 #define LHEAP_ALLOC   ((size_t)(1024*1024*256))
2072 
2073 #else
2074 
2075 #define INITIAL_ALLOC ((size_t)(1024*1024*64))
2076 #define LHEAP_ALLOC   ((size_t)(1024*1024*32))
2077 
2078 #endif  // BIT64
2079 
2080 #else //SERVER_GC
2081 
2082 #ifdef BIT64
2083 
2084 #define INITIAL_ALLOC ((size_t)(1024*1024*256))
2085 #define LHEAP_ALLOC   ((size_t)(1024*1024*128))
2086 
2087 #else
2088 
2089 #define INITIAL_ALLOC ((size_t)(1024*1024*16))
2090 #define LHEAP_ALLOC   ((size_t)(1024*1024*16))
2091 
2092 #endif  // BIT64
2093 
2094 #endif //SERVER_GC
2095 
2096 //amount in bytes of the etw allocation tick
2097 const size_t etw_allocation_tick = 100*1024;
2098 
2099 const size_t low_latency_alloc = 256*1024;
2100 
2101 const size_t fgn_check_quantum = 2*1024*1024;
2102 
2103 #ifdef MH_SC_MARK
2104 const int max_snoop_level = 128;
2105 #endif //MH_SC_MARK
2106 
2107 
2108 #ifdef CARD_BUNDLE
2109 //threshold of heap size to turn on card bundles.
2110 #define SH_TH_CARD_BUNDLE  (40*1024*1024)
2111 #define MH_TH_CARD_BUNDLE  (180*1024*1024)
2112 #endif //CARD_BUNDLE
2113 
2114 #define page_size OS_PAGE_SIZE
2115 
2116 #define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000
2117 
2118 inline
align_on_page(size_t add)2119 size_t align_on_page (size_t add)
2120 {
2121     return ((add + page_size - 1) & ~(page_size - 1));
2122 }
2123 
2124 inline
align_on_page(uint8_t * add)2125 uint8_t* align_on_page (uint8_t* add)
2126 {
2127     return (uint8_t*)align_on_page ((size_t) add);
2128 }
2129 
2130 inline
align_lower_page(size_t add)2131 size_t align_lower_page (size_t add)
2132 {
2133     return (add & ~(page_size - 1));
2134 }
2135 
2136 inline
align_lower_page(uint8_t * add)2137 uint8_t* align_lower_page (uint8_t* add)
2138 {
2139     return (uint8_t*)align_lower_page ((size_t)add);
2140 }
2141 
2142 inline
power_of_two_p(size_t integer)2143 BOOL power_of_two_p (size_t integer)
2144 {
2145     return !(integer & (integer-1));
2146 }
2147 
2148 inline
oddp(size_t integer)2149 BOOL oddp (size_t integer)
2150 {
2151     return (integer & 1) != 0;
2152 }
2153 
2154 // we only ever use this for WORDs.
logcount(size_t word)2155 size_t logcount (size_t word)
2156 {
2157     //counts the number of high bits in a 16 bit word.
2158     assert (word < 0x10000);
2159     size_t count;
2160     count = (word & 0x5555) + ( (word >> 1 ) & 0x5555);
2161     count = (count & 0x3333) + ( (count >> 2) & 0x3333);
2162     count = (count & 0x0F0F) + ( (count >> 4) & 0x0F0F);
2163     count = (count & 0x00FF) + ( (count >> 8) & 0x00FF);
2164     return count;
2165 }
2166 
2167 //n!=0
log2(unsigned int n)2168 int log2(unsigned int n)
2169 {
2170     int pos = 0;
2171     if (n >= 1<<16) { n >>= 16; pos += 16; }
2172     if (n >= 1<< 8) { n >>=  8; pos +=  8; }
2173     if (n >= 1<< 4) { n >>=  4; pos +=  4; }
2174     if (n >= 1<< 2) { n >>=  2; pos +=  2; }
2175     if (n >= 1<< 1) {           pos +=  1; }
2176     return pos;
2177 }
2178 
2179 #ifndef DACCESS_COMPILE
2180 
stomp_write_barrier_resize(bool is_runtime_suspended,bool requires_upper_bounds_check)2181 void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_bounds_check)
2182 {
2183     WriteBarrierParameters args = {};
2184     args.operation = WriteBarrierOp::StompResize;
2185     args.is_runtime_suspended = is_runtime_suspended;
2186     args.requires_upper_bounds_check = requires_upper_bounds_check;
2187     args.card_table = g_gc_card_table;
2188     args.lowest_address = g_gc_lowest_address;
2189     args.highest_address = g_gc_highest_address;
2190 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2191     if (SoftwareWriteWatch::IsEnabledForGCHeap())
2192     {
2193         args.write_watch_table = g_gc_sw_ww_table;
2194     }
2195 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2196     GCToEEInterface::StompWriteBarrier(&args);
2197 }
2198 
stomp_write_barrier_ephemeral(uint8_t * ephemeral_low,uint8_t * ephemeral_high)2199 void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2200 {
2201     WriteBarrierParameters args = {};
2202     args.operation = WriteBarrierOp::StompEphemeral;
2203     args.is_runtime_suspended = true;
2204     args.ephemeral_low = ephemeral_low;
2205     args.ephemeral_high = ephemeral_high;
2206     GCToEEInterface::StompWriteBarrier(&args);
2207 }
2208 
stomp_write_barrier_initialize()2209 void stomp_write_barrier_initialize()
2210 {
2211     WriteBarrierParameters args = {};
2212     args.operation = WriteBarrierOp::Initialize;
2213     args.is_runtime_suspended = true;
2214     args.requires_upper_bounds_check = false;
2215     args.card_table = g_gc_card_table;
2216     args.lowest_address = g_gc_lowest_address;
2217     args.highest_address = g_gc_highest_address;
2218     args.ephemeral_low = reinterpret_cast<uint8_t*>(1);
2219     args.ephemeral_high = reinterpret_cast<uint8_t*>(~0);
2220     GCToEEInterface::StompWriteBarrier(&args);
2221 }
2222 
2223 #endif // DACCESS_COMPILE
2224 
2225 //extract the low bits [0,low[ of a uint32_t
2226 #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1))
2227 //extract the high bits [high, 32] of a uint32_t
2228 #define highbits(wrd, bits) ((wrd) & ~((1 << (bits))-1))
2229 
2230 
2231 class mark;
2232 class generation;
2233 class heap_segment;
2234 class CObjectHeader;
2235 class dynamic_data;
2236 class l_heap;
2237 class sorted_table;
2238 class c_synchronize;
2239 
2240 #ifdef FEATURE_PREMORTEM_FINALIZATION
2241 #ifndef DACCESS_COMPILE
2242 static
2243 HRESULT AllocateCFinalize(CFinalize **pCFinalize);
2244 #endif //!DACCESS_COMPILE
2245 #endif // FEATURE_PREMORTEM_FINALIZATION
2246 
2247 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address);
2248 
2249 
2250 #ifdef USE_INTROSORT
2251 #define _sort introsort::sort
2252 #else //USE_INTROSORT
2253 #define _sort qsort1
2254 void qsort1(uint8_t** low, uint8_t** high, unsigned int depth);
2255 #endif //USE_INTROSORT
2256 
2257 void* virtual_alloc (size_t size);
2258 void virtual_free (void* add, size_t size);
2259 
2260 /* per heap static initialization */
2261 #ifdef MARK_ARRAY
2262 #ifndef MULTIPLE_HEAPS
2263 SPTR_IMPL_NS(uint32_t, WKS, gc_heap, mark_array);
2264 #endif //!MULTIPLE_HEAPS
2265 #endif //MARK_ARRAY
2266 
2267 #ifdef MARK_LIST
2268 uint8_t**   gc_heap::g_mark_list;
2269 
2270 #ifdef PARALLEL_MARK_LIST_SORT
2271 uint8_t**   gc_heap::g_mark_list_copy;
2272 #endif //PARALLEL_MARK_LIST_SORT
2273 
2274 size_t      gc_heap::mark_list_size;
2275 #endif //MARK_LIST
2276 
2277 #ifdef SEG_MAPPING_TABLE
2278 seg_mapping* seg_mapping_table;
2279 #endif //SEG_MAPPING_TABLE
2280 
2281 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2282 sorted_table* gc_heap::seg_table;
2283 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
2284 
2285 #ifdef MULTIPLE_HEAPS
2286 CLREvent    gc_heap::ee_suspend_event;
2287 size_t      gc_heap::min_balance_threshold = 0;
2288 #endif //MULTIPLE_HEAPS
2289 
2290 VOLATILE(BOOL) gc_heap::gc_started;
2291 
2292 #ifdef MULTIPLE_HEAPS
2293 
2294 CLREvent    gc_heap::gc_start_event;
2295 
2296 bool        gc_heap::gc_thread_no_affinitize_p = false;
2297 
2298 SVAL_IMPL_NS(int, SVR, gc_heap, n_heaps);
2299 SPTR_IMPL_NS(PTR_gc_heap, SVR, gc_heap, g_heaps);
2300 
2301 size_t*     gc_heap::g_promoted;
2302 
2303 #ifdef MH_SC_MARK
2304 int*        gc_heap::g_mark_stack_busy;
2305 #endif //MH_SC_MARK
2306 
2307 
2308 #ifdef BACKGROUND_GC
2309 size_t*     gc_heap::g_bpromoted;
2310 #endif //BACKGROUND_GC
2311 
2312 #else  //MULTIPLE_HEAPS
2313 
2314 size_t      gc_heap::g_promoted;
2315 
2316 #ifdef BACKGROUND_GC
2317 size_t      gc_heap::g_bpromoted;
2318 #endif //BACKGROUND_GC
2319 
2320 #endif //MULTIPLE_HEAPS
2321 
2322 size_t      gc_heap::reserved_memory = 0;
2323 size_t      gc_heap::reserved_memory_limit = 0;
2324 BOOL        gc_heap::g_low_memory_status;
2325 
2326 #ifndef DACCESS_COMPILE
2327 static gc_reason gc_trigger_reason = reason_empty;
2328 #endif //DACCESS_COMPILE
2329 
2330 gc_mechanisms  gc_heap::settings;
2331 
2332 gc_history_global gc_heap::gc_data_global;
2333 
2334 size_t      gc_heap::gc_last_ephemeral_decommit_time = 0;
2335 
2336 size_t      gc_heap::gc_gen0_desired_high;
2337 
2338 #ifdef SHORT_PLUGS
2339 double       gc_heap::short_plugs_pad_ratio = 0;
2340 #endif //SHORT_PLUGS
2341 
2342 #if defined(BIT64)
2343 #define MAX_ALLOWED_MEM_LOAD 85
2344 
2345 // consider putting this in dynamic data -
2346 // we may want different values for workstation
2347 // and server GC.
2348 #define MIN_YOUNGEST_GEN_DESIRED (16*1024*1024)
2349 
2350 size_t      gc_heap::youngest_gen_desired_th;
2351 #endif //BIT64
2352 
2353 uint64_t    gc_heap::mem_one_percent;
2354 
2355 uint32_t    gc_heap::high_memory_load_th;
2356 
2357 uint64_t    gc_heap::total_physical_mem;
2358 
2359 uint64_t    gc_heap::entry_available_physical_mem;
2360 
2361 #ifdef BACKGROUND_GC
2362 CLREvent    gc_heap::bgc_start_event;
2363 
2364 gc_mechanisms gc_heap::saved_bgc_settings;
2365 
2366 CLREvent    gc_heap::background_gc_done_event;
2367 
2368 CLREvent    gc_heap::ee_proceed_event;
2369 
2370 bool        gc_heap::gc_can_use_concurrent = false;
2371 
2372 bool        gc_heap::temp_disable_concurrent_p = false;
2373 
2374 uint32_t    gc_heap::cm_in_progress = FALSE;
2375 
2376 BOOL        gc_heap::dont_restart_ee_p = FALSE;
2377 
2378 BOOL        gc_heap::keep_bgc_threads_p = FALSE;
2379 
2380 CLREvent    gc_heap::bgc_threads_sync_event;
2381 
2382 BOOL        gc_heap::do_ephemeral_gc_p = FALSE;
2383 
2384 BOOL        gc_heap::do_concurrent_p = FALSE;
2385 
2386 size_t      gc_heap::ephemeral_fgc_counts[max_generation];
2387 
2388 BOOL        gc_heap::alloc_wait_event_p = FALSE;
2389 
2390 #if defined (DACCESS_COMPILE) && !defined (MULTIPLE_HEAPS)
2391 SVAL_IMPL_NS_INIT(gc_heap::c_gc_state, WKS, gc_heap, current_c_gc_state, c_gc_state_free);
2392 #else
2393 VOLATILE(gc_heap::c_gc_state) gc_heap::current_c_gc_state = c_gc_state_free;
2394 #endif //DACCESS_COMPILE && !MULTIPLE_HEAPS
2395 
2396 #endif //BACKGROUND_GC
2397 
2398 #ifndef MULTIPLE_HEAPS
2399 #ifdef SPINLOCK_HISTORY
2400 int         gc_heap::spinlock_info_index = 0;
2401 spinlock_info gc_heap::last_spinlock_info[max_saved_spinlock_info + 8];
2402 #endif //SPINLOCK_HISTORY
2403 
2404 size_t      gc_heap::fgn_last_alloc = 0;
2405 
2406 int         gc_heap::generation_skip_ratio = 100;
2407 
2408 uint64_t    gc_heap::loh_alloc_since_cg = 0;
2409 
2410 BOOL        gc_heap::elevation_requested = FALSE;
2411 
2412 BOOL        gc_heap::last_gc_before_oom = FALSE;
2413 
2414 #ifdef BACKGROUND_GC
2415 SPTR_IMPL_NS_INIT(uint8_t, WKS, gc_heap, background_saved_lowest_address, 0);
2416 SPTR_IMPL_NS_INIT(uint8_t, WKS, gc_heap, background_saved_highest_address, 0);
2417 SPTR_IMPL_NS_INIT(uint8_t, WKS, gc_heap, next_sweep_obj, 0);
2418 uint8_t*    gc_heap::current_sweep_pos = 0;
2419 exclusive_sync* gc_heap::bgc_alloc_lock;
2420 #endif //BACKGROUND_GC
2421 
2422 SVAL_IMPL_NS(oom_history, WKS, gc_heap, oom_info);
2423 
2424 fgm_history gc_heap::fgm_result;
2425 
2426 BOOL        gc_heap::ro_segments_in_range;
2427 
2428 size_t      gc_heap::gen0_big_free_spaces = 0;
2429 
2430 uint8_t*    gc_heap::ephemeral_low;
2431 
2432 uint8_t*    gc_heap::ephemeral_high;
2433 
2434 uint8_t*    gc_heap::lowest_address;
2435 
2436 uint8_t*    gc_heap::highest_address;
2437 
2438 BOOL        gc_heap::ephemeral_promotion;
2439 
2440 uint8_t*    gc_heap::saved_ephemeral_plan_start[NUMBERGENERATIONS-1];
2441 size_t      gc_heap::saved_ephemeral_plan_start_size[NUMBERGENERATIONS-1];
2442 
2443 short*      gc_heap::brick_table;
2444 
2445 uint32_t*   gc_heap::card_table;
2446 
2447 #ifdef CARD_BUNDLE
2448 uint32_t*   gc_heap::card_bundle_table;
2449 #endif //CARD_BUNDLE
2450 
2451 uint8_t*    gc_heap::gc_low;
2452 
2453 uint8_t*    gc_heap::gc_high;
2454 
2455 uint8_t*    gc_heap::demotion_low;
2456 
2457 uint8_t*    gc_heap::demotion_high;
2458 
2459 BOOL        gc_heap::demote_gen1_p = TRUE;
2460 
2461 uint8_t*    gc_heap::last_gen1_pin_end;
2462 
2463 gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons;
2464 
2465 size_t      gc_heap::etw_allocation_running_amount[2];
2466 
2467 int         gc_heap::gc_policy = 0;
2468 
2469 size_t      gc_heap::allocation_running_time;
2470 
2471 size_t      gc_heap::allocation_running_amount;
2472 
2473 SPTR_IMPL_NS_INIT(heap_segment, WKS, gc_heap, ephemeral_heap_segment, 0);
2474 
2475 BOOL        gc_heap::blocking_collection = FALSE;
2476 
2477 heap_segment* gc_heap::freeable_large_heap_segment = 0;
2478 
2479 size_t      gc_heap::time_bgc_last = 0;
2480 
2481 size_t      gc_heap::mark_stack_tos = 0;
2482 
2483 size_t      gc_heap::mark_stack_bos = 0;
2484 
2485 size_t      gc_heap::mark_stack_array_length = 0;
2486 
2487 mark*       gc_heap::mark_stack_array = 0;
2488 
2489 BOOL        gc_heap::verify_pinned_queue_p = FALSE;
2490 
2491 uint8_t*    gc_heap::oldest_pinned_plug = 0;
2492 
2493 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
2494 size_t      gc_heap::num_pinned_objects = 0;
2495 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
2496 
2497 #ifdef FEATURE_LOH_COMPACTION
2498 size_t      gc_heap::loh_pinned_queue_tos = 0;
2499 
2500 size_t      gc_heap::loh_pinned_queue_bos = 0;
2501 
2502 size_t      gc_heap::loh_pinned_queue_length = 0;
2503 
2504 mark*       gc_heap::loh_pinned_queue = 0;
2505 
2506 BOOL        gc_heap::loh_compacted_p = FALSE;
2507 #endif //FEATURE_LOH_COMPACTION
2508 
2509 #ifdef BACKGROUND_GC
2510 
2511 EEThreadId  gc_heap::bgc_thread_id;
2512 
2513 uint8_t*    gc_heap::background_written_addresses [array_size+2];
2514 
2515 heap_segment* gc_heap::freeable_small_heap_segment = 0;
2516 
2517 size_t      gc_heap::bgc_overflow_count = 0;
2518 
2519 size_t      gc_heap::bgc_begin_loh_size = 0;
2520 size_t      gc_heap::end_loh_size = 0;
2521 
2522 uint32_t    gc_heap::bgc_alloc_spin_loh = 0;
2523 
2524 size_t      gc_heap::bgc_loh_size_increased = 0;
2525 
2526 size_t      gc_heap::bgc_loh_allocated_in_free = 0;
2527 
2528 size_t      gc_heap::background_soh_alloc_count = 0;
2529 
2530 size_t      gc_heap::background_loh_alloc_count = 0;
2531 
2532 uint8_t**   gc_heap::background_mark_stack_tos = 0;
2533 
2534 uint8_t**   gc_heap::background_mark_stack_array = 0;
2535 
2536 size_t      gc_heap::background_mark_stack_array_length = 0;
2537 
2538 uint8_t*    gc_heap::background_min_overflow_address =0;
2539 
2540 uint8_t*    gc_heap::background_max_overflow_address =0;
2541 
2542 BOOL        gc_heap::processed_soh_overflow_p = FALSE;
2543 
2544 uint8_t*    gc_heap::background_min_soh_overflow_address =0;
2545 
2546 uint8_t*    gc_heap::background_max_soh_overflow_address =0;
2547 
2548 SPTR_IMPL_NS_INIT(heap_segment, WKS, gc_heap, saved_sweep_ephemeral_seg, 0);
2549 SPTR_IMPL_NS_INIT(uint8_t, WKS, gc_heap, saved_sweep_ephemeral_start, 0);
2550 
2551 heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0;
2552 
2553 Thread*     gc_heap::bgc_thread = 0;
2554 
2555 BOOL        gc_heap::expanded_in_fgc = FALSE;
2556 
2557 uint8_t**   gc_heap::c_mark_list = 0;
2558 
2559 size_t      gc_heap::c_mark_list_length = 0;
2560 
2561 size_t      gc_heap::c_mark_list_index = 0;
2562 
2563 gc_history_per_heap gc_heap::bgc_data_per_heap;
2564 
2565 BOOL    gc_heap::bgc_thread_running;
2566 
2567 CLRCriticalSection gc_heap::bgc_threads_timeout_cs;
2568 
2569 CLREvent gc_heap::gc_lh_block_event;
2570 
2571 #endif //BACKGROUND_GC
2572 
2573 #ifdef MARK_LIST
2574 uint8_t**   gc_heap::mark_list;
2575 uint8_t**   gc_heap::mark_list_index;
2576 uint8_t**   gc_heap::mark_list_end;
2577 #endif //MARK_LIST
2578 
2579 #ifdef SNOOP_STATS
2580 snoop_stats_data gc_heap::snoop_stat;
2581 #endif //SNOOP_STATS
2582 
2583 uint8_t*    gc_heap::min_overflow_address = MAX_PTR;
2584 
2585 uint8_t*    gc_heap::max_overflow_address = 0;
2586 
2587 uint8_t*    gc_heap::shigh = 0;
2588 
2589 uint8_t*    gc_heap::slow = MAX_PTR;
2590 
2591 size_t      gc_heap::ordered_free_space_indices[MAX_NUM_BUCKETS];
2592 
2593 size_t      gc_heap::saved_ordered_free_space_indices[MAX_NUM_BUCKETS];
2594 
2595 size_t      gc_heap::ordered_plug_indices[MAX_NUM_BUCKETS];
2596 
2597 size_t      gc_heap::saved_ordered_plug_indices[MAX_NUM_BUCKETS];
2598 
2599 BOOL        gc_heap::ordered_plug_indices_init = FALSE;
2600 
2601 BOOL        gc_heap::use_bestfit = FALSE;
2602 
2603 uint8_t*    gc_heap::bestfit_first_pin = 0;
2604 
2605 BOOL        gc_heap::commit_end_of_seg = FALSE;
2606 
2607 size_t      gc_heap::max_free_space_items = 0;
2608 
2609 size_t      gc_heap::free_space_buckets = 0;
2610 
2611 size_t      gc_heap::free_space_items = 0;
2612 
2613 int         gc_heap::trimmed_free_space_index = 0;
2614 
2615 size_t      gc_heap::total_ephemeral_plugs = 0;
2616 
2617 seg_free_spaces* gc_heap::bestfit_seg = 0;
2618 
2619 size_t      gc_heap::total_ephemeral_size = 0;
2620 
2621 #ifdef HEAP_ANALYZE
2622 
2623 size_t      gc_heap::internal_root_array_length = initial_internal_roots;
2624 
2625 SPTR_IMPL_NS_INIT(PTR_uint8_t, WKS, gc_heap, internal_root_array, 0);
2626 SVAL_IMPL_NS_INIT(size_t, WKS, gc_heap, internal_root_array_index, 0);
2627 SVAL_IMPL_NS_INIT(BOOL, WKS, gc_heap, heap_analyze_success, TRUE);
2628 
2629 uint8_t*    gc_heap::current_obj = 0;
2630 size_t      gc_heap::current_obj_size = 0;
2631 
2632 #endif //HEAP_ANALYZE
2633 
2634 #ifdef GC_CONFIG_DRIVEN
2635 size_t gc_heap::interesting_data_per_gc[max_idp_count];
2636 //size_t gc_heap::interesting_data_per_heap[max_idp_count];
2637 //size_t gc_heap::interesting_mechanisms_per_heap[max_im_count];
2638 #endif //GC_CONFIG_DRIVEN
2639 #endif //MULTIPLE_HEAPS
2640 
2641 no_gc_region_info gc_heap::current_no_gc_region_info;
2642 BOOL gc_heap::proceed_with_gc_p = FALSE;
2643 GCSpinLock gc_heap::gc_lock;
2644 
2645 size_t gc_heap::eph_gen_starts_size = 0;
2646 heap_segment* gc_heap::segment_standby_list;
2647 size_t        gc_heap::last_gc_index = 0;
2648 size_t        gc_heap::min_segment_size = 0;
2649 
2650 #ifdef GC_CONFIG_DRIVEN
2651 size_t gc_heap::time_init = 0;
2652 size_t gc_heap::time_since_init = 0;
2653 size_t gc_heap::compact_or_sweep_gcs[2];
2654 #endif //GC_CONFIG_DRIVEN
2655 
2656 #ifdef FEATURE_LOH_COMPACTION
2657 BOOL                   gc_heap::loh_compaction_always_p = FALSE;
2658 gc_loh_compaction_mode gc_heap::loh_compaction_mode = loh_compaction_default;
2659 int                    gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY;
2660 
2661 #endif //FEATURE_LOH_COMPACTION
2662 
2663 CLREvent gc_heap::full_gc_approach_event;
2664 
2665 CLREvent gc_heap::full_gc_end_event;
2666 
2667 uint32_t gc_heap::fgn_maxgen_percent = 0;
2668 
2669 uint32_t gc_heap::fgn_loh_percent = 0;
2670 
2671 #ifdef BACKGROUND_GC
2672 BOOL gc_heap::fgn_last_gc_was_concurrent = FALSE;
2673 #endif //BACKGROUND_GC
2674 
2675 VOLATILE(bool) gc_heap::full_gc_approach_event_set;
2676 
2677 size_t gc_heap::full_gc_counts[gc_type_max];
2678 
2679 BOOL gc_heap::should_expand_in_full_gc = FALSE;
2680 
2681 #ifdef HEAP_ANALYZE
2682 BOOL        gc_heap::heap_analyze_enabled = FALSE;
2683 #endif //HEAP_ANALYZE
2684 
2685 #ifndef MULTIPLE_HEAPS
2686 
2687 #ifndef DACCESS_COMPILE
2688 extern "C" {
2689 #endif //!DACCESS_COMPILE
2690 GARY_IMPL(generation, generation_table,NUMBERGENERATIONS+1);
2691 #ifdef GC_CONFIG_DRIVEN
2692 GARY_IMPL(size_t, interesting_data_per_heap, max_idp_count);
2693 GARY_IMPL(size_t, compact_reasons_per_heap, max_compact_reasons_count);
2694 GARY_IMPL(size_t, expand_mechanisms_per_heap, max_expand_mechanisms_count);
2695 GARY_IMPL(size_t, interesting_mechanism_bits_per_heap, max_gc_mechanism_bits_count);
2696 #endif //GC_CONFIG_DRIVEN
2697 #ifndef DACCESS_COMPILE
2698 }
2699 #endif //!DACCESS_COMPILE
2700 
2701 #endif //MULTIPLE_HEAPS
2702 
2703 #ifndef MULTIPLE_HEAPS
2704 
2705 alloc_list gc_heap::loh_alloc_list [NUM_LOH_ALIST-1];
2706 alloc_list gc_heap::gen2_alloc_list[NUM_GEN2_ALIST-1];
2707 
2708 dynamic_data gc_heap::dynamic_data_table [NUMBERGENERATIONS+1];
2709 gc_history_per_heap gc_heap::gc_data_per_heap;
2710 size_t gc_heap::maxgen_pinned_compact_before_advance = 0;
2711 
2712 SPTR_IMPL_NS_INIT(uint8_t, WKS, gc_heap, alloc_allocated, 0);
2713 
2714 size_t gc_heap::allocation_quantum = CLR_SIZE;
2715 
2716 GCSpinLock gc_heap::more_space_lock;
2717 
2718 #ifdef SYNCHRONIZATION_STATS
2719 unsigned int gc_heap::good_suspension = 0;
2720 unsigned int gc_heap::bad_suspension = 0;
2721 uint64_t     gc_heap::total_msl_acquire = 0;
2722 unsigned int gc_heap::num_msl_acquired = 0;
2723 unsigned int gc_heap::num_high_msl_acquire = 0;
2724 unsigned int gc_heap::num_low_msl_acquire = 0;
2725 #endif //SYNCHRONIZATION_STATS
2726 
2727 size_t   gc_heap::alloc_contexts_used = 0;
2728 size_t   gc_heap::soh_allocation_no_gc = 0;
2729 size_t   gc_heap::loh_allocation_no_gc = 0;
2730 bool     gc_heap::no_gc_oom_p = false;
2731 heap_segment* gc_heap::saved_loh_segment_no_gc = 0;
2732 
2733 #endif //MULTIPLE_HEAPS
2734 
2735 #ifndef MULTIPLE_HEAPS
2736 
2737 BOOL        gc_heap::gen0_bricks_cleared = FALSE;
2738 
2739 #ifdef FFIND_OBJECT
2740 int         gc_heap::gen0_must_clear_bricks = 0;
2741 #endif //FFIND_OBJECT
2742 
2743 #ifdef FEATURE_PREMORTEM_FINALIZATION
2744 SPTR_IMPL_NS_INIT(CFinalize, WKS, gc_heap, finalize_queue, 0);
2745 #endif // FEATURE_PREMORTEM_FINALIZATION
2746 
2747 #endif // MULTIPLE_HEAPS
2748 
2749 /* end of per heap static initialization */
2750 
2751 /* end of static initialization */
2752 
2753 #ifndef DACCESS_COMPILE
2754 
print(int heap_num)2755 void gen_to_condemn_tuning::print (int heap_num)
2756 {
2757 #ifdef DT_LOG
2758     dprintf (DT_LOG_0, ("condemned reasons (%d %d)", condemn_reasons_gen, condemn_reasons_condition));
2759     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_gen_header));
2760     gc_condemn_reason_gen r_gen;
2761     for (int i = 0; i < gcrg_max; i++)
2762     {
2763         r_gen = (gc_condemn_reason_gen)(i);
2764         str_reasons_gen[i * 2] = get_gen_char (get_gen (r_gen));
2765     }
2766     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_gen));
2767 
2768     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_condition_header));
2769     gc_condemn_reason_condition r_condition;
2770     for (int i = 0; i < gcrc_max; i++)
2771     {
2772         r_condition = (gc_condemn_reason_condition)(i);
2773         str_reasons_condition[i * 2] = get_condition_char (get_condition (r_condition));
2774     }
2775 
2776     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_condition));
2777 #else
2778     UNREFERENCED_PARAMETER(heap_num);
2779 #endif //DT_LOG
2780 }
2781 
print(int heap_num,int gen_num)2782 void gc_generation_data::print (int heap_num, int gen_num)
2783 {
2784 #if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2785     dprintf (DT_LOG_0, ("[%2d]gen%d beg %Id fl %Id fo %Id end %Id fl %Id fo %Id in %Id p %Id np %Id alloc %Id",
2786                 heap_num, gen_num,
2787                 size_before,
2788                 free_list_space_before, free_obj_space_before,
2789                 size_after,
2790                 free_list_space_after, free_obj_space_after,
2791                 in, pinned_surv, npinned_surv,
2792                 new_allocation));
2793 #else
2794     UNREFERENCED_PARAMETER(heap_num);
2795     UNREFERENCED_PARAMETER(gen_num);
2796 #endif //SIMPLE_DPRINTF && DT_LOG
2797 }
2798 
set_mechanism(gc_mechanism_per_heap mechanism_per_heap,uint32_t value)2799 void gc_history_per_heap::set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value)
2800 {
2801     uint32_t* mechanism = &mechanisms[mechanism_per_heap];
2802     *mechanism = 0;
2803     *mechanism |= mechanism_mask;
2804     *mechanism |= (1 << value);
2805 
2806 #ifdef DT_LOG
2807     gc_mechanism_descr* descr = &gc_mechanisms_descr[mechanism_per_heap];
2808     dprintf (DT_LOG_0, ("setting %s: %s",
2809             descr->name,
2810             (descr->descr)[value]));
2811 #endif //DT_LOG
2812 }
2813 
print()2814 void gc_history_per_heap::print()
2815 {
2816 #if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2817     for (int i = 0; i < (sizeof (gen_data)/sizeof (gc_generation_data)); i++)
2818     {
2819         gen_data[i].print (heap_index, i);
2820     }
2821 
2822     dprintf (DT_LOG_0, ("fla %Id flr %Id esa %Id ca %Id pa %Id paa %Id, rfle %d, ec %Id",
2823                     maxgen_size_info.free_list_allocated,
2824                     maxgen_size_info.free_list_rejected,
2825                     maxgen_size_info.end_seg_allocated,
2826                     maxgen_size_info.condemned_allocated,
2827                     maxgen_size_info.pinned_allocated,
2828                     maxgen_size_info.pinned_allocated_advance,
2829                     maxgen_size_info.running_free_list_efficiency,
2830                     extra_gen0_committed));
2831 
2832     int mechanism = 0;
2833     gc_mechanism_descr* descr = 0;
2834 
2835     for (int i = 0; i < max_mechanism_per_heap; i++)
2836     {
2837         mechanism = get_mechanism ((gc_mechanism_per_heap)i);
2838 
2839         if (mechanism >= 0)
2840         {
2841             descr = &gc_mechanisms_descr[(gc_mechanism_per_heap)i];
2842             dprintf (DT_LOG_0, ("[%2d]%s%s",
2843                         heap_index,
2844                         descr->name,
2845                         (descr->descr)[mechanism]));
2846         }
2847     }
2848 #endif //SIMPLE_DPRINTF && DT_LOG
2849 }
2850 
print()2851 void gc_history_global::print()
2852 {
2853 #ifdef DT_LOG
2854     char str_settings[64];
2855     memset (str_settings, '|', sizeof (char) * 64);
2856     str_settings[max_global_mechanisms_count*2] = 0;
2857 
2858     for (int i = 0; i < max_global_mechanisms_count; i++)
2859     {
2860         str_settings[i * 2] = (get_mechanism_p ((gc_global_mechanism_p)i) ? 'Y' : 'N');
2861     }
2862 
2863     dprintf (DT_LOG_0, ("[hp]|c|p|o|d|b|e|"));
2864 
2865     dprintf (DT_LOG_0, ("%4d|%s", num_heaps, str_settings));
2866     dprintf (DT_LOG_0, ("Condemned gen%d(reason: %s; mode: %s), youngest budget %Id(%d), memload %d",
2867                         condemned_generation,
2868                         str_gc_reasons[reason],
2869                         str_gc_pause_modes[pause_mode],
2870                         final_youngest_desired,
2871                         gen0_reduction_count,
2872                         mem_pressure));
2873 #endif //DT_LOG
2874 }
2875 
fire_per_heap_hist_event(gc_history_per_heap * current_gc_data_per_heap,int heap_num)2876 void gc_heap::fire_per_heap_hist_event (gc_history_per_heap* current_gc_data_per_heap, int heap_num)
2877 {
2878     maxgen_size_increase* maxgen_size_info = &(current_gc_data_per_heap->maxgen_size_info);
2879     FireEtwGCPerHeapHistory_V3(GetClrInstanceId(),
2880                                (uint8_t*)(maxgen_size_info->free_list_allocated),
2881                                (uint8_t*)(maxgen_size_info->free_list_rejected),
2882                                (uint8_t*)(maxgen_size_info->end_seg_allocated),
2883                                (uint8_t*)(maxgen_size_info->condemned_allocated),
2884                                (uint8_t*)(maxgen_size_info->pinned_allocated),
2885                                (uint8_t*)(maxgen_size_info->pinned_allocated_advance),
2886                                maxgen_size_info->running_free_list_efficiency,
2887                                current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons0(),
2888                                current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons1(),
2889                                current_gc_data_per_heap->mechanisms[gc_heap_compact],
2890                                current_gc_data_per_heap->mechanisms[gc_heap_expand],
2891                                current_gc_data_per_heap->heap_index,
2892                                (uint8_t*)(current_gc_data_per_heap->extra_gen0_committed),
2893                                (max_generation + 2),
2894                                sizeof (gc_generation_data),
2895                                &(current_gc_data_per_heap->gen_data[0]));
2896 
2897     current_gc_data_per_heap->print();
2898     current_gc_data_per_heap->gen_to_condemn_reasons.print (heap_num);
2899 }
2900 
fire_pevents()2901 void gc_heap::fire_pevents()
2902 {
2903 #ifndef CORECLR
2904     settings.record (&gc_data_global);
2905     gc_data_global.print();
2906 
2907     FireEtwGCGlobalHeapHistory_V2(gc_data_global.final_youngest_desired,
2908                                   gc_data_global.num_heaps,
2909                                   gc_data_global.condemned_generation,
2910                                   gc_data_global.gen0_reduction_count,
2911                                   gc_data_global.reason,
2912                                   gc_data_global.global_mechanims_p,
2913                                   GetClrInstanceId(),
2914                                   gc_data_global.pause_mode,
2915                                   gc_data_global.mem_pressure);
2916 
2917 #ifdef MULTIPLE_HEAPS
2918     for (int i = 0; i < gc_heap::n_heaps; i++)
2919     {
2920         gc_heap* hp = gc_heap::g_heaps[i];
2921         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
2922         fire_per_heap_hist_event (current_gc_data_per_heap, hp->heap_number);
2923     }
2924 #else
2925     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
2926     fire_per_heap_hist_event (current_gc_data_per_heap, heap_number);
2927 #endif
2928 #endif //!CORECLR
2929 }
2930 
2931 inline BOOL
dt_low_ephemeral_space_p(gc_tuning_point tp)2932 gc_heap::dt_low_ephemeral_space_p (gc_tuning_point tp)
2933 {
2934     BOOL ret = FALSE;
2935 
2936     switch (tp)
2937     {
2938         case tuning_deciding_condemned_gen:
2939         case tuning_deciding_compaction:
2940         case tuning_deciding_expansion:
2941         case tuning_deciding_full_gc:
2942         {
2943             ret = (!ephemeral_gen_fit_p (tp));
2944             break;
2945         }
2946         case tuning_deciding_promote_ephemeral:
2947         {
2948             size_t new_gen0size = approximate_new_allocation();
2949             ptrdiff_t plan_ephemeral_size = total_ephemeral_size;
2950 
2951             dprintf (GTC_LOG, ("h%d: plan eph size is %Id, new gen0 is %Id",
2952                 heap_number, plan_ephemeral_size, new_gen0size));
2953             ret = ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - (heap_segment_mem (ephemeral_heap_segment))) <
2954                     (plan_ephemeral_size + new_gen0size));
2955             break;
2956         }
2957         default:
2958             break;
2959     }
2960 
2961     return ret;
2962 }
2963 
2964 BOOL
dt_high_frag_p(gc_tuning_point tp,int gen_number,BOOL elevate_p)2965 gc_heap::dt_high_frag_p (gc_tuning_point tp,
2966                          int gen_number,
2967                          BOOL elevate_p)
2968 {
2969     BOOL ret = FALSE;
2970 
2971     switch (tp)
2972     {
2973         case tuning_deciding_condemned_gen:
2974         {
2975             dynamic_data* dd = dynamic_data_of (gen_number);
2976             float fragmentation_burden = 0;
2977 
2978             if (elevate_p)
2979             {
2980                 ret = (dd_fragmentation (dynamic_data_of (max_generation)) >= dd_max_size(dd));
2981                 dprintf (GTC_LOG, ("h%d: frag is %Id, max size is %Id",
2982                     heap_number, dd_fragmentation (dd), dd_max_size(dd)));
2983             }
2984             else
2985             {
2986 #ifndef MULTIPLE_HEAPS
2987                 if (gen_number == max_generation)
2988                 {
2989                     float frag_ratio = (float)(dd_fragmentation (dynamic_data_of (max_generation))) / (float)generation_size (max_generation);
2990                     if (frag_ratio > 0.65)
2991                     {
2992                         dprintf (GTC_LOG, ("g2 FR: %d%%", (int)(frag_ratio*100)));
2993                         return TRUE;
2994                     }
2995                 }
2996 #endif //!MULTIPLE_HEAPS
2997                 size_t fr = generation_unusable_fragmentation (generation_of (gen_number));
2998                 ret = (fr > dd_fragmentation_limit(dd));
2999                 if (ret)
3000                 {
3001                     fragmentation_burden = (float)fr / generation_size (gen_number);
3002                     ret = (fragmentation_burden > dd_v_fragmentation_burden_limit (dd));
3003                 }
3004                 dprintf (GTC_LOG, ("h%d: gen%d, frag is %Id, alloc effi: %d%%, unusable frag is %Id, ratio is %d",
3005                     heap_number, gen_number, dd_fragmentation (dd),
3006                     (int)(100*generation_allocator_efficiency (generation_of (gen_number))),
3007                     fr, (int)(fragmentation_burden*100)));
3008             }
3009             break;
3010         }
3011         default:
3012             break;
3013     }
3014 
3015     return ret;
3016 }
3017 
3018 inline BOOL
dt_estimate_reclaim_space_p(gc_tuning_point tp,int gen_number)3019 gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number)
3020 {
3021     BOOL ret = FALSE;
3022 
3023     switch (tp)
3024     {
3025         case tuning_deciding_condemned_gen:
3026         {
3027             if (gen_number == max_generation)
3028             {
3029                 dynamic_data* dd = dynamic_data_of (gen_number);
3030                 size_t maxgen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd));
3031                 size_t maxgen_total_size = maxgen_allocated + dd_current_size (dd);
3032                 size_t est_maxgen_surv = (size_t)((float) (maxgen_total_size) * dd_surv (dd));
3033                 size_t est_maxgen_free = maxgen_total_size - est_maxgen_surv + dd_fragmentation (dd);
3034 
3035                 dprintf (GTC_LOG, ("h%d: Total gen2 size: %Id, est gen2 dead space: %Id (s: %d, allocated: %Id), frag: %Id",
3036                             heap_number,
3037                             maxgen_total_size,
3038                             est_maxgen_free,
3039                             (int)(dd_surv (dd) * 100),
3040                             maxgen_allocated,
3041                             dd_fragmentation (dd)));
3042 
3043                 uint32_t num_heaps = 1;
3044 
3045 #ifdef MULTIPLE_HEAPS
3046                 num_heaps = gc_heap::n_heaps;
3047 #endif //MULTIPLE_HEAPS
3048 
3049                 size_t min_frag_th = min_reclaim_fragmentation_threshold (num_heaps);
3050                 dprintf (GTC_LOG, ("h%d, min frag is %Id", heap_number, min_frag_th));
3051                 ret = (est_maxgen_free >= min_frag_th);
3052             }
3053             else
3054             {
3055                 assert (0);
3056             }
3057             break;
3058         }
3059 
3060         default:
3061             break;
3062     }
3063 
3064     return ret;
3065 }
3066 
3067 // DTREVIEW: Right now we only estimate gen2 fragmentation.
3068 // on 64-bit though we should consider gen1 or even gen0 fragmentatioin as
3069 // well
3070 inline BOOL
dt_estimate_high_frag_p(gc_tuning_point tp,int gen_number,uint64_t available_mem)3071 gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, uint64_t available_mem)
3072 {
3073     BOOL ret = FALSE;
3074 
3075     switch (tp)
3076     {
3077         case tuning_deciding_condemned_gen:
3078         {
3079             if (gen_number == max_generation)
3080             {
3081                 dynamic_data* dd = dynamic_data_of (gen_number);
3082                 float est_frag_ratio = 0;
3083                 if (dd_current_size (dd) == 0)
3084                 {
3085                     est_frag_ratio = 1;
3086                 }
3087                 else if ((dd_fragmentation (dd) == 0) || (dd_fragmentation (dd) + dd_current_size (dd) == 0))
3088                 {
3089                     est_frag_ratio = 0;
3090                 }
3091                 else
3092                 {
3093                     est_frag_ratio = (float)dd_fragmentation (dd) / (float)(dd_fragmentation (dd) + dd_current_size (dd));
3094                 }
3095 
3096                 size_t est_frag = (dd_fragmentation (dd) + (size_t)((dd_desired_allocation (dd) - dd_new_allocation (dd)) * est_frag_ratio));
3097                 dprintf (GTC_LOG, ("h%d: gen%d: current_size is %Id, frag is %Id, est_frag_ratio is %d%%, estimated frag is %Id",
3098                     heap_number,
3099                     gen_number,
3100                     dd_current_size (dd),
3101                     dd_fragmentation (dd),
3102                     (int)(est_frag_ratio*100),
3103                     est_frag));
3104 
3105                 uint32_t num_heaps = 1;
3106 
3107 #ifdef MULTIPLE_HEAPS
3108                 num_heaps = gc_heap::n_heaps;
3109 #endif //MULTIPLE_HEAPS
3110                 uint64_t min_frag_th = min_high_fragmentation_threshold(available_mem, num_heaps);
3111                 //dprintf (GTC_LOG, ("h%d, min frag is %I64d", heap_number, min_frag_th));
3112                 ret = (est_frag >= min_frag_th);
3113             }
3114             else
3115             {
3116                 assert (0);
3117             }
3118             break;
3119         }
3120 
3121         default:
3122             break;
3123     }
3124 
3125     return ret;
3126 }
3127 
3128 inline BOOL
dt_low_card_table_efficiency_p(gc_tuning_point tp)3129 gc_heap::dt_low_card_table_efficiency_p (gc_tuning_point tp)
3130 {
3131     BOOL ret = FALSE;
3132 
3133     switch (tp)
3134     {
3135     case tuning_deciding_condemned_gen:
3136     {
3137         /* promote into max-generation if the card table has too many
3138         * generation faults besides the n -> 0
3139         */
3140         ret = (generation_skip_ratio < 30);
3141         break;
3142     }
3143 
3144     default:
3145         break;
3146     }
3147 
3148     return ret;
3149 }
3150 
3151 inline BOOL
in_range_for_segment(uint8_t * add,heap_segment * seg)3152 in_range_for_segment(uint8_t* add, heap_segment* seg)
3153 {
3154     return ((add >= heap_segment_mem (seg)) && (add < heap_segment_reserved (seg)));
3155 }
3156 
3157 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
3158 // The array we allocate is organized as follows:
3159 // 0th element is the address of the last array we allocated.
3160 // starting from the 1st element are the segment addresses, that's
3161 // what buckets() returns.
3162 struct bk
3163 {
3164     uint8_t* add;
3165     size_t val;
3166 };
3167 
3168 class sorted_table
3169 {
3170 private:
3171     ptrdiff_t size;
3172     ptrdiff_t count;
3173     bk* slots;
buckets()3174     bk* buckets() { return (slots + 1); }
last_slot(bk * arr)3175     uint8_t*& last_slot (bk* arr) { return arr[0].add; }
3176     bk* old_slots;
3177 public:
3178     static  sorted_table* make_sorted_table ();
3179     BOOL    insert (uint8_t* add, size_t val);;
3180     size_t  lookup (uint8_t*& add);
3181     void    remove (uint8_t* add);
3182     void    clear ();
3183     void    delete_sorted_table();
3184     void    delete_old_slots();
3185     void    enqueue_old_slot(bk* sl);
3186     BOOL    ensure_space_for_insert();
3187 };
3188 
3189 sorted_table*
make_sorted_table()3190 sorted_table::make_sorted_table ()
3191 {
3192     size_t size = 400;
3193 
3194     // allocate one more bk to store the older slot address.
3195     sorted_table* res = (sorted_table*)new char [sizeof (sorted_table) + (size + 1) * sizeof (bk)];
3196     if (!res)
3197         return 0;
3198     res->size = size;
3199     res->slots = (bk*)(res + 1);
3200     res->old_slots = 0;
3201     res->clear();
3202     return res;
3203 }
3204 
3205 void
delete_sorted_table()3206 sorted_table::delete_sorted_table()
3207 {
3208     if (slots != (bk*)(this+1))
3209     {
3210         delete slots;
3211     }
3212     delete_old_slots();
3213     delete this;
3214 }
3215 void
delete_old_slots()3216 sorted_table::delete_old_slots()
3217 {
3218     uint8_t* sl = (uint8_t*)old_slots;
3219     while (sl)
3220     {
3221         uint8_t* dsl = sl;
3222         sl = last_slot ((bk*)sl);
3223         delete dsl;
3224     }
3225     old_slots = 0;
3226 }
3227 void
enqueue_old_slot(bk * sl)3228 sorted_table::enqueue_old_slot(bk* sl)
3229 {
3230     last_slot (sl) = (uint8_t*)old_slots;
3231     old_slots = sl;
3232 }
3233 
3234 inline
3235 size_t
lookup(uint8_t * & add)3236 sorted_table::lookup (uint8_t*& add)
3237 {
3238     ptrdiff_t high = (count-1);
3239     ptrdiff_t low = 0;
3240     ptrdiff_t ti;
3241     ptrdiff_t mid;
3242     bk* buck = buckets();
3243     while (low <= high)
3244     {
3245         mid = ((low + high)/2);
3246         ti = mid;
3247         if (buck[ti].add > add)
3248         {
3249             if ((ti > 0) && (buck[ti-1].add <= add))
3250             {
3251                 add = buck[ti-1].add;
3252                 return buck[ti - 1].val;
3253             }
3254             high = mid - 1;
3255         }
3256         else
3257         {
3258             if (buck[ti+1].add > add)
3259             {
3260                 add = buck[ti].add;
3261                 return buck[ti].val;
3262             }
3263             low = mid + 1;
3264         }
3265     }
3266     add = 0;
3267     return 0;
3268 }
3269 
3270 BOOL
ensure_space_for_insert()3271 sorted_table::ensure_space_for_insert()
3272 {
3273     if (count == size)
3274     {
3275         size = (size * 3)/2;
3276         assert((size * sizeof (bk)) > 0);
3277         bk* res = (bk*)new (nothrow) char [(size + 1) * sizeof (bk)];
3278         assert (res);
3279         if (!res)
3280             return FALSE;
3281 
3282         last_slot (res) = 0;
3283         memcpy (((bk*)res + 1), buckets(), count * sizeof (bk));
3284         bk* last_old_slots = slots;
3285         slots = res;
3286         if (last_old_slots != (bk*)(this + 1))
3287             enqueue_old_slot (last_old_slots);
3288     }
3289     return TRUE;
3290 }
3291 
3292 BOOL
insert(uint8_t * add,size_t val)3293 sorted_table::insert (uint8_t* add, size_t val)
3294 {
3295     //grow if no more room
3296     assert (count < size);
3297 
3298     //insert sorted
3299     ptrdiff_t high = (count-1);
3300     ptrdiff_t low = 0;
3301     ptrdiff_t ti;
3302     ptrdiff_t mid;
3303     bk* buck = buckets();
3304     while (low <= high)
3305     {
3306         mid = ((low + high)/2);
3307         ti = mid;
3308         if (buck[ti].add > add)
3309         {
3310             if ((ti == 0) || (buck[ti-1].add <= add))
3311             {
3312                 // found insertion point
3313                 for (ptrdiff_t k = count; k > ti;k--)
3314                 {
3315                     buck [k] = buck [k-1];
3316                 }
3317                 buck[ti].add = add;
3318                 buck[ti].val = val;
3319                 count++;
3320                 return TRUE;
3321             }
3322             high = mid - 1;
3323         }
3324         else
3325         {
3326             if (buck[ti+1].add > add)
3327             {
3328                 //found the insertion point
3329                 for (ptrdiff_t k = count; k > ti+1;k--)
3330                 {
3331                     buck [k] = buck [k-1];
3332                 }
3333                 buck[ti+1].add = add;
3334                 buck[ti+1].val = val;
3335                 count++;
3336                 return TRUE;
3337             }
3338             low = mid + 1;
3339         }
3340     }
3341     assert (0);
3342     return TRUE;
3343 }
3344 
3345 void
remove(uint8_t * add)3346 sorted_table::remove (uint8_t* add)
3347 {
3348     ptrdiff_t high = (count-1);
3349     ptrdiff_t low = 0;
3350     ptrdiff_t ti;
3351     ptrdiff_t mid;
3352     bk* buck = buckets();
3353     while (low <= high)
3354     {
3355         mid = ((low + high)/2);
3356         ti = mid;
3357         if (buck[ti].add > add)
3358         {
3359             if (buck[ti-1].add <= add)
3360             {
3361                 // found the guy to remove
3362                 for (ptrdiff_t k = ti; k < count; k++)
3363                     buck[k-1] = buck[k];
3364                 count--;
3365                 return;
3366             }
3367             high = mid - 1;
3368         }
3369         else
3370         {
3371             if (buck[ti+1].add > add)
3372             {
3373                 // found the guy to remove
3374                 for (ptrdiff_t k = ti+1; k < count; k++)
3375                     buck[k-1] = buck[k];
3376                 count--;
3377                 return;
3378             }
3379             low = mid + 1;
3380         }
3381     }
3382     assert (0);
3383 }
3384 
3385 void
clear()3386 sorted_table::clear()
3387 {
3388     count = 1;
3389     buckets()[0].add = MAX_PTR;
3390 }
3391 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
3392 
3393 #ifdef SEG_MAPPING_TABLE
3394 #ifdef GROWABLE_SEG_MAPPING_TABLE
3395 inline
align_on_segment(uint8_t * add)3396 uint8_t* align_on_segment (uint8_t* add)
3397 {
3398     return (uint8_t*)((size_t)(add + (gc_heap::min_segment_size - 1)) & ~(gc_heap::min_segment_size - 1));
3399 }
3400 
3401 inline
align_lower_segment(uint8_t * add)3402 uint8_t* align_lower_segment (uint8_t* add)
3403 {
3404     return (uint8_t*)((size_t)(add) & ~(gc_heap::min_segment_size - 1));
3405 }
3406 
size_seg_mapping_table_of(uint8_t * from,uint8_t * end)3407 size_t size_seg_mapping_table_of (uint8_t* from, uint8_t* end)
3408 {
3409     from = align_lower_segment (from);
3410     end = align_on_segment (end);
3411     dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((end - from) / gc_heap::min_segment_size))));
3412     return sizeof (seg_mapping)*((end - from) / gc_heap::min_segment_size);
3413 }
3414 
3415 // for seg_mapping_table we want it to start from a pointer sized address.
3416 inline
align_for_seg_mapping_table(size_t size)3417 size_t align_for_seg_mapping_table (size_t size)
3418 {
3419     return ((size + (sizeof (uint8_t*) - 1)) &~ (sizeof (uint8_t*) - 1));
3420 }
3421 
3422 inline
seg_mapping_word_of(uint8_t * add)3423 size_t seg_mapping_word_of (uint8_t* add)
3424 {
3425     return (size_t)add / gc_heap::min_segment_size;
3426 }
3427 #else //GROWABLE_SEG_MAPPING_TABLE
seg_mapping_table_init()3428 BOOL seg_mapping_table_init()
3429 {
3430 #ifdef BIT64
3431     uint64_t total_address_space = (uint64_t)8*1024*1024*1024*1024;
3432 #else
3433     uint64_t total_address_space = (uint64_t)4*1024*1024*1024;
3434 #endif // BIT64
3435 
3436     size_t num_entries = (size_t)(total_address_space / gc_heap::min_segment_size);
3437     seg_mapping_table = new seg_mapping[num_entries];
3438 
3439     if (seg_mapping_table)
3440     {
3441         memset (seg_mapping_table, 0, num_entries * sizeof (seg_mapping));
3442         dprintf (1, ("created %d entries for heap mapping (%Id bytes)",
3443                      num_entries, (num_entries * sizeof (seg_mapping))));
3444         return TRUE;
3445     }
3446     else
3447     {
3448         dprintf (1, ("failed to create %d entries for heap mapping (%Id bytes)",
3449                      num_entries, (num_entries * sizeof (seg_mapping))));
3450         return FALSE;
3451     }
3452 }
3453 #endif //GROWABLE_SEG_MAPPING_TABLE
3454 
3455 #ifdef FEATURE_BASICFREEZE
3456 inline
ro_seg_begin_index(heap_segment * seg)3457 size_t ro_seg_begin_index (heap_segment* seg)
3458 {
3459     size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3460     begin_index = max (begin_index, (size_t)g_gc_lowest_address / gc_heap::min_segment_size);
3461     return begin_index;
3462 }
3463 
3464 inline
ro_seg_end_index(heap_segment * seg)3465 size_t ro_seg_end_index (heap_segment* seg)
3466 {
3467     size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) / gc_heap::min_segment_size;
3468     end_index = min (end_index, (size_t)g_gc_highest_address / gc_heap::min_segment_size);
3469     return end_index;
3470 }
3471 
seg_mapping_table_add_ro_segment(heap_segment * seg)3472 void seg_mapping_table_add_ro_segment (heap_segment* seg)
3473 {
3474 #ifdef GROWABLE_SEG_MAPPING_TABLE
3475     if ((heap_segment_reserved (seg) <= g_gc_lowest_address) || (heap_segment_mem (seg) >= g_gc_highest_address))
3476         return;
3477 #endif //GROWABLE_SEG_MAPPING_TABLE
3478 
3479     for (size_t entry_index = ro_seg_begin_index (seg); entry_index <= ro_seg_end_index (seg); entry_index++)
3480         seg_mapping_table[entry_index].seg1 = (heap_segment*)((size_t)seg_mapping_table[entry_index].seg1 | ro_in_entry);
3481 }
3482 
seg_mapping_table_remove_ro_segment(heap_segment * seg)3483 void seg_mapping_table_remove_ro_segment (heap_segment* seg)
3484 {
3485     UNREFERENCED_PARAMETER(seg);
3486 #if 0
3487 // POSSIBLE PERF TODO: right now we are not doing anything because we can't simply remove the flag. If it proves
3488 // to be a perf problem, we can search in the current ro segs and see if any lands in this range and only
3489 // remove the flag if none lands in this range.
3490 #endif //0
3491 }
3492 
ro_segment_lookup(uint8_t * o)3493 heap_segment* ro_segment_lookup (uint8_t* o)
3494 {
3495     uint8_t* ro_seg_start = o;
3496     heap_segment* seg = (heap_segment*)gc_heap::seg_table->lookup (ro_seg_start);
3497 
3498     if (ro_seg_start && in_range_for_segment (o, seg))
3499         return seg;
3500     else
3501         return 0;
3502 }
3503 
3504 #endif //FEATURE_BASICFREEZE
3505 
seg_mapping_table_add_segment(heap_segment * seg,gc_heap * hp)3506 void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
3507 {
3508     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3509     size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3510     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3511     size_t end_index = seg_end / gc_heap::min_segment_size;
3512     seg_mapping* end_entry = &seg_mapping_table[end_index];
3513     dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)",
3514         seg, begin_index, heap_segment_reserved (seg), end_index));
3515 
3516     dprintf (1, ("before add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3517         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3518         end_index, (seg_mapping_table[end_index].boundary + 1)));
3519 
3520 #ifdef MULTIPLE_HEAPS
3521 #ifdef SIMPLE_DPRINTF
3522     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end %d: h0: %Ix(%d), h1: %Ix(%d)",
3523         begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3524         (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3525         end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3526         (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3527 #endif //SIMPLE_DPRINTF
3528     assert (end_entry->boundary == 0);
3529     assert (end_entry->h0 == 0);
3530     end_entry->h0 = hp;
3531     assert (begin_entry->h1 == 0);
3532     begin_entry->h1 = hp;
3533 #else
3534     UNREFERENCED_PARAMETER(hp);
3535 #endif //MULTIPLE_HEAPS
3536 
3537     end_entry->boundary = (uint8_t*)seg_end;
3538 
3539     dprintf (1, ("set entry %d seg1 and %d seg0 to %Ix", begin_index, end_index, seg));
3540     assert ((begin_entry->seg1 == 0) || ((size_t)(begin_entry->seg1) == ro_in_entry));
3541     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) | (size_t)seg);
3542     end_entry->seg0 = seg;
3543 
3544     // for every entry inbetween we need to set its heap too.
3545     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3546     {
3547         assert (seg_mapping_table[entry_index].boundary == 0);
3548 #ifdef MULTIPLE_HEAPS
3549         assert (seg_mapping_table[entry_index].h0 == 0);
3550         seg_mapping_table[entry_index].h1 = hp;
3551 #endif //MULTIPLE_HEAPS
3552         seg_mapping_table[entry_index].seg1 = seg;
3553     }
3554 
3555     dprintf (1, ("after add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3556         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3557         end_index, (seg_mapping_table[end_index].boundary + 1)));
3558 #if defined(MULTIPLE_HEAPS) && defined(SIMPLE_DPRINTF)
3559     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end: %d h0: %Ix(%d), h1: %Ix(%d)",
3560         begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3561         (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3562         end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3563         (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3564 #endif //MULTIPLE_HEAPS && SIMPLE_DPRINTF
3565 }
3566 
seg_mapping_table_remove_segment(heap_segment * seg)3567 void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
3568 {
3569     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3570     size_t begin_index = (size_t)seg / gc_heap::min_segment_size;
3571     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3572     size_t end_index = seg_end / gc_heap::min_segment_size;
3573     seg_mapping* end_entry = &seg_mapping_table[end_index];
3574     dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)",
3575         seg, begin_index, heap_segment_reserved (seg), end_index));
3576 
3577     assert (end_entry->boundary == (uint8_t*)seg_end);
3578     end_entry->boundary = 0;
3579 
3580 #ifdef MULTIPLE_HEAPS
3581     gc_heap* hp = heap_segment_heap (seg);
3582     assert (end_entry->h0 == hp);
3583     end_entry->h0 = 0;
3584     assert (begin_entry->h1 == hp);
3585     begin_entry->h1 = 0;
3586 #endif //MULTIPLE_HEAPS
3587 
3588     assert (begin_entry->seg1 != 0);
3589     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) & ro_in_entry);
3590     end_entry->seg0 = 0;
3591 
3592     // for every entry inbetween we need to reset its heap too.
3593     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3594     {
3595         assert (seg_mapping_table[entry_index].boundary == 0);
3596 #ifdef MULTIPLE_HEAPS
3597         assert (seg_mapping_table[entry_index].h0 == 0);
3598         assert (seg_mapping_table[entry_index].h1 == hp);
3599         seg_mapping_table[entry_index].h1 = 0;
3600 #endif //MULTIPLE_HEAPS
3601         seg_mapping_table[entry_index].seg1 = 0;
3602     }
3603 
3604     dprintf (1, ("after remove: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3605         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3606         end_index, (seg_mapping_table[end_index].boundary + 1)));
3607 #ifdef MULTIPLE_HEAPS
3608     dprintf (1, ("begin %d: h0: %Ix, h1: %Ix; end: %d h0: %Ix, h1: %Ix",
3609         begin_index, (uint8_t*)(begin_entry->h0), (uint8_t*)(begin_entry->h1),
3610         end_index, (uint8_t*)(end_entry->h0), (uint8_t*)(end_entry->h1)));
3611 #endif //MULTIPLE_HEAPS
3612 }
3613 
3614 #ifdef MULTIPLE_HEAPS
3615 inline
seg_mapping_table_heap_of_worker(uint8_t * o)3616 gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o)
3617 {
3618     size_t index = (size_t)o / gc_heap::min_segment_size;
3619     seg_mapping* entry = &seg_mapping_table[index];
3620 
3621     gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
3622 
3623     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
3624         o, index, (entry->boundary + 1),
3625         (uint8_t*)(entry->h0), (uint8_t*)(entry->seg0),
3626         (uint8_t*)(entry->h1), (uint8_t*)(entry->seg1)));
3627 
3628 #ifdef _DEBUG
3629     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3630 #ifdef FEATURE_BASICFREEZE
3631     if ((size_t)seg & ro_in_entry)
3632         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3633 #endif //FEATURE_BASICFREEZE
3634 
3635     if (seg)
3636     {
3637         if (in_range_for_segment (o, seg))
3638         {
3639             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, seg, (uint8_t*)heap_segment_allocated (seg)));
3640         }
3641         else
3642         {
3643             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg",
3644                 seg, (uint8_t*)heap_segment_allocated (seg), o));
3645         }
3646     }
3647     else
3648     {
3649         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3650     }
3651 #endif //_DEBUG
3652 
3653     return hp;
3654 }
3655 
seg_mapping_table_heap_of(uint8_t * o)3656 gc_heap* seg_mapping_table_heap_of (uint8_t* o)
3657 {
3658 #ifdef GROWABLE_SEG_MAPPING_TABLE
3659     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3660         return 0;
3661 #endif //GROWABLE_SEG_MAPPING_TABLE
3662 
3663     return seg_mapping_table_heap_of_worker (o);
3664 }
3665 
seg_mapping_table_heap_of_gc(uint8_t * o)3666 gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o)
3667 {
3668 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3669     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3670         return 0;
3671 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3672 
3673     return seg_mapping_table_heap_of_worker (o);
3674 }
3675 #endif //MULTIPLE_HEAPS
3676 
3677 // Only returns a valid seg if we can actually find o on the seg.
seg_mapping_table_segment_of(uint8_t * o)3678 heap_segment* seg_mapping_table_segment_of (uint8_t* o)
3679 {
3680 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3681     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3682 #ifdef FEATURE_BASICFREEZE
3683         return ro_segment_lookup (o);
3684 #else
3685         return 0;
3686 #endif //FEATURE_BASICFREEZE
3687 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3688 
3689     size_t index = (size_t)o / gc_heap::min_segment_size;
3690     seg_mapping* entry = &seg_mapping_table[index];
3691 
3692     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
3693         o, index, (entry->boundary + 1),
3694         (uint8_t*)(entry->seg0), (uint8_t*)(entry->seg1)));
3695 
3696     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3697 #ifdef FEATURE_BASICFREEZE
3698     if ((size_t)seg & ro_in_entry)
3699         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3700 #endif //FEATURE_BASICFREEZE
3701 
3702     if (seg)
3703     {
3704         // Can't assert this when it's callled by everyone (it's true when it's called by mark cards).
3705         //assert (in_range_for_segment (o, seg));
3706         if (in_range_for_segment (o, seg))
3707         {
3708             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg)));
3709         }
3710         else
3711         {
3712             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg, setting it to 0",
3713                 (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg), o));
3714             seg = 0;
3715         }
3716     }
3717     else
3718     {
3719         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3720     }
3721 
3722 #ifdef FEATURE_BASICFREEZE
3723     // TODO: This was originally written assuming that the seg_mapping_table would always contain entries for ro
3724     // segments whenever the ro segment falls into the [g_gc_lowest_address,g_gc_highest_address) range.  I.e., it had an
3725     // extra "&& (size_t)(entry->seg1) & ro_in_entry" expression.  However, at the moment, grow_brick_card_table does
3726     // not correctly go through the ro segments and add them back to the seg_mapping_table when the [lowest,highest)
3727     // range changes.  We should probably go ahead and modify grow_brick_card_table and put back the
3728     // "&& (size_t)(entry->seg1) & ro_in_entry" here.
3729     if (!seg)
3730     {
3731         seg = ro_segment_lookup (o);
3732         if (seg && !in_range_for_segment (o, seg))
3733             seg = 0;
3734     }
3735 #endif //FEATURE_BASICFREEZE
3736 
3737     return seg;
3738 }
3739 #endif //SEG_MAPPING_TABLE
3740 
3741 size_t gcard_of ( uint8_t*);
3742 void gset_card (size_t card);
3743 
3744 #define memref(i) *(uint8_t**)(i)
3745 
3746 //GC Flags
3747 #define GC_MARKED       (size_t)0x1
3748 #define slot(i, j) ((uint8_t**)(i))[j+1]
3749 
3750 #define free_object_base_size (plug_skew + sizeof(ArrayBase))
3751 
3752 class CObjectHeader : public Object
3753 {
3754 public:
3755 
3756 #ifdef FEATURE_REDHAWK
3757     // The GC expects the following methods that are provided by the Object class in the CLR but not provided
3758     // by Redhawk's version of Object.
GetNumComponents()3759     uint32_t GetNumComponents()
3760     {
3761         return ((ArrayBase *)this)->GetNumComponents();
3762     }
3763 
Validate(BOOL bDeep=TRUE,BOOL bVerifyNextHeader=TRUE)3764     void Validate(BOOL bDeep=TRUE, BOOL bVerifyNextHeader = TRUE)
3765     {
3766         UNREFERENCED_PARAMETER(bVerifyNextHeader);
3767 
3768         if (this == NULL)
3769             return;
3770 
3771         MethodTable * pMT = GetMethodTable();
3772 
3773         _ASSERTE(pMT->SanityCheck());
3774 
3775         bool noRangeChecks =
3776             (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_NO_RANGE_CHECKS) == EEConfig::HEAPVERIFY_NO_RANGE_CHECKS;
3777 
3778         BOOL fSmallObjectHeapPtr = FALSE, fLargeObjectHeapPtr = FALSE;
3779         if (!noRangeChecks)
3780         {
3781             fSmallObjectHeapPtr = g_theGCHeap->IsHeapPointer(this, TRUE);
3782             if (!fSmallObjectHeapPtr)
3783                 fLargeObjectHeapPtr = g_theGCHeap->IsHeapPointer(this);
3784 
3785             _ASSERTE(fSmallObjectHeapPtr || fLargeObjectHeapPtr);
3786         }
3787 
3788 #ifdef FEATURE_STRUCTALIGN
3789         _ASSERTE(IsStructAligned((uint8_t *)this, GetMethodTable()->GetBaseAlignment()));
3790 #endif // FEATURE_STRUCTALIGN
3791 
3792 #ifdef FEATURE_64BIT_ALIGNMENT
3793         if (pMT->RequiresAlign8())
3794         {
3795             _ASSERTE((((size_t)this) & 0x7) == (pMT->IsValueType() ? 4U : 0U));
3796         }
3797 #endif // FEATURE_64BIT_ALIGNMENT
3798 
3799 #ifdef VERIFY_HEAP
3800         if (bDeep && (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC))
3801             g_theGCHeap->ValidateObjectMember(this);
3802 #endif
3803         if (fSmallObjectHeapPtr)
3804         {
3805 #ifdef FEATURE_BASICFREEZE
3806             _ASSERTE(!g_theGCHeap->IsLargeObject(pMT) || g_theGCHeap->IsInFrozenSegment(this));
3807 #else
3808             _ASSERTE(!g_theGCHeap->IsLargeObject(pMT));
3809 #endif
3810         }
3811     }
3812 
ValidatePromote(ScanContext * sc,uint32_t flags)3813     void ValidatePromote(ScanContext *sc, uint32_t flags)
3814     {
3815         UNREFERENCED_PARAMETER(sc);
3816         UNREFERENCED_PARAMETER(flags);
3817 
3818         Validate();
3819     }
3820 
ValidateHeap(Object * from,BOOL bDeep)3821     void ValidateHeap(Object *from, BOOL bDeep)
3822     {
3823         UNREFERENCED_PARAMETER(from);
3824 
3825         Validate(bDeep, FALSE);
3826     }
3827 
GetAppDomainIndex()3828     ADIndex GetAppDomainIndex()
3829     {
3830         return (ADIndex)RH_DEFAULT_DOMAIN_ID;
3831     }
3832 #endif //FEATURE_REDHAWK
3833 
3834     /////
3835     //
3836     // Header Status Information
3837     //
3838 
GetMethodTable() const3839     MethodTable    *GetMethodTable() const
3840     {
3841         return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED))));
3842     }
3843 
SetMarked()3844     void SetMarked()
3845     {
3846         RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED));
3847     }
3848 
IsMarked() const3849     BOOL IsMarked() const
3850     {
3851         return !!(((size_t)RawGetMethodTable()) & GC_MARKED);
3852     }
3853 
SetPinned()3854     void SetPinned()
3855     {
3856         assert (!(gc_heap::settings.concurrent));
3857         GetHeader()->SetGCBit();
3858     }
3859 
IsPinned() const3860     BOOL IsPinned() const
3861     {
3862         return !!((((CObjectHeader*)this)->GetHeader()->GetBits()) & BIT_SBLK_GC_RESERVE);
3863     }
3864 
ClearMarked()3865     void ClearMarked()
3866     {
3867         RawSetMethodTable( GetMethodTable() );
3868     }
3869 
GetSlotMap()3870     CGCDesc *GetSlotMap ()
3871     {
3872         assert (GetMethodTable()->ContainsPointers());
3873         return CGCDesc::GetCGCDescFromMT(GetMethodTable());
3874     }
3875 
SetFree(size_t size)3876     void SetFree(size_t size)
3877     {
3878         assert (size >= free_object_base_size);
3879 
3880         assert (g_pFreeObjectMethodTable->GetBaseSize() == free_object_base_size);
3881         assert (g_pFreeObjectMethodTable->RawGetComponentSize() == 1);
3882 
3883         RawSetMethodTable( g_pFreeObjectMethodTable );
3884 
3885         size_t* numComponentsPtr = (size_t*) &((uint8_t*) this)[ArrayBase::GetOffsetOfNumComponents()];
3886         *numComponentsPtr = size - free_object_base_size;
3887 #ifdef VERIFY_HEAP
3888         //This introduces a bug in the free list management.
3889         //((void**) this)[-1] = 0;    // clear the sync block,
3890         assert (*numComponentsPtr >= 0);
3891         if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
3892             memset (((uint8_t*)this)+sizeof(ArrayBase), 0xcc, *numComponentsPtr);
3893 #endif //VERIFY_HEAP
3894     }
3895 
UnsetFree()3896     void UnsetFree()
3897     {
3898         size_t size = free_object_base_size - plug_skew;
3899 
3900         // since we only need to clear 2 ptr size, we do it manually
3901         PTR_PTR m = (PTR_PTR) this;
3902         for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
3903             *(m++) = 0;
3904     }
3905 
IsFree() const3906     BOOL IsFree () const
3907     {
3908         return (GetMethodTable() == g_pFreeObjectMethodTable);
3909     }
3910 
3911 #ifdef FEATURE_STRUCTALIGN
GetRequiredAlignment() const3912     int GetRequiredAlignment () const
3913     {
3914         return GetMethodTable()->GetRequiredAlignment();
3915     }
3916 #endif // FEATURE_STRUCTALIGN
3917 
ContainsPointers() const3918     BOOL ContainsPointers() const
3919     {
3920         return GetMethodTable()->ContainsPointers();
3921     }
3922 
3923 #ifdef COLLECTIBLE_CLASS
Collectible() const3924     BOOL Collectible() const
3925     {
3926         return GetMethodTable()->Collectible();
3927     }
3928 
ContainsPointersOrCollectible() const3929     FORCEINLINE BOOL ContainsPointersOrCollectible() const
3930     {
3931         MethodTable *pMethodTable = GetMethodTable();
3932         return (pMethodTable->ContainsPointers() || pMethodTable->Collectible());
3933     }
3934 #endif //COLLECTIBLE_CLASS
3935 
GetObjectBase() const3936     Object* GetObjectBase() const
3937     {
3938         return (Object*) this;
3939     }
3940 };
3941 
3942 #define header(i) ((CObjectHeader*)(i))
3943 
3944 #define free_list_slot(x) ((uint8_t**)(x))[2]
3945 #define free_list_undo(x) ((uint8_t**)(x))[-1]
3946 #define UNDO_EMPTY ((uint8_t*)1)
3947 
3948 #ifdef SHORT_PLUGS
3949 inline
set_plug_padded(uint8_t * node)3950 void set_plug_padded (uint8_t* node)
3951 {
3952     header(node)->SetMarked();
3953 }
3954 inline
clear_plug_padded(uint8_t * node)3955 void clear_plug_padded (uint8_t* node)
3956 {
3957     header(node)->ClearMarked();
3958 }
3959 inline
is_plug_padded(uint8_t * node)3960 BOOL is_plug_padded (uint8_t* node)
3961 {
3962     return header(node)->IsMarked();
3963 }
3964 #else //SHORT_PLUGS
set_plug_padded(uint8_t * node)3965 inline void set_plug_padded (uint8_t* node){}
clear_plug_padded(uint8_t * node)3966 inline void clear_plug_padded (uint8_t* node){}
3967 inline
is_plug_padded(uint8_t * node)3968 BOOL is_plug_padded (uint8_t* node){return FALSE;}
3969 #endif //SHORT_PLUGS
3970 
3971 
unused_array_size(uint8_t * p)3972 inline size_t unused_array_size(uint8_t * p)
3973 {
3974     assert(((CObjectHeader*)p)->IsFree());
3975 
3976     size_t* numComponentsPtr = (size_t*)(p + ArrayBase::GetOffsetOfNumComponents());
3977     return free_object_base_size + *numComponentsPtr;
3978 }
3979 
heap_segment_rw(heap_segment * ns)3980 heap_segment* heap_segment_rw (heap_segment* ns)
3981 {
3982     if ((ns == 0) || !heap_segment_read_only_p (ns))
3983     {
3984         return ns;
3985     }
3986     else
3987     {
3988         do
3989         {
3990             ns = heap_segment_next (ns);
3991         } while ((ns != 0) && heap_segment_read_only_p (ns));
3992         return ns;
3993     }
3994 }
3995 
3996 //returns the next non ro segment.
heap_segment_next_rw(heap_segment * seg)3997 heap_segment* heap_segment_next_rw (heap_segment* seg)
3998 {
3999     heap_segment* ns = heap_segment_next (seg);
4000     return heap_segment_rw (ns);
4001 }
4002 
4003 // returns the segment before seg.
heap_segment_prev_rw(heap_segment * begin,heap_segment * seg)4004 heap_segment* heap_segment_prev_rw (heap_segment* begin, heap_segment* seg)
4005 {
4006     assert (begin != 0);
4007     heap_segment* prev = begin;
4008     heap_segment* current = heap_segment_next_rw (begin);
4009 
4010     while (current && current != seg)
4011     {
4012         prev = current;
4013         current = heap_segment_next_rw (current);
4014     }
4015 
4016     if (current == seg)
4017     {
4018         return prev;
4019     }
4020     else
4021     {
4022         return 0;
4023     }
4024 }
4025 
4026 // returns the segment before seg.
heap_segment_prev(heap_segment * begin,heap_segment * seg)4027 heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg)
4028 {
4029     assert (begin != 0);
4030     heap_segment* prev = begin;
4031     heap_segment* current = heap_segment_next (begin);
4032 
4033     while (current && current != seg)
4034     {
4035         prev = current;
4036         current = heap_segment_next (current);
4037     }
4038 
4039     if (current == seg)
4040     {
4041         return prev;
4042     }
4043     else
4044     {
4045         return 0;
4046     }
4047 }
4048 
heap_segment_in_range(heap_segment * ns)4049 heap_segment* heap_segment_in_range (heap_segment* ns)
4050 {
4051     if ((ns == 0) || heap_segment_in_range_p (ns))
4052     {
4053         return ns;
4054     }
4055     else
4056     {
4057         do
4058         {
4059             ns = heap_segment_next (ns);
4060         } while ((ns != 0) && !heap_segment_in_range_p (ns));
4061         return ns;
4062     }
4063 }
4064 
heap_segment_next_in_range(heap_segment * seg)4065 heap_segment* heap_segment_next_in_range (heap_segment* seg)
4066 {
4067     heap_segment* ns = heap_segment_next (seg);
4068     return heap_segment_in_range (ns);
4069 }
4070 
4071 typedef struct
4072 {
4073     uint8_t* memory_base;
4074 } imemory_data;
4075 
4076 typedef struct
4077 {
4078     imemory_data *initial_memory;
4079     imemory_data *initial_normal_heap; // points into initial_memory_array
4080     imemory_data *initial_large_heap;  // points into initial_memory_array
4081 
4082     size_t block_size_normal;
4083     size_t block_size_large;
4084 
4085     size_t block_count;                // # of blocks in each
4086     size_t current_block_normal;
4087     size_t current_block_large;
4088 
4089     enum
4090     {
4091         ALLATONCE = 1,
4092         TWO_STAGE,
4093         EACH_BLOCK
4094     };
4095 
4096     size_t allocation_pattern;
4097 } initial_memory_details;
4098 
4099 initial_memory_details memory_details;
4100 
reserve_initial_memory(size_t normal_size,size_t large_size,size_t num_heaps)4101 BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
4102 {
4103     BOOL reserve_success = FALSE;
4104 
4105     // should only be called once
4106     assert (memory_details.initial_memory == 0);
4107 
4108     memory_details.initial_memory = new (nothrow) imemory_data[num_heaps*2];
4109     if (memory_details.initial_memory == 0)
4110     {
4111         dprintf (2, ("failed to reserve %Id bytes for imemory_data", num_heaps*2*sizeof(imemory_data)));
4112         return FALSE;
4113     }
4114 
4115     memory_details.initial_normal_heap = memory_details.initial_memory;
4116     memory_details.initial_large_heap = memory_details.initial_memory + num_heaps;
4117     memory_details.block_size_normal = normal_size;
4118     memory_details.block_size_large = large_size;
4119     memory_details.block_count = num_heaps;
4120 
4121     memory_details.current_block_normal = 0;
4122     memory_details.current_block_large = 0;
4123 
4124     g_gc_lowest_address = MAX_PTR;
4125     g_gc_highest_address = 0;
4126 
4127     if (((size_t)MAX_PTR - large_size) < normal_size)
4128     {
4129         // we are already overflowing with just one heap.
4130         dprintf (2, ("0x%Ix + 0x%Ix already overflow", normal_size, large_size));
4131         return FALSE;
4132     }
4133 
4134     if (((size_t)MAX_PTR / memory_details.block_count) < (normal_size + large_size))
4135     {
4136         dprintf (2, ("(0x%Ix + 0x%Ix)*0x%Ix overflow", normal_size, large_size, memory_details.block_count));
4137         return FALSE;
4138     }
4139 
4140     size_t requestedMemory = memory_details.block_count * (normal_size + large_size);
4141 
4142     uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory);
4143     if (allatonce_block)
4144     {
4145         g_gc_lowest_address =  allatonce_block;
4146         g_gc_highest_address = allatonce_block + requestedMemory;
4147         memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
4148 
4149         for(size_t i = 0; i < memory_details.block_count; i++)
4150         {
4151             memory_details.initial_normal_heap[i].memory_base = allatonce_block + (i*normal_size);
4152             memory_details.initial_large_heap[i].memory_base = allatonce_block +
4153                             (memory_details.block_count*normal_size) + (i*large_size);
4154             reserve_success = TRUE;
4155         }
4156     }
4157     else
4158     {
4159         // try to allocate 2 blocks
4160         uint8_t* b1 = 0;
4161         uint8_t* b2 = 0;
4162         b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size);
4163         if (b1)
4164         {
4165             b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size);
4166             if (b2)
4167             {
4168                 memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
4169                 g_gc_lowest_address = min(b1,b2);
4170                 g_gc_highest_address = max(b1 + memory_details.block_count*normal_size,
4171                                         b2 + memory_details.block_count*large_size);
4172                 for(size_t i = 0; i < memory_details.block_count; i++)
4173                 {
4174                     memory_details.initial_normal_heap[i].memory_base = b1 + (i*normal_size);
4175                     memory_details.initial_large_heap[i].memory_base = b2 + (i*large_size);
4176                     reserve_success = TRUE;
4177                 }
4178             }
4179             else
4180             {
4181                 // b2 allocation failed, we'll go on to try allocating each block.
4182                 // We could preserve the b1 alloc, but code complexity increases
4183                 virtual_free (b1, memory_details.block_count * normal_size);
4184             }
4185         }
4186 
4187         if ((b2==NULL) && ( memory_details.block_count > 1))
4188         {
4189             memory_details.allocation_pattern = initial_memory_details::EACH_BLOCK;
4190 
4191             imemory_data *current_block = memory_details.initial_memory;
4192             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4193             {
4194                 size_t block_size = ((i < memory_details.block_count) ?
4195                                      memory_details.block_size_normal :
4196                                      memory_details.block_size_large);
4197                 current_block->memory_base =
4198                     (uint8_t*)virtual_alloc (block_size);
4199                 if (current_block->memory_base == 0)
4200                 {
4201                     // Free the blocks that we've allocated so far
4202                     current_block = memory_details.initial_memory;
4203                     for(size_t j = 0; j < i; j++, current_block++){
4204                         if (current_block->memory_base != 0){
4205                             block_size = ((j < memory_details.block_count) ?
4206                                      memory_details.block_size_normal :
4207                                      memory_details.block_size_large);
4208                              virtual_free (current_block->memory_base , block_size);
4209                         }
4210                     }
4211                     reserve_success = FALSE;
4212                     break;
4213                 }
4214                 else
4215                 {
4216                     if (current_block->memory_base < g_gc_lowest_address)
4217                         g_gc_lowest_address =  current_block->memory_base;
4218                     if (((uint8_t *) current_block->memory_base + block_size) > g_gc_highest_address)
4219                         g_gc_highest_address = (current_block->memory_base + block_size);
4220                 }
4221                 reserve_success = TRUE;
4222             }
4223         }
4224     }
4225 
4226     return reserve_success;
4227 }
4228 
destroy_initial_memory()4229 void destroy_initial_memory()
4230 {
4231     if (memory_details.initial_memory != NULL)
4232     {
4233         if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE)
4234         {
4235             virtual_free(memory_details.initial_memory[0].memory_base,
4236                 memory_details.block_count*(memory_details.block_size_normal +
4237                 memory_details.block_size_large));
4238         }
4239         else if (memory_details.allocation_pattern == initial_memory_details::TWO_STAGE)
4240         {
4241             virtual_free (memory_details.initial_normal_heap[0].memory_base,
4242                 memory_details.block_count*memory_details.block_size_normal);
4243 
4244             virtual_free (memory_details.initial_large_heap[0].memory_base,
4245                 memory_details.block_count*memory_details.block_size_large);
4246         }
4247         else
4248         {
4249             assert (memory_details.allocation_pattern == initial_memory_details::EACH_BLOCK);
4250             imemory_data *current_block = memory_details.initial_memory;
4251             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4252             {
4253                 size_t block_size = (i < memory_details.block_count) ? memory_details.block_size_normal :
4254                                                                        memory_details.block_size_large;
4255                 if (current_block->memory_base != NULL)
4256                 {
4257                     virtual_free (current_block->memory_base, block_size);
4258                 }
4259             }
4260         }
4261 
4262         delete [] memory_details.initial_memory;
4263         memory_details.initial_memory = NULL;
4264         memory_details.initial_normal_heap = NULL;
4265         memory_details.initial_large_heap = NULL;
4266     }
4267 }
4268 
next_initial_memory(size_t size)4269 void* next_initial_memory (size_t size)
4270 {
4271     assert ((size == memory_details.block_size_normal) || (size == memory_details.block_size_large));
4272     void *res = NULL;
4273 
4274     if ((size != memory_details.block_size_normal) ||
4275         ((memory_details.current_block_normal == memory_details.block_count) &&
4276          (memory_details.block_size_normal == memory_details.block_size_large)))
4277     {
4278         // If the block sizes are the same, flow block requests from normal to large
4279         assert (memory_details.current_block_large < memory_details.block_count);
4280         assert (memory_details.initial_large_heap != 0);
4281 
4282         res = memory_details.initial_large_heap[memory_details.current_block_large].memory_base;
4283         memory_details.current_block_large++;
4284     }
4285     else
4286     {
4287         assert (memory_details.current_block_normal < memory_details.block_count);
4288         assert (memory_details.initial_normal_heap != NULL);
4289 
4290         res = memory_details.initial_normal_heap[memory_details.current_block_normal].memory_base;
4291         memory_details.current_block_normal++;
4292     }
4293 
4294     return res;
4295 }
4296 
get_initial_segment(size_t size,int h_number)4297 heap_segment* get_initial_segment (size_t size, int h_number)
4298 {
4299     void* mem = next_initial_memory (size);
4300     heap_segment* res = gc_heap::make_heap_segment ((uint8_t*)mem, size , h_number);
4301 
4302     return res;
4303 }
4304 
virtual_alloc(size_t size)4305 void* virtual_alloc (size_t size)
4306 {
4307     size_t requested_size = size;
4308 
4309     if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4310     {
4311         gc_heap::reserved_memory_limit =
4312             GCScan::AskForMoreReservedMemory (gc_heap::reserved_memory_limit, requested_size);
4313         if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4314         {
4315             return 0;
4316         }
4317     }
4318 
4319     uint32_t flags = VirtualReserveFlags::None;
4320 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4321     if (virtual_alloc_hardware_write_watch)
4322     {
4323         flags = VirtualReserveFlags::WriteWatch;
4324     }
4325 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4326     void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags);
4327     void *aligned_mem = prgmem;
4328 
4329     // We don't want (prgmem + size) to be right at the end of the address space
4330     // because we'd have to worry about that everytime we do (address + size).
4331     // We also want to make sure that we leave LARGE_OBJECT_SIZE at the end
4332     // so we allocate a small object we don't need to worry about overflow there
4333     // when we do alloc_ptr+size.
4334     if (prgmem)
4335     {
4336         uint8_t* end_mem = (uint8_t*)prgmem + requested_size;
4337 
4338         if ((end_mem == 0) || ((size_t)(MAX_PTR - end_mem) <= END_SPACE_AFTER_GC))
4339         {
4340             GCToOSInterface::VirtualRelease (prgmem, requested_size);
4341             dprintf (2, ("Virtual Alloc size %Id returned memory right against 4GB [%Ix, %Ix[ - discarding",
4342                         requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4343             prgmem = 0;
4344             aligned_mem = 0;
4345         }
4346     }
4347 
4348     if (prgmem)
4349     {
4350         gc_heap::reserved_memory += requested_size;
4351     }
4352 
4353     dprintf (2, ("Virtual Alloc size %Id: [%Ix, %Ix[",
4354                  requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4355 
4356     return aligned_mem;
4357 }
4358 
virtual_free(void * add,size_t size)4359 void virtual_free (void* add, size_t size)
4360 {
4361     GCToOSInterface::VirtualRelease (add, size);
4362     gc_heap::reserved_memory -= size;
4363     dprintf (2, ("Virtual Free size %Id: [%Ix, %Ix[",
4364                  size, (size_t)add, (size_t)((uint8_t*)add+size)));
4365 }
4366 
4367 // We have a few places that call this but the seg size doesn't change so call it
4368 // once and save the result.
4369 // TODO: move back after we do this.
get_valid_segment_size(BOOL large_seg=FALSE)4370 static size_t get_valid_segment_size (BOOL large_seg=FALSE)
4371 {
4372     size_t seg_size, initial_seg_size;
4373 
4374     if (!large_seg)
4375     {
4376         initial_seg_size = INITIAL_ALLOC;
4377         seg_size = g_pConfig->GetSegmentSize();
4378     }
4379     else
4380     {
4381         initial_seg_size = LHEAP_ALLOC;
4382         seg_size = g_pConfig->GetSegmentSize() / 2;
4383     }
4384 
4385 #ifdef MULTIPLE_HEAPS
4386 #ifdef BIT64
4387     if (!large_seg)
4388 #endif // BIT64
4389     {
4390         if (g_SystemInfo.dwNumberOfProcessors > 4)
4391             initial_seg_size /= 2;
4392         if (g_SystemInfo.dwNumberOfProcessors > 8)
4393             initial_seg_size /= 2;
4394     }
4395 #endif //MULTIPLE_HEAPS
4396 
4397     // if seg_size is small but not 0 (0 is default if config not set)
4398     // then set the segment to the minimum size
4399     if (!g_theGCHeap->IsValidSegmentSize(seg_size))
4400     {
4401         // if requested size is between 1 byte and 4MB, use min
4402         if ((seg_size >> 1) && !(seg_size >> 22))
4403             seg_size = 1024*1024*4;
4404         else
4405             seg_size = initial_seg_size;
4406     }
4407 
4408     return (seg_size);
4409 }
4410 
4411 void
compute_new_ephemeral_size()4412 gc_heap::compute_new_ephemeral_size()
4413 {
4414     int eph_gen_max = max_generation - 1 - (settings.promotion ? 1 : 0);
4415     size_t padding_size = 0;
4416 
4417     for (int i = 0; i <= eph_gen_max; i++)
4418     {
4419         dynamic_data* dd = dynamic_data_of (i);
4420         total_ephemeral_size += (dd_survived_size (dd) - dd_pinned_survived_size (dd));
4421 #ifdef RESPECT_LARGE_ALIGNMENT
4422         total_ephemeral_size += dd_num_npinned_plugs (dd) * switch_alignment_size (FALSE);
4423 #endif //RESPECT_LARGE_ALIGNMENT
4424 #ifdef FEATURE_STRUCTALIGN
4425         total_ephemeral_size += dd_num_npinned_plugs (dd) * MAX_STRUCTALIGN;
4426 #endif //FEATURE_STRUCTALIGN
4427 
4428 #ifdef SHORT_PLUGS
4429         padding_size += dd_padding_size (dd);
4430 #endif //SHORT_PLUGS
4431     }
4432 
4433     total_ephemeral_size += eph_gen_starts_size;
4434 
4435 #ifdef RESPECT_LARGE_ALIGNMENT
4436     size_t planned_ephemeral_size = heap_segment_plan_allocated (ephemeral_heap_segment) -
4437                                        generation_plan_allocation_start (generation_of (max_generation-1));
4438     total_ephemeral_size = min (total_ephemeral_size, planned_ephemeral_size);
4439 #endif //RESPECT_LARGE_ALIGNMENT
4440 
4441 #ifdef SHORT_PLUGS
4442     total_ephemeral_size = Align ((size_t)((double)total_ephemeral_size * short_plugs_pad_ratio) + 1);
4443     total_ephemeral_size += Align (DESIRED_PLUG_LENGTH);
4444 #endif //SHORT_PLUGS
4445 
4446     dprintf (3, ("total ephemeral size is %Ix, padding %Ix(%Ix)",
4447         total_ephemeral_size,
4448         padding_size, (total_ephemeral_size - padding_size)));
4449 }
4450 
4451 #ifdef _MSC_VER
4452 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
4453 #endif // _MSC_VER
4454 
4455 heap_segment*
soh_get_segment_to_expand()4456 gc_heap::soh_get_segment_to_expand()
4457 {
4458     size_t size = get_valid_segment_size();
4459 
4460     ordered_plug_indices_init = FALSE;
4461     use_bestfit = FALSE;
4462 
4463     //compute the size of the new ephemeral heap segment.
4464     compute_new_ephemeral_size();
4465 
4466     if ((settings.pause_mode != pause_low_latency) &&
4467         (settings.pause_mode != pause_no_gc)
4468 #ifdef BACKGROUND_GC
4469         && (!recursive_gc_sync::background_running_p())
4470 #endif //BACKGROUND_GC
4471         )
4472     {
4473         allocator*  gen_alloc = ((settings.condemned_generation == max_generation) ? 0 :
4474                               generation_allocator (generation_of (max_generation)));
4475         dprintf (2, ("(gen%d)soh_get_segment_to_expand", settings.condemned_generation));
4476 
4477         // try to find one in the gen 2 segment list, search backwards because the first segments
4478         // tend to be more compact than the later ones.
4479         heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
4480 
4481         PREFIX_ASSUME(fseg != NULL);
4482 
4483 #ifdef SEG_REUSE_STATS
4484         int try_reuse = 0;
4485 #endif //SEG_REUSE_STATS
4486 
4487         heap_segment* seg = ephemeral_heap_segment;
4488         while ((seg = heap_segment_prev_rw (fseg, seg)) && (seg != fseg))
4489         {
4490 #ifdef SEG_REUSE_STATS
4491         try_reuse++;
4492 #endif //SEG_REUSE_STATS
4493 
4494             if (can_expand_into_p (seg, size/3, total_ephemeral_size, gen_alloc))
4495             {
4496                 get_gc_data_per_heap()->set_mechanism (gc_heap_expand,
4497                     (use_bestfit ? expand_reuse_bestfit : expand_reuse_normal));
4498                 if (settings.condemned_generation == max_generation)
4499                 {
4500                     if (use_bestfit)
4501                     {
4502                         build_ordered_free_spaces (seg);
4503                         dprintf (GTC_LOG, ("can use best fit"));
4504                     }
4505 
4506 #ifdef SEG_REUSE_STATS
4507                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse",
4508                         settings.condemned_generation, try_reuse));
4509 #endif //SEG_REUSE_STATS
4510                     dprintf (GTC_LOG, ("max_gen: Found existing segment to expand into %Ix", (size_t)seg));
4511                     return seg;
4512                 }
4513                 else
4514                 {
4515 #ifdef SEG_REUSE_STATS
4516                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse - returning",
4517                         settings.condemned_generation, try_reuse));
4518 #endif //SEG_REUSE_STATS
4519                     dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix", (size_t)seg));
4520 
4521                     // If we return 0 here, the allocator will think since we are short on end
4522                     // of seg we neeed to trigger a full compacting GC. So if sustained low latency
4523                     // is set we should acquire a new seg instead, that way we wouldn't be short.
4524                     // The real solution, of course, is to actually implement seg reuse in gen1.
4525                     if (settings.pause_mode != pause_sustained_low_latency)
4526                     {
4527                         dprintf (GTC_LOG, ("max_gen-1: SustainedLowLatency is set, acquire a new seg"));
4528                         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_next_full_gc);
4529                         return 0;
4530                     }
4531                 }
4532             }
4533         }
4534     }
4535 
4536     heap_segment* result = get_segment (size, FALSE);
4537 
4538     if(result)
4539     {
4540 #ifdef BACKGROUND_GC
4541         if (current_c_gc_state == c_gc_state_planning)
4542         {
4543             // When we expand heap during bgc sweep, we set the seg to be swept so
4544             // we'll always look at cards for objects on the new segment.
4545             result->flags |= heap_segment_flags_swept;
4546         }
4547 #endif //BACKGROUND_GC
4548 
4549         FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(result),
4550                                   (size_t)(heap_segment_reserved (result) - heap_segment_mem(result)),
4551                                   ETW::GCLog::ETW_GC_INFO::SMALL_OBJECT_HEAP,
4552                                   GetClrInstanceId());
4553     }
4554 
4555     get_gc_data_per_heap()->set_mechanism (gc_heap_expand, (result ? expand_new_seg : expand_no_memory));
4556 
4557     if (result == 0)
4558     {
4559         dprintf (2, ("h%d: failed to allocate a new segment!", heap_number));
4560     }
4561     else
4562     {
4563 #ifdef MULTIPLE_HEAPS
4564         heap_segment_heap (result) = this;
4565 #endif //MULTIPLE_HEAPS
4566     }
4567 
4568     dprintf (GTC_LOG, ("(gen%d)creating new segment %Ix", settings.condemned_generation, result));
4569     return result;
4570 }
4571 
4572 #ifdef _MSC_VER
4573 #pragma warning(default:4706)
4574 #endif // _MSC_VER
4575 
4576 //returns 0 in case of allocation failure
4577 heap_segment*
get_segment(size_t size,BOOL loh_p)4578 gc_heap::get_segment (size_t size, BOOL loh_p)
4579 {
4580     heap_segment* result = 0;
4581 
4582     if (segment_standby_list != 0)
4583     {
4584         result = segment_standby_list;
4585         heap_segment* last = 0;
4586         while (result)
4587         {
4588             size_t hs = (size_t)(heap_segment_reserved (result) - (uint8_t*)result);
4589             if ((hs >= size) && ((hs / 2) < size))
4590             {
4591                 dprintf (2, ("Hoarded segment %Ix found", (size_t) result));
4592                 if (last)
4593                 {
4594                     heap_segment_next (last) = heap_segment_next (result);
4595                 }
4596                 else
4597                 {
4598                     segment_standby_list = heap_segment_next (result);
4599                 }
4600                 break;
4601             }
4602             else
4603             {
4604                 last = result;
4605                 result = heap_segment_next (result);
4606             }
4607         }
4608     }
4609 
4610     if (result)
4611     {
4612         init_heap_segment (result);
4613 #ifdef BACKGROUND_GC
4614         if (should_commit_mark_array())
4615         {
4616             dprintf (GC_TABLE_LOG, ("hoarded seg %Ix, mark_array is %Ix", result, mark_array));
4617             if (!commit_mark_array_new_seg (__this, result))
4618             {
4619                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for hoarded seg"));
4620                 // If we can't use it we need to thread it back.
4621                 if (segment_standby_list != 0)
4622                 {
4623                     heap_segment_next (result) = segment_standby_list;
4624                     segment_standby_list = result;
4625                 }
4626                 else
4627                 {
4628                     segment_standby_list = result;
4629                 }
4630 
4631                 result = 0;
4632             }
4633         }
4634 #endif //BACKGROUND_GC
4635 
4636 #ifdef SEG_MAPPING_TABLE
4637         if (result)
4638             seg_mapping_table_add_segment (result, __this);
4639 #endif //SEG_MAPPING_TABLE
4640     }
4641 
4642     if (!result)
4643     {
4644 #ifndef SEG_MAPPING_TABLE
4645         if (!seg_table->ensure_space_for_insert ())
4646             return 0;
4647 #endif //SEG_MAPPING_TABLE
4648         void* mem = virtual_alloc (size);
4649         if (!mem)
4650         {
4651             fgm_result.set_fgm (fgm_reserve_segment, size, loh_p);
4652             return 0;
4653         }
4654 
4655         result = gc_heap::make_heap_segment ((uint8_t*)mem, size, heap_number);
4656 
4657         if (result)
4658         {
4659             uint8_t* start;
4660             uint8_t* end;
4661             if (mem < g_gc_lowest_address)
4662             {
4663                 start =  (uint8_t*)mem;
4664             }
4665             else
4666             {
4667                 start = (uint8_t*)g_gc_lowest_address;
4668             }
4669 
4670             if (((uint8_t*)mem + size) > g_gc_highest_address)
4671             {
4672                 end = (uint8_t*)mem + size;
4673             }
4674             else
4675             {
4676                 end = (uint8_t*)g_gc_highest_address;
4677             }
4678 
4679             if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0)
4680             {
4681                 virtual_free (mem, size);
4682                 return 0;
4683             }
4684         }
4685         else
4686         {
4687             fgm_result.set_fgm (fgm_commit_segment_beg, SEGMENT_INITIAL_COMMIT, loh_p);
4688             virtual_free (mem, size);
4689         }
4690 
4691         if (result)
4692         {
4693 #ifdef SEG_MAPPING_TABLE
4694             seg_mapping_table_add_segment (result, __this);
4695 #else //SEG_MAPPING_TABLE
4696             gc_heap::seg_table->insert ((uint8_t*)result, delta);
4697 #endif //SEG_MAPPING_TABLE
4698         }
4699     }
4700 
4701 #ifdef BACKGROUND_GC
4702     if (result)
4703     {
4704         ::record_changed_seg ((uint8_t*)result, heap_segment_reserved (result),
4705                             settings.gc_index, current_bgc_state,
4706                             seg_added);
4707         bgc_verify_mark_array_cleared (result);
4708     }
4709 #endif //BACKGROUND_GC
4710 
4711     dprintf (GC_TABLE_LOG, ("h%d: new seg: %Ix-%Ix (%Id)", heap_number, result, ((uint8_t*)result + size), size));
4712     return result;
4713 }
4714 
release_segment(heap_segment * sg)4715 void release_segment (heap_segment* sg)
4716 {
4717     ptrdiff_t delta = 0;
4718     FireEtwGCFreeSegment_V1((size_t)heap_segment_mem(sg), GetClrInstanceId());
4719     virtual_free (sg, (uint8_t*)heap_segment_reserved (sg)-(uint8_t*)sg);
4720 }
4721 
get_segment_for_loh(size_t size,gc_heap * hp)4722 heap_segment* gc_heap::get_segment_for_loh (size_t size
4723 #ifdef MULTIPLE_HEAPS
4724                                            , gc_heap* hp
4725 #endif //MULTIPLE_HEAPS
4726                                            )
4727 {
4728 #ifndef MULTIPLE_HEAPS
4729     gc_heap* hp = 0;
4730 #endif //MULTIPLE_HEAPS
4731     heap_segment* res = hp->get_segment (size, TRUE);
4732     if (res != 0)
4733     {
4734 #ifdef MULTIPLE_HEAPS
4735         heap_segment_heap (res) = hp;
4736 #endif //MULTIPLE_HEAPS
4737         res->flags |= heap_segment_flags_loh;
4738 
4739         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());
4740 
4741         GCToEEInterface::DiagUpdateGenerationBounds();
4742 
4743 #ifdef MULTIPLE_HEAPS
4744         hp->thread_loh_segment (res);
4745 #else
4746         thread_loh_segment (res);
4747 #endif //MULTIPLE_HEAPS
4748     }
4749 
4750     return res;
4751 }
4752 
thread_loh_segment(heap_segment * new_seg)4753 void gc_heap::thread_loh_segment (heap_segment* new_seg)
4754 {
4755     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
4756 
4757     while (heap_segment_next_rw (seg))
4758         seg = heap_segment_next_rw (seg);
4759     heap_segment_next (seg) = new_seg;
4760 }
4761 
4762 heap_segment*
get_large_segment(size_t size,BOOL * did_full_compact_gc)4763 gc_heap::get_large_segment (size_t size, BOOL* did_full_compact_gc)
4764 {
4765     *did_full_compact_gc = FALSE;
4766     size_t last_full_compact_gc_count = get_full_compact_gc_count();
4767 
4768     //access to get_segment needs to be serialized
4769     add_saved_spinlock_info (me_release, mt_get_large_seg);
4770 
4771     dprintf (SPINLOCK_LOG, ("[%d]Seg: Lmsl", heap_number));
4772     leave_spin_lock (&more_space_lock);
4773     enter_spin_lock (&gc_heap::gc_lock);
4774     dprintf (SPINLOCK_LOG, ("[%d]Seg: Egc", heap_number));
4775     // if a GC happened between here and before we ask for a segment in
4776     // get_large_segment, we need to count that GC.
4777     size_t current_full_compact_gc_count = get_full_compact_gc_count();
4778 
4779     if (current_full_compact_gc_count > last_full_compact_gc_count)
4780     {
4781         *did_full_compact_gc = TRUE;
4782     }
4783 
4784 #ifdef BACKGROUND_GC
4785     while (current_c_gc_state == c_gc_state_planning)
4786     {
4787         dprintf (3, ("lh state planning, waiting to get a large seg"));
4788 
4789         dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Lgc", heap_number));
4790         leave_spin_lock (&gc_lock);
4791         background_gc_wait_lh (awr_get_loh_seg);
4792         enter_spin_lock (&gc_lock);
4793         dprintf (SPINLOCK_LOG, ("[%d]Seg: P, Egc", heap_number));
4794     }
4795     assert ((current_c_gc_state == c_gc_state_free) ||
4796             (current_c_gc_state == c_gc_state_marking));
4797 #endif //BACKGROUND_GC
4798 
4799     heap_segment* res = get_segment_for_loh (size
4800 #ifdef MULTIPLE_HEAPS
4801                                             , this
4802 #endif //MULTIPLE_HEAPS
4803                                             );
4804 
4805     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Lgc", heap_number));
4806     leave_spin_lock (&gc_heap::gc_lock);
4807     enter_spin_lock (&more_space_lock);
4808     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Emsl", heap_number));
4809     add_saved_spinlock_info (me_acquire, mt_get_large_seg);
4810 
4811 #ifdef BACKGROUND_GC
4812     wait_for_background_planning (awr_get_loh_seg);
4813 #endif //BACKGROUND_GC
4814 
4815     return res;
4816 }
4817 
4818 #if 0
4819 BOOL gc_heap::unprotect_segment (heap_segment* seg)
4820 {
4821     uint8_t* start = align_lower_page (heap_segment_mem (seg));
4822     ptrdiff_t region_size = heap_segment_allocated (seg) - start;
4823 
4824     if (region_size != 0 )
4825     {
4826         dprintf (3, ("unprotecting segment %Ix:", (size_t)seg));
4827 
4828         BOOL status = GCToOSInterface::VirtualUnprotect (start, region_size);
4829         assert (status);
4830         return status;
4831     }
4832     return FALSE;
4833 }
4834 #endif
4835 
4836 #ifdef MULTIPLE_HEAPS
4837 #ifdef _X86_
4838 #ifdef _MSC_VER
4839 #pragma warning(disable:4035)
get_cycle_count()4840     static ptrdiff_t  get_cycle_count()
4841     {
4842         __asm   rdtsc
4843     }
4844 #pragma warning(default:4035)
4845 #elif defined(__GNUC__)
get_cycle_count()4846     static ptrdiff_t  get_cycle_count()
4847     {
4848         ptrdiff_t cycles;
4849         ptrdiff_t cyclesHi;
4850         __asm__ __volatile__
4851         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4852         return cycles;
4853     }
4854 #else //_MSC_VER
4855 #error Unknown compiler
4856 #endif //_MSC_VER
4857 #elif defined(_TARGET_AMD64_)
4858 #ifdef _MSC_VER
4859 extern "C" uint64_t __rdtsc();
4860 #pragma intrinsic(__rdtsc)
get_cycle_count()4861     static ptrdiff_t get_cycle_count()
4862     {
4863         return (ptrdiff_t)__rdtsc();
4864     }
4865 #elif defined(__clang__)
get_cycle_count()4866     static ptrdiff_t get_cycle_count()
4867     {
4868         ptrdiff_t cycles;
4869         ptrdiff_t cyclesHi;
4870         __asm__ __volatile__
4871         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
4872         return (cyclesHi << 32) | cycles;
4873     }
4874 #else // _MSC_VER
4875     extern "C" ptrdiff_t get_cycle_count(void);
4876 #endif // _MSC_VER
4877 #elif defined(_TARGET_ARM_)
get_cycle_count()4878     static ptrdiff_t get_cycle_count()
4879     {
4880         // @ARMTODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
4881         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
4882         // all buffer access times being reported as equal in access_time().
4883         return 0;
4884     }
4885 #elif defined(_TARGET_ARM64_)
get_cycle_count()4886     static ptrdiff_t get_cycle_count()
4887     {
4888         // @ARM64TODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
4889         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
4890         // all buffer access times being reported as equal in access_time().
4891         return 0;
4892     }
4893 #elif defined(_TARGET_WASM_)
get_cycle_count()4894     static ptrdiff_t get_cycle_count()
4895     {
4896         // @WASMTODO: cycle counter is not exposed to user mode by WebAsssembly. For now (until we can show this
4897         // makes a difference on the configurations on which we'll run) just return 0. This will result in
4898         // all buffer access times being reported as equal in access_time().
4899         return 0;
4900     }
4901 #else
4902 #error NYI platform: get_cycle_count
4903 #endif //_TARGET_X86_
4904 
4905 class heap_select
4906 {
heap_select()4907     heap_select() {}
4908     static uint8_t* sniff_buffer;
4909     static unsigned n_sniff_buffers;
4910     static unsigned cur_sniff_index;
4911 
4912     static uint8_t proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
4913     static uint8_t heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
4914     static uint8_t heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
4915     static uint8_t heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
4916     static uint8_t heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
4917     static uint8_t numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
4918 
access_time(uint8_t * sniff_buffer,int heap_number,unsigned sniff_index,unsigned n_sniff_buffers)4919     static int access_time(uint8_t *sniff_buffer, int heap_number, unsigned sniff_index, unsigned n_sniff_buffers)
4920     {
4921         ptrdiff_t start_cycles = get_cycle_count();
4922         uint8_t sniff = sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE];
4923         assert (sniff == 0);
4924         ptrdiff_t elapsed_cycles = get_cycle_count() - start_cycles;
4925         // add sniff here just to defeat the optimizer
4926         elapsed_cycles += sniff;
4927         return (int) elapsed_cycles;
4928     }
4929 
4930 public:
init(int n_heaps)4931     static BOOL init(int n_heaps)
4932     {
4933         assert (sniff_buffer == NULL && n_sniff_buffers == 0);
4934         if (!GCToOSInterface::CanGetCurrentProcessorNumber())
4935         {
4936             n_sniff_buffers = n_heaps*2+1;
4937             size_t sniff_buf_size = 0;
4938 #ifdef FEATURE_REDHAWK
4939             size_t n_cache_lines = 1 + n_heaps*n_sniff_buffers + 1;
4940             sniff_buf_size = n_cache_lines * HS_CACHE_LINE_SIZE;
4941 #else
4942             S_SIZE_T safe_sniff_buf_size = S_SIZE_T(1 + n_heaps*n_sniff_buffers + 1);
4943             safe_sniff_buf_size *= HS_CACHE_LINE_SIZE;
4944             if (safe_sniff_buf_size.IsOverflow())
4945             {
4946                 return FALSE;
4947             }
4948             sniff_buf_size = safe_sniff_buf_size.Value();
4949 #endif //FEATURE_REDHAWK
4950             sniff_buffer = new (nothrow) uint8_t[sniff_buf_size];
4951             if (sniff_buffer == 0)
4952                 return FALSE;
4953             memset(sniff_buffer, 0, sniff_buf_size*sizeof(uint8_t));
4954         }
4955 
4956         //can not enable gc numa aware, force all heaps to be in
4957         //one numa node by filling the array with all 0s
4958         if (!NumaNodeInfo::CanEnableGCNumaAware())
4959             memset(heap_no_to_numa_node, 0, MAX_SUPPORTED_CPUS);
4960 
4961         return TRUE;
4962     }
4963 
init_cpu_mapping(gc_heap *,int heap_number)4964     static void init_cpu_mapping(gc_heap * /*heap*/, int heap_number)
4965     {
4966         if (GCToOSInterface::CanGetCurrentProcessorNumber())
4967         {
4968             uint32_t proc_no = GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps;
4969             // We can safely cast heap_number to a BYTE 'cause GetCurrentProcessCpuCount
4970             // only returns up to MAX_SUPPORTED_CPUS procs right now. We only ever create at most
4971             // MAX_SUPPORTED_CPUS GC threads.
4972             proc_no_to_heap_no[proc_no] = (uint8_t)heap_number;
4973         }
4974     }
4975 
mark_heap(int heap_number)4976     static void mark_heap(int heap_number)
4977     {
4978         if (GCToOSInterface::CanGetCurrentProcessorNumber())
4979             return;
4980 
4981         for (unsigned sniff_index = 0; sniff_index < n_sniff_buffers; sniff_index++)
4982             sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
4983     }
4984 
select_heap(alloc_context * acontext,int)4985     static int select_heap(alloc_context* acontext, int /*hint*/)
4986     {
4987         UNREFERENCED_PARAMETER(acontext); // only referenced by dprintf
4988 
4989         if (GCToOSInterface::CanGetCurrentProcessorNumber())
4990             return proc_no_to_heap_no[GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps];
4991 
4992         unsigned sniff_index = Interlocked::Increment(&cur_sniff_index);
4993         sniff_index %= n_sniff_buffers;
4994 
4995         int best_heap = 0;
4996         int best_access_time = 1000*1000*1000;
4997         int second_best_access_time = best_access_time;
4998 
4999         uint8_t *l_sniff_buffer = sniff_buffer;
5000         unsigned l_n_sniff_buffers = n_sniff_buffers;
5001         for (int heap_number = 0; heap_number < gc_heap::n_heaps; heap_number++)
5002         {
5003             int this_access_time = access_time(l_sniff_buffer, heap_number, sniff_index, l_n_sniff_buffers);
5004             if (this_access_time < best_access_time)
5005             {
5006                 second_best_access_time = best_access_time;
5007                 best_access_time = this_access_time;
5008                 best_heap = heap_number;
5009             }
5010             else if (this_access_time < second_best_access_time)
5011             {
5012                 second_best_access_time = this_access_time;
5013             }
5014         }
5015 
5016         if (best_access_time*2 < second_best_access_time)
5017         {
5018             sniff_buffer[(1 + best_heap*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5019 
5020             dprintf (3, ("select_heap yields crisp %d for context %p\n", best_heap, (void *)acontext));
5021         }
5022         else
5023         {
5024             dprintf (3, ("select_heap yields vague %d for context %p\n", best_heap, (void *)acontext ));
5025         }
5026 
5027         return best_heap;
5028     }
5029 
can_find_heap_fast()5030     static bool can_find_heap_fast()
5031     {
5032         return GCToOSInterface::CanGetCurrentProcessorNumber();
5033     }
5034 
find_proc_no_from_heap_no(int heap_number)5035     static uint8_t find_proc_no_from_heap_no(int heap_number)
5036     {
5037         return heap_no_to_proc_no[heap_number];
5038     }
5039 
set_proc_no_for_heap(int heap_number,uint8_t proc_no)5040     static void set_proc_no_for_heap(int heap_number, uint8_t proc_no)
5041     {
5042         heap_no_to_proc_no[heap_number] = proc_no;
5043     }
5044 
find_numa_node_from_heap_no(int heap_number)5045     static uint8_t find_numa_node_from_heap_no(int heap_number)
5046     {
5047         return heap_no_to_numa_node[heap_number];
5048     }
5049 
set_numa_node_for_heap(int heap_number,uint8_t numa_node)5050     static void set_numa_node_for_heap(int heap_number, uint8_t numa_node)
5051     {
5052         heap_no_to_numa_node[heap_number] = numa_node;
5053     }
5054 
find_cpu_group_from_heap_no(int heap_number)5055     static uint8_t find_cpu_group_from_heap_no(int heap_number)
5056     {
5057         return heap_no_to_cpu_group[heap_number];
5058     }
5059 
set_cpu_group_for_heap(int heap_number,uint8_t group_number)5060     static void set_cpu_group_for_heap(int heap_number, uint8_t group_number)
5061     {
5062         heap_no_to_cpu_group[heap_number] = group_number;
5063     }
5064 
find_group_proc_from_heap_no(int heap_number)5065     static uint8_t find_group_proc_from_heap_no(int heap_number)
5066     {
5067         return heap_no_to_group_proc[heap_number];
5068     }
5069 
set_group_proc_for_heap(int heap_number,uint8_t group_proc)5070     static void set_group_proc_for_heap(int heap_number, uint8_t group_proc)
5071     {
5072         heap_no_to_group_proc[heap_number] = group_proc;
5073     }
5074 
init_numa_node_to_heap_map(int nheaps)5075     static void init_numa_node_to_heap_map(int nheaps)
5076     {   // called right after GCHeap::Init() for each heap is finished
5077         // when numa is not enabled, heap_no_to_numa_node[] are all filled
5078         // with 0s during initialization, and will be treated as one node
5079         numa_node_to_heap_map[0] = 0;
5080         int node_index = 1;
5081 
5082         for (int i=1; i < nheaps; i++)
5083         {
5084             if (heap_no_to_numa_node[i] != heap_no_to_numa_node[i-1])
5085                 numa_node_to_heap_map[node_index++] = (uint8_t)i;
5086         }
5087         numa_node_to_heap_map[node_index] = (uint8_t)nheaps; //mark the end with nheaps
5088     }
5089 
get_heap_range_for_heap(int hn,int * start,int * end)5090     static void get_heap_range_for_heap(int hn, int* start, int* end)
5091     {   // 1-tier/no numa case: heap_no_to_numa_node[] all zeros,
5092         // and treated as in one node. thus: start=0, end=n_heaps
5093         uint8_t numa_node = heap_no_to_numa_node[hn];
5094         *start = (int)numa_node_to_heap_map[numa_node];
5095         *end   = (int)(numa_node_to_heap_map[numa_node+1]);
5096     }
5097 };
5098 uint8_t* heap_select::sniff_buffer;
5099 unsigned heap_select::n_sniff_buffers;
5100 unsigned heap_select::cur_sniff_index;
5101 uint8_t heap_select::proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5102 uint8_t heap_select::heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5103 uint8_t heap_select::heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5104 uint8_t heap_select::heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5105 uint8_t heap_select::heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5106 uint8_t heap_select::numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5107 
create_thread_support(unsigned number_of_heaps)5108 BOOL gc_heap::create_thread_support (unsigned number_of_heaps)
5109 {
5110     BOOL ret = FALSE;
5111     if (!gc_start_event.CreateOSManualEventNoThrow (FALSE))
5112     {
5113         goto cleanup;
5114     }
5115     if (!ee_suspend_event.CreateOSAutoEventNoThrow (FALSE))
5116     {
5117         goto cleanup;
5118     }
5119     if (!gc_t_join.init (number_of_heaps, join_flavor_server_gc))
5120     {
5121         goto cleanup;
5122     }
5123 
5124     ret = TRUE;
5125 
5126 cleanup:
5127 
5128     if (!ret)
5129     {
5130         destroy_thread_support();
5131     }
5132 
5133     return ret;
5134 }
5135 
destroy_thread_support()5136 void gc_heap::destroy_thread_support ()
5137 {
5138     if (ee_suspend_event.IsValid())
5139     {
5140         ee_suspend_event.CloseEvent();
5141     }
5142     if (gc_start_event.IsValid())
5143     {
5144         gc_start_event.CloseEvent();
5145     }
5146 }
5147 
5148 #if !defined(FEATURE_PAL)
set_thread_group_affinity_for_heap(int heap_number,GCThreadAffinity * affinity)5149 void set_thread_group_affinity_for_heap(int heap_number, GCThreadAffinity* affinity)
5150 {
5151     affinity->Group = GCThreadAffinity::None;
5152     affinity->Processor = GCThreadAffinity::None;
5153 
5154     uint16_t gn, gpn;
5155     CPUGroupInfo::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
5156 
5157     int bit_number = 0;
5158     for (uintptr_t mask = 1; mask !=0; mask <<=1)
5159     {
5160         if (bit_number == gpn)
5161         {
5162             dprintf(3, ("using processor group %d, mask %Ix for heap %d\n", gn, mask, heap_number));
5163             affinity->Processor = gpn;
5164             affinity->Group = gn;
5165             heap_select::set_cpu_group_for_heap(heap_number, (uint8_t)gn);
5166             heap_select::set_group_proc_for_heap(heap_number, (uint8_t)gpn);
5167             if (NumaNodeInfo::CanEnableGCNumaAware())
5168             {
5169                 PROCESSOR_NUMBER proc_no;
5170                 proc_no.Group    = gn;
5171                 proc_no.Number   = (uint8_t)gpn;
5172                 proc_no.Reserved = 0;
5173 
5174                 uint16_t node_no = 0;
5175                 if (NumaNodeInfo::GetNumaProcessorNodeEx(&proc_no, &node_no))
5176                     heap_select::set_numa_node_for_heap(heap_number, (uint8_t)node_no);
5177             }
5178             else
5179             {   // no numa setting, each cpu group is treated as a node
5180                 heap_select::set_numa_node_for_heap(heap_number, (uint8_t)gn);
5181             }
5182             return;
5183         }
5184         bit_number++;
5185     }
5186 }
5187 
set_thread_affinity_mask_for_heap(int heap_number,GCThreadAffinity * affinity)5188 void set_thread_affinity_mask_for_heap(int heap_number, GCThreadAffinity* affinity)
5189 {
5190     affinity->Group = GCThreadAffinity::None;
5191     affinity->Processor = GCThreadAffinity::None;
5192 
5193     uintptr_t pmask, smask;
5194     if (GCToOSInterface::GetCurrentProcessAffinityMask(&pmask, &smask))
5195     {
5196         pmask &= smask;
5197         int bit_number = 0;
5198         uint8_t proc_number = 0;
5199         for (uintptr_t mask = 1; mask != 0; mask <<= 1)
5200         {
5201             if ((mask & pmask) != 0)
5202             {
5203                 if (bit_number == heap_number)
5204                 {
5205                     dprintf (3, ("Using processor %d for heap %d", proc_number, heap_number));
5206                     affinity->Processor = proc_number;
5207                     heap_select::set_proc_no_for_heap(heap_number, proc_number);
5208                     if (NumaNodeInfo::CanEnableGCNumaAware())
5209                     {
5210                         uint16_t node_no = 0;
5211                         PROCESSOR_NUMBER proc_no;
5212                         proc_no.Group = 0;
5213                         proc_no.Number = (uint8_t)proc_number;
5214                         proc_no.Reserved = 0;
5215                         if (NumaNodeInfo::GetNumaProcessorNodeEx(&proc_no, &node_no))
5216                         {
5217                             heap_select::set_numa_node_for_heap(heap_number, (uint8_t)node_no);
5218                         }
5219                     }
5220                     return;
5221                 }
5222                 bit_number++;
5223             }
5224             proc_number++;
5225         }
5226     }
5227 }
5228 #endif // !FEATURE_PAL
5229 
create_gc_thread()5230 bool gc_heap::create_gc_thread ()
5231 {
5232     dprintf (3, ("Creating gc thread\n"));
5233 
5234     GCThreadAffinity affinity;
5235     affinity.Group = GCThreadAffinity::None;
5236     affinity.Processor = GCThreadAffinity::None;
5237 
5238 #if !defined(FEATURE_PAL)
5239     if (!gc_thread_no_affinitize_p)
5240     {
5241         //We are about to set affinity for GC threads, it is a good place to setup NUMA and
5242         //CPU groups, because the process mask, processor number, group number are all
5243         //readyly available.
5244         if (CPUGroupInfo::CanEnableGCCPUGroups())
5245             set_thread_group_affinity_for_heap(heap_number, &affinity);
5246         else
5247             set_thread_affinity_mask_for_heap(heap_number, &affinity);
5248     }
5249 #endif // !FEATURE_PAL
5250 
5251     return GCToOSInterface::CreateThread(gc_thread_stub, this, &affinity);
5252 }
5253 
5254 #ifdef _MSC_VER
5255 #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
5256 #endif //_MSC_VER
gc_thread_function()5257 void gc_heap::gc_thread_function ()
5258 {
5259     assert (gc_done_event.IsValid());
5260     assert (gc_start_event.IsValid());
5261     dprintf (3, ("gc thread started"));
5262 
5263     heap_select::init_cpu_mapping(this, heap_number);
5264 
5265     while (1)
5266     {
5267         assert (!gc_t_join.joined());
5268 
5269         if (heap_number == 0)
5270         {
5271             gc_heap::ee_suspend_event.Wait(INFINITE, FALSE);
5272 
5273             BEGIN_TIMING(suspend_ee_during_log);
5274             GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
5275             END_TIMING(suspend_ee_during_log);
5276 
5277             proceed_with_gc_p = TRUE;
5278 
5279             if (!should_proceed_with_gc())
5280             {
5281                 update_collection_counts_for_no_gc();
5282                 proceed_with_gc_p = FALSE;
5283             }
5284             else
5285                 settings.init_mechanisms();
5286             dprintf (3, ("%d gc thread waiting...", heap_number));
5287             gc_start_event.Set();
5288         }
5289         else
5290         {
5291             gc_start_event.Wait(INFINITE, FALSE);
5292             dprintf (3, ("%d gc thread waiting... Done", heap_number));
5293         }
5294 
5295         if (proceed_with_gc_p)
5296             garbage_collect (GCHeap::GcCondemnedGeneration);
5297 
5298         if (heap_number == 0)
5299         {
5300             if (proceed_with_gc_p && (!settings.concurrent))
5301             {
5302                 do_post_gc();
5303             }
5304 
5305 #ifdef BACKGROUND_GC
5306             recover_bgc_settings();
5307 #endif //BACKGROUND_GC
5308 
5309 #ifdef MULTIPLE_HEAPS
5310             for (int i = 0; i < gc_heap::n_heaps; i++)
5311             {
5312                 gc_heap* hp = gc_heap::g_heaps[i];
5313                 hp->add_saved_spinlock_info (me_release, mt_block_gc);
5314                 dprintf (SPINLOCK_LOG, ("[%d]GC Lmsl", i));
5315                 leave_spin_lock(&hp->more_space_lock);
5316             }
5317 #endif //MULTIPLE_HEAPS
5318 
5319             gc_heap::gc_started = FALSE;
5320 
5321             BEGIN_TIMING(restart_ee_during_log);
5322             GCToEEInterface::RestartEE(TRUE);
5323             END_TIMING(restart_ee_during_log);
5324             process_sync_log_stats();
5325 
5326             dprintf (SPINLOCK_LOG, ("GC Lgc"));
5327             leave_spin_lock (&gc_heap::gc_lock);
5328 
5329             gc_heap::internal_gc_done = true;
5330 
5331             set_gc_done();
5332         }
5333         else
5334         {
5335             int spin_count = 32 * (g_SystemInfo.dwNumberOfProcessors - 1);
5336 
5337             // wait until RestartEE has progressed to a stage where we can restart user threads
5338             while (!gc_heap::internal_gc_done && !GCHeap::SafeToRestartManagedThreads())
5339             {
5340                 spin_and_switch (spin_count, (gc_heap::internal_gc_done || GCHeap::SafeToRestartManagedThreads()));
5341             }
5342             set_gc_done();
5343         }
5344     }
5345 }
5346 #ifdef _MSC_VER
5347 #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
5348 #endif //_MSC_VER
5349 
5350 #endif //MULTIPLE_HEAPS
5351 
virtual_alloc_commit_for_heap(void * addr,size_t size,int h_number)5352 bool virtual_alloc_commit_for_heap(void* addr, size_t size, int h_number)
5353 {
5354 #if defined(MULTIPLE_HEAPS) && !defined(FEATURE_REDHAWK) && !defined(FEATURE_PAL)
5355     // Currently there is no way for us to specific the numa node to allocate on via hosting interfaces to
5356     // a host. This will need to be added later.
5357     if (!CLRMemoryHosted())
5358     {
5359         if (NumaNodeInfo::CanEnableGCNumaAware())
5360         {
5361             uint32_t numa_node = heap_select::find_numa_node_from_heap_no(h_number);
5362             void * ret = NumaNodeInfo::VirtualAllocExNuma(GetCurrentProcess(), addr, size,
5363                                                           MEM_COMMIT, PAGE_READWRITE, numa_node);
5364             if (ret != NULL)
5365                 return true;
5366         }
5367     }
5368 #else
5369     UNREFERENCED_PARAMETER(h_number);
5370 #endif
5371 
5372     //numa aware not enabled, or call failed --> fallback to VirtualCommit()
5373     return GCToOSInterface::VirtualCommit(addr, size);
5374 }
5375 
5376 #ifndef SEG_MAPPING_TABLE
5377 inline
segment_of(uint8_t * add,ptrdiff_t & delta,BOOL verify_p)5378 heap_segment* gc_heap::segment_of (uint8_t* add, ptrdiff_t& delta, BOOL verify_p)
5379 {
5380     uint8_t* sadd = add;
5381     heap_segment* hs = 0;
5382     heap_segment* hs1 = 0;
5383     if (!((add >= g_gc_lowest_address) && (add < g_gc_highest_address)))
5384     {
5385         delta = 0;
5386         return 0;
5387     }
5388     //repeat in case there is a concurrent insertion in the table.
5389     do
5390     {
5391         hs = hs1;
5392         sadd = add;
5393         seg_table->lookup (sadd);
5394         hs1 = (heap_segment*)sadd;
5395     } while (hs1 && !in_range_for_segment (add, hs1) && (hs != hs1));
5396 
5397     hs = hs1;
5398 
5399     if ((hs == 0) ||
5400         (verify_p && (add > heap_segment_reserved ((heap_segment*)(sadd + delta)))))
5401         delta = 0;
5402     return hs;
5403 }
5404 #endif //SEG_MAPPING_TABLE
5405 
5406 class mark
5407 {
5408 public:
5409     uint8_t* first;
5410     size_t len;
5411 
5412     // If we want to save space we can have a pool of plug_and_gap's instead of
5413     // always having 2 allocated for each pinned plug.
5414     gap_reloc_pair saved_pre_plug;
5415     // If we decide to not compact, we need to restore the original values.
5416     gap_reloc_pair saved_pre_plug_reloc;
5417 
5418     gap_reloc_pair saved_post_plug;
5419 
5420     // Supposedly Pinned objects cannot have references but we are seeing some from pinvoke
5421     // frames. Also if it's an artificially pinned plug created by us, it can certainly
5422     // have references.
5423     // We know these cases will be rare so we can optimize this to be only allocated on decommand.
5424     gap_reloc_pair saved_post_plug_reloc;
5425 
5426     // We need to calculate this after we are done with plan phase and before compact
5427     // phase because compact phase will change the bricks so relocate_address will no
5428     // longer work.
5429     uint8_t* saved_pre_plug_info_reloc_start;
5430 
5431     // We need to save this because we will have no way to calculate it, unlike the
5432     // pre plug info start which is right before this plug.
5433     uint8_t* saved_post_plug_info_start;
5434 
5435 #ifdef SHORT_PLUGS
5436     uint8_t* allocation_context_start_region;
5437 #endif //SHORT_PLUGS
5438 
5439     // How the bits in these bytes are organized:
5440     // MSB --> LSB
5441     // 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
5442     // 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.
5443     BOOL saved_pre_p;
5444     BOOL saved_post_p;
5445 
5446 #ifdef _DEBUG
5447     // We are seeing this is getting corrupted for a PP with a NP after.
5448     // Save it when we first set it and make sure it doesn't change.
5449     gap_reloc_pair saved_post_plug_debug;
5450 #endif //_DEBUG
5451 
get_max_short_bits()5452     size_t get_max_short_bits()
5453     {
5454         return (sizeof (gap_reloc_pair) / sizeof (uint8_t*));
5455     }
5456 
5457     // pre bits
get_pre_short_start_bit()5458     size_t get_pre_short_start_bit ()
5459     {
5460         return (sizeof (saved_pre_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5461     }
5462 
pre_short_p()5463     BOOL pre_short_p()
5464     {
5465         return (saved_pre_p & (1 << (sizeof (saved_pre_p) * 8 - 1)));
5466     }
5467 
set_pre_short()5468     void set_pre_short()
5469     {
5470         saved_pre_p |= (1 << (sizeof (saved_pre_p) * 8 - 1));
5471     }
5472 
set_pre_short_bit(size_t bit)5473     void set_pre_short_bit (size_t bit)
5474     {
5475         saved_pre_p |= 1 << (get_pre_short_start_bit() + bit);
5476     }
5477 
pre_short_bit_p(size_t bit)5478     BOOL pre_short_bit_p (size_t bit)
5479     {
5480         return (saved_pre_p & (1 << (get_pre_short_start_bit() + bit)));
5481     }
5482 
5483 #ifdef COLLECTIBLE_CLASS
set_pre_short_collectible()5484     void set_pre_short_collectible()
5485     {
5486         saved_pre_p |= 2;
5487     }
5488 
pre_short_collectible_p()5489     BOOL pre_short_collectible_p()
5490     {
5491         return (saved_pre_p & 2);
5492     }
5493 #endif //COLLECTIBLE_CLASS
5494 
5495     // post bits
get_post_short_start_bit()5496     size_t get_post_short_start_bit ()
5497     {
5498         return (sizeof (saved_post_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5499     }
5500 
post_short_p()5501     BOOL post_short_p()
5502     {
5503         return (saved_post_p & (1 << (sizeof (saved_post_p) * 8 - 1)));
5504     }
5505 
set_post_short()5506     void set_post_short()
5507     {
5508         saved_post_p |= (1 << (sizeof (saved_post_p) * 8 - 1));
5509     }
5510 
set_post_short_bit(size_t bit)5511     void set_post_short_bit (size_t bit)
5512     {
5513         saved_post_p |= 1 << (get_post_short_start_bit() + bit);
5514     }
5515 
post_short_bit_p(size_t bit)5516     BOOL post_short_bit_p (size_t bit)
5517     {
5518         return (saved_post_p & (1 << (get_post_short_start_bit() + bit)));
5519     }
5520 
5521 #ifdef COLLECTIBLE_CLASS
set_post_short_collectible()5522     void set_post_short_collectible()
5523     {
5524         saved_post_p |= 2;
5525     }
5526 
post_short_collectible_p()5527     BOOL post_short_collectible_p()
5528     {
5529         return (saved_post_p & 2);
5530     }
5531 #endif //COLLECTIBLE_CLASS
5532 
get_plug_address()5533     uint8_t* get_plug_address() { return first; }
5534 
has_pre_plug_info()5535     BOOL has_pre_plug_info() { return saved_pre_p; }
has_post_plug_info()5536     BOOL has_post_plug_info() { return saved_post_p; }
5537 
get_pre_plug_reloc_info()5538     gap_reloc_pair* get_pre_plug_reloc_info() { return &saved_pre_plug_reloc; }
get_post_plug_reloc_info()5539     gap_reloc_pair* get_post_plug_reloc_info() { return &saved_post_plug_reloc; }
set_pre_plug_info_reloc_start(uint8_t * reloc)5540     void set_pre_plug_info_reloc_start (uint8_t* reloc) { saved_pre_plug_info_reloc_start = reloc; }
get_post_plug_info_start()5541     uint8_t* get_post_plug_info_start() { return saved_post_plug_info_start; }
5542 
5543     // We need to temporarily recover the shortened plugs for compact phase so we can
5544     // copy over the whole plug and their related info (mark bits/cards). But we will
5545     // need to set the artificial gap back so compact phase can keep reading the plug info.
5546     // We also need to recover the saved info because we'll need to recover it later.
5547     //
5548     // So we would call swap_p*_plug_and_saved once to recover the object info; then call
5549     // it again to recover the artificial gap.
swap_pre_plug_and_saved()5550     void swap_pre_plug_and_saved()
5551     {
5552         gap_reloc_pair temp;
5553         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5554         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5555         saved_pre_plug_reloc = temp;
5556     }
5557 
swap_post_plug_and_saved()5558     void swap_post_plug_and_saved()
5559     {
5560         gap_reloc_pair temp;
5561         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5562         memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5563         saved_post_plug_reloc = temp;
5564     }
5565 
swap_pre_plug_and_saved_for_profiler()5566     void swap_pre_plug_and_saved_for_profiler()
5567     {
5568         gap_reloc_pair temp;
5569         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5570         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5571         saved_pre_plug = temp;
5572     }
5573 
swap_post_plug_and_saved_for_profiler()5574     void swap_post_plug_and_saved_for_profiler()
5575     {
5576         gap_reloc_pair temp;
5577         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5578         memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5579         saved_post_plug = temp;
5580     }
5581 
5582     // We should think about whether it's really necessary to have to copy back the pre plug
5583     // info since it was already copied during compacting plugs. But if a plug doesn't move
5584     // by < 3 ptr size, it means we'd have to recover pre plug info.
recover_plug_info()5585     void recover_plug_info()
5586     {
5587         if (saved_pre_p)
5588         {
5589             if (gc_heap::settings.compaction)
5590             {
5591                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix",
5592                     first,
5593                     &saved_pre_plug_reloc,
5594                     saved_pre_plug_info_reloc_start));
5595                 memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5596             }
5597             else
5598             {
5599                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix",
5600                     first,
5601                     &saved_pre_plug,
5602                     (first - sizeof (plug_and_gap))));
5603                 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5604             }
5605         }
5606 
5607         if (saved_post_p)
5608         {
5609             if (gc_heap::settings.compaction)
5610             {
5611                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix",
5612                     first,
5613                     &saved_post_plug_reloc,
5614                     saved_post_plug_info_start));
5615                 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5616             }
5617             else
5618             {
5619                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix",
5620                     first,
5621                     &saved_post_plug,
5622                     saved_post_plug_info_start));
5623                 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5624             }
5625         }
5626     }
5627 };
5628 
5629 
init_mechanisms()5630 void gc_mechanisms::init_mechanisms()
5631 {
5632     condemned_generation = 0;
5633     promotion = FALSE;//TRUE;
5634     compaction = TRUE;
5635 #ifdef FEATURE_LOH_COMPACTION
5636     loh_compaction = gc_heap::should_compact_loh();
5637 #else
5638     loh_compaction = FALSE;
5639 #endif //FEATURE_LOH_COMPACTION
5640     heap_expansion = FALSE;
5641     concurrent = FALSE;
5642     demotion = FALSE;
5643     elevation_reduced = FALSE;
5644     found_finalizers = FALSE;
5645 #ifdef BACKGROUND_GC
5646     background_p = recursive_gc_sync::background_running_p() != FALSE;
5647     allocations_allowed = TRUE;
5648 #endif //BACKGROUND_GC
5649 
5650 #ifdef BIT64
5651     entry_memory_load = 0;
5652 #endif // BIT64
5653 
5654 #ifdef STRESS_HEAP
5655     stress_induced = FALSE;
5656 #endif // STRESS_HEAP
5657 }
5658 
first_init()5659 void gc_mechanisms::first_init()
5660 {
5661     gc_index = 0;
5662     gen0_reduction_count = 0;
5663     should_lock_elevation = FALSE;
5664     elevation_locked_count = 0;
5665     reason = reason_empty;
5666 #ifdef BACKGROUND_GC
5667     pause_mode = gc_heap::gc_can_use_concurrent ? pause_interactive : pause_batch;
5668 #ifdef _DEBUG
5669     int debug_pause_mode = g_pConfig->GetGCLatencyMode();
5670     if (debug_pause_mode >= 0)
5671     {
5672         assert (debug_pause_mode <= pause_sustained_low_latency);
5673         pause_mode = (gc_pause_mode)debug_pause_mode;
5674     }
5675 #endif //_DEBUG
5676 #else //BACKGROUND_GC
5677     pause_mode = pause_batch;
5678 #endif //BACKGROUND_GC
5679 
5680     init_mechanisms();
5681 }
5682 
record(gc_history_global * history)5683 void gc_mechanisms::record (gc_history_global* history)
5684 {
5685 #ifdef MULTIPLE_HEAPS
5686     history->num_heaps = gc_heap::n_heaps;
5687 #else
5688     history->num_heaps = 1;
5689 #endif //MULTIPLE_HEAPS
5690 
5691     history->condemned_generation = condemned_generation;
5692     history->gen0_reduction_count = gen0_reduction_count;
5693     history->reason = reason;
5694     history->pause_mode = (int)pause_mode;
5695     history->mem_pressure = entry_memory_load;
5696     history->global_mechanims_p = 0;
5697 
5698     // start setting the boolean values.
5699     if (concurrent)
5700         history->set_mechanism_p (global_concurrent);
5701 
5702     if (compaction)
5703         history->set_mechanism_p (global_compaction);
5704 
5705     if (promotion)
5706         history->set_mechanism_p (global_promotion);
5707 
5708     if (demotion)
5709         history->set_mechanism_p (global_demotion);
5710 
5711     if (card_bundles)
5712         history->set_mechanism_p (global_card_bundles);
5713 
5714     if (elevation_reduced)
5715         history->set_mechanism_p (global_elevation);
5716 }
5717 
5718 /**********************************
5719    called at the beginning of GC to fix the allocated size to
5720    what is really allocated, or to turn the free area into an unused object
5721    It needs to be called after all of the other allocation contexts have been
5722    fixed since it relies on alloc_allocated.
5723  ********************************/
5724 
5725 //for_gc_p indicates that the work is being done for GC,
5726 //as opposed to concurrent heap verification
fix_youngest_allocation_area(BOOL for_gc_p)5727 void gc_heap::fix_youngest_allocation_area (BOOL for_gc_p)
5728 {
5729     assert (alloc_allocated);
5730     alloc_context* acontext = generation_alloc_context (youngest_generation);
5731     dprintf (3, ("generation 0 alloc context: ptr: %Ix, limit %Ix",
5732                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5733     fix_allocation_context (acontext, for_gc_p, get_alignment_constant (TRUE));
5734     if (for_gc_p)
5735     {
5736         acontext->alloc_ptr = alloc_allocated;
5737         acontext->alloc_limit = acontext->alloc_ptr;
5738     }
5739     heap_segment_allocated (ephemeral_heap_segment) =
5740         alloc_allocated;
5741 }
5742 
fix_large_allocation_area(BOOL for_gc_p)5743 void gc_heap::fix_large_allocation_area (BOOL for_gc_p)
5744 {
5745     UNREFERENCED_PARAMETER(for_gc_p);
5746 
5747 #ifdef _DEBUG
5748     alloc_context* acontext =
5749 #endif // _DEBUG
5750         generation_alloc_context (large_object_generation);
5751     assert (acontext->alloc_ptr == 0);
5752     assert (acontext->alloc_limit == 0);
5753 #if 0
5754     dprintf (3, ("Large object alloc context: ptr: %Ix, limit %Ix",
5755                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5756     fix_allocation_context (acontext, FALSE, get_alignment_constant (FALSE));
5757     if (for_gc_p)
5758     {
5759         acontext->alloc_ptr = 0;
5760         acontext->alloc_limit = acontext->alloc_ptr;
5761     }
5762 #endif //0
5763 }
5764 
5765 //for_gc_p indicates that the work is being done for GC,
5766 //as opposed to concurrent heap verification
fix_allocation_context(alloc_context * acontext,BOOL for_gc_p,int align_const)5767 void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
5768                                       int align_const)
5769 {
5770     dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
5771                  (size_t)acontext,
5772                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5773 
5774     if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) ||
5775         !for_gc_p)
5776     {
5777         uint8_t*  point = acontext->alloc_ptr;
5778         if (point != 0)
5779         {
5780             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
5781             // the allocation area was from the free list
5782             // it was shortened by Align (min_obj_size) to make room for
5783             // at least the shortest unused object
5784             size += Align (min_obj_size, align_const);
5785             assert ((size >= Align (min_obj_size)));
5786 
5787             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point,
5788                        (size_t)point + size ));
5789             make_unused_array (point, size);
5790 
5791             if (for_gc_p)
5792             {
5793                 generation_free_obj_space (generation_of (0)) += size;
5794                 alloc_contexts_used ++;
5795             }
5796         }
5797     }
5798     else if (for_gc_p)
5799     {
5800         alloc_allocated = acontext->alloc_ptr;
5801         assert (heap_segment_allocated (ephemeral_heap_segment) <=
5802                 heap_segment_committed (ephemeral_heap_segment));
5803         alloc_contexts_used ++;
5804     }
5805 
5806 
5807     if (for_gc_p)
5808     {
5809         acontext->alloc_ptr = 0;
5810         acontext->alloc_limit = acontext->alloc_ptr;
5811     }
5812 }
5813 
5814 //used by the heap verification for concurrent gc.
5815 //it nulls out the words set by fix_allocation_context for heap_verification
repair_allocation(gc_alloc_context * acontext,void *)5816 void repair_allocation (gc_alloc_context* acontext, void*)
5817 {
5818     uint8_t*  point = acontext->alloc_ptr;
5819 
5820     if (point != 0)
5821     {
5822         dprintf (3, ("Clearing [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5823                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
5824         memclr (acontext->alloc_ptr - plug_skew,
5825                 (acontext->alloc_limit - acontext->alloc_ptr)+Align (min_obj_size));
5826     }
5827 }
5828 
void_allocation(gc_alloc_context * acontext,void *)5829 void void_allocation (gc_alloc_context* acontext, void*)
5830 {
5831     uint8_t*  point = acontext->alloc_ptr;
5832 
5833     if (point != 0)
5834     {
5835         dprintf (3, ("Void [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5836                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
5837         acontext->alloc_ptr = 0;
5838         acontext->alloc_limit = acontext->alloc_ptr;
5839     }
5840 }
5841 
repair_allocation_contexts(BOOL repair_p)5842 void gc_heap::repair_allocation_contexts (BOOL repair_p)
5843 {
5844     GCToEEInterface::GcEnumAllocContexts (repair_p ? repair_allocation : void_allocation, NULL);
5845 
5846     alloc_context* acontext = generation_alloc_context (youngest_generation);
5847     if (repair_p)
5848         repair_allocation (acontext, NULL);
5849     else
5850         void_allocation (acontext, NULL);
5851 }
5852 
5853 struct fix_alloc_context_args
5854 {
5855     BOOL for_gc_p;
5856     void* heap;
5857 };
5858 
fix_alloc_context(gc_alloc_context * acontext,void * param)5859 void fix_alloc_context(gc_alloc_context* acontext, void* param)
5860 {
5861     fix_alloc_context_args* args = (fix_alloc_context_args*)param;
5862     g_theGCHeap->FixAllocContext(acontext, FALSE, (void*)(size_t)(args->for_gc_p), args->heap);
5863 }
5864 
fix_allocation_contexts(BOOL for_gc_p)5865 void gc_heap::fix_allocation_contexts(BOOL for_gc_p)
5866 {
5867     fix_alloc_context_args args;
5868     args.for_gc_p = for_gc_p;
5869     args.heap = __this;
5870     GCToEEInterface::GcEnumAllocContexts(fix_alloc_context, &args);
5871 
5872     fix_youngest_allocation_area(for_gc_p);
5873     fix_large_allocation_area(for_gc_p);
5874 }
5875 
fix_older_allocation_area(generation * older_gen)5876 void gc_heap::fix_older_allocation_area (generation* older_gen)
5877 {
5878     heap_segment* older_gen_seg = generation_allocation_segment (older_gen);
5879     if (generation_allocation_limit (older_gen) !=
5880         heap_segment_plan_allocated (older_gen_seg))
5881     {
5882         uint8_t*  point = generation_allocation_pointer (older_gen);
5883 
5884         size_t  size = (generation_allocation_limit (older_gen) -
5885                                generation_allocation_pointer (older_gen));
5886         if (size != 0)
5887         {
5888             assert ((size >= Align (min_obj_size)));
5889             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, (size_t)point+size));
5890             make_unused_array (point, size);
5891         }
5892     }
5893     else
5894     {
5895         assert (older_gen_seg != ephemeral_heap_segment);
5896         heap_segment_plan_allocated (older_gen_seg) =
5897             generation_allocation_pointer (older_gen);
5898         generation_allocation_limit (older_gen) =
5899             generation_allocation_pointer (older_gen);
5900     }
5901 }
5902 
set_allocation_heap_segment(generation * gen)5903 void gc_heap::set_allocation_heap_segment (generation* gen)
5904 {
5905     uint8_t* p = generation_allocation_start (gen);
5906     assert (p);
5907     heap_segment* seg = generation_allocation_segment (gen);
5908     if (in_range_for_segment (p, seg))
5909         return;
5910 
5911     // try ephemeral heap segment in case of heap expansion
5912     seg = ephemeral_heap_segment;
5913     if (!in_range_for_segment (p, seg))
5914     {
5915         seg = heap_segment_rw (generation_start_segment (gen));
5916 
5917         PREFIX_ASSUME(seg != NULL);
5918 
5919         while (!in_range_for_segment (p, seg))
5920         {
5921             seg = heap_segment_next_rw (seg);
5922             PREFIX_ASSUME(seg != NULL);
5923         }
5924     }
5925 
5926     generation_allocation_segment (gen) = seg;
5927 }
5928 
reset_allocation_pointers(generation * gen,uint8_t * start)5929 void gc_heap::reset_allocation_pointers (generation* gen, uint8_t* start)
5930 {
5931     assert (start);
5932     assert (Align ((size_t)start) == (size_t)start);
5933     generation_allocation_start (gen) = start;
5934     generation_allocation_pointer (gen) =  0;//start + Align (min_obj_size);
5935     generation_allocation_limit (gen) = 0;//generation_allocation_pointer (gen);
5936     set_allocation_heap_segment (gen);
5937 }
5938 
5939 #ifdef BACKGROUND_GC
5940 //TODO BACKGROUND_GC this is for test only
5941 void
disallow_new_allocation(int gen_number)5942 gc_heap::disallow_new_allocation (int gen_number)
5943 {
5944     UNREFERENCED_PARAMETER(gen_number);
5945     settings.allocations_allowed = FALSE;
5946 }
5947 void
allow_new_allocation(int gen_number)5948 gc_heap::allow_new_allocation (int gen_number)
5949 {
5950     UNREFERENCED_PARAMETER(gen_number);
5951     settings.allocations_allowed = TRUE;
5952 }
5953 
5954 #endif //BACKGROUND_GC
5955 
new_allocation_allowed(int gen_number)5956 bool gc_heap::new_allocation_allowed (int gen_number)
5957 {
5958 #ifdef BACKGROUND_GC
5959     //TODO BACKGROUND_GC this is for test only
5960     if (!settings.allocations_allowed)
5961     {
5962         dprintf (2, ("new allocation not allowed"));
5963         return FALSE;
5964     }
5965 #endif //BACKGROUND_GC
5966 
5967     if (dd_new_allocation (dynamic_data_of (gen_number)) < 0)
5968     {
5969         if (gen_number != 0)
5970         {
5971             // For LOH we will give it more budget before we try a GC.
5972             if (settings.concurrent)
5973             {
5974                 dynamic_data* dd2 = dynamic_data_of (max_generation + 1 );
5975 
5976                 if (dd_new_allocation (dd2) <= (ptrdiff_t)(-2 * dd_desired_allocation (dd2)))
5977                 {
5978                     return TRUE;
5979                 }
5980             }
5981         }
5982         return FALSE;
5983     }
5984 #ifndef MULTIPLE_HEAPS
5985     else if ((settings.pause_mode != pause_no_gc) && (gen_number == 0))
5986     {
5987         dprintf (3, ("evaluating allocation rate"));
5988         dynamic_data* dd0 = dynamic_data_of (0);
5989         if ((allocation_running_amount - dd_new_allocation (dd0)) >
5990             dd_min_gc_size (dd0))
5991         {
5992             uint32_t ctime = GCToOSInterface::GetLowPrecisionTimeStamp();
5993             if ((ctime - allocation_running_time) > 1000)
5994             {
5995                 dprintf (2, (">1s since last gen0 gc"));
5996                 return FALSE;
5997             }
5998             else
5999             {
6000                 allocation_running_amount = dd_new_allocation (dd0);
6001             }
6002         }
6003     }
6004 #endif //MULTIPLE_HEAPS
6005     return TRUE;
6006 }
6007 
6008 inline
get_desired_allocation(int gen_number)6009 ptrdiff_t gc_heap::get_desired_allocation (int gen_number)
6010 {
6011     return dd_desired_allocation (dynamic_data_of (gen_number));
6012 }
6013 
6014 inline
get_new_allocation(int gen_number)6015 ptrdiff_t  gc_heap::get_new_allocation (int gen_number)
6016 {
6017     return dd_new_allocation (dynamic_data_of (gen_number));
6018 }
6019 
6020 //return the amount allocated so far in gen_number
6021 inline
get_allocation(int gen_number)6022 ptrdiff_t  gc_heap::get_allocation (int gen_number)
6023 {
6024     dynamic_data* dd = dynamic_data_of (gen_number);
6025 
6026     return dd_desired_allocation (dd) - dd_new_allocation (dd);
6027 }
6028 
6029 inline
grow_mark_stack(mark * & m,size_t & len,size_t init_len)6030 BOOL grow_mark_stack (mark*& m, size_t& len, size_t init_len)
6031 {
6032     size_t new_size = max (init_len, 2*len);
6033     mark* tmp = new (nothrow) mark [new_size];
6034     if (tmp)
6035     {
6036         memcpy (tmp, m, len * sizeof (mark));
6037         delete m;
6038         m = tmp;
6039         len = new_size;
6040         return TRUE;
6041     }
6042     else
6043     {
6044         dprintf (1, ("Failed to allocate %Id bytes for mark stack", (len * sizeof (mark))));
6045         return FALSE;
6046     }
6047 }
6048 
6049 inline
pinned_plug(mark * m)6050 uint8_t* pinned_plug (mark* m)
6051 {
6052    return m->first;
6053 }
6054 
6055 inline
pinned_len(mark * m)6056 size_t& pinned_len (mark* m)
6057 {
6058     return m->len;
6059 }
6060 
6061 inline
set_new_pin_info(mark * m,uint8_t * pin_free_space_start)6062 void set_new_pin_info (mark* m, uint8_t* pin_free_space_start)
6063 {
6064     m->len = pinned_plug (m) - pin_free_space_start;
6065 #ifdef SHORT_PLUGS
6066     m->allocation_context_start_region = pin_free_space_start;
6067 #endif //SHORT_PLUGS
6068 }
6069 
6070 #ifdef SHORT_PLUGS
6071 inline
pin_allocation_context_start_region(mark * m)6072 uint8_t*& pin_allocation_context_start_region (mark* m)
6073 {
6074     return m->allocation_context_start_region;
6075 }
6076 
get_plug_start_in_saved(uint8_t * old_loc,mark * pinned_plug_entry)6077 uint8_t* get_plug_start_in_saved (uint8_t* old_loc, mark* pinned_plug_entry)
6078 {
6079     uint8_t* saved_pre_plug_info = (uint8_t*)(pinned_plug_entry->get_pre_plug_reloc_info());
6080     uint8_t* plug_start_in_saved = saved_pre_plug_info + (old_loc - (pinned_plug (pinned_plug_entry) - sizeof (plug_and_gap)));
6081     //dprintf (1, ("detected a very short plug: %Ix before PP %Ix, pad %Ix",
6082     //    old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6083     dprintf (1, ("EP: %Ix(%Ix), %Ix", old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6084     return plug_start_in_saved;
6085 }
6086 
6087 inline
set_padding_in_expand(uint8_t * old_loc,BOOL set_padding_on_saved_p,mark * pinned_plug_entry)6088 void set_padding_in_expand (uint8_t* old_loc,
6089                             BOOL set_padding_on_saved_p,
6090                             mark* pinned_plug_entry)
6091 {
6092     if (set_padding_on_saved_p)
6093     {
6094         set_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6095     }
6096     else
6097     {
6098         set_plug_padded (old_loc);
6099     }
6100 }
6101 
6102 inline
clear_padding_in_expand(uint8_t * old_loc,BOOL set_padding_on_saved_p,mark * pinned_plug_entry)6103 void clear_padding_in_expand (uint8_t* old_loc,
6104                               BOOL set_padding_on_saved_p,
6105                               mark* pinned_plug_entry)
6106 {
6107     if (set_padding_on_saved_p)
6108     {
6109         clear_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6110     }
6111     else
6112     {
6113         clear_plug_padded (old_loc);
6114     }
6115 }
6116 #endif //SHORT_PLUGS
6117 
reset_pinned_queue()6118 void gc_heap::reset_pinned_queue()
6119 {
6120     mark_stack_tos = 0;
6121     mark_stack_bos = 0;
6122 }
6123 
reset_pinned_queue_bos()6124 void gc_heap::reset_pinned_queue_bos()
6125 {
6126     mark_stack_bos = 0;
6127 }
6128 
6129 // last_pinned_plug is only for asserting purpose.
merge_with_last_pinned_plug(uint8_t * last_pinned_plug,size_t plug_size)6130 void gc_heap::merge_with_last_pinned_plug (uint8_t* last_pinned_plug, size_t plug_size)
6131 {
6132     if (last_pinned_plug)
6133     {
6134         mark& last_m = mark_stack_array[mark_stack_tos - 1];
6135         assert (last_pinned_plug == last_m.first);
6136         if (last_m.saved_post_p)
6137         {
6138             last_m.saved_post_p = FALSE;
6139             dprintf (3, ("setting last plug %Ix post to false", last_m.first));
6140             // We need to recover what the gap has overwritten.
6141             memcpy ((last_m.first + last_m.len - sizeof (plug_and_gap)), &(last_m.saved_post_plug), sizeof (gap_reloc_pair));
6142         }
6143         last_m.len += plug_size;
6144         dprintf (3, ("recovered the last part of plug %Ix, setting its plug size to %Ix", last_m.first, last_m.len));
6145     }
6146 }
6147 
set_allocator_next_pin(uint8_t * alloc_pointer,uint8_t * & alloc_limit)6148 void gc_heap::set_allocator_next_pin (uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6149 {
6150     dprintf (3, ("sanp: ptr: %Ix, limit: %Ix", alloc_pointer, alloc_limit));
6151     dprintf (3, ("oldest %Id: %Ix", mark_stack_bos, pinned_plug (oldest_pin())));
6152     if (!(pinned_plug_que_empty_p()))
6153     {
6154         mark*  oldest_entry = oldest_pin();
6155         uint8_t* plug = pinned_plug (oldest_entry);
6156         if ((plug >= alloc_pointer) && (plug < alloc_limit))
6157         {
6158             alloc_limit = pinned_plug (oldest_entry);
6159             dprintf (3, ("now setting alloc context: %Ix->%Ix(%Id)",
6160                 alloc_pointer, alloc_limit, (alloc_limit - alloc_pointer)));
6161         }
6162     }
6163 }
6164 
set_allocator_next_pin(generation * gen)6165 void gc_heap::set_allocator_next_pin (generation* gen)
6166 {
6167     dprintf (3, ("SANP: gen%d, ptr; %Ix, limit: %Ix", gen->gen_num, generation_allocation_pointer (gen), generation_allocation_limit (gen)));
6168     if (!(pinned_plug_que_empty_p()))
6169     {
6170         mark*  oldest_entry = oldest_pin();
6171         uint8_t* plug = pinned_plug (oldest_entry);
6172         if ((plug >= generation_allocation_pointer (gen)) &&
6173             (plug <  generation_allocation_limit (gen)))
6174         {
6175             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
6176             dprintf (3, ("SANP: get next pin free space in gen%d for alloc: %Ix->%Ix(%Id)",
6177                 gen->gen_num,
6178                 generation_allocation_pointer (gen), generation_allocation_limit (gen),
6179                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
6180         }
6181         else
6182             assert (!((plug < generation_allocation_pointer (gen)) &&
6183                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
6184     }
6185 }
6186 
6187 // After we set the info, we increase tos.
set_pinned_info(uint8_t * last_pinned_plug,size_t plug_len,uint8_t * alloc_pointer,uint8_t * & alloc_limit)6188 void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6189 {
6190     UNREFERENCED_PARAMETER(last_pinned_plug);
6191 
6192     mark& m = mark_stack_array[mark_stack_tos];
6193     assert (m.first == last_pinned_plug);
6194 
6195     m.len = plug_len;
6196     mark_stack_tos++;
6197     set_allocator_next_pin (alloc_pointer, alloc_limit);
6198 }
6199 
6200 // After we set the info, we increase tos.
set_pinned_info(uint8_t * last_pinned_plug,size_t plug_len,generation * gen)6201 void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, generation* gen)
6202 {
6203     UNREFERENCED_PARAMETER(last_pinned_plug);
6204 
6205     mark& m = mark_stack_array[mark_stack_tos];
6206     assert (m.first == last_pinned_plug);
6207 
6208     m.len = plug_len;
6209     mark_stack_tos++;
6210     assert (gen != 0);
6211     // Why are we checking here? gen is never 0.
6212     if (gen != 0)
6213     {
6214         set_allocator_next_pin (gen);
6215     }
6216 }
6217 
deque_pinned_plug()6218 size_t gc_heap::deque_pinned_plug ()
6219 {
6220     dprintf (3, ("dequed: %Id", mark_stack_bos));
6221     size_t m = mark_stack_bos;
6222     mark_stack_bos++;
6223     return m;
6224 }
6225 
6226 inline
pinned_plug_of(size_t bos)6227 mark* gc_heap::pinned_plug_of (size_t bos)
6228 {
6229     return &mark_stack_array [ bos ];
6230 }
6231 
6232 inline
oldest_pin()6233 mark* gc_heap::oldest_pin ()
6234 {
6235     return pinned_plug_of (mark_stack_bos);
6236 }
6237 
6238 inline
pinned_plug_que_empty_p()6239 BOOL gc_heap::pinned_plug_que_empty_p ()
6240 {
6241     return (mark_stack_bos == mark_stack_tos);
6242 }
6243 
6244 inline
before_oldest_pin()6245 mark* gc_heap::before_oldest_pin()
6246 {
6247     if (mark_stack_bos >= 1)
6248         return pinned_plug_of (mark_stack_bos-1);
6249     else
6250         return 0;
6251 }
6252 
6253 inline
ephemeral_pointer_p(uint8_t * o)6254 BOOL gc_heap::ephemeral_pointer_p (uint8_t* o)
6255 {
6256     return ((o >= ephemeral_low) && (o < ephemeral_high));
6257 }
6258 
6259 #ifdef MH_SC_MARK
6260 inline
mark_stack_busy()6261 int& gc_heap::mark_stack_busy()
6262 {
6263     return  g_mark_stack_busy [(heap_number+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
6264 }
6265 #endif //MH_SC_MARK
6266 
make_mark_stack(mark * arr)6267 void gc_heap::make_mark_stack (mark* arr)
6268 {
6269     reset_pinned_queue();
6270     mark_stack_array = arr;
6271     mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6272 #ifdef MH_SC_MARK
6273     mark_stack_busy() = 0;
6274 #endif //MH_SC_MARK
6275 }
6276 
6277 #ifdef BACKGROUND_GC
6278 inline
bpromoted_bytes(int thread)6279 size_t& gc_heap::bpromoted_bytes(int thread)
6280 {
6281 #ifdef MULTIPLE_HEAPS
6282     return g_bpromoted [thread*16];
6283 #else //MULTIPLE_HEAPS
6284     UNREFERENCED_PARAMETER(thread);
6285     return g_bpromoted;
6286 #endif //MULTIPLE_HEAPS
6287 }
6288 
make_background_mark_stack(uint8_t ** arr)6289 void gc_heap::make_background_mark_stack (uint8_t** arr)
6290 {
6291     background_mark_stack_array = arr;
6292     background_mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6293     background_mark_stack_tos = arr;
6294 }
6295 
make_c_mark_list(uint8_t ** arr)6296 void gc_heap::make_c_mark_list (uint8_t** arr)
6297 {
6298     c_mark_list = arr;
6299     c_mark_list_index = 0;
6300     c_mark_list_length = 1 + (page_size / MIN_OBJECT_SIZE);
6301 }
6302 #endif //BACKGROUND_GC
6303 
6304 #if defined (_TARGET_AMD64_)
6305 #define brick_size ((size_t)4096)
6306 #else
6307 #define brick_size ((size_t)2048)
6308 #endif //_TARGET_AMD64_
6309 
6310 inline
brick_of(uint8_t * add)6311 size_t gc_heap::brick_of (uint8_t* add)
6312 {
6313     return (size_t)(add - lowest_address) / brick_size;
6314 }
6315 
6316 inline
brick_address(size_t brick)6317 uint8_t* gc_heap::brick_address (size_t brick)
6318 {
6319     return lowest_address + (brick_size * brick);
6320 }
6321 
6322 
clear_brick_table(uint8_t * from,uint8_t * end)6323 void gc_heap::clear_brick_table (uint8_t* from, uint8_t* end)
6324 {
6325     for (size_t i = brick_of (from);i < brick_of (end); i++)
6326         brick_table[i] = 0;
6327 }
6328 
6329 //codes for the brick entries:
6330 //entry == 0 -> not assigned
6331 //entry >0 offset is entry-1
6332 //entry <0 jump back entry bricks
6333 
6334 
6335 inline
set_brick(size_t index,ptrdiff_t val)6336 void gc_heap::set_brick (size_t index, ptrdiff_t val)
6337 {
6338     if (val < -32767)
6339     {
6340         val = -32767;
6341     }
6342     assert (val < 32767);
6343     if (val >= 0)
6344         brick_table [index] = (short)val+1;
6345     else
6346         brick_table [index] = (short)val;
6347 }
6348 
6349 inline
brick_entry(size_t index)6350 int gc_heap::brick_entry (size_t index)
6351 {
6352     int val = brick_table [index];
6353     if (val == 0)
6354     {
6355         return -32768;
6356     }
6357     else if (val < 0)
6358     {
6359         return val;
6360     }
6361     else
6362         return val-1;
6363 }
6364 
6365 
6366 inline
align_on_brick(uint8_t * add)6367 uint8_t* align_on_brick (uint8_t* add)
6368 {
6369     return (uint8_t*)((size_t)(add + brick_size - 1) & ~(brick_size - 1));
6370 }
6371 
6372 inline
align_lower_brick(uint8_t * add)6373 uint8_t* align_lower_brick (uint8_t* add)
6374 {
6375     return (uint8_t*)(((size_t)add) & ~(brick_size - 1));
6376 }
6377 
size_brick_of(uint8_t * from,uint8_t * end)6378 size_t size_brick_of (uint8_t* from, uint8_t* end)
6379 {
6380     assert (((size_t)from & (brick_size-1)) == 0);
6381     assert (((size_t)end  & (brick_size-1)) == 0);
6382 
6383     return ((end - from) / brick_size) * sizeof (short);
6384 }
6385 
6386 inline
card_address(size_t card)6387 uint8_t* gc_heap::card_address (size_t card)
6388 {
6389     return  (uint8_t*) (card_size * card);
6390 }
6391 
6392 inline
card_of(uint8_t * object)6393 size_t gc_heap::card_of ( uint8_t* object)
6394 {
6395     return (size_t)(object) / card_size;
6396 }
6397 
6398 inline
card_to_brick(size_t card)6399 size_t gc_heap::card_to_brick (size_t card)
6400 {
6401     return brick_of (card_address (card));
6402 }
6403 
6404 inline
align_on_card(uint8_t * add)6405 uint8_t* align_on_card (uint8_t* add)
6406 {
6407     return (uint8_t*)((size_t)(add + card_size - 1) & ~(card_size - 1 ));
6408 }
6409 inline
align_on_card_word(uint8_t * add)6410 uint8_t* align_on_card_word (uint8_t* add)
6411 {
6412     return (uint8_t*) ((size_t)(add + (card_size*card_word_width)-1) & ~(card_size*card_word_width - 1));
6413 }
6414 
6415 inline
align_lower_card(uint8_t * add)6416 uint8_t* align_lower_card (uint8_t* add)
6417 {
6418     return (uint8_t*)((size_t)add & ~(card_size-1));
6419 }
6420 
6421 inline
clear_card(size_t card)6422 void gc_heap::clear_card (size_t card)
6423 {
6424     card_table [card_word (card)] =
6425         (card_table [card_word (card)] & ~(1 << card_bit (card)));
6426     dprintf (3,("Cleared card %Ix [%Ix, %Ix[", card, (size_t)card_address (card),
6427               (size_t)card_address (card+1)));
6428 }
6429 
6430 inline
set_card(size_t card)6431 void gc_heap::set_card (size_t card)
6432 {
6433     card_table [card_word (card)] =
6434         (card_table [card_word (card)] | (1 << card_bit (card)));
6435 }
6436 
6437 inline
gset_card(size_t card)6438 void gset_card (size_t card)
6439 {
6440     g_gc_card_table [card_word (card)] |= (1 << card_bit (card));
6441 }
6442 
6443 inline
card_set_p(size_t card)6444 BOOL  gc_heap::card_set_p (size_t card)
6445 {
6446     return ( card_table [ card_word (card) ] & (1 << card_bit (card)));
6447 }
6448 
6449 // Returns the number of DWORDs in the card table that cover the
6450 // range of addresses [from, end[.
count_card_of(uint8_t * from,uint8_t * end)6451 size_t count_card_of (uint8_t* from, uint8_t* end)
6452 {
6453     return card_word (gcard_of (end - 1)) - card_word (gcard_of (from)) + 1;
6454 }
6455 
6456 // Returns the number of bytes to allocate for a card table
6457 // that covers the range of addresses [from, end[.
size_card_of(uint8_t * from,uint8_t * end)6458 size_t size_card_of (uint8_t* from, uint8_t* end)
6459 {
6460     return count_card_of (from, end) * sizeof(uint32_t);
6461 }
6462 
6463 #ifdef CARD_BUNDLE
6464 
6465 //The card bundle keeps track of groups of card words
6466 #define card_bundle_word_width ((size_t)32)
6467 //how do we express the fact that 32 bits (card_word_width) is one uint32_t?
6468 #define card_bundle_size ((size_t)(OS_PAGE_SIZE/(sizeof (uint32_t)*card_bundle_word_width)))
6469 
6470 inline
card_bundle_word(size_t cardb)6471 size_t card_bundle_word (size_t cardb)
6472 {
6473     return cardb / card_bundle_word_width;
6474 }
6475 
6476 inline
card_bundle_bit(size_t cardb)6477 uint32_t card_bundle_bit (size_t cardb)
6478 {
6479     return (uint32_t)(cardb % card_bundle_word_width);
6480 }
6481 
align_cardw_on_bundle(size_t cardw)6482 size_t align_cardw_on_bundle (size_t cardw)
6483 {
6484     return ((size_t)(cardw + card_bundle_size - 1) & ~(card_bundle_size - 1 ));
6485 }
6486 
cardw_card_bundle(size_t cardw)6487 size_t cardw_card_bundle (size_t cardw)
6488 {
6489     return cardw/card_bundle_size;
6490 }
6491 
card_bundle_cardw(size_t cardb)6492 size_t card_bundle_cardw (size_t cardb)
6493 {
6494     return cardb*card_bundle_size;
6495 }
6496 
card_bundle_clear(size_t cardb)6497 void gc_heap::card_bundle_clear(size_t cardb)
6498 {
6499     card_bundle_table [card_bundle_word (cardb)] &= ~(1 << card_bundle_bit (cardb));
6500     dprintf (3,("Cleared card bundle %Ix [%Ix, %Ix[", cardb, (size_t)card_bundle_cardw (cardb),
6501               (size_t)card_bundle_cardw (cardb+1)));
6502 //    printf ("Cleared card bundle %Ix\n", cardb);
6503 }
6504 
card_bundles_set(size_t start_cardb,size_t end_cardb)6505 void gc_heap::card_bundles_set (size_t start_cardb, size_t end_cardb)
6506 {
6507     size_t start_word = card_bundle_word (start_cardb);
6508     size_t end_word = card_bundle_word (end_cardb);
6509     if (start_word < end_word)
6510     {
6511         //set the partial words
6512         card_bundle_table [start_word] |= highbits (~0u, card_bundle_bit (start_cardb));
6513 
6514         if (card_bundle_bit (end_cardb))
6515             card_bundle_table [end_word] |= lowbits (~0u, card_bundle_bit (end_cardb));
6516 
6517         for (size_t i = start_word+1; i < end_word; i++)
6518             card_bundle_table [i] = ~0u;
6519 
6520     }
6521     else
6522     {
6523         card_bundle_table [start_word] |= (highbits (~0u, card_bundle_bit (start_cardb)) &
6524                                            lowbits (~0u, card_bundle_bit (end_cardb)));
6525 
6526     }
6527 
6528 }
6529 
card_bundle_set_p(size_t cardb)6530 BOOL gc_heap::card_bundle_set_p (size_t cardb)
6531 {
6532     return ( card_bundle_table [ card_bundle_word (cardb) ] & (1 << card_bundle_bit (cardb)));
6533 }
6534 
size_card_bundle_of(uint8_t * from,uint8_t * end)6535 size_t size_card_bundle_of (uint8_t* from, uint8_t* end)
6536 {
6537     //align from to lower
6538     from = (uint8_t*)((size_t)from & ~(card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1));
6539     //align to to upper
6540     end = (uint8_t*)((size_t)(end + (card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1)) &
6541                   ~(card_size*card_word_width*card_bundle_size*card_bundle_word_width - 1));
6542 
6543     assert (((size_t)from & ((card_size*card_word_width*card_bundle_size*card_bundle_word_width)-1)) == 0);
6544     assert (((size_t)end  & ((card_size*card_word_width*card_bundle_size*card_bundle_word_width)-1)) == 0);
6545 
6546     return ((end - from) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (uint32_t);
6547 }
6548 
translate_card_bundle_table(uint32_t * cb)6549 uint32_t* translate_card_bundle_table (uint32_t* cb)
6550 {
6551     return (uint32_t*)((uint8_t*)cb - ((((size_t)g_gc_lowest_address) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (uint32_t)));
6552 }
6553 
enable_card_bundles()6554 void gc_heap::enable_card_bundles ()
6555 {
6556     if (can_use_write_watch_for_card_table() && (!card_bundles_enabled()))
6557     {
6558         dprintf (3, ("Enabling card bundles"));
6559         //set all of the card bundles
6560         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
6561                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
6562         settings.card_bundles = TRUE;
6563     }
6564 }
6565 
card_bundles_enabled()6566 BOOL gc_heap::card_bundles_enabled ()
6567 {
6568     return settings.card_bundles;
6569 }
6570 
6571 #endif //CARD_BUNDLE
6572 
6573 // We don't store seg_mapping_table in card_table_info because there's only always one view.
6574 class card_table_info
6575 {
6576 public:
6577     unsigned    recount;
6578     uint8_t*    lowest_address;
6579     uint8_t*    highest_address;
6580     short*      brick_table;
6581 
6582 #ifdef CARD_BUNDLE
6583     uint32_t*   card_bundle_table;
6584 #endif //CARD_BUNDLE
6585 
6586     // mark_array is always at the end of the data structure because we
6587     // want to be able to make one commit call for everything before it.
6588 #ifdef MARK_ARRAY
6589     uint32_t*   mark_array;
6590 #endif //MARK_ARRAY
6591 
6592     size_t      size;
6593     uint32_t*   next_card_table;
6594 };
6595 
6596 //These are accessors on untranslated cardtable
6597 inline
card_table_refcount(uint32_t * c_table)6598 unsigned& card_table_refcount (uint32_t* c_table)
6599 {
6600     return *(unsigned*)((char*)c_table - sizeof (card_table_info));
6601 }
6602 
6603 inline
card_table_lowest_address(uint32_t * c_table)6604 uint8_t*& card_table_lowest_address (uint32_t* c_table)
6605 {
6606     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->lowest_address;
6607 }
6608 
translate_card_table(uint32_t * ct)6609 uint32_t* translate_card_table (uint32_t* ct)
6610 {
6611     return (uint32_t*)((uint8_t*)ct - card_word (gcard_of (card_table_lowest_address (ct))) * sizeof(uint32_t));
6612 }
6613 
6614 inline
card_table_highest_address(uint32_t * c_table)6615 uint8_t*& card_table_highest_address (uint32_t* c_table)
6616 {
6617     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->highest_address;
6618 }
6619 
6620 inline
card_table_brick_table(uint32_t * c_table)6621 short*& card_table_brick_table (uint32_t* c_table)
6622 {
6623     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->brick_table;
6624 }
6625 
6626 #ifdef CARD_BUNDLE
6627 inline
card_table_card_bundle_table(uint32_t * c_table)6628 uint32_t*& card_table_card_bundle_table (uint32_t* c_table)
6629 {
6630     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->card_bundle_table;
6631 }
6632 #endif //CARD_BUNDLE
6633 
6634 #ifdef MARK_ARRAY
6635 /* Support for mark_array */
6636 
6637 inline
card_table_mark_array(uint32_t * c_table)6638 uint32_t*& card_table_mark_array (uint32_t* c_table)
6639 {
6640     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->mark_array;
6641 }
6642 
6643 #ifdef BIT64
6644 #define mark_bit_pitch ((size_t)16)
6645 #else
6646 #define mark_bit_pitch ((size_t)8)
6647 #endif // BIT64
6648 #define mark_word_width ((size_t)32)
6649 #define mark_word_size (mark_word_width * mark_bit_pitch)
6650 
6651 inline
align_on_mark_bit(uint8_t * add)6652 uint8_t* align_on_mark_bit (uint8_t* add)
6653 {
6654     return (uint8_t*)((size_t)(add + (mark_bit_pitch - 1)) & ~(mark_bit_pitch - 1));
6655 }
6656 
6657 inline
align_lower_mark_bit(uint8_t * add)6658 uint8_t* align_lower_mark_bit (uint8_t* add)
6659 {
6660     return (uint8_t*)((size_t)(add) & ~(mark_bit_pitch - 1));
6661 }
6662 
6663 inline
is_aligned_on_mark_word(uint8_t * add)6664 BOOL is_aligned_on_mark_word (uint8_t* add)
6665 {
6666     return ((size_t)add == ((size_t)(add) & ~(mark_word_size - 1)));
6667 }
6668 
6669 inline
align_on_mark_word(uint8_t * add)6670 uint8_t* align_on_mark_word (uint8_t* add)
6671 {
6672     return (uint8_t*)((size_t)(add + mark_word_size - 1) & ~(mark_word_size - 1));
6673 }
6674 
6675 inline
align_lower_mark_word(uint8_t * add)6676 uint8_t* align_lower_mark_word (uint8_t* add)
6677 {
6678     return (uint8_t*)((size_t)(add) & ~(mark_word_size - 1));
6679 }
6680 
6681 inline
mark_bit_of(uint8_t * add)6682 size_t mark_bit_of (uint8_t* add)
6683 {
6684     return ((size_t)add / mark_bit_pitch);
6685 }
6686 
6687 inline
mark_bit_bit(size_t mark_bit)6688 unsigned int mark_bit_bit (size_t mark_bit)
6689 {
6690     return (unsigned int)(mark_bit % mark_word_width);
6691 }
6692 
6693 inline
mark_bit_word(size_t mark_bit)6694 size_t mark_bit_word (size_t mark_bit)
6695 {
6696     return (mark_bit / mark_word_width);
6697 }
6698 
6699 inline
mark_word_of(uint8_t * add)6700 size_t mark_word_of (uint8_t* add)
6701 {
6702     return ((size_t)add) / mark_word_size;
6703 }
6704 
mark_word_address(size_t wd)6705 uint8_t* mark_word_address (size_t wd)
6706 {
6707     return (uint8_t*)(wd*mark_word_size);
6708 }
6709 
mark_bit_address(size_t mark_bit)6710 uint8_t* mark_bit_address (size_t mark_bit)
6711 {
6712     return (uint8_t*)(mark_bit*mark_bit_pitch);
6713 }
6714 
6715 inline
mark_bit_bit_of(uint8_t * add)6716 size_t mark_bit_bit_of (uint8_t* add)
6717 {
6718     return  (((size_t)add / mark_bit_pitch) % mark_word_width);
6719 }
6720 
6721 inline
mark_array_marked(uint8_t * add)6722 unsigned int gc_heap::mark_array_marked(uint8_t* add)
6723 {
6724     return mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add));
6725 }
6726 
6727 inline
is_mark_bit_set(uint8_t * add)6728 BOOL gc_heap::is_mark_bit_set (uint8_t* add)
6729 {
6730     return (mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add)));
6731 }
6732 
6733 inline
mark_array_set_marked(uint8_t * add)6734 void gc_heap::mark_array_set_marked (uint8_t* add)
6735 {
6736     size_t index = mark_word_of (add);
6737     uint32_t val = (1 << mark_bit_bit_of (add));
6738 #ifdef MULTIPLE_HEAPS
6739     Interlocked::Or (&(mark_array [index]), val);
6740 #else
6741     mark_array [index] |= val;
6742 #endif
6743 }
6744 
6745 inline
mark_array_clear_marked(uint8_t * add)6746 void gc_heap::mark_array_clear_marked (uint8_t* add)
6747 {
6748     mark_array [mark_word_of (add)] &= ~(1 << mark_bit_bit_of (add));
6749 }
6750 
size_mark_array_of(uint8_t * from,uint8_t * end)6751 size_t size_mark_array_of (uint8_t* from, uint8_t* end)
6752 {
6753     assert (((size_t)from & ((mark_word_size)-1)) == 0);
6754     assert (((size_t)end  & ((mark_word_size)-1)) == 0);
6755     return sizeof (uint32_t)*(((end - from) / mark_word_size));
6756 }
6757 
6758 //In order to eliminate the lowest_address in the mark array
6759 //computations (mark_word_of, etc) mark_array is offset
6760 // according to the lowest_address.
translate_mark_array(uint32_t * ma)6761 uint32_t* translate_mark_array (uint32_t* ma)
6762 {
6763     return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_gc_lowest_address));
6764 }
6765 
6766 // from and end must be page aligned addresses.
clear_mark_array(uint8_t * from,uint8_t * end,BOOL check_only,BOOL read_only)6767 void gc_heap::clear_mark_array (uint8_t* from, uint8_t* end, BOOL check_only/*=TRUE*/
6768 #ifdef FEATURE_BASICFREEZE
6769                                 , BOOL read_only/*=FALSE*/
6770 #endif // FEATURE_BASICFREEZE
6771                                 )
6772 {
6773     if(!gc_can_use_concurrent)
6774         return;
6775 
6776 #ifdef FEATURE_BASICFREEZE
6777     if (!read_only)
6778 #endif // FEATURE_BASICFREEZE
6779     {
6780         assert (from == align_on_mark_word (from));
6781     }
6782     assert (end == align_on_mark_word (end));
6783 
6784 #ifdef BACKGROUND_GC
6785     uint8_t* current_lowest_address = background_saved_lowest_address;
6786     uint8_t* current_highest_address = background_saved_highest_address;
6787 #else
6788     uint8_t* current_lowest_address = lowest_address;
6789     uint8_t* current_highest_address = highest_address;
6790 #endif //BACKGROUND_GC
6791 
6792     //there is a possibility of the addresses to be
6793     //outside of the covered range because of a newly allocated
6794     //large object segment
6795     if ((end <= current_highest_address) && (from >= current_lowest_address))
6796     {
6797         size_t beg_word = mark_word_of (align_on_mark_word (from));
6798         MAYBE_UNUSED_VAR(beg_word);
6799         //align end word to make sure to cover the address
6800         size_t end_word = mark_word_of (align_on_mark_word (end));
6801         MAYBE_UNUSED_VAR(end_word);
6802         dprintf (3, ("Calling clearing mark array [%Ix, %Ix[ for addresses [%Ix, %Ix[(%s)",
6803                      (size_t)mark_word_address (beg_word),
6804                      (size_t)mark_word_address (end_word),
6805                      (size_t)from, (size_t)end,
6806                      (check_only ? "check_only" : "clear")));
6807         if (!check_only)
6808         {
6809             uint8_t* op = from;
6810             while (op < mark_word_address (beg_word))
6811             {
6812                 mark_array_clear_marked (op);
6813                 op += mark_bit_pitch;
6814             }
6815 
6816             memset (&mark_array[beg_word], 0, (end_word - beg_word)*sizeof (uint32_t));
6817         }
6818 #ifdef _DEBUG
6819         else
6820         {
6821             //Beware, it is assumed that the mark array word straddling
6822             //start has been cleared before
6823             //verify that the array is empty.
6824             size_t  markw = mark_word_of (align_on_mark_word (from));
6825             size_t  markw_end = mark_word_of (align_on_mark_word (end));
6826             while (markw < markw_end)
6827             {
6828                 assert (!(mark_array [markw]));
6829                 markw++;
6830             }
6831             uint8_t* p = mark_word_address (markw_end);
6832             while (p < end)
6833             {
6834                 assert (!(mark_array_marked (p)));
6835                 p++;
6836             }
6837         }
6838 #endif //_DEBUG
6839     }
6840 }
6841 #endif //MARK_ARRAY
6842 
6843 //These work on untranslated card tables
6844 inline
card_table_next(uint32_t * c_table)6845 uint32_t*& card_table_next (uint32_t* c_table)
6846 {
6847     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->next_card_table;
6848 }
6849 
6850 inline
card_table_size(uint32_t * c_table)6851 size_t& card_table_size (uint32_t* c_table)
6852 {
6853     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->size;
6854 }
6855 
own_card_table(uint32_t * c_table)6856 void own_card_table (uint32_t* c_table)
6857 {
6858     card_table_refcount (c_table) += 1;
6859 }
6860 
6861 void destroy_card_table (uint32_t* c_table);
6862 
delete_next_card_table(uint32_t * c_table)6863 void delete_next_card_table (uint32_t* c_table)
6864 {
6865     uint32_t* n_table = card_table_next (c_table);
6866     if (n_table)
6867     {
6868         if (card_table_next (n_table))
6869         {
6870             delete_next_card_table (n_table);
6871         }
6872         if (card_table_refcount (n_table) == 0)
6873         {
6874             destroy_card_table (n_table);
6875             card_table_next (c_table) = 0;
6876         }
6877     }
6878 }
6879 
release_card_table(uint32_t * c_table)6880 void release_card_table (uint32_t* c_table)
6881 {
6882     assert (card_table_refcount (c_table) >0);
6883     card_table_refcount (c_table) -= 1;
6884     if (card_table_refcount (c_table) == 0)
6885     {
6886         delete_next_card_table (c_table);
6887         if (card_table_next (c_table) == 0)
6888         {
6889             destroy_card_table (c_table);
6890             // sever the link from the parent
6891             if (&g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))] == c_table)
6892             {
6893                 g_gc_card_table = 0;
6894 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
6895                 SoftwareWriteWatch::StaticClose();
6896 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
6897             }
6898             else
6899             {
6900                 uint32_t* p_table = &g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))];
6901                 if (p_table)
6902                 {
6903                     while (p_table && (card_table_next (p_table) != c_table))
6904                         p_table = card_table_next (p_table);
6905                     card_table_next (p_table) = 0;
6906                 }
6907             }
6908         }
6909     }
6910 }
6911 
destroy_card_table(uint32_t * c_table)6912 void destroy_card_table (uint32_t* c_table)
6913 {
6914 //  delete (uint32_t*)&card_table_refcount(c_table);
6915 
6916     GCToOSInterface::VirtualRelease (&card_table_refcount(c_table), card_table_size(c_table));
6917     dprintf (2, ("Table Virtual Free : %Ix", (size_t)&card_table_refcount(c_table)));
6918 }
6919 
make_card_table(uint8_t * start,uint8_t * end)6920 uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end)
6921 {
6922     assert (g_gc_lowest_address == start);
6923     assert (g_gc_highest_address == end);
6924 
6925     uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
6926 
6927     size_t bs = size_brick_of (start, end);
6928     size_t cs = size_card_of (start, end);
6929 #ifdef MARK_ARRAY
6930     size_t ms = (gc_can_use_concurrent ?
6931                  size_mark_array_of (start, end) :
6932                  0);
6933 #else
6934     size_t ms = 0;
6935 #endif //MARK_ARRAY
6936 
6937     size_t cb = 0;
6938 
6939 #ifdef CARD_BUNDLE
6940     if (can_use_write_watch_for_card_table())
6941     {
6942         virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
6943         cb = size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address);
6944     }
6945 #endif //CARD_BUNDLE
6946 
6947     size_t wws = 0;
6948 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
6949     size_t sw_ww_table_offset = 0;
6950     if (gc_can_use_concurrent)
6951     {
6952         size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
6953         sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
6954         wws = sw_ww_table_offset - sw_ww_size_before_table + SoftwareWriteWatch::GetTableByteSize(start, end);
6955     }
6956 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
6957 
6958 #ifdef GROWABLE_SEG_MAPPING_TABLE
6959     size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
6960     size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
6961     size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
6962 
6963     st += (st_table_offset_aligned - st_table_offset);
6964 #else //GROWABLE_SEG_MAPPING_TABLE
6965     size_t st = 0;
6966 #endif //GROWABLE_SEG_MAPPING_TABLE
6967 
6968     // it is impossible for alloc_size to overflow due bounds on each of
6969     // its components.
6970     size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
6971     size_t alloc_size_aligned = Align (alloc_size, g_SystemInfo.dwAllocationGranularity-1);
6972 
6973     uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size_aligned, 0, virtual_reserve_flags);
6974 
6975     if (!mem)
6976         return 0;
6977 
6978     dprintf (2, ("Init - Card table alloc for %Id bytes: [%Ix, %Ix[",
6979                  alloc_size, (size_t)mem, (size_t)(mem+alloc_size)));
6980 
6981     // mark array will be committed separately (per segment).
6982     size_t commit_size = alloc_size - ms;
6983 
6984     if (!GCToOSInterface::VirtualCommit (mem, commit_size))
6985     {
6986         dprintf (2, ("Card table commit failed"));
6987         GCToOSInterface::VirtualRelease (mem, alloc_size_aligned);
6988         return 0;
6989     }
6990 
6991     // initialize the ref count
6992     uint32_t* ct = (uint32_t*)(mem+sizeof (card_table_info));
6993     card_table_refcount (ct) = 0;
6994     card_table_lowest_address (ct) = start;
6995     card_table_highest_address (ct) = end;
6996     card_table_brick_table (ct) = (short*)((uint8_t*)ct + cs);
6997     card_table_size (ct) = alloc_size_aligned;
6998     card_table_next (ct) = 0;
6999 
7000 #ifdef CARD_BUNDLE
7001     card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7002 #endif //CARD_BUNDLE
7003 
7004 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7005     if (gc_can_use_concurrent)
7006     {
7007         SoftwareWriteWatch::InitializeUntranslatedTable(mem + sw_ww_table_offset, start);
7008     }
7009 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7010 
7011 #ifdef GROWABLE_SEG_MAPPING_TABLE
7012     seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7013     seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table -
7014                                         size_seg_mapping_table_of (0, (align_lower_segment (g_gc_lowest_address))));
7015 #endif //GROWABLE_SEG_MAPPING_TABLE
7016 
7017 #ifdef MARK_ARRAY
7018     if (gc_can_use_concurrent)
7019         card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7020     else
7021         card_table_mark_array (ct) = NULL;
7022 #endif //MARK_ARRAY
7023 
7024     return translate_card_table(ct);
7025 }
7026 
set_fgm_result(failure_get_memory f,size_t s,BOOL loh_p)7027 void gc_heap::set_fgm_result (failure_get_memory f, size_t s, BOOL loh_p)
7028 {
7029 #ifdef MULTIPLE_HEAPS
7030     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
7031     {
7032         gc_heap* hp = gc_heap::g_heaps [hn];
7033         hp->fgm_result.set_fgm (f, s, loh_p);
7034     }
7035 #else //MULTIPLE_HEAPS
7036     fgm_result.set_fgm (f, s, loh_p);
7037 #endif //MULTIPLE_HEAPS
7038 }
7039 
7040 //returns 0 for success, -1 otherwise
7041 // We are doing all the decommitting here because we want to make sure we have
7042 // enough memory to do so - if we do this during copy_brick_card_table and
7043 // and fail to decommit it would make the failure case very complicated to
7044 // handle. This way we can waste some decommit if we call this multiple
7045 // times before the next FGC but it's easier to handle the failure case.
grow_brick_card_tables(uint8_t * start,uint8_t * end,size_t size,heap_segment * new_seg,gc_heap * hp,BOOL loh_p)7046 int gc_heap::grow_brick_card_tables (uint8_t* start,
7047                                      uint8_t* end,
7048                                      size_t size,
7049                                      heap_segment* new_seg,
7050                                      gc_heap* hp,
7051                                      BOOL loh_p)
7052 {
7053     uint8_t* la = g_gc_lowest_address;
7054     uint8_t* ha = g_gc_highest_address;
7055     uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address);
7056     uint8_t* saved_g_highest_address = max (end, g_gc_highest_address);
7057     seg_mapping* new_seg_mapping_table = nullptr;
7058 #ifdef BACKGROUND_GC
7059     // This value is only for logging purpose - it's not necessarily exactly what we
7060     // would commit for mark array but close enough for diagnostics purpose.
7061     size_t logging_ma_commit_size = size_mark_array_of (0, (uint8_t*)size);
7062 #endif //BACKGROUND_GC
7063 
7064     // See if the address is already covered
7065     if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address))
7066     {
7067         {
7068             //modify the higest address so the span covered
7069             //is twice the previous one.
7070             uint8_t* top = (uint8_t*)0 + Align (GCToOSInterface::GetVirtualMemoryLimit());
7071             // On non-Windows systems, we get only an approximate value that can possibly be
7072             // slightly lower than the saved_g_highest_address.
7073             // In such case, we set the top to the saved_g_highest_address so that the
7074             // card and brick tables always cover the whole new range.
7075             if (top < saved_g_highest_address)
7076             {
7077                 top = saved_g_highest_address;
7078             }
7079             size_t ps = ha-la;
7080 #ifdef BIT64
7081             if (ps > (uint64_t)200*1024*1024*1024)
7082                 ps += (uint64_t)100*1024*1024*1024;
7083             else
7084 #endif // BIT64
7085                 ps *= 2;
7086 
7087             if (saved_g_lowest_address < g_gc_lowest_address)
7088             {
7089                 if (ps > (size_t)g_gc_lowest_address)
7090                     saved_g_lowest_address = (uint8_t*)OS_PAGE_SIZE;
7091                 else
7092                 {
7093                     assert (((size_t)g_gc_lowest_address - ps) >= OS_PAGE_SIZE);
7094                     saved_g_lowest_address = min (saved_g_lowest_address, (g_gc_lowest_address - ps));
7095                 }
7096             }
7097 
7098             if (saved_g_highest_address > g_gc_highest_address)
7099             {
7100                 saved_g_highest_address = max ((saved_g_lowest_address + ps), saved_g_highest_address);
7101                 if (saved_g_highest_address > top)
7102                     saved_g_highest_address = top;
7103             }
7104         }
7105         dprintf (GC_TABLE_LOG, ("Growing card table [%Ix, %Ix[",
7106                                 (size_t)saved_g_lowest_address,
7107                                 (size_t)saved_g_highest_address));
7108 
7109         bool write_barrier_updated = false;
7110         uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7111         uint32_t* saved_g_card_table = g_gc_card_table;
7112         uint32_t* ct = 0;
7113         uint32_t* translated_ct = 0;
7114         short* bt = 0;
7115 
7116         size_t cs = size_card_of (saved_g_lowest_address, saved_g_highest_address);
7117         size_t bs = size_brick_of (saved_g_lowest_address, saved_g_highest_address);
7118 
7119 #ifdef MARK_ARRAY
7120         size_t ms = (gc_heap::gc_can_use_concurrent ?
7121                     size_mark_array_of (saved_g_lowest_address, saved_g_highest_address) :
7122                     0);
7123 #else
7124         size_t ms = 0;
7125 #endif //MARK_ARRAY
7126 
7127         size_t cb = 0;
7128 
7129 #ifdef CARD_BUNDLE
7130         if (can_use_write_watch_for_card_table())
7131         {
7132             virtual_reserve_flags = VirtualReserveFlags::WriteWatch;
7133             cb = size_card_bundle_of (saved_g_lowest_address, saved_g_highest_address);
7134         }
7135 #endif //CARD_BUNDLE
7136 
7137         size_t wws = 0;
7138 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7139         size_t sw_ww_table_offset = 0;
7140         if (gc_can_use_concurrent)
7141         {
7142             size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7143             sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7144             wws =
7145                 sw_ww_table_offset -
7146                 sw_ww_size_before_table +
7147                 SoftwareWriteWatch::GetTableByteSize(saved_g_lowest_address, saved_g_highest_address);
7148         }
7149 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7150 
7151 #ifdef GROWABLE_SEG_MAPPING_TABLE
7152         size_t st = size_seg_mapping_table_of (saved_g_lowest_address, saved_g_highest_address);
7153         size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7154         size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7155         st += (st_table_offset_aligned - st_table_offset);
7156 #else //GROWABLE_SEG_MAPPING_TABLE
7157         size_t st = 0;
7158 #endif //GROWABLE_SEG_MAPPING_TABLE
7159 
7160         // it is impossible for alloc_size to overflow due bounds on each of
7161         // its components.
7162         size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7163         size_t alloc_size_aligned = Align (alloc_size, g_SystemInfo.dwAllocationGranularity-1);
7164         dprintf (GC_TABLE_LOG, ("card table: %Id; brick table: %Id; card bundle: %Id; sw ww table: %Id; seg table: %Id; mark array: %Id",
7165                                   cs, bs, cb, wws, st, ms));
7166 
7167         uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size_aligned, 0, virtual_reserve_flags);
7168 
7169         if (!mem)
7170         {
7171             set_fgm_result (fgm_grow_table, alloc_size, loh_p);
7172             goto fail;
7173         }
7174 
7175         dprintf (GC_TABLE_LOG, ("Table alloc for %Id bytes: [%Ix, %Ix[",
7176                                  alloc_size, (size_t)mem, (size_t)((uint8_t*)mem+alloc_size)));
7177 
7178         {
7179             // mark array will be committed separately (per segment).
7180             size_t commit_size = alloc_size - ms;
7181 
7182             if (!GCToOSInterface::VirtualCommit (mem, commit_size))
7183             {
7184                 dprintf (GC_TABLE_LOG, ("Table commit failed"));
7185                 set_fgm_result (fgm_commit_table, commit_size, loh_p);
7186                 goto fail;
7187             }
7188         }
7189 
7190         ct = (uint32_t*)(mem + sizeof (card_table_info));
7191         card_table_refcount (ct) = 0;
7192         card_table_lowest_address (ct) = saved_g_lowest_address;
7193         card_table_highest_address (ct) = saved_g_highest_address;
7194         card_table_next (ct) = &g_gc_card_table[card_word (gcard_of (la))];
7195 
7196         //clear the card table
7197 /*
7198         memclr ((uint8_t*)ct,
7199                 (((saved_g_highest_address - saved_g_lowest_address)*sizeof (uint32_t) /
7200                   (card_size * card_word_width))
7201                  + sizeof (uint32_t)));
7202 */
7203 
7204         bt = (short*)((uint8_t*)ct + cs);
7205 
7206         // No initialization needed, will be done in copy_brick_card
7207 
7208         card_table_brick_table (ct) = bt;
7209 
7210 #ifdef CARD_BUNDLE
7211         card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7212         //set all bundle to look at all of the cards
7213         memset(card_table_card_bundle_table (ct), 0xFF, cb);
7214 #endif //CARD_BUNDLE
7215 
7216 #ifdef GROWABLE_SEG_MAPPING_TABLE
7217         {
7218             new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7219             new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table -
7220                                               size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address))));
7221             memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7222                 &seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7223                 size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address));
7224 
7225             // new_seg_mapping_table gets assigned to seg_mapping_table at the bottom of this function,
7226             // not here. The reason for this is that, if we fail at mark array committing (OOM) and we've
7227             // already switched seg_mapping_table to point to the new mapping table, we'll decommit it and
7228             // run into trouble. By not assigning here, we're making sure that we will not change seg_mapping_table
7229             // if an OOM occurs.
7230         }
7231 #endif //GROWABLE_SEG_MAPPING_TABLE
7232 
7233 #ifdef MARK_ARRAY
7234         if(gc_can_use_concurrent)
7235             card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7236         else
7237             card_table_mark_array (ct) = NULL;
7238 #endif //MARK_ARRAY
7239 
7240         translated_ct = translate_card_table (ct);
7241 
7242         dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix",
7243             (size_t)ct, (size_t)translated_ct, (size_t)new_seg_mapping_table, (size_t)card_table_mark_array (ct)));
7244 
7245 #ifdef BACKGROUND_GC
7246         if (hp->should_commit_mark_array())
7247         {
7248             dprintf (GC_TABLE_LOG, ("new low: %Ix, new high: %Ix, latest mark array is %Ix(translate: %Ix)",
7249                                     saved_g_lowest_address, saved_g_highest_address,
7250                                     card_table_mark_array (ct),
7251                                     translate_mark_array (card_table_mark_array (ct))));
7252             uint32_t* new_mark_array = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, saved_g_lowest_address));
7253             if (!commit_new_mark_array_global (new_mark_array))
7254             {
7255                 dprintf (GC_TABLE_LOG, ("failed to commit portions in the mark array for existing segments"));
7256                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7257                 goto fail;
7258             }
7259 
7260             if (!commit_mark_array_new_seg (hp, new_seg, translated_ct, saved_g_lowest_address))
7261             {
7262                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg"));
7263                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7264                 goto fail;
7265             }
7266         }
7267         else
7268         {
7269             clear_commit_flag_global();
7270         }
7271 #endif //BACKGROUND_GC
7272 
7273 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7274         if (gc_can_use_concurrent)
7275         {
7276             // The current design of software write watch requires that the runtime is suspended during resize. Suspending
7277             // on resize is preferred because it is a far less frequent operation than GetWriteWatch() / ResetWriteWatch().
7278             // Suspending here allows copying dirty state from the old table into the new table, and not have to merge old
7279             // table info lazily as done for card tables.
7280 
7281             // Either this thread was the thread that did the suspension which means we are suspended; or this is called
7282             // from a GC thread which means we are in a blocking GC and also suspended.
7283             BOOL is_runtime_suspended = IsGCThread();
7284             if (!is_runtime_suspended)
7285             {
7286                 // Note on points where the runtime is suspended anywhere in this function. Upon an attempt to suspend the
7287                 // runtime, a different thread may suspend first, causing this thread to block at the point of the suspend call.
7288                 // So, at any suspend point, externally visible state needs to be consistent, as code that depends on that state
7289                 // may run while this thread is blocked. This includes updates to g_gc_card_table, g_gc_lowest_address, and
7290                 // g_gc_highest_address.
7291                 suspend_EE();
7292             }
7293 
7294             g_gc_card_table = translated_ct;
7295             SoftwareWriteWatch::SetResizedUntranslatedTable(
7296                 mem + sw_ww_table_offset,
7297                 saved_g_lowest_address,
7298                 saved_g_highest_address);
7299 
7300             // Since the runtime is already suspended, update the write barrier here as well.
7301             // This passes a bool telling whether we need to switch to the post
7302             // grow version of the write barrier.  This test tells us if the new
7303             // segment was allocated at a lower address than the old, requiring
7304             // that we start doing an upper bounds check in the write barrier.
7305             g_gc_lowest_address = saved_g_lowest_address;
7306             g_gc_highest_address = saved_g_highest_address;
7307             stomp_write_barrier_resize(true, la != saved_g_lowest_address);
7308             write_barrier_updated = true;
7309 
7310             if (!is_runtime_suspended)
7311             {
7312                 restart_EE();
7313             }
7314         }
7315         else
7316 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7317         {
7318             g_gc_card_table = translated_ct;
7319         }
7320 
7321         seg_mapping_table = new_seg_mapping_table;
7322 
7323         GCToOSInterface::FlushProcessWriteBuffers();
7324         g_gc_lowest_address = saved_g_lowest_address;
7325         g_gc_highest_address = saved_g_highest_address;
7326 
7327         if (!write_barrier_updated)
7328         {
7329             // This passes a bool telling whether we need to switch to the post
7330             // grow version of the write barrier.  This test tells us if the new
7331             // segment was allocated at a lower address than the old, requiring
7332             // that we start doing an upper bounds check in the write barrier.
7333             // This will also suspend the runtime if the write barrier type needs
7334             // to be changed, so we are doing this after all global state has
7335             // been updated. See the comment above suspend_EE() above for more
7336             // info.
7337             stomp_write_barrier_resize(!!IsGCThread(), la != saved_g_lowest_address);
7338         }
7339 
7340 
7341         return 0;
7342 
7343 fail:
7344         //cleanup mess and return -1;
7345 
7346         if (mem)
7347         {
7348             assert(g_gc_card_table == saved_g_card_table);
7349 
7350             //delete (uint32_t*)((uint8_t*)ct - sizeof(card_table_info));
7351             if (!GCToOSInterface::VirtualRelease (mem, alloc_size_aligned))
7352             {
7353                 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualRelease failed"));
7354                 assert (!"release failed");
7355             }
7356         }
7357 
7358         return -1;
7359     }
7360     else
7361     {
7362 #ifdef BACKGROUND_GC
7363         if (hp->should_commit_mark_array())
7364         {
7365             dprintf (GC_TABLE_LOG, ("in range new seg %Ix, mark_array is %Ix", new_seg, hp->mark_array));
7366             if (!commit_mark_array_new_seg (hp, new_seg))
7367             {
7368                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg in range"));
7369                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7370                 return -1;
7371             }
7372         }
7373 #endif //BACKGROUND_GC
7374     }
7375 
7376     return 0;
7377 }
7378 
7379 //copy all of the arrays managed by the card table for a page aligned range
copy_brick_card_range(uint8_t * la,uint32_t * old_card_table,short * old_brick_table,heap_segment * seg,uint8_t * start,uint8_t * end)7380 void gc_heap::copy_brick_card_range (uint8_t* la, uint32_t* old_card_table,
7381                                      short* old_brick_table,
7382                                      heap_segment* seg,
7383                                      uint8_t* start, uint8_t* end)
7384 {
7385     ptrdiff_t brick_offset = brick_of (start) - brick_of (la);
7386 
7387 
7388     dprintf (2, ("copying tables for range [%Ix %Ix[", (size_t)start, (size_t)end));
7389 
7390     // copy brick table
7391     short* brick_start = &brick_table [brick_of (start)];
7392     if (old_brick_table)
7393     {
7394         // segments are always on page boundaries
7395         memcpy (brick_start, &old_brick_table[brick_offset],
7396                 size_brick_of (start, end));
7397 
7398     }
7399     else
7400     {
7401         // This is a large heap, just clear the brick table
7402     }
7403 
7404     uint32_t* old_ct = &old_card_table[card_word (card_of (la))];
7405 #ifdef MARK_ARRAY
7406 #ifdef BACKGROUND_GC
7407     UNREFERENCED_PARAMETER(seg);
7408     if (recursive_gc_sync::background_running_p())
7409     {
7410         uint32_t* old_mark_array = card_table_mark_array (old_ct);
7411 
7412         // We don't need to go through all the card tables here because
7413         // we only need to copy from the GC version of the mark array - when we
7414         // mark (even in allocate_large_object) we always use that mark array.
7415         if ((card_table_highest_address (old_ct) >= start) &&
7416             (card_table_lowest_address (old_ct) <= end))
7417         {
7418             if ((background_saved_highest_address >= start) &&
7419                 (background_saved_lowest_address <= end))
7420             {
7421                 //copy the mark bits
7422                 // segments are always on page boundaries
7423                 uint8_t* m_start = max (background_saved_lowest_address, start);
7424                 uint8_t* m_end = min (background_saved_highest_address, end);
7425                 memcpy (&mark_array[mark_word_of (m_start)],
7426                         &old_mark_array[mark_word_of (m_start) - mark_word_of (la)],
7427                         size_mark_array_of (m_start, m_end));
7428             }
7429         }
7430         else
7431         {
7432             //only large segments can be out of range
7433             assert (old_brick_table == 0);
7434         }
7435     }
7436 #else //BACKGROUND_GC
7437     assert (seg != 0);
7438     clear_mark_array (start, heap_segment_committed(seg));
7439 #endif //BACKGROUND_GC
7440 #endif //MARK_ARRAY
7441 
7442     // n way merge with all of the card table ever used in between
7443     uint32_t* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]);
7444 
7445     assert (ct);
7446     while (card_table_next (old_ct) != ct)
7447     {
7448         //copy if old card table contained [start, end[
7449         if ((card_table_highest_address (ct) >= end) &&
7450             (card_table_lowest_address (ct) <= start))
7451         {
7452             // or the card_tables
7453             uint32_t* dest = &card_table [card_word (card_of (start))];
7454             uint32_t* src = &((translate_card_table (ct)) [card_word (card_of (start))]);
7455             ptrdiff_t count = count_card_of (start, end);
7456             for (int x = 0; x < count; x++)
7457             {
7458                 *dest |= *src;
7459                 dest++;
7460                 src++;
7461             }
7462         }
7463         ct = card_table_next (ct);
7464     }
7465 }
7466 
7467 //initialize all of the arrays managed by the card table for a page aligned range when an existing ro segment becomes in range
init_brick_card_range(heap_segment * seg)7468 void gc_heap::init_brick_card_range (heap_segment* seg)
7469 {
7470     dprintf (2, ("initialising tables for range [%Ix %Ix[",
7471                  (size_t)heap_segment_mem (seg),
7472                  (size_t)heap_segment_allocated (seg)));
7473 
7474     // initialize the brick table
7475     for (size_t b = brick_of (heap_segment_mem (seg));
7476          b < brick_of (align_on_brick (heap_segment_allocated (seg)));
7477          b++)
7478     {
7479         set_brick (b, -1);
7480     }
7481 
7482 #ifdef MARK_ARRAY
7483     if (recursive_gc_sync::background_running_p() && (seg->flags & heap_segment_flags_ma_committed))
7484     {
7485         assert (seg != 0);
7486         clear_mark_array (heap_segment_mem (seg), heap_segment_committed(seg));
7487     }
7488 #endif //MARK_ARRAY
7489 
7490     clear_card_for_addresses (heap_segment_mem (seg),
7491                               heap_segment_allocated (seg));
7492 }
7493 
copy_brick_card_table()7494 void gc_heap::copy_brick_card_table()
7495 {
7496     uint8_t* la = lowest_address;
7497     uint8_t* ha = highest_address;
7498     MAYBE_UNUSED_VAR(ha);
7499     uint32_t* old_card_table = card_table;
7500     short* old_brick_table = brick_table;
7501 
7502     assert (la == card_table_lowest_address (&old_card_table[card_word (card_of (la))]));
7503     assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))]));
7504 
7505     /* todo: Need a global lock for this */
7506     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
7507     own_card_table (ct);
7508     card_table = translate_card_table (ct);
7509     /* End of global lock */
7510     highest_address = card_table_highest_address (ct);
7511     lowest_address = card_table_lowest_address (ct);
7512 
7513     brick_table = card_table_brick_table (ct);
7514 
7515 #ifdef MARK_ARRAY
7516     if (gc_can_use_concurrent)
7517     {
7518         mark_array = translate_mark_array (card_table_mark_array (ct));
7519         assert (mark_word_of (g_gc_highest_address) ==
7520             mark_word_of (align_on_mark_word (g_gc_highest_address)));
7521     }
7522     else
7523         mark_array = NULL;
7524 #endif //MARK_ARRAY
7525 
7526 #ifdef CARD_BUNDLE
7527 #if defined(MARK_ARRAY) && defined(_DEBUG)
7528 #ifdef GROWABLE_SEG_MAPPING_TABLE
7529     size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7530 #else  //GROWABLE_SEG_MAPPING_TABLE
7531     size_t st = 0;
7532 #endif //GROWABLE_SEG_MAPPING_TABLE
7533 #endif //MARK_ARRAY && _DEBUG
7534     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct));
7535     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
7536             card_table_card_bundle_table (ct));
7537 
7538     //set the card table if we are in a heap growth scenario
7539     if (card_bundles_enabled())
7540     {
7541         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
7542                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
7543     }
7544     //check if we need to turn on card_bundles.
7545 #ifdef MULTIPLE_HEAPS
7546     // use INT64 arithmetic here because of possible overflow on 32p
7547     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*gc_heap::n_heaps;
7548 #else
7549     // use INT64 arithmetic here because of possible overflow on 32p
7550     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
7551 #endif //MULTIPLE_HEAPS
7552     if (reserved_memory >= th)
7553     {
7554         enable_card_bundles();
7555     }
7556 
7557 #endif //CARD_BUNDLE
7558 
7559     // for each of the segments and heaps, copy the brick table and
7560     // or the card table
7561     heap_segment* seg = generation_start_segment (generation_of (max_generation));
7562     while (seg)
7563     {
7564         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7565         {
7566             //check if it became in range
7567             if ((heap_segment_reserved (seg) > lowest_address) &&
7568                 (heap_segment_mem (seg) < highest_address))
7569             {
7570                 set_ro_segment_in_range (seg);
7571             }
7572         }
7573         else
7574         {
7575 
7576             uint8_t* end = align_on_page (heap_segment_allocated (seg));
7577             copy_brick_card_range (la, old_card_table,
7578                                    old_brick_table,
7579                                    seg,
7580                                    align_lower_page (heap_segment_mem (seg)),
7581                                    end);
7582         }
7583         seg = heap_segment_next (seg);
7584     }
7585 
7586     seg = generation_start_segment (large_object_generation);
7587     while (seg)
7588     {
7589         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7590         {
7591             //check if it became in range
7592             if ((heap_segment_reserved (seg) > lowest_address) &&
7593                 (heap_segment_mem (seg) < highest_address))
7594             {
7595                 set_ro_segment_in_range (seg);
7596             }
7597         }
7598         else
7599         {
7600             uint8_t* end = align_on_page (heap_segment_allocated (seg));
7601             copy_brick_card_range (la, old_card_table,
7602                                    0,
7603                                    seg,
7604                                    align_lower_page (heap_segment_mem (seg)),
7605                                    end);
7606         }
7607         seg = heap_segment_next (seg);
7608     }
7609 
7610     release_card_table (&old_card_table[card_word (card_of(la))]);
7611 }
7612 
7613 #ifdef FEATURE_BASICFREEZE
insert_ro_segment(heap_segment * seg)7614 BOOL gc_heap::insert_ro_segment (heap_segment* seg)
7615 {
7616     enter_spin_lock (&gc_heap::gc_lock);
7617 
7618     if (!gc_heap::seg_table->ensure_space_for_insert ()
7619         || (should_commit_mark_array() && !commit_mark_array_new_seg(__this, seg)))
7620     {
7621         leave_spin_lock(&gc_heap::gc_lock);
7622         return FALSE;
7623     }
7624 
7625     //insert at the head of the segment list
7626     generation* gen2 = generation_of (max_generation);
7627     heap_segment* oldhead = generation_start_segment (gen2);
7628     heap_segment_next (seg) = oldhead;
7629     generation_start_segment (gen2) = seg;
7630 
7631     seg_table->insert (heap_segment_mem(seg), (size_t)seg);
7632 
7633 #ifdef SEG_MAPPING_TABLE
7634     seg_mapping_table_add_ro_segment (seg);
7635 #endif //SEG_MAPPING_TABLE
7636 
7637     //test if in range
7638     if ((heap_segment_reserved (seg) > lowest_address) &&
7639         (heap_segment_mem (seg) < highest_address))
7640     {
7641         set_ro_segment_in_range (seg);
7642     }
7643 
7644     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());
7645 
7646     leave_spin_lock (&gc_heap::gc_lock);
7647     return TRUE;
7648 }
7649 
7650 // No one is calling this function right now. If this is getting called we need
7651 // to take care of decommitting the mark array for it - we will need to remember
7652 // which portion of the mark array was committed and only decommit that.
remove_ro_segment(heap_segment * seg)7653 void gc_heap::remove_ro_segment (heap_segment* seg)
7654 {
7655 //clear the mark bits so a new segment allocated in its place will have a clear mark bits
7656 #ifdef MARK_ARRAY
7657     if (gc_can_use_concurrent)
7658     {
7659         clear_mark_array (align_lower_mark_word (max (heap_segment_mem (seg), lowest_address)),
7660                       align_on_card_word (min (heap_segment_allocated (seg), highest_address)),
7661                       false); // read_only segments need the mark clear
7662     }
7663 #endif //MARK_ARRAY
7664 
7665     enter_spin_lock (&gc_heap::gc_lock);
7666 
7667     seg_table->remove ((uint8_t*)seg);
7668 
7669 #ifdef SEG_MAPPING_TABLE
7670     seg_mapping_table_remove_ro_segment (seg);
7671 #endif //SEG_MAPPING_TABLE
7672 
7673     // Locate segment (and previous segment) in the list.
7674     generation* gen2 = generation_of (max_generation);
7675     heap_segment* curr_seg = generation_start_segment (gen2);
7676     heap_segment* prev_seg = NULL;
7677 
7678     while (curr_seg && curr_seg != seg)
7679     {
7680         prev_seg = curr_seg;
7681         curr_seg = heap_segment_next (curr_seg);
7682     }
7683     assert (curr_seg == seg);
7684 
7685     // Patch previous segment (or list head if there is none) to skip the removed segment.
7686     if (prev_seg)
7687         heap_segment_next (prev_seg) = heap_segment_next (curr_seg);
7688     else
7689         generation_start_segment (gen2) = heap_segment_next (curr_seg);
7690 
7691     leave_spin_lock (&gc_heap::gc_lock);
7692 }
7693 #endif //FEATURE_BASICFREEZE
7694 
set_ro_segment_in_range(heap_segment * seg)7695 BOOL gc_heap::set_ro_segment_in_range (heap_segment* seg)
7696 {
7697     //set it in range
7698     seg->flags |= heap_segment_flags_inrange;
7699 //    init_brick_card_range (seg);
7700     ro_segments_in_range = TRUE;
7701     //right now, segments aren't protected
7702     //unprotect_segment (seg);
7703     return TRUE;
7704 }
7705 
7706 #ifdef MARK_LIST
7707 
make_mark_list(size_t size)7708 uint8_t** make_mark_list (size_t size)
7709 {
7710     uint8_t** mark_list = new (nothrow) uint8_t* [size];
7711     return mark_list;
7712 }
7713 
7714 #define swap(a,b){uint8_t* t; t = a; a = b; b = t;}
7715 
verify_qsort_array(uint8_t ** low,uint8_t ** high)7716 void verify_qsort_array (uint8_t* *low, uint8_t* *high)
7717 {
7718     uint8_t **i = 0;
7719 
7720     for (i = low+1; i <= high; i++)
7721     {
7722         if (*i < *(i-1))
7723         {
7724             FATAL_GC_ERROR();
7725         }
7726     }
7727 }
7728 
7729 #ifndef USE_INTROSORT
qsort1(uint8_t ** low,uint8_t ** high,unsigned int depth)7730 void qsort1( uint8_t* *low, uint8_t* *high, unsigned int depth)
7731 {
7732     if (((low + 16) >= high) || (depth > 100))
7733     {
7734         //insertion sort
7735         uint8_t **i, **j;
7736         for (i = low+1; i <= high; i++)
7737         {
7738             uint8_t* val = *i;
7739             for (j=i;j >low && val<*(j-1);j--)
7740             {
7741                 *j=*(j-1);
7742             }
7743             *j=val;
7744         }
7745     }
7746     else
7747     {
7748         uint8_t *pivot, **left, **right;
7749 
7750         //sort low middle and high
7751         if (*(low+((high-low)/2)) < *low)
7752             swap (*(low+((high-low)/2)), *low);
7753         if (*high < *low)
7754             swap (*low, *high);
7755         if (*high < *(low+((high-low)/2)))
7756             swap (*(low+((high-low)/2)), *high);
7757 
7758         swap (*(low+((high-low)/2)), *(high-1));
7759         pivot =  *(high-1);
7760         left = low; right = high-1;
7761         while (1) {
7762             while (*(--right) > pivot);
7763             while (*(++left)  < pivot);
7764             if (left < right)
7765             {
7766                 swap(*left, *right);
7767             }
7768             else
7769                 break;
7770         }
7771         swap (*left, *(high-1));
7772         qsort1(low, left-1, depth+1);
7773         qsort1(left+1, high, depth+1);
7774     }
7775 }
7776 #endif //USE_INTROSORT
rqsort1(uint8_t ** low,uint8_t ** high)7777 void rqsort1( uint8_t* *low, uint8_t* *high)
7778 {
7779     if ((low + 16) >= high)
7780     {
7781         //insertion sort
7782         uint8_t **i, **j;
7783         for (i = low+1; i <= high; i++)
7784         {
7785             uint8_t* val = *i;
7786             for (j=i;j >low && val>*(j-1);j--)
7787             {
7788                 *j=*(j-1);
7789             }
7790             *j=val;
7791         }
7792     }
7793     else
7794     {
7795         uint8_t *pivot, **left, **right;
7796 
7797         //sort low middle and high
7798         if (*(low+((high-low)/2)) > *low)
7799             swap (*(low+((high-low)/2)), *low);
7800         if (*high > *low)
7801             swap (*low, *high);
7802         if (*high > *(low+((high-low)/2)))
7803             swap (*(low+((high-low)/2)), *high);
7804 
7805         swap (*(low+((high-low)/2)), *(high-1));
7806         pivot =  *(high-1);
7807         left = low; right = high-1;
7808         while (1) {
7809             while (*(--right) < pivot);
7810             while (*(++left)  > pivot);
7811             if (left < right)
7812             {
7813                 swap(*left, *right);
7814             }
7815             else
7816                 break;
7817         }
7818         swap (*left, *(high-1));
7819         rqsort1(low, left-1);
7820         rqsort1(left+1, high);
7821     }
7822 }
7823 
7824 #ifdef USE_INTROSORT
7825 class introsort
7826 {
7827 
7828 private:
7829     static const int size_threshold = 64;
7830     static const int max_depth = 100;
7831 
7832 
swap_elements(uint8_t ** i,uint8_t ** j)7833 inline static void swap_elements(uint8_t** i,uint8_t** j)
7834     {
7835         uint8_t* t=*i;
7836         *i=*j;
7837         *j=t;
7838     }
7839 
7840 public:
sort(uint8_t ** begin,uint8_t ** end,int ignored)7841     static void sort (uint8_t** begin, uint8_t** end, int ignored)
7842     {
7843         ignored = 0;
7844         introsort_loop (begin, end, max_depth);
7845         insertionsort (begin, end);
7846     }
7847 
7848 private:
7849 
introsort_loop(uint8_t ** lo,uint8_t ** hi,int depth_limit)7850     static void introsort_loop (uint8_t** lo, uint8_t** hi, int depth_limit)
7851     {
7852         while (hi-lo >= size_threshold)
7853         {
7854             if (depth_limit == 0)
7855             {
7856                 heapsort (lo, hi);
7857                 return;
7858             }
7859             uint8_t** p=median_partition (lo, hi);
7860             depth_limit=depth_limit-1;
7861             introsort_loop (p, hi, depth_limit);
7862             hi=p-1;
7863         }
7864     }
7865 
median_partition(uint8_t ** low,uint8_t ** high)7866     static uint8_t** median_partition (uint8_t** low, uint8_t** high)
7867     {
7868         uint8_t *pivot, **left, **right;
7869 
7870         //sort low middle and high
7871         if (*(low+((high-low)/2)) < *low)
7872             swap_elements ((low+((high-low)/2)), low);
7873         if (*high < *low)
7874             swap_elements (low, high);
7875         if (*high < *(low+((high-low)/2)))
7876             swap_elements ((low+((high-low)/2)), high);
7877 
7878         swap_elements ((low+((high-low)/2)), (high-1));
7879         pivot =  *(high-1);
7880         left = low; right = high-1;
7881         while (1) {
7882             while (*(--right) > pivot);
7883             while (*(++left)  < pivot);
7884             if (left < right)
7885             {
7886                 swap_elements(left, right);
7887             }
7888             else
7889                 break;
7890         }
7891         swap_elements (left, (high-1));
7892         return left;
7893     }
7894 
7895 
insertionsort(uint8_t ** lo,uint8_t ** hi)7896     static void insertionsort (uint8_t** lo, uint8_t** hi)
7897     {
7898         for (uint8_t** i=lo+1; i <= hi; i++)
7899         {
7900             uint8_t** j = i;
7901             uint8_t* t = *i;
7902             while((j > lo) && (t <*(j-1)))
7903             {
7904                 *j = *(j-1);
7905                 j--;
7906             }
7907             *j = t;
7908         }
7909     }
7910 
heapsort(uint8_t ** lo,uint8_t ** hi)7911     static void heapsort (uint8_t** lo, uint8_t** hi)
7912     {
7913         size_t n = hi - lo + 1;
7914         for (size_t i=n / 2; i >= 1; i--)
7915         {
7916             downheap (i,n,lo);
7917         }
7918         for (size_t i = n; i > 1; i--)
7919         {
7920             swap_elements (lo, lo + i - 1);
7921             downheap(1, i - 1,  lo);
7922         }
7923     }
7924 
downheap(size_t i,size_t n,uint8_t ** lo)7925     static void downheap (size_t i, size_t n, uint8_t** lo)
7926     {
7927         uint8_t* d = *(lo + i - 1);
7928         size_t child;
7929         while (i <= n / 2)
7930         {
7931             child = 2*i;
7932             if (child < n && *(lo + child - 1)<(*(lo + child)))
7933             {
7934                 child++;
7935             }
7936             if (!(d<*(lo + child - 1)))
7937             {
7938                 break;
7939             }
7940             *(lo + i - 1) = *(lo + child - 1);
7941             i = child;
7942         }
7943         *(lo + i - 1) = d;
7944     }
7945 
7946 };
7947 
7948 #endif //USE_INTROSORT
7949 
7950 #ifdef MULTIPLE_HEAPS
7951 #ifdef PARALLEL_MARK_LIST_SORT
sort_mark_list()7952 void gc_heap::sort_mark_list()
7953 {
7954     // if this heap had a mark list overflow, we don't do anything
7955     if (mark_list_index > mark_list_end)
7956     {
7957 //        printf("sort_mark_list: overflow on heap %d\n", heap_number);
7958         return;
7959     }
7960 
7961     // if any other heap had a mark list overflow, we fake one too,
7962     // so we don't use an incomplete mark list by mistake
7963     for (int i = 0; i < n_heaps; i++)
7964     {
7965         if (g_heaps[i]->mark_list_index > g_heaps[i]->mark_list_end)
7966         {
7967             mark_list_index = mark_list_end + 1;
7968 //            printf("sort_mark_list: overflow on heap %d\n", i);
7969             return;
7970         }
7971     }
7972 
7973 //    unsigned long start = GetCycleCount32();
7974 
7975     dprintf (3, ("Sorting mark lists"));
7976     if (mark_list_index > mark_list)
7977         _sort (mark_list, mark_list_index - 1, 0);
7978 
7979 //    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);
7980 //    start = GetCycleCount32();
7981 
7982     // first set the pieces for all heaps to empty
7983     int heap_num;
7984     for (heap_num = 0; heap_num < n_heaps; heap_num++)
7985     {
7986         mark_list_piece_start[heap_num] = NULL;
7987         mark_list_piece_end[heap_num] = NULL;
7988     }
7989 
7990     uint8_t** x = mark_list;
7991 
7992 // predicate means: x is still within the mark list, and within the bounds of this heap
7993 #define predicate(x) (((x) < mark_list_index) && (*(x) < heap->ephemeral_high))
7994 
7995     heap_num = -1;
7996     while (x < mark_list_index)
7997     {
7998         gc_heap* heap;
7999         // find the heap x points into - searching cyclically from the last heap,
8000         // because in many cases the right heap is the next one or comes soon after
8001         int last_heap_num = heap_num;
8002         MAYBE_UNUSED_VAR(last_heap_num);
8003         do
8004         {
8005             heap_num++;
8006             if (heap_num >= n_heaps)
8007                 heap_num = 0;
8008             assert(heap_num != last_heap_num); // we should always find the heap - infinite loop if not!
8009             heap = g_heaps[heap_num];
8010         }
8011         while (!(*x >= heap->ephemeral_low && *x < heap->ephemeral_high));
8012 
8013         // x is the start of the mark list piece for this heap
8014         mark_list_piece_start[heap_num] = x;
8015 
8016         // to find the end of the mark list piece for this heap, find the first x
8017         // that has !predicate(x), i.e. that is either not in this heap, or beyond the end of the list
8018         if (predicate(x))
8019         {
8020             // let's see if we get lucky and the whole rest belongs to this piece
8021             if (predicate(mark_list_index-1))
8022             {
8023                 x = mark_list_index;
8024                 mark_list_piece_end[heap_num] = x;
8025                 break;
8026             }
8027 
8028             // we play a variant of binary search to find the point sooner.
8029             // the first loop advances by increasing steps until the predicate turns false.
8030             // then we retreat the last step, and the second loop advances by decreasing steps, keeping the predicate true.
8031             unsigned inc = 1;
8032             do
8033             {
8034                 inc *= 2;
8035                 uint8_t** temp_x = x;
8036                 x += inc;
8037                 if (temp_x > x)
8038                 {
8039                     break;
8040                 }
8041             }
8042             while (predicate(x));
8043             // we know that only the last step was wrong, so we undo it
8044             x -= inc;
8045             do
8046             {
8047                 // loop invariant - predicate holds at x, but not x + inc
8048                 assert (predicate(x) && !(((x + inc) > x) && predicate(x + inc)));
8049                 inc /= 2;
8050                 if (((x + inc) > x) && predicate(x + inc))
8051                 {
8052                     x += inc;
8053                 }
8054             }
8055             while (inc > 1);
8056             // the termination condition and the loop invariant together imply this:
8057             assert(predicate(x) && !predicate(x + inc) && (inc == 1));
8058             // so the spot we're looking for is one further
8059             x += 1;
8060         }
8061         mark_list_piece_end[heap_num] = x;
8062     }
8063 
8064 #undef predicate
8065 
8066 //    printf("second phase of sort_mark_list for heap %d took %u cycles\n", this->heap_number, GetCycleCount32() - start);
8067 }
8068 
append_to_mark_list(uint8_t ** start,uint8_t ** end)8069 void gc_heap::append_to_mark_list(uint8_t **start, uint8_t **end)
8070 {
8071     size_t slots_needed = end - start;
8072     size_t slots_available = mark_list_end + 1 - mark_list_index;
8073     size_t slots_to_copy = min(slots_needed, slots_available);
8074     memcpy(mark_list_index, start, slots_to_copy*sizeof(*start));
8075     mark_list_index += slots_to_copy;
8076 //    printf("heap %d: appended %Id slots to mark_list\n", heap_number, slots_to_copy);
8077 }
8078 
merge_mark_lists()8079 void gc_heap::merge_mark_lists()
8080 {
8081     uint8_t** source[MAX_SUPPORTED_CPUS];
8082     uint8_t** source_end[MAX_SUPPORTED_CPUS];
8083     int source_heap[MAX_SUPPORTED_CPUS];
8084     int source_count = 0;
8085 
8086     // in case of mark list overflow, don't bother
8087     if (mark_list_index >  mark_list_end)
8088     {
8089 //        printf("merge_mark_lists: overflow\n");
8090         return;
8091     }
8092 
8093     dprintf(3, ("merge_mark_lists: heap_number = %d  starts out with %Id entries", heap_number, mark_list_index - mark_list));
8094 //    unsigned long start = GetCycleCount32();
8095     for (int i = 0; i < n_heaps; i++)
8096     {
8097         gc_heap* heap = g_heaps[i];
8098         if (heap->mark_list_piece_start[heap_number] < heap->mark_list_piece_end[heap_number])
8099         {
8100             source[source_count] = heap->mark_list_piece_start[heap_number];
8101             source_end[source_count] = heap->mark_list_piece_end[heap_number];
8102             source_heap[source_count] = i;
8103             if (source_count < MAX_SUPPORTED_CPUS)
8104                 source_count++;
8105         }
8106     }
8107 //    printf("first phase of merge_mark_lists for heap %d took %u cycles\n", heap_number, GetCycleCount32() - start);
8108 
8109     dprintf(3, ("heap_number = %d  has %d sources\n", heap_number, source_count));
8110 #if defined(_DEBUG) || defined(TRACE_GC)
8111     for (int j = 0; j < source_count; j++)
8112     {
8113         dprintf(3, ("heap_number = %d  ", heap_number));
8114         dprintf(3, (" source from heap %d = %Ix .. %Ix (%Id entries)",
8115             (size_t)(source_heap[j]), (size_t)(source[j][0]), (size_t)(source_end[j][-1]), (size_t)(source_end[j] - source[j])));
8116        // the sources should all be sorted
8117         for (uint8_t **x = source[j]; x < source_end[j] - 1; x++)
8118         {
8119             if (x[0] > x[1])
8120             {
8121                 dprintf(3, ("oops, mark_list from source %d for heap %d isn't sorted\n", j, heap_number));
8122                 assert (0);
8123             }
8124         }
8125     }
8126 #endif //_DEBUG || TRACE_GC
8127 
8128 //    start = GetCycleCount32();
8129 
8130     mark_list = &g_mark_list_copy [heap_number*mark_list_size];
8131     mark_list_index = mark_list;
8132     mark_list_end = &mark_list [mark_list_size-1];
8133     int piece_count = 0;
8134     if (source_count == 0)
8135     {
8136         ; // nothing to do
8137     }
8138     else if (source_count == 1)
8139     {
8140         mark_list = source[0];
8141         mark_list_index = source_end[0];
8142         mark_list_end = mark_list_index;
8143         piece_count++;
8144     }
8145     else
8146     {
8147         while (source_count > 1)
8148         {
8149             // find the lowest and second lowest value in the sources we're merging from
8150             int lowest_source = 0;
8151             uint8_t *lowest = *source[0];
8152             uint8_t *second_lowest = *source[1];
8153             for (int i = 1; i < source_count; i++)
8154             {
8155                 if (lowest > *source[i])
8156                 {
8157                     second_lowest = lowest;
8158                     lowest = *source[i];
8159                     lowest_source = i;
8160                 }
8161                 else if (second_lowest > *source[i])
8162                 {
8163                     second_lowest = *source[i];
8164                 }
8165             }
8166 
8167             // find the point in the lowest source where it either runs out or is not <= second_lowest anymore
8168 
8169             // let's first try to get lucky and see if the whole source is <= second_lowest -- this is actually quite common
8170             uint8_t **x;
8171             if (source_end[lowest_source][-1] <= second_lowest)
8172                 x = source_end[lowest_source];
8173             else
8174             {
8175                 // use linear search to find the end -- could also use binary search as in sort_mark_list,
8176                 // but saw no improvement doing that
8177                 for (x = source[lowest_source]; x < source_end[lowest_source] && *x <= second_lowest; x++)
8178                     ;
8179             }
8180 
8181             // blast this piece to the mark list
8182             append_to_mark_list(source[lowest_source], x);
8183             piece_count++;
8184 
8185             source[lowest_source] = x;
8186 
8187             // check whether this source is now exhausted
8188             if (x >= source_end[lowest_source])
8189             {
8190                 // if it's not the source with the highest index, copy the source with the highest index
8191                 // over it so the non-empty sources are always at the beginning
8192                 if (lowest_source < source_count-1)
8193                 {
8194                     source[lowest_source] = source[source_count-1];
8195                     source_end[lowest_source] = source_end[source_count-1];
8196                 }
8197                 source_count--;
8198             }
8199         }
8200         // we're left with just one source that we copy
8201         append_to_mark_list(source[0], source_end[0]);
8202         piece_count++;
8203     }
8204 
8205 //    printf("second phase of merge_mark_lists for heap %d took %u cycles to merge %d pieces\n", heap_number, GetCycleCount32() - start, piece_count);
8206 
8207 #if defined(_DEBUG) || defined(TRACE_GC)
8208     // the final mark list must be sorted
8209     for (uint8_t **x = mark_list; x < mark_list_index - 1; x++)
8210     {
8211         if (x[0] > x[1])
8212         {
8213             dprintf(3, ("oops, mark_list for heap %d isn't sorted at the end of merge_mark_lists", heap_number));
8214             assert (0);
8215         }
8216     }
8217 #endif //defined(_DEBUG) || defined(TRACE_GC)
8218 }
8219 #else //PARALLEL_MARK_LIST_SORT
combine_mark_lists()8220 void gc_heap::combine_mark_lists()
8221 {
8222     dprintf (3, ("Combining mark lists"));
8223     //verify if a heap has overflowed its mark list
8224     BOOL use_mark_list = TRUE;
8225     for (int i = 0; i < n_heaps; i++)
8226     {
8227         if (g_heaps [i]->mark_list_index >  g_heaps [i]->mark_list_end)
8228         {
8229             use_mark_list = FALSE;
8230             break;
8231         }
8232     }
8233 
8234     if (use_mark_list)
8235     {
8236         dprintf (3, ("Using mark list"));
8237         //compact the gaps out of the mark list
8238         int gn = 0;
8239         uint8_t** current_gap = g_heaps [gn]->mark_list_index;
8240         uint8_t** current_gap_end = g_heaps[gn]->mark_list_end + 1;
8241         uint8_t** dst_last = current_gap-1;
8242 
8243         int srcn = n_heaps-1;
8244         gc_heap* srch = g_heaps [srcn];
8245         uint8_t** src = srch->mark_list_index - 1;
8246         uint8_t** src_beg = srch->mark_list;
8247 
8248         while (current_gap <= src)
8249         {
8250             while ((gn < n_heaps-1) && (current_gap >= current_gap_end))
8251             {
8252                 //go to the next gap
8253                 gn++;
8254                 dprintf (3, ("Going to the next gap %d", gn));
8255                 assert (gn < n_heaps);
8256                 current_gap = g_heaps [gn]->mark_list_index;
8257                 current_gap_end = g_heaps[gn]->mark_list_end + 1;
8258                 assert ((gn == (n_heaps-1)) || (current_gap_end == g_heaps[gn+1]->mark_list));
8259             }
8260             while ((srcn > 0) && (src < src_beg))
8261             {
8262                 //go to the previous source
8263                 srcn--;
8264                 dprintf (3, ("going to the previous source %d", srcn));
8265                 assert (srcn>=0);
8266                 gc_heap* srch = g_heaps [srcn];
8267                 src = srch->mark_list_index - 1;
8268                 src_beg = srch->mark_list;
8269             }
8270             if (current_gap < src)
8271             {
8272                 dst_last = current_gap;
8273                 *current_gap++ = *src--;
8274             }
8275         }
8276         dprintf (3, ("src: %Ix dst_last: %Ix", (size_t)src, (size_t)dst_last));
8277 
8278         uint8_t** end_of_list = max (src, dst_last);
8279 
8280         //sort the resulting compacted list
8281         assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]);
8282         if (end_of_list > &g_mark_list[0])
8283             _sort (&g_mark_list[0], end_of_list, 0);
8284         //adjust the mark_list to the begining of the resulting mark list.
8285         for (int i = 0; i < n_heaps; i++)
8286         {
8287             g_heaps [i]->mark_list = g_mark_list;
8288             g_heaps [i]->mark_list_index = end_of_list + 1;
8289             g_heaps [i]->mark_list_end = end_of_list + 1;
8290         }
8291     }
8292     else
8293     {
8294         uint8_t** end_of_list = g_mark_list;
8295         //adjust the mark_list to the begining of the resulting mark list.
8296         //put the index beyond the end to turn off mark list processing
8297         for (int i = 0; i < n_heaps; i++)
8298         {
8299             g_heaps [i]->mark_list = g_mark_list;
8300             g_heaps [i]->mark_list_index = end_of_list + 1;
8301             g_heaps [i]->mark_list_end = end_of_list;
8302         }
8303     }
8304 }
8305 #endif // PARALLEL_MARK_LIST_SORT
8306 #endif //MULTIPLE_HEAPS
8307 #endif //MARK_LIST
8308 
8309 #ifdef BIT64
8310 #define TOTAL_TIMES_TO_SHIFT 6
8311 #else
8312 #define TOTAL_TIMES_TO_SHIFT 5
8313 #endif // BIT64
8314 
round_up_power2(size_t size)8315 size_t round_up_power2 (size_t size)
8316 {
8317     unsigned short shift = 1;
8318     size_t shifted = 0;
8319 
8320     size--;
8321     for (unsigned short i = 0; i < TOTAL_TIMES_TO_SHIFT; i++)
8322     {
8323         shifted = size | (size >> shift);
8324         if (shifted == size)
8325         {
8326             break;
8327         }
8328 
8329         size = shifted;
8330         shift <<= 1;
8331     }
8332     shifted++;
8333 
8334     return shifted;
8335 }
8336 
8337 inline
round_down_power2(size_t size)8338 size_t round_down_power2 (size_t size)
8339 {
8340     size_t power2 = round_up_power2 (size);
8341 
8342     if (power2 != size)
8343     {
8344         power2 >>= 1;
8345     }
8346 
8347     return power2;
8348 }
8349 
8350 // the index starts from 0.
index_of_set_bit(size_t power2)8351 int index_of_set_bit (size_t power2)
8352 {
8353     int low = 0;
8354     int high = sizeof (size_t) * 8 - 1;
8355     int mid;
8356     while (low <= high)
8357     {
8358         mid = ((low + high)/2);
8359         size_t temp = (size_t)1 << mid;
8360         if (power2 & temp)
8361         {
8362             return mid;
8363         }
8364         else if (power2 < temp)
8365         {
8366             high = mid - 1;
8367         }
8368         else
8369         {
8370             low = mid + 1;
8371         }
8372     }
8373 
8374     return -1;
8375 }
8376 
8377 inline
relative_index_power2_plug(size_t power2)8378 int relative_index_power2_plug (size_t power2)
8379 {
8380     int index = index_of_set_bit (power2);
8381     assert (index <= MAX_INDEX_POWER2);
8382 
8383     return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
8384 }
8385 
8386 inline
relative_index_power2_free_space(size_t power2)8387 int relative_index_power2_free_space (size_t power2)
8388 {
8389     int index = index_of_set_bit (power2);
8390     assert (index <= MAX_INDEX_POWER2);
8391 
8392     return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
8393 }
8394 
8395 class seg_free_spaces
8396 {
8397     struct seg_free_space
8398     {
8399         BOOL is_plug;
8400         void* start;
8401     };
8402 
8403     struct free_space_bucket
8404     {
8405         seg_free_space* free_space;
8406         ptrdiff_t count_add; // Assigned when we first contruct the array.
8407         ptrdiff_t count_fit; // How many items left when we are fitting plugs.
8408     };
8409 
move_bucket(int old_power2,int new_power2)8410     void move_bucket (int old_power2, int new_power2)
8411     {
8412         // PREFAST warning 22015: old_power2 could be negative
8413         assert (old_power2 >= 0);
8414         assert (old_power2 >= new_power2);
8415 
8416         if (old_power2 == new_power2)
8417         {
8418             return;
8419         }
8420 
8421         seg_free_space* src_index = free_space_buckets[old_power2].free_space;
8422         for (int i = old_power2; i > new_power2; i--)
8423         {
8424             seg_free_space** dest = &(free_space_buckets[i].free_space);
8425             (*dest)++;
8426 
8427             seg_free_space* dest_index = free_space_buckets[i - 1].free_space;
8428             if (i > (new_power2 + 1))
8429             {
8430                 seg_free_space temp = *src_index;
8431                 *src_index = *dest_index;
8432                 *dest_index = temp;
8433             }
8434             src_index = dest_index;
8435         }
8436 
8437         free_space_buckets[old_power2].count_fit--;
8438         free_space_buckets[new_power2].count_fit++;
8439     }
8440 
8441 #ifdef _DEBUG
8442 
dump_free_space(seg_free_space * item)8443     void dump_free_space (seg_free_space* item)
8444     {
8445         uint8_t* addr = 0;
8446         size_t len = 0;
8447 
8448         if (item->is_plug)
8449         {
8450             mark* m = (mark*)(item->start);
8451             len = pinned_len (m);
8452             addr = pinned_plug (m) - len;
8453         }
8454         else
8455         {
8456             heap_segment* seg = (heap_segment*)(item->start);
8457             addr = heap_segment_plan_allocated (seg);
8458             len = heap_segment_committed (seg) - addr;
8459         }
8460 
8461         dprintf (SEG_REUSE_LOG_1, ("[%d]0x%Ix %Id", heap_num, addr, len));
8462     }
8463 
dump()8464     void dump()
8465     {
8466         seg_free_space* item = NULL;
8467         int i = 0;
8468 
8469         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------\nnow the free spaces look like:", heap_num));
8470         for (i = 0; i < (free_space_bucket_count - 1); i++)
8471         {
8472             dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8473             dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8474             item = free_space_buckets[i].free_space;
8475             while (item < free_space_buckets[i + 1].free_space)
8476             {
8477                 dump_free_space (item);
8478                 item++;
8479             }
8480             dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8481         }
8482 
8483         dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8484         dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8485         item = free_space_buckets[i].free_space;
8486 
8487         while (item <= &seg_free_space_array[free_space_item_count - 1])
8488         {
8489             dump_free_space (item);
8490             item++;
8491         }
8492         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8493     }
8494 
8495 #endif //_DEBUG
8496 
8497     free_space_bucket* free_space_buckets;
8498     seg_free_space* seg_free_space_array;
8499     ptrdiff_t free_space_bucket_count;
8500     ptrdiff_t free_space_item_count;
8501     int base_power2;
8502     int heap_num;
8503 #ifdef _DEBUG
8504     BOOL has_end_of_seg;
8505 #endif //_DEBUG
8506 
8507 public:
8508 
seg_free_spaces(int h_number)8509     seg_free_spaces (int h_number)
8510     {
8511         heap_num = h_number;
8512     }
8513 
alloc()8514     BOOL alloc ()
8515     {
8516         size_t total_prealloc_size =
8517             MAX_NUM_BUCKETS * sizeof (free_space_bucket) +
8518             MAX_NUM_FREE_SPACES * sizeof (seg_free_space);
8519 
8520         free_space_buckets = (free_space_bucket*) new (nothrow) uint8_t[total_prealloc_size];
8521 
8522         return (!!free_space_buckets);
8523     }
8524 
8525     // We take the ordered free space array we got from the 1st pass,
8526     // and feed the portion that we decided to use to this method, ie,
8527     // the largest item_count free spaces.
add_buckets(int base,size_t * ordered_free_spaces,int bucket_count,size_t item_count)8528     void add_buckets (int base, size_t* ordered_free_spaces, int bucket_count, size_t item_count)
8529     {
8530         assert (free_space_buckets);
8531         assert (item_count <= (size_t)MAX_PTR);
8532 
8533         free_space_bucket_count = bucket_count;
8534         free_space_item_count = item_count;
8535         base_power2 = base;
8536 #ifdef _DEBUG
8537         has_end_of_seg = FALSE;
8538 #endif //_DEBUG
8539 
8540         ptrdiff_t total_item_count = 0;
8541         ptrdiff_t i = 0;
8542 
8543         seg_free_space_array = (seg_free_space*)(free_space_buckets + free_space_bucket_count);
8544 
8545         for (i = 0; i < (ptrdiff_t)item_count; i++)
8546         {
8547             seg_free_space_array[i].start = 0;
8548             seg_free_space_array[i].is_plug = FALSE;
8549         }
8550 
8551         for (i = 0; i < bucket_count; i++)
8552         {
8553             free_space_buckets[i].count_add = ordered_free_spaces[i];
8554             free_space_buckets[i].count_fit = ordered_free_spaces[i];
8555             free_space_buckets[i].free_space = &seg_free_space_array[total_item_count];
8556             total_item_count += free_space_buckets[i].count_add;
8557         }
8558 
8559         assert (total_item_count == (ptrdiff_t)item_count);
8560     }
8561 
8562     // If we are adding a free space before a plug we pass the
8563     // mark stack position so we can update the length; we could
8564     // also be adding the free space after the last plug in which
8565     // case start is the segment which we'll need to update the
8566     // heap_segment_plan_allocated.
add(void * start,BOOL plug_p,BOOL first_p)8567     void add (void* start, BOOL plug_p, BOOL first_p)
8568     {
8569         size_t size = (plug_p ?
8570                        pinned_len ((mark*)start) :
8571                        (heap_segment_committed ((heap_segment*)start) -
8572                            heap_segment_plan_allocated ((heap_segment*)start)));
8573 
8574         if (plug_p)
8575         {
8576             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space before plug: %Id", heap_num, size));
8577         }
8578         else
8579         {
8580             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space at end of seg: %Id", heap_num, size));
8581 #ifdef _DEBUG
8582             has_end_of_seg = TRUE;
8583 #endif //_DEBUG
8584         }
8585 
8586         if (first_p)
8587         {
8588             size_t eph_gen_starts = gc_heap::eph_gen_starts_size;
8589             size -= eph_gen_starts;
8590             if (plug_p)
8591             {
8592                 mark* m = (mark*)(start);
8593                 pinned_len (m) -= eph_gen_starts;
8594             }
8595             else
8596             {
8597                 heap_segment* seg = (heap_segment*)start;
8598                 heap_segment_plan_allocated (seg) += eph_gen_starts;
8599             }
8600         }
8601 
8602         int bucket_power2 = index_of_set_bit (round_down_power2 (size));
8603         if (bucket_power2 < base_power2)
8604         {
8605             return;
8606         }
8607 
8608         free_space_bucket* bucket = &free_space_buckets[bucket_power2 - base_power2];
8609 
8610         seg_free_space* bucket_free_space = bucket->free_space;
8611         assert (plug_p || (!plug_p && bucket->count_add));
8612 
8613         if (bucket->count_add == 0)
8614         {
8615             dprintf (SEG_REUSE_LOG_1, ("[%d]Already have enough of 2^%d", heap_num, bucket_power2));
8616             return;
8617         }
8618 
8619         ptrdiff_t index = bucket->count_add - 1;
8620 
8621         dprintf (SEG_REUSE_LOG_1, ("[%d]Building free spaces: adding %Ix; len: %Id (2^%d)",
8622                     heap_num,
8623                     (plug_p ?
8624                         (pinned_plug ((mark*)start) - pinned_len ((mark*)start)) :
8625                         heap_segment_plan_allocated ((heap_segment*)start)),
8626                     size,
8627                     bucket_power2));
8628 
8629         if (plug_p)
8630         {
8631             bucket_free_space[index].is_plug = TRUE;
8632         }
8633 
8634         bucket_free_space[index].start = start;
8635         bucket->count_add--;
8636     }
8637 
8638 #ifdef _DEBUG
8639 
8640     // Do a consistency check after all free spaces are added.
check()8641     void check()
8642     {
8643         ptrdiff_t i = 0;
8644         int end_of_seg_count = 0;
8645 
8646         for (i = 0; i < free_space_item_count; i++)
8647         {
8648             assert (seg_free_space_array[i].start);
8649             if (!(seg_free_space_array[i].is_plug))
8650             {
8651                 end_of_seg_count++;
8652             }
8653         }
8654 
8655         if (has_end_of_seg)
8656         {
8657             assert (end_of_seg_count == 1);
8658         }
8659         else
8660         {
8661             assert (end_of_seg_count == 0);
8662         }
8663 
8664         for (i = 0; i < free_space_bucket_count; i++)
8665         {
8666             assert (free_space_buckets[i].count_add == 0);
8667         }
8668     }
8669 
8670 #endif //_DEBUG
8671 
fit(uint8_t * old_loc,BOOL set_padding_on_saved_p,mark * pinned_plug_entry,size_t plug_size REQD_ALIGN_AND_OFFSET_DCL)8672     uint8_t* fit (uint8_t* old_loc,
8673 #ifdef SHORT_PLUGS
8674                BOOL set_padding_on_saved_p,
8675                mark* pinned_plug_entry,
8676 #endif //SHORT_PLUGS
8677                size_t plug_size
8678                REQD_ALIGN_AND_OFFSET_DCL)
8679     {
8680         if (old_loc)
8681         {
8682 #ifdef SHORT_PLUGS
8683             assert (!is_plug_padded (old_loc));
8684 #endif //SHORT_PLUGS
8685             assert (!node_realigned (old_loc));
8686         }
8687 
8688         size_t saved_plug_size = plug_size;
8689 
8690 #ifdef FEATURE_STRUCTALIGN
8691         // BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account
8692         _ASSERTE(requiredAlignment == DATA_ALIGNMENT && false);
8693 #endif // FEATURE_STRUCTALIGN
8694         // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the
8695         // the bucket.
8696 
8697         size_t plug_size_to_fit = plug_size;
8698 
8699         int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
8700 
8701 #ifdef SHORT_PLUGS
8702         plug_size_to_fit += (pad_in_front ? Align(min_obj_size) : 0);
8703 #endif //SHORT_PLUGS
8704 
8705         int plug_power2 = index_of_set_bit (round_up_power2 (plug_size_to_fit + Align(min_obj_size)));
8706         ptrdiff_t i;
8707         uint8_t* new_address = 0;
8708 
8709         if (plug_power2 < base_power2)
8710         {
8711             plug_power2 = base_power2;
8712         }
8713 
8714         int chosen_power2 = plug_power2 - base_power2;
8715 retry:
8716         for (i = chosen_power2; i < free_space_bucket_count; i++)
8717         {
8718             if (free_space_buckets[i].count_fit != 0)
8719             {
8720                 break;
8721             }
8722             chosen_power2++;
8723         }
8724 
8725         dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting plug len %Id (2^%d) using 2^%d free space",
8726             heap_num,
8727             plug_size,
8728             plug_power2,
8729             (chosen_power2 + base_power2)));
8730 
8731         assert (i < free_space_bucket_count);
8732 
8733         seg_free_space* bucket_free_space = free_space_buckets[chosen_power2].free_space;
8734         ptrdiff_t free_space_count = free_space_buckets[chosen_power2].count_fit;
8735         size_t new_free_space_size = 0;
8736         BOOL can_fit = FALSE;
8737         size_t pad = 0;
8738 
8739         for (i = 0; i < free_space_count; i++)
8740         {
8741             size_t free_space_size = 0;
8742             pad = 0;
8743 #ifdef SHORT_PLUGS
8744             BOOL short_plugs_padding_p = FALSE;
8745 #endif //SHORT_PLUGS
8746             BOOL realign_padding_p = FALSE;
8747 
8748             if (bucket_free_space[i].is_plug)
8749             {
8750                 mark* m = (mark*)(bucket_free_space[i].start);
8751                 uint8_t* plug_free_space_start = pinned_plug (m) - pinned_len (m);
8752 
8753 #ifdef SHORT_PLUGS
8754                 if ((pad_in_front & USE_PADDING_FRONT) &&
8755                     (((plug_free_space_start - pin_allocation_context_start_region (m))==0) ||
8756                     ((plug_free_space_start - pin_allocation_context_start_region (m))>=DESIRED_PLUG_LENGTH)))
8757                 {
8758                     pad = Align (min_obj_size);
8759                     short_plugs_padding_p = TRUE;
8760                 }
8761 #endif //SHORT_PLUGS
8762 
8763                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, plug_free_space_start+pad)))
8764                 {
8765                     pad += switch_alignment_size (pad != 0);
8766                     realign_padding_p = TRUE;
8767                 }
8768 
8769                 plug_size = saved_plug_size + pad;
8770 
8771                 free_space_size = pinned_len (m);
8772                 new_address = pinned_plug (m) - pinned_len (m);
8773 
8774                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8775                     free_space_size == plug_size)
8776                 {
8777                     new_free_space_size = free_space_size - plug_size;
8778                     pinned_len (m) = new_free_space_size;
8779 #ifdef SIMPLE_DPRINTF
8780                     dprintf (SEG_REUSE_LOG_0, ("[%d]FP: 0x%Ix->0x%Ix(%Ix)(%Ix), [0x%Ix (2^%d) -> [0x%Ix (2^%d)",
8781                                 heap_num,
8782                                 old_loc,
8783                                 new_address,
8784                                 (plug_size - pad),
8785                                 pad,
8786                                 pinned_plug (m),
8787                                 index_of_set_bit (round_down_power2 (free_space_size)),
8788                                 (pinned_plug (m) - pinned_len (m)),
8789                                 index_of_set_bit (round_down_power2 (new_free_space_size))));
8790 #endif //SIMPLE_DPRINTF
8791 
8792 #ifdef SHORT_PLUGS
8793                     if (short_plugs_padding_p)
8794                     {
8795                         pin_allocation_context_start_region (m) = plug_free_space_start;
8796                         set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
8797                     }
8798 #endif //SHORT_PLUGS
8799 
8800                     if (realign_padding_p)
8801                     {
8802                         set_node_realigned (old_loc);
8803                     }
8804 
8805                     can_fit = TRUE;
8806                 }
8807             }
8808             else
8809             {
8810                 heap_segment* seg = (heap_segment*)(bucket_free_space[i].start);
8811                 free_space_size = heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
8812 
8813                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, heap_segment_plan_allocated (seg))))
8814                 {
8815                     pad = switch_alignment_size (FALSE);
8816                     realign_padding_p = TRUE;
8817                 }
8818 
8819                 plug_size = saved_plug_size + pad;
8820 
8821                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8822                     free_space_size == plug_size)
8823                 {
8824                     new_address = heap_segment_plan_allocated (seg);
8825                     new_free_space_size = free_space_size - plug_size;
8826                     heap_segment_plan_allocated (seg) = new_address + plug_size;
8827 #ifdef SIMPLE_DPRINTF
8828                     dprintf (SEG_REUSE_LOG_0, ("[%d]FS: 0x%Ix-> 0x%Ix(%Ix) (2^%d) -> 0x%Ix (2^%d)",
8829                                 heap_num,
8830                                 old_loc,
8831                                 new_address,
8832                                 (plug_size - pad),
8833                                 index_of_set_bit (round_down_power2 (free_space_size)),
8834                                 heap_segment_plan_allocated (seg),
8835                                 index_of_set_bit (round_down_power2 (new_free_space_size))));
8836 #endif //SIMPLE_DPRINTF
8837 
8838                     if (realign_padding_p)
8839                         set_node_realigned (old_loc);
8840 
8841                     can_fit = TRUE;
8842                 }
8843             }
8844 
8845             if (can_fit)
8846             {
8847                 break;
8848             }
8849         }
8850 
8851         if (!can_fit)
8852         {
8853             assert (chosen_power2 == 0);
8854             chosen_power2 = 1;
8855             goto retry;
8856         }
8857         else
8858         {
8859             if (pad)
8860             {
8861                 new_address += pad;
8862             }
8863             assert ((chosen_power2 && (i == 0)) ||
8864                     (!chosen_power2) && (i < free_space_count));
8865         }
8866 
8867         int new_bucket_power2 = index_of_set_bit (round_down_power2 (new_free_space_size));
8868 
8869         if (new_bucket_power2 < base_power2)
8870         {
8871             new_bucket_power2 = base_power2;
8872         }
8873 
8874         move_bucket (chosen_power2, new_bucket_power2 - base_power2);
8875 
8876         //dump();
8877 
8878         return new_address;
8879     }
8880 
cleanup()8881     void cleanup ()
8882     {
8883         if (free_space_buckets)
8884         {
8885             delete [] free_space_buckets;
8886         }
8887         if (seg_free_space_array)
8888         {
8889             delete [] seg_free_space_array;
8890         }
8891     }
8892 };
8893 
8894 
8895 #define marked(i) header(i)->IsMarked()
8896 #define set_marked(i) header(i)->SetMarked()
8897 #define clear_marked(i) header(i)->ClearMarked()
8898 #define pinned(i) header(i)->IsPinned()
8899 #define set_pinned(i) header(i)->SetPinned()
8900 #define clear_pinned(i) header(i)->GetHeader()->ClrGCBit();
8901 
my_get_size(Object * ob)8902 inline size_t my_get_size (Object* ob)
8903 {
8904     MethodTable* mT = header(ob)->GetMethodTable();
8905     return (mT->GetBaseSize() +
8906             (mT->HasComponentSize() ?
8907              ((size_t)((CObjectHeader*)ob)->GetNumComponents() * mT->RawGetComponentSize()) : 0));
8908 }
8909 
8910 //#define size(i) header(i)->GetSize()
8911 #define size(i) my_get_size (header(i))
8912 
8913 #define contain_pointers(i) header(i)->ContainsPointers()
8914 #ifdef COLLECTIBLE_CLASS
8915 #define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible()
8916 
8917 #define get_class_object(i) method_table(i)->GetLoaderAllocatorObjectForGC()
8918 #define is_collectible(i) method_table(i)->Collectible()
8919 #else //COLLECTIBLE_CLASS
8920 #define contain_pointers_or_collectible(i) header(i)->ContainsPointers()
8921 #endif //COLLECTIBLE_CLASS
8922 
8923 #if defined (MARK_ARRAY) && defined (BACKGROUND_GC)
8924 inline
seg_clear_mark_array_bits_soh(heap_segment * seg)8925 void gc_heap::seg_clear_mark_array_bits_soh (heap_segment* seg)
8926 {
8927     uint8_t* range_beg = 0;
8928     uint8_t* range_end = 0;
8929     if (bgc_mark_array_range (seg, FALSE, &range_beg, &range_end))
8930     {
8931         clear_mark_array (range_beg, align_on_mark_word (range_end), FALSE
8932 #ifdef FEATURE_BASICFREEZE
8933             , TRUE
8934 #endif // FEATURE_BASICFREEZE
8935             );
8936     }
8937 }
8938 
clear_batch_mark_array_bits(uint8_t * start,uint8_t * end)8939 void gc_heap::clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
8940 {
8941     if ((start < background_saved_highest_address) &&
8942         (end > background_saved_lowest_address))
8943     {
8944         start = max (start, background_saved_lowest_address);
8945         end = min (end, background_saved_highest_address);
8946 
8947         size_t start_mark_bit = mark_bit_of (start);
8948         size_t end_mark_bit = mark_bit_of (end);
8949         unsigned int startbit = mark_bit_bit (start_mark_bit);
8950         unsigned int endbit = mark_bit_bit (end_mark_bit);
8951         size_t startwrd = mark_bit_word (start_mark_bit);
8952         size_t endwrd = mark_bit_word (end_mark_bit);
8953 
8954         dprintf (3, ("Clearing all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
8955             (size_t)start, (size_t)start_mark_bit,
8956             (size_t)end, (size_t)end_mark_bit));
8957 
8958         unsigned int firstwrd = lowbits (~0, startbit);
8959         unsigned int lastwrd = highbits (~0, endbit);
8960 
8961         if (startwrd == endwrd)
8962         {
8963             unsigned int wrd = firstwrd | lastwrd;
8964             mark_array[startwrd] &= wrd;
8965             return;
8966         }
8967 
8968         // clear the first mark word.
8969         if (startbit)
8970         {
8971             mark_array[startwrd] &= firstwrd;
8972             startwrd++;
8973         }
8974 
8975         for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
8976         {
8977             mark_array[wrdtmp] = 0;
8978         }
8979 
8980         // clear the last mark word.
8981         if (endbit)
8982         {
8983             mark_array[endwrd] &= lastwrd;
8984         }
8985     }
8986 }
8987 
bgc_clear_batch_mark_array_bits(uint8_t * start,uint8_t * end)8988 void gc_heap::bgc_clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
8989 {
8990     if ((start < background_saved_highest_address) &&
8991         (end > background_saved_lowest_address))
8992     {
8993         start = max (start, background_saved_lowest_address);
8994         end = min (end, background_saved_highest_address);
8995 
8996         clear_batch_mark_array_bits (start, end);
8997     }
8998 }
8999 
clear_mark_array_by_objects(uint8_t * from,uint8_t * end,BOOL loh_p)9000 void gc_heap::clear_mark_array_by_objects (uint8_t* from, uint8_t* end, BOOL loh_p)
9001 {
9002     dprintf (3, ("clearing mark array bits by objects for addr [%Ix,[%Ix",
9003                   from, end));
9004     int align_const = get_alignment_constant (!loh_p);
9005 
9006     uint8_t* o = from;
9007 
9008     while (o < end)
9009     {
9010         uint8_t*  next_o = o + Align (size (o), align_const);
9011 
9012         if (background_object_marked (o, TRUE))
9013         {
9014             dprintf (3, ("%Ix was marked by bgc, is now cleared", o));
9015         }
9016 
9017         o = next_o;
9018     }
9019 }
9020 #endif //MARK_ARRAY && BACKGROUND_GC
9021 
9022 inline
is_mark_set(uint8_t * o)9023 BOOL gc_heap::is_mark_set (uint8_t* o)
9024 {
9025     return marked (o);
9026 }
9027 
9028 #if defined (_MSC_VER) && defined (_TARGET_X86_)
9029 #pragma optimize("y", on)        // Small critical routines, don't put in EBP frame
9030 #endif //_MSC_VER && _TARGET_X86_
9031 
9032 // return the generation number of an object.
9033 // It is assumed that the object is valid.
9034 //Note that this will return max_generation for a LOH object
object_gennum(uint8_t * o)9035 int gc_heap::object_gennum (uint8_t* o)
9036 {
9037     if (in_range_for_segment (o, ephemeral_heap_segment) &&
9038         (o >= generation_allocation_start (generation_of (max_generation-1))))
9039     {
9040         // in an ephemeral generation.
9041         for ( int i = 0; i < max_generation-1; i++)
9042         {
9043             if ((o >= generation_allocation_start (generation_of (i))))
9044                 return i;
9045         }
9046         return max_generation-1;
9047     }
9048     else
9049     {
9050         return max_generation;
9051     }
9052 }
9053 
object_gennum_plan(uint8_t * o)9054 int gc_heap::object_gennum_plan (uint8_t* o)
9055 {
9056     if (in_range_for_segment (o, ephemeral_heap_segment))
9057     {
9058         for (int i = 0; i <= max_generation-1; i++)
9059         {
9060             uint8_t* plan_start = generation_plan_allocation_start (generation_of (i));
9061             if (plan_start && (o >= plan_start))
9062             {
9063                 return i;
9064             }
9065         }
9066     }
9067     return max_generation;
9068 }
9069 
9070 #if defined(_MSC_VER) && defined(_TARGET_X86_)
9071 #pragma optimize("", on)        // Go back to command line default optimizations
9072 #endif //_MSC_VER && _TARGET_X86_
9073 
make_heap_segment(uint8_t * new_pages,size_t size,int h_number)9074 heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h_number)
9075 {
9076     size_t initial_commit = SEGMENT_INITIAL_COMMIT;
9077 
9078     //Commit the first page
9079     if (!virtual_alloc_commit_for_heap (new_pages, initial_commit, h_number))
9080     {
9081         return 0;
9082     }
9083 
9084     //overlay the heap_segment
9085     heap_segment* new_segment = (heap_segment*)new_pages;
9086 
9087     uint8_t* start = 0;
9088 #ifdef BACKGROUND_GC
9089     //leave the first page to contain only segment info
9090     //because otherwise we could need to revisit the first page frequently in
9091     // background GC.
9092     start = new_pages + OS_PAGE_SIZE;
9093 #else
9094     start = new_pages +
9095         Align (sizeof (heap_segment), get_alignment_constant (FALSE));
9096 #endif //BACKGROUND_GC
9097     heap_segment_mem (new_segment) = start;
9098     heap_segment_used (new_segment) = start;
9099     heap_segment_reserved (new_segment) = new_pages + size;
9100     heap_segment_committed (new_segment) = new_pages + initial_commit;
9101     init_heap_segment (new_segment);
9102     dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
9103     return new_segment;
9104 }
9105 
init_heap_segment(heap_segment * seg)9106 void gc_heap::init_heap_segment (heap_segment* seg)
9107 {
9108     seg->flags = 0;
9109     heap_segment_next (seg) = 0;
9110     heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
9111     heap_segment_allocated (seg) = heap_segment_mem (seg);
9112 #ifdef BACKGROUND_GC
9113     heap_segment_background_allocated (seg) = 0;
9114     heap_segment_saved_bg_allocated (seg) = 0;
9115 #endif //BACKGROUND_GC
9116 }
9117 
9118 //Releases the segment to the OS.
9119 // this is always called on one thread only so calling seg_table->remove is fine.
delete_heap_segment(heap_segment * seg,BOOL consider_hoarding)9120 void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding)
9121 {
9122     if (!heap_segment_loh_p (seg))
9123     {
9124         //cleanup the brick table back to the empty value
9125         clear_brick_table (heap_segment_mem (seg), heap_segment_reserved (seg));
9126     }
9127 
9128     if (consider_hoarding)
9129     {
9130         assert ((heap_segment_mem (seg) - (uint8_t*)seg) <= 2*OS_PAGE_SIZE);
9131         size_t ss = (size_t) (heap_segment_reserved (seg) - (uint8_t*)seg);
9132         //Don't keep the big ones.
9133         if (ss <= INITIAL_ALLOC)
9134         {
9135             dprintf (2, ("Hoarding segment %Ix", (size_t)seg));
9136 #ifdef BACKGROUND_GC
9137             // We don't need to clear the decommitted flag because when this segment is used
9138             // for a new segment the flags will be cleared.
9139             if (!heap_segment_decommitted_p (seg))
9140 #endif //BACKGROUND_GC
9141             {
9142                 decommit_heap_segment (seg);
9143             }
9144 
9145 #ifdef SEG_MAPPING_TABLE
9146             seg_mapping_table_remove_segment (seg);
9147 #endif //SEG_MAPPING_TABLE
9148 
9149             heap_segment_next (seg) = segment_standby_list;
9150             segment_standby_list = seg;
9151             seg = 0;
9152         }
9153     }
9154 
9155     if (seg != 0)
9156     {
9157         dprintf (2, ("h%d: del seg: [%Ix, %Ix[",
9158                      heap_number, (size_t)seg,
9159                      (size_t)(heap_segment_reserved (seg))));
9160 
9161 #ifdef BACKGROUND_GC
9162         ::record_changed_seg ((uint8_t*)seg, heap_segment_reserved (seg),
9163                             settings.gc_index, current_bgc_state,
9164                             seg_deleted);
9165         decommit_mark_array_by_seg (seg);
9166 #endif //BACKGROUND_GC
9167 
9168 #ifdef SEG_MAPPING_TABLE
9169         seg_mapping_table_remove_segment (seg);
9170 #else //SEG_MAPPING_TABLE
9171         seg_table->remove ((uint8_t*)seg);
9172 #endif //SEG_MAPPING_TABLE
9173 
9174         release_segment (seg);
9175     }
9176 }
9177 
9178 //resets the pages beyond allocates size so they won't be swapped out and back in
9179 
reset_heap_segment_pages(heap_segment * seg)9180 void gc_heap::reset_heap_segment_pages (heap_segment* seg)
9181 {
9182 #ifndef FEATURE_PAL // No MEM_RESET support in PAL VirtualAlloc
9183     size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg));
9184     size_t size = (size_t)heap_segment_committed (seg) - page_start;
9185     if (size != 0)
9186         GCToOSInterface::VirtualReset((void*)page_start, size, false /* unlock */);
9187 #endif //!FEATURE_PAL
9188 }
9189 
decommit_heap_segment_pages(heap_segment * seg,size_t extra_space)9190 void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
9191                                            size_t extra_space)
9192 {
9193     uint8_t*  page_start = align_on_page (heap_segment_allocated(seg));
9194     size_t size = heap_segment_committed (seg) - page_start;
9195     extra_space = align_on_page (extra_space);
9196     if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE))
9197     {
9198         page_start += max(extra_space, 32*OS_PAGE_SIZE);
9199         size -= max (extra_space, 32*OS_PAGE_SIZE);
9200 
9201         GCToOSInterface::VirtualDecommit (page_start, size);
9202         dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)",
9203             (size_t)page_start,
9204             (size_t)(page_start + size),
9205             size));
9206         heap_segment_committed (seg) = page_start;
9207         if (heap_segment_used (seg) > heap_segment_committed (seg))
9208         {
9209             heap_segment_used (seg) = heap_segment_committed (seg);
9210         }
9211     }
9212 }
9213 
9214 //decommit all pages except one or 2
decommit_heap_segment(heap_segment * seg)9215 void gc_heap::decommit_heap_segment (heap_segment* seg)
9216 {
9217     uint8_t*  page_start = align_on_page (heap_segment_mem (seg));
9218 
9219     dprintf (3, ("Decommitting heap segment %Ix", (size_t)seg));
9220 
9221 #ifdef BACKGROUND_GC
9222     page_start += OS_PAGE_SIZE;
9223 #endif //BACKGROUND_GC
9224 
9225     size_t size = heap_segment_committed (seg) - page_start;
9226     GCToOSInterface::VirtualDecommit (page_start, size);
9227 
9228     //re-init the segment object
9229     heap_segment_committed (seg) = page_start;
9230     if (heap_segment_used (seg) > heap_segment_committed (seg))
9231     {
9232         heap_segment_used (seg) = heap_segment_committed (seg);
9233     }
9234 }
9235 
clear_gen0_bricks()9236 void gc_heap::clear_gen0_bricks()
9237 {
9238     if (!gen0_bricks_cleared)
9239     {
9240         gen0_bricks_cleared = TRUE;
9241         //initialize brick table for gen 0
9242         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
9243                 b < brick_of (align_on_brick
9244                             (heap_segment_allocated (ephemeral_heap_segment)));
9245                 b++)
9246         {
9247             set_brick (b, -1);
9248         }
9249     }
9250 }
9251 
9252 #ifdef BACKGROUND_GC
rearrange_small_heap_segments()9253 void gc_heap::rearrange_small_heap_segments()
9254 {
9255     heap_segment* seg = freeable_small_heap_segment;
9256     while (seg)
9257     {
9258         heap_segment* next_seg = heap_segment_next (seg);
9259         // TODO: we need to consider hoarding here.
9260         delete_heap_segment (seg, FALSE);
9261         seg = next_seg;
9262     }
9263     freeable_small_heap_segment = 0;
9264 }
9265 #endif //BACKGROUND_GC
9266 
rearrange_large_heap_segments()9267 void gc_heap::rearrange_large_heap_segments()
9268 {
9269     dprintf (2, ("deleting empty large segments"));
9270     heap_segment* seg = freeable_large_heap_segment;
9271     while (seg)
9272     {
9273         heap_segment* next_seg = heap_segment_next (seg);
9274         delete_heap_segment (seg, (g_pConfig->GetGCRetainVM() != 0));
9275         seg = next_seg;
9276     }
9277     freeable_large_heap_segment = 0;
9278 }
9279 
rearrange_heap_segments(BOOL compacting)9280 void gc_heap::rearrange_heap_segments(BOOL compacting)
9281 {
9282     heap_segment* seg =
9283         generation_start_segment (generation_of (max_generation));
9284 
9285     heap_segment* prev_seg = 0;
9286     heap_segment* next_seg = 0;
9287     while (seg)
9288     {
9289         next_seg = heap_segment_next (seg);
9290 
9291         //link ephemeral segment when expanding
9292         if ((next_seg == 0) && (seg != ephemeral_heap_segment))
9293         {
9294             seg->next = ephemeral_heap_segment;
9295             next_seg = heap_segment_next (seg);
9296         }
9297 
9298         //re-used expanded heap segment
9299         if ((seg == ephemeral_heap_segment) && next_seg)
9300         {
9301             heap_segment_next (prev_seg) = next_seg;
9302             heap_segment_next (seg) = 0;
9303         }
9304         else
9305         {
9306             uint8_t* end_segment = (compacting ?
9307                                  heap_segment_plan_allocated (seg) :
9308                                  heap_segment_allocated (seg));
9309             // check if the segment was reached by allocation
9310             if ((end_segment == heap_segment_mem (seg))&&
9311                 !heap_segment_read_only_p (seg))
9312             {
9313                 //if not, unthread and delete
9314                 assert (prev_seg);
9315                 assert (seg != ephemeral_heap_segment);
9316                 heap_segment_next (prev_seg) = next_seg;
9317                 delete_heap_segment (seg, (g_pConfig->GetGCRetainVM() != 0));
9318 
9319                 dprintf (2, ("Deleting heap segment %Ix", (size_t)seg));
9320             }
9321             else
9322             {
9323                 if (!heap_segment_read_only_p (seg))
9324                 {
9325                     if (compacting)
9326                     {
9327                         heap_segment_allocated (seg) =
9328                             heap_segment_plan_allocated (seg);
9329                     }
9330 
9331                     // reset the pages between allocated and committed.
9332                     if (seg != ephemeral_heap_segment)
9333                     {
9334                         decommit_heap_segment_pages (seg, 0);
9335                     }
9336                 }
9337                 prev_seg = seg;
9338             }
9339         }
9340 
9341         seg = next_seg;
9342     }
9343 }
9344 
9345 
9346 #ifdef WRITE_WATCH
9347 
9348 uint8_t* g_addresses [array_size+2]; // to get around the bug in GetWriteWatch
9349 
9350 #ifdef TIME_WRITE_WATCH
9351 static unsigned int tot_cycles = 0;
9352 #endif //TIME_WRITE_WATCH
9353 
9354 #ifdef CARD_BUNDLE
9355 
update_card_table_bundle()9356 void gc_heap::update_card_table_bundle()
9357 {
9358     if (card_bundles_enabled())
9359     {
9360         uint8_t* base_address = (uint8_t*)(&card_table[card_word (card_of (lowest_address))]);
9361         uint8_t* saved_base_address = base_address;
9362         uintptr_t bcount = array_size;
9363         uint8_t* high_address = (uint8_t*)(&card_table[card_word (card_of (highest_address))]);
9364         size_t saved_region_size = align_on_page (high_address) - saved_base_address;
9365 
9366         do
9367         {
9368             size_t region_size = align_on_page (high_address) - base_address;
9369             dprintf (3,("Probing card table pages [%Ix, %Ix[", (size_t)base_address, (size_t)base_address+region_size));
9370             bool success = GCToOSInterface::GetWriteWatch (false /* resetState */ , base_address, region_size,
9371                                                            (void**)g_addresses,
9372                                                            &bcount);
9373             assert (success && "GetWriteWatch failed!");
9374             dprintf (3,("Found %d pages written", bcount));
9375             for (unsigned  i = 0; i < bcount; i++)
9376             {
9377                 size_t bcardw = (uint32_t*)(max(g_addresses[i],base_address)) - &card_table[0];
9378                 size_t ecardw = (uint32_t*)(min(g_addresses[i]+OS_PAGE_SIZE, high_address)) - &card_table[0];
9379                 assert (bcardw >= card_word (card_of (g_gc_lowest_address)));
9380 
9381                 card_bundles_set (cardw_card_bundle (bcardw),
9382                                   cardw_card_bundle (align_cardw_on_bundle (ecardw)));
9383 
9384                 dprintf (3,("Set Card bundle [%Ix, %Ix[",
9385                             cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))));
9386 
9387 #ifdef _DEBUG
9388                 for (size_t x = cardw_card_bundle (bcardw); x < cardw_card_bundle (ecardw); x++)
9389                 {
9390                     if (!card_bundle_set_p (x))
9391                     {
9392                         assert (!"Card bundle not set");
9393                         dprintf (3, ("Card bundle %Ix not set", x));
9394                     }
9395                 }
9396 #endif //_DEBUG
9397 
9398             }
9399             if (bcount >= array_size){
9400                 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
9401                 bcount = array_size;
9402             }
9403         } while ((bcount >= array_size) && (base_address < high_address));
9404 
9405         GCToOSInterface::ResetWriteWatch (saved_base_address, saved_region_size);
9406 
9407 #ifdef _DEBUG
9408 
9409         size_t lowest_card = card_word (card_of (lowest_address));
9410         size_t highest_card = card_word (card_of (highest_address));
9411         size_t cardb = cardw_card_bundle (lowest_card);
9412         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (highest_card));
9413 
9414         //find a non null bundle
9415         while (cardb < end_cardb)
9416         {
9417             if (card_bundle_set_p (cardb)==0)
9418             {
9419                 //verify that the cards are indeed empty
9420                 uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb), lowest_card)];
9421                 uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1), highest_card)];
9422                 while (card_word < card_word_end)
9423                 {
9424                     if ((*card_word) != 0)
9425                     {
9426                         dprintf  (3, ("gc: %d, Card word %Ix for address %Ix set, card_bundle %Ix clear",
9427                                 dd_collection_count (dynamic_data_of (0)),
9428                                 (size_t)(card_word-&card_table[0]),
9429                                 (size_t)(card_address ((size_t)(card_word-&card_table[0]) * card_word_width)), cardb));
9430                     }
9431                     assert((*card_word)==0);
9432                     card_word++;
9433                 }
9434             }
9435             //end of verification
9436             cardb++;
9437         }
9438 #endif //_DEBUG
9439     }
9440 }
9441 #endif //CARD_BUNDLE
9442 
9443 // static
reset_write_watch_for_gc_heap(void * base_address,size_t region_size)9444 void gc_heap::reset_write_watch_for_gc_heap(void* base_address, size_t region_size)
9445 {
9446 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9447     SoftwareWriteWatch::ClearDirty(base_address, region_size);
9448 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9449     GCToOSInterface::ResetWriteWatch(base_address, region_size);
9450 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9451 }
9452 
9453 // static
get_write_watch_for_gc_heap(bool reset,void * base_address,size_t region_size,void ** dirty_pages,uintptr_t * dirty_page_count_ref,bool is_runtime_suspended)9454 void gc_heap::get_write_watch_for_gc_heap(bool reset, void *base_address, size_t region_size, void** dirty_pages, uintptr_t* dirty_page_count_ref, bool is_runtime_suspended)
9455 {
9456 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9457     SoftwareWriteWatch::GetDirty(base_address, region_size, dirty_pages, dirty_page_count_ref, reset, is_runtime_suspended);
9458 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9459     UNREFERENCED_PARAMETER(is_runtime_suspended);
9460     bool success = GCToOSInterface::GetWriteWatch(reset, base_address, region_size, dirty_pages, dirty_page_count_ref);
9461     assert(success);
9462 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9463 }
9464 
9465 const size_t ww_reset_quantum = 128*1024*1024;
9466 
9467 inline
switch_one_quantum()9468 void gc_heap::switch_one_quantum()
9469 {
9470     Thread* current_thread = GetThread();
9471     enable_preemptive (current_thread);
9472     GCToOSInterface::Sleep (1);
9473     disable_preemptive (current_thread, TRUE);
9474 }
9475 
reset_ww_by_chunk(uint8_t * start_address,size_t total_reset_size)9476 void gc_heap::reset_ww_by_chunk (uint8_t* start_address, size_t total_reset_size)
9477 {
9478     size_t reset_size = 0;
9479     size_t remaining_reset_size = 0;
9480     size_t next_reset_size = 0;
9481 
9482     while (reset_size != total_reset_size)
9483     {
9484         remaining_reset_size = total_reset_size - reset_size;
9485         next_reset_size = ((remaining_reset_size >= ww_reset_quantum) ? ww_reset_quantum : remaining_reset_size);
9486         if (next_reset_size)
9487         {
9488             reset_write_watch_for_gc_heap(start_address, next_reset_size);
9489             reset_size += next_reset_size;
9490 
9491             switch_one_quantum();
9492         }
9493     }
9494 
9495     assert (reset_size == total_reset_size);
9496 }
9497 
9498 // This does a Sleep(1) for every reset ww_reset_quantum bytes of reset
9499 // we do concurrently.
switch_on_reset(BOOL concurrent_p,size_t * current_total_reset_size,size_t last_reset_size)9500 void gc_heap::switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size)
9501 {
9502     if (concurrent_p)
9503     {
9504         *current_total_reset_size += last_reset_size;
9505 
9506         dprintf (2, ("reset %Id bytes so far", *current_total_reset_size));
9507 
9508         if (*current_total_reset_size > ww_reset_quantum)
9509         {
9510             switch_one_quantum();
9511 
9512             *current_total_reset_size = 0;
9513         }
9514     }
9515 }
9516 
reset_write_watch(BOOL concurrent_p)9517 void gc_heap::reset_write_watch (BOOL concurrent_p)
9518 {
9519 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9520     // Software write watch currently requires the runtime to be suspended during reset. See SoftwareWriteWatch::ClearDirty().
9521     assert(!concurrent_p);
9522 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9523 
9524     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
9525 
9526     PREFIX_ASSUME(seg != NULL);
9527 
9528     size_t reset_size = 0;
9529     size_t region_size = 0;
9530 
9531     dprintf (2, ("bgc lowest: %Ix, bgc highest: %Ix", background_saved_lowest_address, background_saved_highest_address));
9532 
9533     while (seg)
9534     {
9535         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9536         base_address = max (base_address, background_saved_lowest_address);
9537 
9538         uint8_t* high_address = 0;
9539         high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
9540         high_address = min (high_address, background_saved_highest_address);
9541 
9542         if (base_address < high_address)
9543         {
9544             region_size = high_address - base_address;
9545 
9546 #ifdef TIME_WRITE_WATCH
9547             unsigned int time_start = GetCycleCount32();
9548 #endif //TIME_WRITE_WATCH
9549             dprintf (3, ("h%d: soh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9550             //reset_ww_by_chunk (base_address, region_size);
9551             reset_write_watch_for_gc_heap(base_address, region_size);
9552 
9553 #ifdef TIME_WRITE_WATCH
9554             unsigned int time_stop = GetCycleCount32();
9555             tot_cycles += time_stop - time_start;
9556             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9557                     time_stop - time_start, tot_cycles);
9558 #endif //TIME_WRITE_WATCH
9559 
9560             switch_on_reset (concurrent_p, &reset_size, region_size);
9561         }
9562 
9563         seg = heap_segment_next_rw (seg);
9564 
9565         concurrent_print_time_delta ("CRWW soh");
9566     }
9567 
9568     //concurrent_print_time_delta ("CRW soh");
9569 
9570     seg = heap_segment_rw (generation_start_segment (large_object_generation));
9571 
9572     PREFIX_ASSUME(seg != NULL);
9573 
9574     while (seg)
9575     {
9576         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9577         uint8_t* high_address =  heap_segment_allocated (seg);
9578 
9579         base_address = max (base_address, background_saved_lowest_address);
9580         high_address = min (high_address, background_saved_highest_address);
9581 
9582         if (base_address < high_address)
9583         {
9584             region_size = high_address - base_address;
9585 
9586 #ifdef TIME_WRITE_WATCH
9587             unsigned int time_start = GetCycleCount32();
9588 #endif //TIME_WRITE_WATCH
9589             dprintf (3, ("h%d: loh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9590             //reset_ww_by_chunk (base_address, region_size);
9591             reset_write_watch_for_gc_heap(base_address, region_size);
9592 
9593 #ifdef TIME_WRITE_WATCH
9594             unsigned int time_stop = GetCycleCount32();
9595             tot_cycles += time_stop - time_start;
9596             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9597                     time_stop - time_start, tot_cycles);
9598 #endif //TIME_WRITE_WATCH
9599 
9600             switch_on_reset (concurrent_p, &reset_size, region_size);
9601         }
9602 
9603         seg = heap_segment_next_rw (seg);
9604 
9605         concurrent_print_time_delta ("CRWW loh");
9606     }
9607 
9608 #ifdef DEBUG_WRITE_WATCH
9609     debug_write_watch = (uint8_t**)~0;
9610 #endif //DEBUG_WRITE_WATCH
9611 }
9612 
9613 #endif //WRITE_WATCH
9614 
9615 #ifdef BACKGROUND_GC
restart_vm()9616 void gc_heap::restart_vm()
9617 {
9618     //assert (generation_allocation_pointer (youngest_generation) == 0);
9619     dprintf (3, ("Restarting EE"));
9620     STRESS_LOG0(LF_GC, LL_INFO10000, "Concurrent GC: Retarting EE\n");
9621     ee_proceed_event.Set();
9622 }
9623 
9624 inline
fire_alloc_wait_event(alloc_wait_reason awr,BOOL begin_p)9625 void fire_alloc_wait_event (alloc_wait_reason awr, BOOL begin_p)
9626 {
9627     if (awr != awr_ignored)
9628     {
9629         if (begin_p)
9630         {
9631             FireEtwBGCAllocWaitBegin (awr, GetClrInstanceId());
9632         }
9633         else
9634         {
9635             FireEtwBGCAllocWaitEnd (awr, GetClrInstanceId());
9636         }
9637     }
9638 }
9639 
9640 
fire_alloc_wait_event_begin(alloc_wait_reason awr)9641 void gc_heap::fire_alloc_wait_event_begin (alloc_wait_reason awr)
9642 {
9643     fire_alloc_wait_event (awr, TRUE);
9644 }
9645 
9646 
fire_alloc_wait_event_end(alloc_wait_reason awr)9647 void gc_heap::fire_alloc_wait_event_end (alloc_wait_reason awr)
9648 {
9649     fire_alloc_wait_event (awr, FALSE);
9650 }
9651 #endif //BACKGROUND_GC
make_generation(generation & gen,heap_segment * seg,uint8_t * start,uint8_t * pointer)9652 void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* start, uint8_t* pointer)
9653 {
9654     gen.allocation_start = start;
9655     gen.allocation_context.alloc_ptr = pointer;
9656     gen.allocation_context.alloc_limit = pointer;
9657     gen.allocation_context.alloc_bytes = 0;
9658     gen.allocation_context.alloc_bytes_loh = 0;
9659     gen.allocation_context_start_region = pointer;
9660     gen.start_segment = seg;
9661     gen.allocation_segment = seg;
9662     gen.plan_allocation_start = 0;
9663     gen.free_list_space = 0;
9664     gen.pinned_allocated = 0;
9665     gen.free_list_allocated = 0;
9666     gen.end_seg_allocated = 0;
9667     gen.condemned_allocated = 0;
9668     gen.free_obj_space = 0;
9669     gen.allocation_size = 0;
9670     gen.pinned_allocation_sweep_size = 0;
9671     gen.pinned_allocation_compact_size = 0;
9672     gen.allocate_end_seg_p = FALSE;
9673     gen.free_list_allocator.clear();
9674 
9675 #ifdef FREE_USAGE_STATS
9676     memset (gen.gen_free_spaces, 0, sizeof (gen.gen_free_spaces));
9677     memset (gen.gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces));
9678     memset (gen.gen_plugs, 0, sizeof (gen.gen_plugs));
9679 #endif //FREE_USAGE_STATS
9680 }
9681 
adjust_ephemeral_limits()9682 void gc_heap::adjust_ephemeral_limits ()
9683 {
9684     ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
9685     ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);
9686 
9687     dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
9688                  (size_t)ephemeral_low, (size_t)ephemeral_high))
9689 
9690 #ifndef MULTIPLE_HEAPS
9691     // This updates the write barrier helpers with the new info.
9692     stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high);
9693 #endif // MULTIPLE_HEAPS
9694 }
9695 
9696 #if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN)
CreateLogFile(const CLRConfig::ConfigStringInfo & info,BOOL is_config)9697 FILE* CreateLogFile(const CLRConfig::ConfigStringInfo & info, BOOL is_config)
9698 {
9699     FILE* logFile;
9700     TCHAR * temp_logfile_name = NULL;
9701     CLRConfig::GetConfigValue(info, &temp_logfile_name);
9702 
9703     TCHAR logfile_name[MAX_LONGPATH+1];
9704     if (temp_logfile_name != 0)
9705     {
9706         _tcscpy(logfile_name, temp_logfile_name);
9707     }
9708 
9709     size_t logfile_name_len = _tcslen(logfile_name);
9710     TCHAR* szPid = logfile_name + logfile_name_len;
9711     size_t remaining_space = MAX_LONGPATH + 1 - logfile_name_len;
9712 
9713     _stprintf_s(szPid, remaining_space, _T(".%d%s"), GCToOSInterface::GetCurrentProcessId(), (is_config ? _T(".config.log") : _T(".log")));
9714 
9715     logFile = _tfopen(logfile_name, _T("wb"));
9716 
9717     delete temp_logfile_name;
9718 
9719     return logFile;
9720 }
9721 #endif //TRACE_GC || GC_CONFIG_DRIVEN
9722 
initialize_gc(size_t segment_size,size_t heap_size,unsigned number_of_heaps)9723 HRESULT gc_heap::initialize_gc (size_t segment_size,
9724                                 size_t heap_size
9725 #ifdef MULTIPLE_HEAPS
9726                                 ,unsigned number_of_heaps
9727 #endif //MULTIPLE_HEAPS
9728 )
9729 {
9730 #ifdef TRACE_GC
9731     int log_last_gcs = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCLogEnabled);
9732     if (log_last_gcs)
9733     {
9734         gc_log = CreateLogFile(CLRConfig::UNSUPPORTED_GCLogFile, FALSE);
9735 
9736         if (gc_log == NULL)
9737             return E_FAIL;
9738 
9739         // GCLogFileSize in MBs.
9740         gc_log_file_size = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCLogFileSize);
9741 
9742         if (gc_log_file_size > 500)
9743         {
9744             fclose (gc_log);
9745             return E_FAIL;
9746         }
9747 
9748         gc_log_lock.Initialize();
9749         gc_log_buffer = new (nothrow) uint8_t [gc_log_buffer_size];
9750         if (!gc_log_buffer)
9751         {
9752             fclose(gc_log);
9753             return E_FAIL;
9754         }
9755 
9756         memset (gc_log_buffer, '*', gc_log_buffer_size);
9757 
9758         max_gc_buffers = gc_log_file_size * 1024 * 1024 / gc_log_buffer_size;
9759     }
9760 #endif // TRACE_GC
9761 
9762 #ifdef GC_CONFIG_DRIVEN
9763     gc_config_log_on = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCConfigLogEnabled);
9764     if (gc_config_log_on)
9765     {
9766         gc_config_log = CreateLogFile(CLRConfig::UNSUPPORTED_GCConfigLogFile, TRUE);
9767 
9768         if (gc_config_log == NULL)
9769             return E_FAIL;
9770 
9771         gc_config_log_buffer = new (nothrow) uint8_t [gc_config_log_buffer_size];
9772         if (!gc_config_log_buffer)
9773         {
9774             fclose(gc_config_log);
9775             return E_FAIL;
9776         }
9777 
9778         compact_ratio = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCCompactRatio);
9779 
9780         //         h#  | GC  | gen | C   | EX   | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
9781         cprintf (("%2s | %6s | %1s | %1s | %2s | %2s | %2s | %2s | %2s || %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s |",
9782                 "h#", // heap index
9783                 "GC", // GC index
9784                 "g", // generation
9785                 "C",  // compaction (empty means sweeping), 'M' means it was mandatory, 'W' means it was not
9786                 "EX", // heap expansion
9787                 "NF", // normal fit
9788                 "BF", // best fit (if it indicates neither NF nor BF it means it had to acquire a new seg.
9789                 "ML", // mark list
9790                 "DM", // demotion
9791                 "PreS", // short object before pinned plug
9792                 "PostS", // short object after pinned plug
9793                 "Merge", // merged pinned plugs
9794                 "Conv", // converted to pinned plug
9795                 "Pre", // plug before pinned plug but not after
9796                 "Post", // plug after pinned plug but not before
9797                 "PrPo", // plug both before and after pinned plug
9798                 "PreP", // pre short object padded
9799                 "PostP" // post short object padded
9800                 ));
9801     }
9802 #endif //GC_CONFIG_DRIVEN
9803 
9804 #ifdef GC_STATS
9805     GCStatistics::logFileName = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCMixLog);
9806     if (GCStatistics::logFileName != NULL)
9807     {
9808         GCStatistics::logFile = _tfopen(GCStatistics::logFileName, _T("a"));
9809     }
9810 #endif // GC_STATS
9811 
9812     HRESULT hres = S_OK;
9813 
9814 #ifdef WRITE_WATCH
9815     hardware_write_watch_api_supported();
9816 #ifdef BACKGROUND_GC
9817     if (can_use_write_watch_for_gc_heap() && g_pConfig->GetGCconcurrent() != 0)
9818     {
9819         gc_can_use_concurrent = true;
9820 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9821         virtual_alloc_hardware_write_watch = true;
9822 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9823     }
9824     else
9825     {
9826         gc_can_use_concurrent = false;
9827     }
9828 #endif //BACKGROUND_GC
9829 #endif //WRITE_WATCH
9830 
9831     reserved_memory = 0;
9832     unsigned block_count;
9833 #ifdef MULTIPLE_HEAPS
9834     reserved_memory_limit = (segment_size + heap_size) * number_of_heaps;
9835     block_count = number_of_heaps;
9836 #else //MULTIPLE_HEAPS
9837     reserved_memory_limit = segment_size + heap_size;
9838     block_count = 1;
9839 #endif //MULTIPLE_HEAPS
9840 
9841     if (!reserve_initial_memory(segment_size,heap_size,block_count))
9842         return E_OUTOFMEMORY;
9843 
9844 #ifdef CARD_BUNDLE
9845     //check if we need to turn on card_bundles.
9846 #ifdef MULTIPLE_HEAPS
9847     // use INT64 arithmetic here because of possible overflow on 32p
9848     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*number_of_heaps;
9849 #else
9850     // use INT64 arithmetic here because of possible overflow on 32p
9851     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
9852 #endif //MULTIPLE_HEAPS
9853 
9854     if (can_use_write_watch_for_card_table() && reserved_memory >= th)
9855     {
9856         settings.card_bundles = TRUE;
9857     }
9858     else
9859     {
9860         settings.card_bundles = FALSE;
9861     }
9862 #endif //CARD_BUNDLE
9863 
9864     settings.first_init();
9865 
9866     g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address);
9867 
9868     if (!g_gc_card_table)
9869         return E_OUTOFMEMORY;
9870 
9871     gc_started = FALSE;
9872 
9873 #ifdef MULTIPLE_HEAPS
9874     n_heaps = number_of_heaps;
9875 
9876     g_heaps = new (nothrow) gc_heap* [number_of_heaps];
9877     if (!g_heaps)
9878         return E_OUTOFMEMORY;
9879 
9880 #ifdef _PREFAST_
9881 #pragma warning(push)
9882 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
9883 #endif // _PREFAST_
9884     g_promoted = new (nothrow) size_t [number_of_heaps*16];
9885     g_bpromoted = new (nothrow) size_t [number_of_heaps*16];
9886 #ifdef MH_SC_MARK
9887     g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
9888 #endif //MH_SC_MARK
9889 #ifdef _PREFAST_
9890 #pragma warning(pop)
9891 #endif // _PREFAST_
9892     if (!g_promoted || !g_bpromoted)
9893         return E_OUTOFMEMORY;
9894 
9895 #ifdef MH_SC_MARK
9896     if (!g_mark_stack_busy)
9897         return E_OUTOFMEMORY;
9898 #endif //MH_SC_MARK
9899 
9900     if (!create_thread_support (number_of_heaps))
9901         return E_OUTOFMEMORY;
9902 
9903     if (!heap_select::init (number_of_heaps))
9904         return E_OUTOFMEMORY;
9905 
9906 #endif //MULTIPLE_HEAPS
9907 
9908 #ifdef TRACE_GC
9909     print_level = g_pConfig->GetGCprnLvl();
9910     gc_trace_fac = g_pConfig->GetGCtraceFac();
9911 #endif //TRACE_GC
9912 
9913     if (!init_semi_shared())
9914     {
9915         hres = E_FAIL;
9916     }
9917 
9918     return hres;
9919 }
9920 
9921 //Initializes PER_HEAP_ISOLATED data members.
9922 int
init_semi_shared()9923 gc_heap::init_semi_shared()
9924 {
9925     int ret = 0;
9926 
9927     // This is used for heap expansion - it's to fix exactly the start for gen 0
9928     // through (max_generation-1). When we expand the heap we allocate all these
9929     // gen starts at the beginning of the new ephemeral seg.
9930     eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
9931 
9932 #ifdef MARK_LIST
9933     size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
9934     MAYBE_UNUSED_VAR(gen0size);
9935 
9936 #ifdef MULTIPLE_HEAPS
9937 
9938     mark_list_size = min (150*1024, max (8192, get_valid_segment_size()/(2*10*32)));
9939     g_mark_list = make_mark_list (mark_list_size*n_heaps);
9940 
9941     min_balance_threshold = alloc_quantum_balance_units * CLR_SIZE * 2;
9942 #ifdef PARALLEL_MARK_LIST_SORT
9943     g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
9944     if (!g_mark_list_copy)
9945     {
9946         goto cleanup;
9947     }
9948 #endif //PARALLEL_MARK_LIST_SORT
9949 
9950 #else //MULTIPLE_HEAPS
9951 
9952     mark_list_size = max (8192, get_valid_segment_size()/(64*32));
9953     g_mark_list = make_mark_list (mark_list_size);
9954 
9955 #endif //MULTIPLE_HEAPS
9956 
9957     dprintf (3, ("gen0 size: %d, mark_list_size: %d",
9958                  gen0size, mark_list_size));
9959 
9960     if (!g_mark_list)
9961     {
9962         goto cleanup;
9963     }
9964 #endif //MARK_LIST
9965 
9966 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
9967     if (!seg_mapping_table_init())
9968         goto cleanup;
9969 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
9970 
9971 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
9972     seg_table = sorted_table::make_sorted_table();
9973 
9974     if (!seg_table)
9975         goto cleanup;
9976 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
9977 
9978     segment_standby_list = 0;
9979 
9980     if (!full_gc_approach_event.CreateManualEventNoThrow(FALSE))
9981     {
9982         goto cleanup;
9983     }
9984     if (!full_gc_end_event.CreateManualEventNoThrow(FALSE))
9985     {
9986         goto cleanup;
9987     }
9988 
9989     fgn_maxgen_percent = 0;
9990     fgn_loh_percent = 0;
9991     full_gc_approach_event_set = false;
9992 
9993     memset (full_gc_counts, 0, sizeof (full_gc_counts));
9994 
9995     last_gc_index = 0;
9996     should_expand_in_full_gc = FALSE;
9997 
9998 #ifdef FEATURE_LOH_COMPACTION
9999     loh_compaction_always_p = (g_pConfig->GetGCLOHCompactionMode() != 0);
10000     loh_compaction_mode = loh_compaction_default;
10001 #endif //FEATURE_LOH_COMPACTION
10002 
10003 #ifdef BACKGROUND_GC
10004     memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
10005     bgc_alloc_spin_count = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BGCSpinCount);
10006     bgc_alloc_spin = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BGCSpin);
10007 
10008     {
10009         int number_bgc_threads = 1;
10010 #ifdef MULTIPLE_HEAPS
10011         number_bgc_threads = n_heaps;
10012 #endif //MULTIPLE_HEAPS
10013         if (!create_bgc_threads_support (number_bgc_threads))
10014         {
10015             goto cleanup;
10016         }
10017     }
10018 #endif //BACKGROUND_GC
10019 
10020     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
10021 
10022 #ifdef GC_CONFIG_DRIVEN
10023     compact_or_sweep_gcs[0] = 0;
10024     compact_or_sweep_gcs[1] = 0;
10025 #endif //GC_CONFIG_DRIVEN
10026 
10027 #ifdef SHORT_PLUGS
10028     short_plugs_pad_ratio = (double)DESIRED_PLUG_LENGTH / (double)(DESIRED_PLUG_LENGTH - Align (min_obj_size));
10029 #endif //SHORT_PLUGS
10030 
10031     ret = 1;
10032 
10033 cleanup:
10034 
10035     if (!ret)
10036     {
10037         if (full_gc_approach_event.IsValid())
10038         {
10039             full_gc_approach_event.CloseEvent();
10040         }
10041         if (full_gc_end_event.IsValid())
10042         {
10043             full_gc_end_event.CloseEvent();
10044         }
10045     }
10046 
10047     return ret;
10048 }
10049 
make_gc_heap(GCHeap * vm_hp,int heap_number)10050 gc_heap* gc_heap::make_gc_heap (
10051 #ifdef MULTIPLE_HEAPS
10052                                 GCHeap* vm_hp,
10053                                 int heap_number
10054 #endif //MULTIPLE_HEAPS
10055                                 )
10056 {
10057     gc_heap* res = 0;
10058 
10059 #ifdef MULTIPLE_HEAPS
10060     res = new (nothrow) gc_heap;
10061     if (!res)
10062         return 0;
10063 
10064     res->vm_heap = vm_hp;
10065     res->alloc_context_count = 0;
10066 
10067 #ifdef MARK_LIST
10068 #ifdef PARALLEL_MARK_LIST_SORT
10069     res->mark_list_piece_start = new (nothrow) uint8_t**[n_heaps];
10070     if (!res->mark_list_piece_start)
10071         return 0;
10072 
10073 #ifdef _PREFAST_
10074 #pragma warning(push)
10075 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10076 #endif // _PREFAST_
10077     res->mark_list_piece_end = new (nothrow) uint8_t**[n_heaps + 32]; // +32 is padding to reduce false sharing
10078 #ifdef _PREFAST_
10079 #pragma warning(pop)
10080 #endif // _PREFAST_
10081 
10082     if (!res->mark_list_piece_end)
10083         return 0;
10084 #endif //PARALLEL_MARK_LIST_SORT
10085 #endif //MARK_LIST
10086 
10087 
10088 #endif //MULTIPLE_HEAPS
10089 
10090     if (res->init_gc_heap (
10091 #ifdef MULTIPLE_HEAPS
10092         heap_number
10093 #else  //MULTIPLE_HEAPS
10094         0
10095 #endif //MULTIPLE_HEAPS
10096         )==0)
10097     {
10098         return 0;
10099     }
10100 
10101 #ifdef MULTIPLE_HEAPS
10102     return res;
10103 #else
10104     return (gc_heap*)1;
10105 #endif //MULTIPLE_HEAPS
10106 }
10107 
10108 uint32_t
wait_for_gc_done(int32_t timeOut)10109 gc_heap::wait_for_gc_done(int32_t timeOut)
10110 {
10111     Thread* current_thread = GetThread();
10112     BOOL cooperative_mode = enable_preemptive (current_thread);
10113 
10114     uint32_t dwWaitResult = NOERROR;
10115 
10116     gc_heap* wait_heap = NULL;
10117     while (gc_heap::gc_started)
10118     {
10119 #ifdef MULTIPLE_HEAPS
10120         wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
10121         dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
10122 #endif // MULTIPLE_HEAPS
10123 
10124 #ifdef _PREFAST_
10125         PREFIX_ASSUME(wait_heap != NULL);
10126 #endif // _PREFAST_
10127 
10128         dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE);
10129     }
10130     disable_preemptive (current_thread, cooperative_mode);
10131 
10132     return dwWaitResult;
10133 }
10134 
10135 void
set_gc_done()10136 gc_heap::set_gc_done()
10137 {
10138     enter_gc_done_event_lock();
10139     if (!gc_done_event_set)
10140     {
10141         gc_done_event_set = true;
10142         dprintf (2, ("heap %d: setting gc_done_event", heap_number));
10143         gc_done_event.Set();
10144     }
10145     exit_gc_done_event_lock();
10146 }
10147 
10148 void
reset_gc_done()10149 gc_heap::reset_gc_done()
10150 {
10151     enter_gc_done_event_lock();
10152     if (gc_done_event_set)
10153     {
10154         gc_done_event_set = false;
10155         dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
10156         gc_done_event.Reset();
10157     }
10158     exit_gc_done_event_lock();
10159 }
10160 
10161 void
enter_gc_done_event_lock()10162 gc_heap::enter_gc_done_event_lock()
10163 {
10164     uint32_t dwSwitchCount = 0;
10165 retry:
10166 
10167     if (Interlocked::Exchange (&gc_done_event_lock, 0) >= 0)
10168     {
10169         while (gc_done_event_lock >= 0)
10170         {
10171             if  (g_SystemInfo.dwNumberOfProcessors > 1)
10172             {
10173                 int spin_count = 32 * g_SystemInfo.dwNumberOfProcessors;
10174                 for (int j = 0; j < spin_count; j++)
10175                 {
10176                     if  (gc_done_event_lock < 0)
10177                         break;
10178                     YieldProcessor();           // indicate to the processor that we are spining
10179                 }
10180                 if  (gc_done_event_lock >= 0)
10181                     GCToOSInterface::YieldThread(++dwSwitchCount);
10182             }
10183             else
10184                 GCToOSInterface::YieldThread(++dwSwitchCount);
10185         }
10186         goto retry;
10187     }
10188 }
10189 
10190 void
exit_gc_done_event_lock()10191 gc_heap::exit_gc_done_event_lock()
10192 {
10193     gc_done_event_lock = -1;
10194 }
10195 
10196 #ifndef MULTIPLE_HEAPS
10197 
10198 #ifdef RECORD_LOH_STATE
10199 int gc_heap::loh_state_index = 0;
10200 gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
10201 #endif //RECORD_LOH_STATE
10202 
10203 VOLATILE(int32_t) gc_heap::gc_done_event_lock;
10204 VOLATILE(bool) gc_heap::gc_done_event_set;
10205 CLREvent gc_heap::gc_done_event;
10206 #endif //!MULTIPLE_HEAPS
10207 VOLATILE(bool) gc_heap::internal_gc_done;
10208 
add_saved_spinlock_info(msl_enter_state enter_state,msl_take_state take_state)10209 void gc_heap::add_saved_spinlock_info (
10210             msl_enter_state enter_state,
10211             msl_take_state take_state)
10212 
10213 {
10214 #ifdef SPINLOCK_HISTORY
10215     spinlock_info* current = &last_spinlock_info[spinlock_info_index];
10216 
10217     current->enter_state = enter_state;
10218     current->take_state = take_state;
10219     current->thread_id.SetToCurrentThread();
10220 
10221     spinlock_info_index++;
10222 
10223     assert (spinlock_info_index <= max_saved_spinlock_info);
10224 
10225     if (spinlock_info_index >= max_saved_spinlock_info)
10226     {
10227         spinlock_info_index = 0;
10228     }
10229 #else
10230     MAYBE_UNUSED_VAR(enter_state);
10231     MAYBE_UNUSED_VAR(take_state);
10232 #endif //SPINLOCK_HISTORY
10233 }
10234 
10235 int
init_gc_heap(int h_number)10236 gc_heap::init_gc_heap (int  h_number)
10237 {
10238 #ifdef MULTIPLE_HEAPS
10239 
10240     time_bgc_last = 0;
10241 
10242 #ifdef SPINLOCK_HISTORY
10243     spinlock_info_index = 0;
10244     memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
10245 #endif //SPINLOCK_HISTORY
10246 
10247     // initialize per heap members.
10248     ephemeral_low = (uint8_t*)1;
10249 
10250     ephemeral_high = MAX_PTR;
10251 
10252     ephemeral_heap_segment = 0;
10253 
10254     freeable_large_heap_segment = 0;
10255 
10256     condemned_generation_num = 0;
10257 
10258     blocking_collection = FALSE;
10259 
10260     generation_skip_ratio = 100;
10261 
10262     mark_stack_tos = 0;
10263 
10264     mark_stack_bos = 0;
10265 
10266     mark_stack_array_length = 0;
10267 
10268     mark_stack_array = 0;
10269 
10270     verify_pinned_queue_p = FALSE;
10271 
10272     loh_pinned_queue_tos = 0;
10273 
10274     loh_pinned_queue_bos = 0;
10275 
10276     loh_pinned_queue_length = 0;
10277 
10278     loh_pinned_queue_decay = LOH_PIN_DECAY;
10279 
10280     loh_pinned_queue = 0;
10281 
10282     min_overflow_address = MAX_PTR;
10283 
10284     max_overflow_address = 0;
10285 
10286     gen0_bricks_cleared = FALSE;
10287 
10288     gen0_must_clear_bricks = 0;
10289 
10290     allocation_quantum = CLR_SIZE;
10291 
10292     more_space_lock = gc_lock;
10293 
10294     ro_segments_in_range = FALSE;
10295 
10296     loh_alloc_since_cg = 0;
10297 
10298     new_heap_segment = NULL;
10299 
10300 #ifdef RECORD_LOH_STATE
10301     loh_state_index = 0;
10302 #endif //RECORD_LOH_STATE
10303 #endif //MULTIPLE_HEAPS
10304 
10305 #ifdef MULTIPLE_HEAPS
10306     if (h_number > n_heaps)
10307     {
10308         assert (!"Number of heaps exceeded");
10309         return 0;
10310     }
10311 
10312     heap_number = h_number;
10313 #endif //MULTIPLE_HEAPS
10314 
10315     memset (&oom_info, 0, sizeof (oom_info));
10316     memset (&fgm_result, 0, sizeof (fgm_result));
10317     if (!gc_done_event.CreateManualEventNoThrow(FALSE))
10318     {
10319         return 0;
10320     }
10321     gc_done_event_lock = -1;
10322     gc_done_event_set = false;
10323 
10324 #ifndef SEG_MAPPING_TABLE
10325     if (!gc_heap::seg_table->ensure_space_for_insert ())
10326     {
10327         return 0;
10328     }
10329 #endif //!SEG_MAPPING_TABLE
10330 
10331     heap_segment* seg = get_initial_segment (get_valid_segment_size(), h_number);
10332     if (!seg)
10333         return 0;
10334 
10335     FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(seg),
10336                               (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)),
10337                               ETW::GCLog::ETW_GC_INFO::SMALL_OBJECT_HEAP,
10338                               GetClrInstanceId());
10339 
10340 #ifdef SEG_MAPPING_TABLE
10341     seg_mapping_table_add_segment (seg, __this);
10342 #else //SEG_MAPPING_TABLE
10343     seg_table->insert ((uint8_t*)seg, sdelta);
10344 #endif //SEG_MAPPING_TABLE
10345 
10346 #ifdef MULTIPLE_HEAPS
10347     heap_segment_heap (seg) = this;
10348 #endif //MULTIPLE_HEAPS
10349 
10350     /* todo: Need a global lock for this */
10351     uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))];
10352     own_card_table (ct);
10353     card_table = translate_card_table (ct);
10354     /* End of global lock */
10355 
10356     brick_table = card_table_brick_table (ct);
10357     highest_address = card_table_highest_address (ct);
10358     lowest_address = card_table_lowest_address (ct);
10359 
10360 #ifdef CARD_BUNDLE
10361     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct));
10362     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
10363             card_table_card_bundle_table (ct));
10364 #endif //CARD_BUNDLE
10365 
10366 #ifdef MARK_ARRAY
10367     if (gc_can_use_concurrent)
10368         mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))]));
10369     else
10370         mark_array = NULL;
10371 #endif //MARK_ARRAY
10372 
10373     uint8_t*  start = heap_segment_mem (seg);
10374 
10375     for (int i = 0; i < 1 + max_generation; i++)
10376     {
10377         make_generation (generation_table [ (max_generation - i) ],
10378                          seg, start, 0);
10379         generation_table [(max_generation - i)].gen_num = max_generation - i;
10380         start += Align (min_obj_size);
10381     }
10382 
10383     heap_segment_allocated (seg) = start;
10384     alloc_allocated = start;
10385     heap_segment_used (seg) = start - plug_skew;
10386 
10387     ephemeral_heap_segment = seg;
10388 
10389 #ifndef SEG_MAPPING_TABLE
10390     if (!gc_heap::seg_table->ensure_space_for_insert ())
10391     {
10392         return 0;
10393     }
10394 #endif //!SEG_MAPPING_TABLE
10395     //Create the large segment generation
10396     heap_segment* lseg = get_initial_segment(get_valid_segment_size(TRUE), h_number);
10397     if (!lseg)
10398         return 0;
10399     lseg->flags |= heap_segment_flags_loh;
10400 
10401     FireEtwGCCreateSegment_V1((size_t)heap_segment_mem(lseg),
10402                               (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)),
10403                               ETW::GCLog::ETW_GC_INFO::LARGE_OBJECT_HEAP,
10404                               GetClrInstanceId());
10405 
10406 #ifdef SEG_MAPPING_TABLE
10407     seg_mapping_table_add_segment (lseg, __this);
10408 #else //SEG_MAPPING_TABLE
10409     seg_table->insert ((uint8_t*)lseg, sdelta);
10410 #endif //SEG_MAPPING_TABLE
10411 
10412     generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
10413     //assign the alloc_list for the large generation
10414     generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
10415     generation_table [max_generation+1].gen_num = max_generation+1;
10416     make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
10417     heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
10418     heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
10419 
10420     for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
10421     {
10422         generation*  gen = generation_of (gen_num);
10423         make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
10424     }
10425 
10426 #ifdef MULTIPLE_HEAPS
10427     heap_segment_heap (lseg) = this;
10428 
10429     //initialize the alloc context heap
10430     generation_alloc_context (generation_of (0))->set_alloc_heap(vm_heap);
10431 
10432     //initialize the alloc context heap
10433     generation_alloc_context (generation_of (max_generation+1))->set_alloc_heap(vm_heap);
10434 
10435 #endif //MULTIPLE_HEAPS
10436 
10437     //Do this only once
10438 #ifdef MULTIPLE_HEAPS
10439     if (h_number == 0)
10440 #endif //MULTIPLE_HEAPS
10441     {
10442 #ifndef INTERIOR_POINTERS
10443         //set the brick_table for large objects
10444         //but default value is clearded
10445         //clear_brick_table ((uint8_t*)heap_segment_mem (lseg),
10446         //                   (uint8_t*)heap_segment_reserved (lseg));
10447 
10448 #else //INTERIOR_POINTERS
10449 
10450         //Because of the interior pointer business, we have to clear
10451         //the whole brick table
10452         //but the default value is cleared
10453         // clear_brick_table (lowest_address, highest_address);
10454 #endif //INTERIOR_POINTERS
10455     }
10456 
10457     if (!init_dynamic_data())
10458     {
10459         return 0;
10460     }
10461 
10462     etw_allocation_running_amount[0] = 0;
10463     etw_allocation_running_amount[1] = 0;
10464 
10465     //needs to be done after the dynamic data has been initialized
10466 #ifndef MULTIPLE_HEAPS
10467     allocation_running_amount = dd_min_gc_size (dynamic_data_of (0));
10468 #endif //!MULTIPLE_HEAPS
10469 
10470     fgn_last_alloc = dd_min_gc_size (dynamic_data_of (0));
10471 
10472     mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
10473     if (!arr)
10474         return 0;
10475 
10476     make_mark_stack(arr);
10477 
10478 #ifdef BACKGROUND_GC
10479     freeable_small_heap_segment = 0;
10480     gchist_index_per_heap = 0;
10481     uint8_t** b_arr = new (nothrow) (uint8_t* [MARK_STACK_INITIAL_LENGTH]);
10482     if (!b_arr)
10483         return 0;
10484 
10485     make_background_mark_stack (b_arr);
10486 #endif //BACKGROUND_GC
10487 
10488     adjust_ephemeral_limits();
10489 
10490 #ifdef MARK_ARRAY
10491     // why would we clear the mark array for this page? it should be cleared..
10492     // clear the first committed page
10493     //if(gc_can_use_concurrent)
10494     //{
10495     //    clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10496     //}
10497 #endif //MARK_ARRAY
10498 
10499 #ifdef MULTIPLE_HEAPS
10500     //register the heap in the heaps array
10501 
10502     if (!create_gc_thread ())
10503         return 0;
10504 
10505     g_heaps [heap_number] = this;
10506 
10507 #endif //MULTIPLE_HEAPS
10508 
10509 #ifdef FEATURE_PREMORTEM_FINALIZATION
10510     HRESULT hr = AllocateCFinalize(&finalize_queue);
10511     if (FAILED(hr))
10512         return 0;
10513 #endif // FEATURE_PREMORTEM_FINALIZATION
10514 
10515     max_free_space_items = MAX_NUM_FREE_SPACES;
10516 
10517     bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10518 
10519     if (!bestfit_seg)
10520     {
10521         return 0;
10522     }
10523 
10524     if (!bestfit_seg->alloc())
10525     {
10526         return 0;
10527     }
10528 
10529     last_gc_before_oom = FALSE;
10530 
10531 #ifdef MULTIPLE_HEAPS
10532 
10533 #ifdef HEAP_ANALYZE
10534 
10535     heap_analyze_success = TRUE;
10536 
10537     internal_root_array  = 0;
10538 
10539     internal_root_array_index = 0;
10540 
10541     internal_root_array_length = initial_internal_roots;
10542 
10543     current_obj          = 0;
10544 
10545     current_obj_size     = 0;
10546 
10547 #endif //HEAP_ANALYZE
10548 
10549 #endif // MULTIPLE_HEAPS
10550 
10551 #ifdef BACKGROUND_GC
10552     bgc_thread_id.Clear();
10553 
10554     if (!create_bgc_thread_support())
10555     {
10556         return 0;
10557     }
10558 
10559     bgc_alloc_lock = new (nothrow) exclusive_sync;
10560     if (!bgc_alloc_lock)
10561     {
10562         return 0;
10563     }
10564 
10565     bgc_alloc_lock->init();
10566 
10567     if (h_number == 0)
10568     {
10569         if (!recursive_gc_sync::init())
10570             return 0;
10571     }
10572 
10573     bgc_thread_running = 0;
10574     bgc_thread = 0;
10575     bgc_threads_timeout_cs.Initialize();
10576     expanded_in_fgc = 0;
10577     current_bgc_state = bgc_not_in_process;
10578     background_soh_alloc_count = 0;
10579     background_loh_alloc_count = 0;
10580     bgc_overflow_count = 0;
10581     end_loh_size = dd_min_gc_size (dynamic_data_of (max_generation + 1));
10582 #endif //BACKGROUND_GC
10583 
10584 #ifdef GC_CONFIG_DRIVEN
10585     memset (interesting_data_per_heap, 0, sizeof (interesting_data_per_heap));
10586     memset(compact_reasons_per_heap, 0, sizeof (compact_reasons_per_heap));
10587     memset(expand_mechanisms_per_heap, 0, sizeof (expand_mechanisms_per_heap));
10588     memset(interesting_mechanism_bits_per_heap, 0, sizeof (interesting_mechanism_bits_per_heap));
10589 #endif //GC_CONFIG_DRIVEN
10590 
10591     return 1;
10592 }
10593 
10594 void
destroy_semi_shared()10595 gc_heap::destroy_semi_shared()
10596 {
10597 //TODO: will need to move this to per heap
10598 //#ifdef BACKGROUND_GC
10599 //    if (c_mark_list)
10600 //        delete c_mark_list;
10601 //#endif //BACKGROUND_GC
10602 
10603 #ifdef MARK_LIST
10604     if (g_mark_list)
10605         delete g_mark_list;
10606 #endif //MARK_LIST
10607 
10608 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10609     if (seg_mapping_table)
10610         delete seg_mapping_table;
10611 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10612 
10613 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10614     //destroy the segment map
10615     seg_table->delete_sorted_table();
10616 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10617 }
10618 
10619 void
self_destroy()10620 gc_heap::self_destroy()
10621 {
10622 #ifdef BACKGROUND_GC
10623     kill_gc_thread();
10624 #endif //BACKGROUND_GC
10625 
10626     if (gc_done_event.IsValid())
10627     {
10628         gc_done_event.CloseEvent();
10629     }
10630 
10631     // destroy every segment.
10632     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10633 
10634     PREFIX_ASSUME(seg != NULL);
10635 
10636     heap_segment* next_seg;
10637     while (seg)
10638     {
10639         next_seg = heap_segment_next_rw (seg);
10640         delete_heap_segment (seg);
10641         seg = next_seg;
10642     }
10643 
10644     seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10645 
10646     PREFIX_ASSUME(seg != NULL);
10647 
10648     while (seg)
10649     {
10650         next_seg = heap_segment_next_rw (seg);
10651         delete_heap_segment (seg);
10652         seg = next_seg;
10653     }
10654 
10655     // get rid of the card table
10656     release_card_table (card_table);
10657 
10658     // destroy the mark stack
10659     delete mark_stack_array;
10660 
10661 #ifdef FEATURE_PREMORTEM_FINALIZATION
10662     if (finalize_queue)
10663         delete finalize_queue;
10664 #endif // FEATURE_PREMORTEM_FINALIZATION
10665 }
10666 
10667 void
destroy_gc_heap(gc_heap * heap)10668 gc_heap::destroy_gc_heap(gc_heap* heap)
10669 {
10670     heap->self_destroy();
10671     delete heap;
10672 }
10673 
10674 // Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10675 // the finalizer queue has been drained.
shutdown_gc()10676 void gc_heap::shutdown_gc()
10677 {
10678     destroy_semi_shared();
10679 
10680 #ifdef MULTIPLE_HEAPS
10681     //delete the heaps array
10682     delete g_heaps;
10683     destroy_thread_support();
10684     n_heaps = 0;
10685 #endif //MULTIPLE_HEAPS
10686     //destroy seg_manager
10687 
10688     destroy_initial_memory();
10689 
10690     GCToOSInterface::Shutdown();
10691 }
10692 
10693 inline
size_fit_p(size_t size REQD_ALIGN_AND_OFFSET_DCL,uint8_t * alloc_pointer,uint8_t * alloc_limit,uint8_t * old_loc,int use_padding)10694 BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10695                           uint8_t* old_loc, int use_padding)
10696 {
10697     BOOL already_padded = FALSE;
10698 #ifdef SHORT_PLUGS
10699     if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
10700     {
10701         alloc_pointer = alloc_pointer + Align (min_obj_size);
10702         already_padded = TRUE;
10703     }
10704 #endif //SHORT_PLUGS
10705 
10706     if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
10707         size = size + switch_alignment_size (already_padded);
10708 
10709 #ifdef FEATURE_STRUCTALIGN
10710     alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
10711 #endif // FEATURE_STRUCTALIGN
10712 
10713     // in allocate_in_condemned_generation we can have this when we
10714     // set the alloc_limit to plan_allocated which could be less than
10715     // alloc_ptr
10716     if (alloc_limit < alloc_pointer)
10717     {
10718         return FALSE;
10719     }
10720 
10721     if (old_loc != 0)
10722     {
10723         return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0)))
10724 #ifdef SHORT_PLUGS
10725                 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
10726 #else //SHORT_PLUGS
10727                 ||((alloc_pointer + size) == alloc_limit)
10728 #endif //SHORT_PLUGS
10729             );
10730     }
10731     else
10732     {
10733         assert (size == Align (min_obj_size));
10734         return ((size_t)(alloc_limit - alloc_pointer) >= size);
10735     }
10736 }
10737 
10738 inline
a_size_fit_p(size_t size,uint8_t * alloc_pointer,uint8_t * alloc_limit,int align_const)10739 BOOL gc_heap::a_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10740                             int align_const)
10741 {
10742     // We could have run into cases where this is true when alloc_allocated is the
10743     // the same as the seg committed.
10744     if (alloc_limit < alloc_pointer)
10745     {
10746         return FALSE;
10747     }
10748 
10749     return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
10750 }
10751 
10752 // Grow by committing more pages
grow_heap_segment(heap_segment * seg,uint8_t * high_address)10753 BOOL gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* high_address)
10754 {
10755     assert (high_address <= heap_segment_reserved (seg));
10756 
10757     //return 0 if we are at the end of the segment.
10758     if (align_on_page (high_address) > heap_segment_reserved (seg))
10759         return FALSE;
10760 
10761     if (high_address <= heap_segment_committed (seg))
10762         return TRUE;
10763 
10764     size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
10765     c_size = max (c_size, 16*OS_PAGE_SIZE);
10766     c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
10767 
10768     if (c_size == 0)
10769         return FALSE;
10770 
10771     STRESS_LOG2(LF_GC, LL_INFO10000,
10772                 "Growing heap_segment: %Ix high address: %Ix\n",
10773                 (size_t)seg, (size_t)high_address);
10774 
10775     dprintf(3, ("Growing segment allocation %Ix %Ix", (size_t)heap_segment_committed(seg),c_size));
10776 
10777     if (!virtual_alloc_commit_for_heap(heap_segment_committed (seg), c_size, heap_number))
10778     {
10779         dprintf(3, ("Cannot grow heap segment"));
10780         return FALSE;
10781     }
10782 #ifdef MARK_ARRAY
10783 #ifndef BACKGROUND_GC
10784     clear_mark_array (heap_segment_committed (seg),
10785                       heap_segment_committed (seg)+c_size, TRUE);
10786 #endif //BACKGROUND_GC
10787 #endif //MARK_ARRAY
10788     heap_segment_committed (seg) += c_size;
10789     STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
10790                 (size_t)heap_segment_committed (seg));
10791 
10792     assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
10793 
10794     assert (high_address <= heap_segment_committed (seg));
10795 
10796     return TRUE;
10797 }
10798 
10799 inline
grow_heap_segment(heap_segment * seg,uint8_t * allocated,uint8_t * old_loc,size_t size,BOOL pad_front_p REQD_ALIGN_AND_OFFSET_DCL)10800 int gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* allocated, uint8_t* old_loc, size_t size, BOOL pad_front_p  REQD_ALIGN_AND_OFFSET_DCL)
10801 {
10802 #ifdef SHORT_PLUGS
10803     if ((old_loc != 0) && pad_front_p)
10804     {
10805         allocated = allocated + Align (min_obj_size);
10806     }
10807 #endif //SHORT_PLUGS
10808 
10809     if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
10810         size = size + switch_alignment_size (FALSE);
10811 #ifdef FEATURE_STRUCTALIGN
10812     size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
10813     return grow_heap_segment (seg, allocated + pad + size);
10814 #else // FEATURE_STRUCTALIGN
10815     return grow_heap_segment (seg, allocated + size);
10816 #endif // FEATURE_STRUCTALIGN
10817 }
10818 
10819 //used only in older generation allocation (i.e during gc).
adjust_limit(uint8_t * start,size_t limit_size,generation * gen,int gennum)10820 void gc_heap::adjust_limit (uint8_t* start, size_t limit_size, generation* gen,
10821                             int gennum)
10822 {
10823     UNREFERENCED_PARAMETER(gennum);
10824     dprintf (3, ("gc Expanding segment allocation"));
10825     heap_segment* seg = generation_allocation_segment (gen);
10826     if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
10827     {
10828         if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
10829         {
10830             assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
10831             assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
10832             heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
10833         }
10834         else
10835         {
10836             uint8_t*  hole = generation_allocation_pointer (gen);
10837             size_t  size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
10838 
10839             if (size != 0)
10840             {
10841                 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
10842                 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
10843                 if (size >= Align (min_free_list))
10844                 {
10845                     if (allocated_size < min_free_list)
10846                     {
10847                         if (size >= (Align (min_free_list) + Align (min_obj_size)))
10848                         {
10849                             //split hole into min obj + threadable free item
10850                             make_unused_array (hole, min_obj_size);
10851                             generation_free_obj_space (gen) += Align (min_obj_size);
10852                             make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
10853                             generation_free_list_space (gen) += size - Align (min_obj_size);
10854                             generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size),
10855                                                                           size - Align (min_obj_size));
10856                             add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
10857                         }
10858                         else
10859                         {
10860                             dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
10861                             make_unused_array (hole, size);
10862                             generation_free_obj_space (gen) += size;
10863                         }
10864                     }
10865                     else
10866                     {
10867                         dprintf (3, ("threading hole in front of free list"));
10868                         make_unused_array (hole, size);
10869                         generation_free_list_space (gen) += size;
10870                         generation_allocator(gen)->thread_item_front (hole, size);
10871                         add_gen_free (gen->gen_num, size);
10872                     }
10873                 }
10874                 else
10875                 {
10876                     make_unused_array (hole, size);
10877                     generation_free_obj_space (gen) += size;
10878                 }
10879             }
10880         }
10881         generation_allocation_pointer (gen) = start;
10882         generation_allocation_context_start_region (gen) = start;
10883     }
10884     generation_allocation_limit (gen) = (start + limit_size);
10885 }
10886 
verify_mem_cleared(uint8_t * start,size_t size)10887 void verify_mem_cleared (uint8_t* start, size_t size)
10888 {
10889     if (!Aligned (size))
10890     {
10891         FATAL_GC_ERROR();
10892     }
10893 
10894     PTR_PTR curr_ptr = (PTR_PTR) start;
10895     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
10896     {
10897         if (*(curr_ptr++) != 0)
10898         {
10899             FATAL_GC_ERROR();
10900         }
10901     }
10902 }
10903 
10904 #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
set_batch_mark_array_bits(uint8_t * start,uint8_t * end)10905 void gc_heap::set_batch_mark_array_bits (uint8_t* start, uint8_t* end)
10906 {
10907     size_t start_mark_bit = mark_bit_of (start);
10908     size_t end_mark_bit = mark_bit_of (end);
10909     unsigned int startbit = mark_bit_bit (start_mark_bit);
10910     unsigned int endbit = mark_bit_bit (end_mark_bit);
10911     size_t startwrd = mark_bit_word (start_mark_bit);
10912     size_t endwrd = mark_bit_word (end_mark_bit);
10913 
10914     dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
10915         (size_t)start, (size_t)start_mark_bit,
10916         (size_t)end, (size_t)end_mark_bit));
10917 
10918     unsigned int firstwrd = ~(lowbits (~0, startbit));
10919     unsigned int lastwrd = ~(highbits (~0, endbit));
10920 
10921     if (startwrd == endwrd)
10922     {
10923         unsigned int wrd = firstwrd & lastwrd;
10924         mark_array[startwrd] |= wrd;
10925         return;
10926     }
10927 
10928     // set the first mark word.
10929     if (startbit)
10930     {
10931         mark_array[startwrd] |= firstwrd;
10932         startwrd++;
10933     }
10934 
10935     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
10936     {
10937         mark_array[wrdtmp] = ~(unsigned int)0;
10938     }
10939 
10940     // set the last mark word.
10941     if (endbit)
10942     {
10943         mark_array[endwrd] |= lastwrd;
10944     }
10945 }
10946 
10947 // makes sure that the mark array bits between start and end are 0.
check_batch_mark_array_bits(uint8_t * start,uint8_t * end)10948 void gc_heap::check_batch_mark_array_bits (uint8_t* start, uint8_t* end)
10949 {
10950     size_t start_mark_bit = mark_bit_of (start);
10951     size_t end_mark_bit = mark_bit_of (end);
10952     unsigned int startbit = mark_bit_bit (start_mark_bit);
10953     unsigned int endbit = mark_bit_bit (end_mark_bit);
10954     size_t startwrd = mark_bit_word (start_mark_bit);
10955     size_t endwrd = mark_bit_word (end_mark_bit);
10956 
10957     //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
10958     //    (size_t)start, (size_t)start_mark_bit,
10959     //    (size_t)end, (size_t)end_mark_bit));
10960 
10961     unsigned int firstwrd = ~(lowbits (~0, startbit));
10962     unsigned int lastwrd = ~(highbits (~0, endbit));
10963 
10964     if (startwrd == endwrd)
10965     {
10966         unsigned int wrd = firstwrd & lastwrd;
10967         if (mark_array[startwrd] & wrd)
10968         {
10969             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
10970                             wrd, startwrd,
10971                             mark_array [startwrd], mark_word_address (startwrd)));
10972             FATAL_GC_ERROR();
10973         }
10974         return;
10975     }
10976 
10977     // set the first mark word.
10978     if (startbit)
10979     {
10980         if (mark_array[startwrd] & firstwrd)
10981         {
10982             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
10983                             firstwrd, startwrd,
10984                             mark_array [startwrd], mark_word_address (startwrd)));
10985             FATAL_GC_ERROR();
10986         }
10987 
10988         startwrd++;
10989     }
10990 
10991     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
10992     {
10993         if (mark_array[wrdtmp])
10994         {
10995             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
10996                             wrdtmp,
10997                             mark_array [wrdtmp], mark_word_address (wrdtmp)));
10998             FATAL_GC_ERROR();
10999         }
11000     }
11001 
11002     // set the last mark word.
11003     if (endbit)
11004     {
11005         if (mark_array[endwrd] & lastwrd)
11006         {
11007             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
11008                             lastwrd, lastwrd,
11009                             mark_array [lastwrd], mark_word_address (lastwrd)));
11010             FATAL_GC_ERROR();
11011         }
11012     }
11013 }
11014 #endif //VERIFY_HEAP && BACKGROUND_GC
11015 
allocator(unsigned int num_b,size_t fbs,alloc_list * b)11016 allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
11017 {
11018     assert (num_b < MAX_BUCKET_COUNT);
11019     num_buckets = num_b;
11020     frst_bucket_size = fbs;
11021     buckets = b;
11022 }
11023 
alloc_list_of(unsigned int bn)11024 alloc_list& allocator::alloc_list_of (unsigned int bn)
11025 {
11026     assert (bn < num_buckets);
11027     if (bn == 0)
11028         return first_bucket;
11029     else
11030         return buckets [bn-1];
11031 }
11032 
alloc_list_damage_count_of(unsigned int bn)11033 size_t& allocator::alloc_list_damage_count_of (unsigned int bn)
11034 {
11035     assert (bn < num_buckets);
11036     if (bn == 0)
11037         return first_bucket.alloc_list_damage_count();
11038     else
11039         return buckets [bn-1].alloc_list_damage_count();
11040 }
11041 
unlink_item(unsigned int bn,uint8_t * item,uint8_t * prev_item,BOOL use_undo_p)11042 void allocator::unlink_item (unsigned int bn, uint8_t* item, uint8_t* prev_item, BOOL use_undo_p)
11043 {
11044     //unlink the free_item
11045     alloc_list* al = &alloc_list_of (bn);
11046     if (prev_item)
11047     {
11048         if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
11049         {
11050             assert (item == free_list_slot (prev_item));
11051             free_list_undo (prev_item) = item;
11052             alloc_list_damage_count_of (bn)++;
11053         }
11054         free_list_slot (prev_item) = free_list_slot(item);
11055     }
11056     else
11057     {
11058         al->alloc_list_head() = (uint8_t*)free_list_slot(item);
11059     }
11060     if (al->alloc_list_tail() == item)
11061     {
11062         al->alloc_list_tail() = prev_item;
11063     }
11064 }
11065 
clear()11066 void allocator::clear()
11067 {
11068     for (unsigned int i = 0; i < num_buckets; i++)
11069     {
11070         alloc_list_head_of (i) = 0;
11071         alloc_list_tail_of (i) = 0;
11072     }
11073 }
11074 
11075 //always thread to the end.
thread_free_item(uint8_t * item,uint8_t * & head,uint8_t * & tail)11076 void allocator::thread_free_item (uint8_t* item, uint8_t*& head, uint8_t*& tail)
11077 {
11078     free_list_slot (item) = 0;
11079     free_list_undo (item) = UNDO_EMPTY;
11080     assert (item != head);
11081 
11082     if (head == 0)
11083     {
11084        head = item;
11085     }
11086     //TODO: This shouldn't happen anymore - verify that's the case.
11087     //the following is necessary because the last free element
11088     //may have been truncated, and tail isn't updated.
11089     else if (free_list_slot (head) == 0)
11090     {
11091         free_list_slot (head) = item;
11092     }
11093     else
11094     {
11095         assert (item != tail);
11096         assert (free_list_slot(tail) == 0);
11097         free_list_slot (tail) = item;
11098     }
11099     tail = item;
11100 }
11101 
thread_item(uint8_t * item,size_t size)11102 void allocator::thread_item (uint8_t* item, size_t size)
11103 {
11104     size_t sz = frst_bucket_size;
11105     unsigned int a_l_number = 0;
11106 
11107     for (; a_l_number < (num_buckets-1); a_l_number++)
11108     {
11109         if (size < sz)
11110         {
11111             break;
11112         }
11113         sz = sz * 2;
11114     }
11115     alloc_list* al = &alloc_list_of (a_l_number);
11116     thread_free_item (item,
11117                       al->alloc_list_head(),
11118                       al->alloc_list_tail());
11119 }
11120 
thread_item_front(uint8_t * item,size_t size)11121 void allocator::thread_item_front (uint8_t* item, size_t size)
11122 {
11123     //find right free list
11124     size_t sz = frst_bucket_size;
11125     unsigned int a_l_number = 0;
11126     for (; a_l_number < (num_buckets-1); a_l_number++)
11127     {
11128         if (size < sz)
11129         {
11130             break;
11131         }
11132         sz = sz * 2;
11133     }
11134     alloc_list* al = &alloc_list_of (a_l_number);
11135     free_list_slot (item) = al->alloc_list_head();
11136     free_list_undo (item) = UNDO_EMPTY;
11137 
11138     if (al->alloc_list_tail() == 0)
11139     {
11140         al->alloc_list_tail() = al->alloc_list_head();
11141     }
11142     al->alloc_list_head() = item;
11143     if (al->alloc_list_tail() == 0)
11144     {
11145         al->alloc_list_tail() = item;
11146     }
11147 }
11148 
copy_to_alloc_list(alloc_list * toalist)11149 void allocator::copy_to_alloc_list (alloc_list* toalist)
11150 {
11151     for (unsigned int i = 0; i < num_buckets; i++)
11152     {
11153         toalist [i] = alloc_list_of (i);
11154 #ifdef FL_VERIFICATION
11155         uint8_t* free_item = alloc_list_head_of (i);
11156         size_t count = 0;
11157         while (free_item)
11158         {
11159             count++;
11160             free_item = free_list_slot (free_item);
11161         }
11162 
11163         toalist[i].item_count = count;
11164 #endif //FL_VERIFICATION
11165     }
11166 }
11167 
copy_from_alloc_list(alloc_list * fromalist)11168 void allocator::copy_from_alloc_list (alloc_list* fromalist)
11169 {
11170     BOOL repair_list = !discard_if_no_fit_p ();
11171     for (unsigned int i = 0; i < num_buckets; i++)
11172     {
11173         size_t count = alloc_list_damage_count_of (i);
11174         alloc_list_of (i) = fromalist [i];
11175         assert (alloc_list_damage_count_of (i) == 0);
11176 
11177         if (repair_list)
11178         {
11179             //repair the the list
11180             //new items may have been added during the plan phase
11181             //items may have been unlinked.
11182             uint8_t* free_item = alloc_list_head_of (i);
11183             while (free_item && count)
11184             {
11185                 assert (((CObjectHeader*)free_item)->IsFree());
11186                 if ((free_list_undo (free_item) != UNDO_EMPTY))
11187                 {
11188                     count--;
11189                     free_list_slot (free_item) = free_list_undo (free_item);
11190                     free_list_undo (free_item) = UNDO_EMPTY;
11191                 }
11192 
11193                 free_item = free_list_slot (free_item);
11194             }
11195 
11196 #ifdef FL_VERIFICATION
11197             free_item = alloc_list_head_of (i);
11198             size_t item_count = 0;
11199             while (free_item)
11200             {
11201                 item_count++;
11202                 free_item = free_list_slot (free_item);
11203             }
11204 
11205             assert (item_count == alloc_list_of (i).item_count);
11206 #endif //FL_VERIFICATION
11207         }
11208 #ifdef DEBUG
11209         uint8_t* tail_item = alloc_list_tail_of (i);
11210         assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
11211 #endif
11212     }
11213 }
11214 
commit_alloc_list_changes()11215 void allocator::commit_alloc_list_changes()
11216 {
11217     BOOL repair_list = !discard_if_no_fit_p ();
11218     if (repair_list)
11219     {
11220         for (unsigned int i = 0; i < num_buckets; i++)
11221         {
11222             //remove the undo info from list.
11223             uint8_t* free_item = alloc_list_head_of (i);
11224             size_t count = alloc_list_damage_count_of (i);
11225             while (free_item && count)
11226             {
11227                 assert (((CObjectHeader*)free_item)->IsFree());
11228 
11229                 if (free_list_undo (free_item) != UNDO_EMPTY)
11230                 {
11231                     free_list_undo (free_item) = UNDO_EMPTY;
11232                     count--;
11233                 }
11234 
11235                 free_item = free_list_slot (free_item);
11236             }
11237 
11238             alloc_list_damage_count_of (i) = 0;
11239         }
11240     }
11241 }
11242 
adjust_limit_clr(uint8_t * start,size_t limit_size,alloc_context * acontext,heap_segment * seg,int align_const,int gen_number)11243 void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size,
11244                                 alloc_context* acontext, heap_segment* seg,
11245                                 int align_const, int gen_number)
11246 {
11247     size_t aligned_min_obj_size = Align(min_obj_size, align_const);
11248 
11249     //probably should pass seg==0 for free lists.
11250     if (seg)
11251     {
11252         assert (heap_segment_used (seg) <= heap_segment_committed (seg));
11253     }
11254 
11255     dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
11256                (size_t)start + limit_size - aligned_min_obj_size));
11257 
11258     if ((acontext->alloc_limit != start) &&
11259         (acontext->alloc_limit + aligned_min_obj_size)!= start)
11260     {
11261         uint8_t*  hole = acontext->alloc_ptr;
11262         if (hole != 0)
11263         {
11264             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
11265             dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
11266             // when we are finishing an allocation from a free list
11267             // we know that the free area was Align(min_obj_size) larger
11268             acontext->alloc_bytes -= size;
11269             size_t free_obj_size = size + aligned_min_obj_size;
11270             make_unused_array (hole, free_obj_size);
11271             generation_free_obj_space (generation_of (gen_number)) += free_obj_size;
11272         }
11273         acontext->alloc_ptr = start;
11274     }
11275     acontext->alloc_limit = (start + limit_size - aligned_min_obj_size);
11276     acontext->alloc_bytes += limit_size - ((gen_number < max_generation + 1) ? aligned_min_obj_size : 0);
11277 
11278 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11279     if (g_fEnableARM)
11280     {
11281         AppDomain* alloc_appdomain = GetAppDomain();
11282         alloc_appdomain->RecordAllocBytes (limit_size, heap_number);
11283     }
11284 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11285 
11286     uint8_t* saved_used = 0;
11287 
11288     if (seg)
11289     {
11290         saved_used = heap_segment_used (seg);
11291     }
11292 
11293     if (seg == ephemeral_heap_segment)
11294     {
11295         //Sometimes the allocated size is advanced without clearing the
11296         //memory. Let's catch up here
11297         if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
11298         {
11299 #ifdef MARK_ARRAY
11300 #ifndef BACKGROUND_GC
11301             clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
11302 #endif //BACKGROUND_GC
11303 #endif //MARK_ARRAY
11304             heap_segment_used (seg) = alloc_allocated - plug_skew;
11305         }
11306     }
11307 #ifdef BACKGROUND_GC
11308     else if (seg)
11309     {
11310         uint8_t* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
11311 #ifdef FEATURE_LOH_COMPACTION
11312         old_allocated -= Align (loh_padding_obj_size, align_const);
11313 #endif //FEATURE_LOH_COMPACTION
11314 
11315         assert (heap_segment_used (seg) >= old_allocated);
11316     }
11317 #endif //BACKGROUND_GC
11318     if ((seg == 0) ||
11319         (start - plug_skew + limit_size) <= heap_segment_used (seg))
11320     {
11321         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory(1)", heap_number));
11322         add_saved_spinlock_info (me_release, mt_clr_mem);
11323         leave_spin_lock (&more_space_lock);
11324         dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
11325         memclr (start - plug_skew, limit_size);
11326     }
11327     else
11328     {
11329         uint8_t* used = heap_segment_used (seg);
11330         heap_segment_used (seg) = start + limit_size - plug_skew;
11331 
11332         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear memory", heap_number));
11333         add_saved_spinlock_info (me_release, mt_clr_mem);
11334         leave_spin_lock (&more_space_lock);
11335         if ((start - plug_skew) < used)
11336         {
11337             if (used != saved_used)
11338             {
11339                 FATAL_GC_ERROR ();
11340             }
11341 
11342             dprintf (2, ("clearing memory before used at %Ix for %Id bytes",
11343                 (start - plug_skew), (plug_skew + used - start)));
11344             memclr (start - plug_skew, used - (start - plug_skew));
11345         }
11346     }
11347 
11348     //this portion can be done after we release the lock
11349     if (seg == ephemeral_heap_segment)
11350     {
11351 #ifdef FFIND_OBJECT
11352         if (gen0_must_clear_bricks > 0)
11353         {
11354             //set the brick table to speed up find_object
11355             size_t b = brick_of (acontext->alloc_ptr);
11356             set_brick (b, acontext->alloc_ptr - brick_address (b));
11357             b++;
11358             dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
11359                          b, brick_of (align_on_brick (start + limit_size))));
11360             volatile short* x = &brick_table [b];
11361             short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
11362 
11363             for (;x < end_x;x++)
11364                 *x = -1;
11365         }
11366         else
11367 #endif //FFIND_OBJECT
11368         {
11369             gen0_bricks_cleared = FALSE;
11370         }
11371     }
11372 
11373     // verifying the memory is completely cleared.
11374     //verify_mem_cleared (start - plug_skew, limit_size);
11375 }
11376 
11377 /* in order to make the allocator faster, allocate returns a
11378  * 0 filled object. Care must be taken to set the allocation limit to the
11379  * allocation pointer after gc
11380  */
11381 
limit_from_size(size_t size,size_t room,int gen_number,int align_const)11382 size_t gc_heap::limit_from_size (size_t size, size_t room, int gen_number,
11383                                  int align_const)
11384 {
11385     size_t new_limit = new_allocation_limit ((size + Align (min_obj_size, align_const)),
11386                                              min (room,max (size + Align (min_obj_size, align_const),
11387                                                             ((gen_number < max_generation+1) ?
11388                                                              allocation_quantum :
11389                                                              0))),
11390                                              gen_number);
11391     assert (new_limit >= (size + Align (min_obj_size, align_const)));
11392     dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
11393     return new_limit;
11394 }
11395 
handle_oom(int heap_num,oom_reason reason,size_t alloc_size,uint8_t * allocated,uint8_t * reserved)11396 void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size,
11397                           uint8_t* allocated, uint8_t* reserved)
11398 {
11399     dprintf (1, ("total committed on the heap is %Id", get_total_committed_size()));
11400 
11401     UNREFERENCED_PARAMETER(heap_num);
11402 
11403     if (reason == oom_budget)
11404     {
11405         alloc_size = dd_min_gc_size (dynamic_data_of (0)) / 2;
11406     }
11407 
11408     if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
11409     {
11410         // This means during the last GC we needed to reserve and/or commit more memory
11411         // but we couldn't. We proceeded with the GC and ended up not having enough
11412         // memory at the end. This is a legitimate OOM situtation. Otherwise we
11413         // probably made a mistake and didn't expand the heap when we should have.
11414         reason = oom_low_mem;
11415     }
11416 
11417     oom_info.reason = reason;
11418     oom_info.allocated = allocated;
11419     oom_info.reserved = reserved;
11420     oom_info.alloc_size = alloc_size;
11421     oom_info.gc_index = settings.gc_index;
11422     oom_info.fgm = fgm_result.fgm;
11423     oom_info.size = fgm_result.size;
11424     oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
11425     oom_info.loh_p = fgm_result.loh_p;
11426 
11427     fgm_result.fgm = fgm_no_failure;
11428 
11429     // Break early - before the more_space_lock is release so no other threads
11430     // could have allocated on the same heap when OOM happened.
11431     if (g_pConfig->IsGCBreakOnOOMEnabled())
11432     {
11433         GCToOSInterface::DebugBreak();
11434     }
11435 }
11436 
11437 #ifdef BACKGROUND_GC
background_allowed_p()11438 BOOL gc_heap::background_allowed_p()
11439 {
11440     return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
11441 }
11442 #endif //BACKGROUND_GC
11443 
check_for_full_gc(int gen_num,size_t size)11444 void gc_heap::check_for_full_gc (int gen_num, size_t size)
11445 {
11446     BOOL should_notify = FALSE;
11447     // if we detect full gc because of the allocation budget specified this is TRUE;
11448     // it's FALSE if it's due to other factors.
11449     BOOL alloc_factor = TRUE;
11450     int i = 0;
11451     int n = 0;
11452     int n_initial = gen_num;
11453     BOOL local_blocking_collection = FALSE;
11454     BOOL local_elevation_requested = FALSE;
11455     int new_alloc_remain_percent = 0;
11456 
11457     if (full_gc_approach_event_set)
11458     {
11459         return;
11460     }
11461 
11462     if (gen_num != (max_generation + 1))
11463     {
11464         gen_num = max_generation;
11465     }
11466 
11467     dynamic_data* dd_full = dynamic_data_of (gen_num);
11468     ptrdiff_t new_alloc_remain = 0;
11469     uint32_t pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
11470 
11471     for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
11472     {
11473         dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)",
11474                      heap_number, gen_index,
11475                      dd_new_allocation (dynamic_data_of (gen_index)),
11476                      dd_desired_allocation (dynamic_data_of (gen_index))));
11477     }
11478 
11479     // For small object allocations we only check every fgn_check_quantum bytes.
11480     if (n_initial == 0)
11481     {
11482         dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
11483         dynamic_data* dd_0 = dynamic_data_of (n_initial);
11484         if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
11485             (dd_new_allocation (dd_0) >= 0))
11486         {
11487             return;
11488         }
11489         else
11490         {
11491             fgn_last_alloc = dd_new_allocation (dd_0);
11492             dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
11493         }
11494 
11495         // We don't consider the size that came from soh 'cause it doesn't contribute to the
11496         // gen2 budget.
11497         size = 0;
11498     }
11499 
11500     for (i = n+1; i <= max_generation; i++)
11501     {
11502         if (get_new_allocation (i) <= 0)
11503         {
11504             n = min (i, max_generation);
11505         }
11506         else
11507             break;
11508     }
11509 
11510     dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
11511     if (gen_num == max_generation)
11512     {
11513         // If it's small object heap we should first see if we will even be looking at gen2 budget
11514         // in the next GC or not. If not we should go directly to checking other factors.
11515         if (n < (max_generation - 1))
11516         {
11517             goto check_other_factors;
11518         }
11519     }
11520 
11521     new_alloc_remain = dd_new_allocation (dd_full) - size;
11522 
11523     new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
11524 
11525     dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%",
11526                  gen_num, pct, new_alloc_remain_percent));
11527 
11528     if (new_alloc_remain_percent <= (int)pct)
11529     {
11530 #ifdef BACKGROUND_GC
11531         // If background GC is enabled, we still want to check whether this will
11532         // be a blocking GC or not because we only want to notify when it's a
11533         // blocking full GC.
11534         if (background_allowed_p())
11535         {
11536             goto check_other_factors;
11537         }
11538 #endif //BACKGROUND_GC
11539 
11540         should_notify = TRUE;
11541         goto done;
11542     }
11543 
11544 check_other_factors:
11545 
11546     dprintf (2, ("FGC: checking other factors"));
11547     n = generation_to_condemn (n,
11548                                &local_blocking_collection,
11549                                &local_elevation_requested,
11550                                TRUE);
11551 
11552     if (local_elevation_requested && (n == max_generation))
11553     {
11554         if (settings.should_lock_elevation)
11555         {
11556             int local_elevation_locked_count = settings.elevation_locked_count + 1;
11557             if (local_elevation_locked_count != 6)
11558             {
11559                 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1",
11560                     local_elevation_locked_count));
11561                 n = max_generation - 1;
11562             }
11563         }
11564     }
11565 
11566     dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11567 
11568 #ifdef BACKGROUND_GC
11569     // When background GC is enabled it decreases the accurancy of our predictability -
11570     // by the time the GC happens, we may not be under BGC anymore. If we try to
11571     // predict often enough it should be ok.
11572     if ((n == max_generation) &&
11573         (recursive_gc_sync::background_running_p()))
11574     {
11575         n = max_generation - 1;
11576         dprintf (2, ("FGN: bgc - 1 instead of 2"));
11577     }
11578 
11579     if ((n == max_generation) && !local_blocking_collection)
11580     {
11581         if (!background_allowed_p())
11582         {
11583             local_blocking_collection = TRUE;
11584         }
11585     }
11586 #endif //BACKGROUND_GC
11587 
11588     dprintf (2, ("FGN: we estimate gen%d will be collected: %s",
11589                        n,
11590                        (local_blocking_collection ? "blocking" : "background")));
11591 
11592     if ((n == max_generation) && local_blocking_collection)
11593     {
11594         alloc_factor = FALSE;
11595         should_notify = TRUE;
11596         goto done;
11597     }
11598 
11599 done:
11600 
11601     if (should_notify)
11602     {
11603         dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)",
11604                      n_initial,
11605                      (alloc_factor ? "alloc" : "other"),
11606                      dd_collection_count (dynamic_data_of (0)),
11607                      new_alloc_remain_percent,
11608                      gen_num));
11609 
11610         send_full_gc_notification (n_initial, alloc_factor);
11611     }
11612 }
11613 
send_full_gc_notification(int gen_num,BOOL due_to_alloc_p)11614 void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11615 {
11616     if (!full_gc_approach_event_set)
11617     {
11618         assert (full_gc_approach_event.IsValid());
11619         FireEtwGCFullNotify_V1 (gen_num, due_to_alloc_p, GetClrInstanceId());
11620 
11621         full_gc_end_event.Reset();
11622         full_gc_approach_event.Set();
11623         full_gc_approach_event_set = true;
11624     }
11625 }
11626 
full_gc_wait(CLREvent * event,int time_out_ms)11627 wait_full_gc_status gc_heap::full_gc_wait (CLREvent *event, int time_out_ms)
11628 {
11629     if (fgn_maxgen_percent == 0)
11630     {
11631         return wait_full_gc_na;
11632     }
11633 
11634     uint32_t wait_result = user_thread_wait(event, FALSE, time_out_ms);
11635 
11636     if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11637     {
11638         if (fgn_maxgen_percent == 0)
11639         {
11640             return wait_full_gc_cancelled;
11641         }
11642 
11643         if (wait_result == WAIT_OBJECT_0)
11644         {
11645 #ifdef BACKGROUND_GC
11646             if (fgn_last_gc_was_concurrent)
11647             {
11648                 fgn_last_gc_was_concurrent = FALSE;
11649                 return wait_full_gc_na;
11650             }
11651             else
11652 #endif //BACKGROUND_GC
11653             {
11654                 return wait_full_gc_success;
11655             }
11656         }
11657         else
11658         {
11659             return wait_full_gc_timeout;
11660         }
11661     }
11662     else
11663     {
11664         return wait_full_gc_failed;
11665     }
11666 }
11667 
get_full_compact_gc_count()11668 size_t gc_heap::get_full_compact_gc_count()
11669 {
11670     return full_gc_counts[gc_type_compacting];
11671 }
11672 
11673 // DTREVIEW - we should check this in dt_low_ephemeral_space_p
11674 // as well.
11675 inline
short_on_end_of_seg(int gen_number,heap_segment * seg,int align_const)11676 BOOL gc_heap::short_on_end_of_seg (int gen_number,
11677                                    heap_segment* seg,
11678                                    int align_const)
11679 {
11680     UNREFERENCED_PARAMETER(gen_number);
11681     uint8_t* allocated = heap_segment_allocated(seg);
11682 
11683     return (!a_size_fit_p (end_space_after_gc(),
11684                           allocated,
11685                           heap_segment_reserved (seg),
11686                           align_const));
11687 }
11688 
11689 #ifdef _MSC_VER
11690 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
11691 #endif // _MSC_VER
11692 
11693 inline
a_fit_free_list_p(int gen_number,size_t size,alloc_context * acontext,int align_const)11694 BOOL gc_heap::a_fit_free_list_p (int gen_number,
11695                                  size_t size,
11696                                  alloc_context* acontext,
11697                                  int align_const)
11698 {
11699     BOOL can_fit = FALSE;
11700     generation* gen = generation_of (gen_number);
11701     allocator* gen_allocator = generation_allocator (gen);
11702     size_t sz_list = gen_allocator->first_bucket_size();
11703     for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
11704     {
11705         if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
11706         {
11707             uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
11708             uint8_t* prev_free_item = 0;
11709 
11710             while (free_list != 0)
11711             {
11712                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11713                 size_t free_list_size = unused_array_size (free_list);
11714                 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
11715                 {
11716                     dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
11717                                  (size_t)free_list, free_list_size));
11718 
11719                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11720                     // We ask for more Align (min_obj_size)
11721                     // to make sure that we can insert a free object
11722                     // in adjust_limit will set the limit lower
11723                     size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
11724 
11725                     uint8_t*  remain = (free_list + limit);
11726                     size_t remain_size = (free_list_size - limit);
11727                     if (remain_size >= Align(min_free_list, align_const))
11728                     {
11729                         make_unused_array (remain, remain_size);
11730                         gen_allocator->thread_item_front (remain, remain_size);
11731                         assert (remain_size >= Align (min_obj_size, align_const));
11732                     }
11733                     else
11734                     {
11735                         //absorb the entire free list
11736                         limit += remain_size;
11737                     }
11738                     generation_free_list_space (gen) -= limit;
11739 
11740                     adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
11741 
11742                     can_fit = TRUE;
11743                     goto end;
11744                 }
11745                 else if (gen_allocator->discard_if_no_fit_p())
11746                 {
11747                     assert (prev_free_item == 0);
11748                     dprintf (3, ("couldn't use this free area, discarding"));
11749                     generation_free_obj_space (gen) += free_list_size;
11750 
11751                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11752                     generation_free_list_space (gen) -= free_list_size;
11753                 }
11754                 else
11755                 {
11756                     prev_free_item = free_list;
11757                 }
11758                 free_list = free_list_slot (free_list);
11759             }
11760         }
11761         sz_list = sz_list * 2;
11762     }
11763 end:
11764     return can_fit;
11765 }
11766 
11767 
11768 #ifdef BACKGROUND_GC
bgc_loh_alloc_clr(uint8_t * alloc_start,size_t size,alloc_context * acontext,int align_const,int lock_index,BOOL check_used_p,heap_segment * seg)11769 void gc_heap::bgc_loh_alloc_clr (uint8_t* alloc_start,
11770                                  size_t size,
11771                                  alloc_context* acontext,
11772                                  int align_const,
11773                                  int lock_index,
11774                                  BOOL check_used_p,
11775                                  heap_segment* seg)
11776 {
11777     make_unused_array (alloc_start, size);
11778 
11779 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11780     if (g_fEnableARM)
11781     {
11782         AppDomain* alloc_appdomain = GetAppDomain();
11783         alloc_appdomain->RecordAllocBytes (size, heap_number);
11784     }
11785 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11786 
11787     size_t size_of_array_base = sizeof(ArrayBase);
11788 
11789     bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
11790 
11791     // clear memory while not holding the lock.
11792     size_t size_to_skip = size_of_array_base;
11793     size_t size_to_clear = size - size_to_skip - plug_skew;
11794     size_t saved_size_to_clear = size_to_clear;
11795     if (check_used_p)
11796     {
11797         uint8_t* end = alloc_start + size - plug_skew;
11798         uint8_t* used = heap_segment_used (seg);
11799         if (used < end)
11800         {
11801             if ((alloc_start + size_to_skip) < used)
11802             {
11803                 size_to_clear = used - (alloc_start + size_to_skip);
11804             }
11805             else
11806             {
11807                 size_to_clear = 0;
11808             }
11809             dprintf (2, ("bgc loh: setting used to %Ix", end));
11810             heap_segment_used (seg) = end;
11811         }
11812 
11813         dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
11814                      used, alloc_start, end, size_to_clear));
11815     }
11816     else
11817     {
11818         dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
11819     }
11820 
11821 #ifdef VERIFY_HEAP
11822     // since we filled in 0xcc for free object when we verify heap,
11823     // we need to make sure we clear those bytes.
11824     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
11825     {
11826         if (size_to_clear < saved_size_to_clear)
11827         {
11828             size_to_clear = saved_size_to_clear;
11829         }
11830     }
11831 #endif //VERIFY_HEAP
11832 
11833     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
11834     add_saved_spinlock_info (me_release, mt_clr_large_mem);
11835     leave_spin_lock (&more_space_lock);
11836     memclr (alloc_start + size_to_skip, size_to_clear);
11837 
11838     bgc_alloc_lock->loh_alloc_set (alloc_start);
11839 
11840     acontext->alloc_ptr = alloc_start;
11841     acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
11842 
11843     // need to clear the rest of the object before we hand it out.
11844     clear_unused_array(alloc_start, size);
11845 }
11846 #endif //BACKGROUND_GC
11847 
a_fit_free_list_large_p(size_t size,alloc_context * acontext,int align_const)11848 BOOL gc_heap::a_fit_free_list_large_p (size_t size,
11849                                        alloc_context* acontext,
11850                                        int align_const)
11851 {
11852 #ifdef BACKGROUND_GC
11853     wait_for_background_planning (awr_loh_alloc_during_plan);
11854 #endif //BACKGROUND_GC
11855 
11856     BOOL can_fit = FALSE;
11857     int gen_number = max_generation + 1;
11858     generation* gen = generation_of (gen_number);
11859     allocator* loh_allocator = generation_allocator (gen);
11860 
11861 #ifdef FEATURE_LOH_COMPACTION
11862     size_t loh_pad = Align (loh_padding_obj_size, align_const);
11863 #endif //FEATURE_LOH_COMPACTION
11864 
11865 #ifdef BACKGROUND_GC
11866     int cookie = -1;
11867 #endif //BACKGROUND_GC
11868     size_t sz_list = loh_allocator->first_bucket_size();
11869     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
11870     {
11871         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
11872         {
11873             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
11874             uint8_t* prev_free_item = 0;
11875             while (free_list != 0)
11876             {
11877                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11878 
11879                 size_t free_list_size = unused_array_size(free_list);
11880 
11881 #ifdef FEATURE_LOH_COMPACTION
11882                 if ((size + loh_pad) <= free_list_size)
11883 #else
11884                 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
11885                     (size == free_list_size))
11886 #endif //FEATURE_LOH_COMPACTION
11887                 {
11888 #ifdef BACKGROUND_GC
11889                     cookie = bgc_alloc_lock->loh_alloc_set (free_list);
11890 #endif //BACKGROUND_GC
11891 
11892                     //unlink the free_item
11893                     loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11894 
11895                     // Substract min obj size because limit_from_size adds it. Not needed for LOH
11896                     size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size,
11897                                                     gen_number, align_const);
11898 
11899 #ifdef FEATURE_LOH_COMPACTION
11900                     make_unused_array (free_list, loh_pad);
11901                     limit -= loh_pad;
11902                     free_list += loh_pad;
11903                     free_list_size -= loh_pad;
11904 #endif //FEATURE_LOH_COMPACTION
11905 
11906                     uint8_t*  remain = (free_list + limit);
11907                     size_t remain_size = (free_list_size - limit);
11908                     if (remain_size != 0)
11909                     {
11910                         assert (remain_size >= Align (min_obj_size, align_const));
11911                         make_unused_array (remain, remain_size);
11912                     }
11913                     if (remain_size >= Align(min_free_list, align_const))
11914                     {
11915                         loh_thread_gap_front (remain, remain_size, gen);
11916                         assert (remain_size >= Align (min_obj_size, align_const));
11917                     }
11918                     else
11919                     {
11920                         generation_free_obj_space (gen) += remain_size;
11921                     }
11922                     generation_free_list_space (gen) -= free_list_size;
11923                     dprintf (3, ("found fit on loh at %Ix", free_list));
11924 #ifdef BACKGROUND_GC
11925                     if (cookie != -1)
11926                     {
11927                         bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
11928                     }
11929                     else
11930 #endif //BACKGROUND_GC
11931                     {
11932                         adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
11933                     }
11934 
11935                     //fix the limit to compensate for adjust_limit_clr making it too short
11936                     acontext->alloc_limit += Align (min_obj_size, align_const);
11937                     can_fit = TRUE;
11938                     goto exit;
11939                 }
11940                 prev_free_item = free_list;
11941                 free_list = free_list_slot (free_list);
11942             }
11943         }
11944         sz_list = sz_list * 2;
11945     }
11946 exit:
11947     return can_fit;
11948 }
11949 
11950 #ifdef _MSC_VER
11951 #pragma warning(default:4706)
11952 #endif // _MSC_VER
11953 
a_fit_segment_end_p(int gen_number,heap_segment * seg,size_t size,alloc_context * acontext,int align_const,BOOL * commit_failed_p)11954 BOOL gc_heap::a_fit_segment_end_p (int gen_number,
11955                                    heap_segment* seg,
11956                                    size_t size,
11957                                    alloc_context* acontext,
11958                                    int align_const,
11959                                    BOOL* commit_failed_p)
11960 {
11961     *commit_failed_p = FALSE;
11962     size_t limit = 0;
11963 #ifdef BACKGROUND_GC
11964     int cookie = -1;
11965 #endif //BACKGROUND_GC
11966 
11967     uint8_t*& allocated = ((gen_number == 0) ?
11968                         alloc_allocated :
11969                         heap_segment_allocated(seg));
11970 
11971     size_t pad = Align (min_obj_size, align_const);
11972 
11973 #ifdef FEATURE_LOH_COMPACTION
11974     if (gen_number == (max_generation + 1))
11975     {
11976         pad += Align (loh_padding_obj_size, align_const);
11977     }
11978 #endif //FEATURE_LOH_COMPACTION
11979 
11980     uint8_t* end = heap_segment_committed (seg) - pad;
11981 
11982     if (a_size_fit_p (size, allocated, end, align_const))
11983     {
11984         limit = limit_from_size (size,
11985                                  (end - allocated),
11986                                  gen_number, align_const);
11987         goto found_fit;
11988     }
11989 
11990     end = heap_segment_reserved (seg) - pad;
11991 
11992     if (a_size_fit_p (size, allocated, end, align_const))
11993     {
11994         limit = limit_from_size (size,
11995                                  (end - allocated),
11996                                  gen_number, align_const);
11997         if (grow_heap_segment (seg, allocated + limit))
11998         {
11999             goto found_fit;
12000         }
12001         else
12002         {
12003             dprintf (2, ("can't grow segment, doing a full gc"));
12004             *commit_failed_p = TRUE;
12005         }
12006     }
12007     goto found_no_fit;
12008 
12009 found_fit:
12010 
12011 #ifdef BACKGROUND_GC
12012     if (gen_number != 0)
12013     {
12014         cookie = bgc_alloc_lock->loh_alloc_set (allocated);
12015     }
12016 #endif //BACKGROUND_GC
12017 
12018     uint8_t* old_alloc;
12019     old_alloc = allocated;
12020 #ifdef FEATURE_LOH_COMPACTION
12021     if (gen_number == (max_generation + 1))
12022     {
12023         size_t loh_pad = Align (loh_padding_obj_size, align_const);
12024         make_unused_array (old_alloc, loh_pad);
12025         old_alloc += loh_pad;
12026         allocated += loh_pad;
12027         limit -= loh_pad;
12028     }
12029 #endif //FEATURE_LOH_COMPACTION
12030 
12031 #if defined (VERIFY_HEAP) && defined (_DEBUG)
12032         ((void**) allocated)[-1] = 0;     //clear the sync block
12033 #endif //VERIFY_HEAP && _DEBUG
12034     allocated += limit;
12035 
12036     dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
12037 
12038 #ifdef BACKGROUND_GC
12039     if (cookie != -1)
12040     {
12041         bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
12042     }
12043     else
12044 #endif //BACKGROUND_GC
12045     {
12046         adjust_limit_clr (old_alloc, limit, acontext, seg, align_const, gen_number);
12047     }
12048 
12049     return TRUE;
12050 
12051 found_no_fit:
12052 
12053     return FALSE;
12054 }
12055 
loh_a_fit_segment_end_p(int gen_number,size_t size,alloc_context * acontext,int align_const,BOOL * commit_failed_p,oom_reason * oom_r)12056 BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
12057                                        size_t size,
12058                                        alloc_context* acontext,
12059                                        int align_const,
12060                                        BOOL* commit_failed_p,
12061                                        oom_reason* oom_r)
12062 {
12063     *commit_failed_p = FALSE;
12064     heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
12065     BOOL can_allocate_p = FALSE;
12066 
12067     while (seg)
12068     {
12069         if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)),
12070                                  acontext, align_const, commit_failed_p))
12071         {
12072             acontext->alloc_limit += Align (min_obj_size, align_const);
12073             can_allocate_p = TRUE;
12074             break;
12075         }
12076         else
12077         {
12078             if (*commit_failed_p)
12079             {
12080                 *oom_r = oom_cant_commit;
12081                 break;
12082             }
12083             else
12084             {
12085                 seg = heap_segment_next_rw (seg);
12086             }
12087         }
12088     }
12089 
12090     return can_allocate_p;
12091 }
12092 
12093 #ifdef BACKGROUND_GC
12094 inline
wait_for_background(alloc_wait_reason awr)12095 void gc_heap::wait_for_background (alloc_wait_reason awr)
12096 {
12097     dprintf (2, ("BGC is already in progress, waiting for it to finish"));
12098     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc done", heap_number));
12099     add_saved_spinlock_info (me_release, mt_wait_bgc);
12100     leave_spin_lock (&more_space_lock);
12101     background_gc_wait (awr);
12102     enter_spin_lock (&more_space_lock);
12103     add_saved_spinlock_info (me_acquire, mt_wait_bgc);
12104     dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc done", heap_number));
12105 }
12106 
wait_for_bgc_high_memory(alloc_wait_reason awr)12107 void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr)
12108 {
12109     if (recursive_gc_sync::background_running_p())
12110     {
12111         uint32_t memory_load;
12112         get_memory_info (&memory_load);
12113         if (memory_load >= 95)
12114         {
12115             dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
12116             wait_for_background (awr);
12117         }
12118     }
12119 }
12120 
12121 #endif //BACKGROUND_GC
12122 
12123 // We request to trigger an ephemeral GC but we may get a full compacting GC.
12124 // return TRUE if that's the case.
trigger_ephemeral_gc(gc_reason gr)12125 BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
12126 {
12127 #ifdef BACKGROUND_GC
12128     wait_for_bgc_high_memory (awr_loh_oos_bgc);
12129 #endif //BACKGROUND_GC
12130 
12131     BOOL did_full_compact_gc = FALSE;
12132 
12133     dprintf (2, ("triggering a gen1 GC"));
12134     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12135     vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
12136 
12137 #ifdef MULTIPLE_HEAPS
12138     enter_spin_lock (&more_space_lock);
12139     add_saved_spinlock_info (me_acquire, mt_t_eph_gc);
12140     dprintf (SPINLOCK_LOG, ("[%d]Emsl after a GC", heap_number));
12141 #endif //MULTIPLE_HEAPS
12142 
12143     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12144 
12145     if (current_full_compact_gc_count > last_full_compact_gc_count)
12146     {
12147         dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
12148         did_full_compact_gc = TRUE;
12149     }
12150 
12151     return did_full_compact_gc;
12152 }
12153 
soh_try_fit(int gen_number,size_t size,alloc_context * acontext,int align_const,BOOL * commit_failed_p,BOOL * short_seg_end_p)12154 BOOL gc_heap::soh_try_fit (int gen_number,
12155                            size_t size,
12156                            alloc_context* acontext,
12157                            int align_const,
12158                            BOOL* commit_failed_p,
12159                            BOOL* short_seg_end_p)
12160 {
12161     BOOL can_allocate = TRUE;
12162     if (short_seg_end_p)
12163     {
12164         *short_seg_end_p = FALSE;
12165     }
12166 
12167     can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
12168     if (!can_allocate)
12169     {
12170         if (short_seg_end_p)
12171         {
12172             *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
12173         }
12174         // If the caller doesn't care, we always try to fit at the end of seg;
12175         // otherwise we would only try if we are actually not short at end of seg.
12176         if (!short_seg_end_p || !(*short_seg_end_p))
12177         {
12178             can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size,
12179                                                 acontext, align_const, commit_failed_p);
12180         }
12181     }
12182 
12183     return can_allocate;
12184 }
12185 
allocate_small(int gen_number,size_t size,alloc_context * acontext,int align_const)12186 BOOL gc_heap::allocate_small (int gen_number,
12187                               size_t size,
12188                               alloc_context* acontext,
12189                               int align_const)
12190 {
12191 #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
12192     if (recursive_gc_sync::background_running_p())
12193     {
12194         background_soh_alloc_count++;
12195         if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
12196         {
12197             Thread* current_thread = GetThread();
12198             add_saved_spinlock_info (me_release, mt_alloc_small);
12199             dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl", heap_number));
12200             leave_spin_lock (&more_space_lock);
12201             BOOL cooperative_mode = enable_preemptive (current_thread);
12202             GCToOSInterface::Sleep (bgc_alloc_spin);
12203             disable_preemptive (current_thread, cooperative_mode);
12204             enter_spin_lock (&more_space_lock);
12205             add_saved_spinlock_info (me_acquire, mt_alloc_small);
12206             dprintf (SPINLOCK_LOG, ("[%d]spin Emsl", heap_number));
12207         }
12208         else
12209         {
12210             //GCToOSInterface::YieldThread (0);
12211         }
12212     }
12213 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
12214 
12215     gc_reason gr = reason_oos_soh;
12216     oom_reason oom_r = oom_no_failure;
12217 
12218     // No variable values should be "carried over" from one state to the other.
12219     // That's why there are local variable for each state
12220 
12221     allocation_state soh_alloc_state = a_state_start;
12222 
12223     // If we can get a new seg it means allocation will succeed.
12224     while (1)
12225     {
12226         dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
12227         switch (soh_alloc_state)
12228         {
12229             case a_state_can_allocate:
12230             case a_state_cant_allocate:
12231             {
12232                 goto exit;
12233             }
12234             case a_state_start:
12235             {
12236                 soh_alloc_state = a_state_try_fit;
12237                 break;
12238             }
12239             case a_state_try_fit:
12240             {
12241                 BOOL commit_failed_p = FALSE;
12242                 BOOL can_use_existing_p = FALSE;
12243 
12244                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12245                                                   align_const, &commit_failed_p,
12246                                                   NULL);
12247                 soh_alloc_state = (can_use_existing_p ?
12248                                         a_state_can_allocate :
12249                                         (commit_failed_p ?
12250                                             a_state_trigger_full_compact_gc :
12251                                             a_state_trigger_ephemeral_gc));
12252                 break;
12253             }
12254             case a_state_try_fit_after_bgc:
12255             {
12256                 BOOL commit_failed_p = FALSE;
12257                 BOOL can_use_existing_p = FALSE;
12258                 BOOL short_seg_end_p = FALSE;
12259 
12260                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12261                                                   align_const, &commit_failed_p,
12262                                                   &short_seg_end_p);
12263                 soh_alloc_state = (can_use_existing_p ?
12264                                         a_state_can_allocate :
12265                                         (short_seg_end_p ?
12266                                             a_state_trigger_2nd_ephemeral_gc :
12267                                             a_state_trigger_full_compact_gc));
12268                 break;
12269             }
12270             case a_state_try_fit_after_cg:
12271             {
12272                 BOOL commit_failed_p = FALSE;
12273                 BOOL can_use_existing_p = FALSE;
12274                 BOOL short_seg_end_p = FALSE;
12275 
12276                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12277                                                   align_const, &commit_failed_p,
12278                                                   &short_seg_end_p);
12279                 if (short_seg_end_p)
12280                 {
12281                     soh_alloc_state = a_state_cant_allocate;
12282                     oom_r = oom_budget;
12283                 }
12284                 else
12285                 {
12286                     if (can_use_existing_p)
12287                     {
12288                         soh_alloc_state = a_state_can_allocate;
12289                     }
12290                     else
12291                     {
12292 #ifdef MULTIPLE_HEAPS
12293                         if (!commit_failed_p)
12294                         {
12295                             // some other threads already grabbed the more space lock and allocated
12296                             // so we should attempt an ephemeral GC again.
12297                             assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
12298                             soh_alloc_state = a_state_trigger_ephemeral_gc;
12299                         }
12300                         else
12301 #endif //MULTIPLE_HEAPS
12302                         {
12303                             assert (commit_failed_p);
12304                             soh_alloc_state = a_state_cant_allocate;
12305                             oom_r = oom_cant_commit;
12306                         }
12307                     }
12308                 }
12309                 break;
12310             }
12311             case a_state_check_and_wait_for_bgc:
12312             {
12313                 BOOL bgc_in_progress_p = FALSE;
12314                 BOOL did_full_compacting_gc = FALSE;
12315 
12316                 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc);
12317                 soh_alloc_state = (did_full_compacting_gc ?
12318                                         a_state_try_fit_after_cg :
12319                                         a_state_try_fit_after_bgc);
12320                 break;
12321             }
12322             case a_state_trigger_ephemeral_gc:
12323             {
12324                 BOOL commit_failed_p = FALSE;
12325                 BOOL can_use_existing_p = FALSE;
12326                 BOOL short_seg_end_p = FALSE;
12327                 BOOL bgc_in_progress_p = FALSE;
12328                 BOOL did_full_compacting_gc = FALSE;
12329 
12330                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12331                 if (did_full_compacting_gc)
12332                 {
12333                     soh_alloc_state = a_state_try_fit_after_cg;
12334                 }
12335                 else
12336                 {
12337                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12338                                                       align_const, &commit_failed_p,
12339                                                       &short_seg_end_p);
12340 #ifdef BACKGROUND_GC
12341                     bgc_in_progress_p = recursive_gc_sync::background_running_p();
12342 #endif //BACKGROUND_GC
12343 
12344                     if (short_seg_end_p)
12345                     {
12346                         soh_alloc_state = (bgc_in_progress_p ?
12347                                                 a_state_check_and_wait_for_bgc :
12348                                                 a_state_trigger_full_compact_gc);
12349 
12350                         if (fgn_maxgen_percent)
12351                         {
12352                             dprintf (2, ("FGN: doing last GC before we throw OOM"));
12353                             send_full_gc_notification (max_generation, FALSE);
12354                         }
12355                     }
12356                     else
12357                     {
12358                         if (can_use_existing_p)
12359                         {
12360                             soh_alloc_state = a_state_can_allocate;
12361                         }
12362                         else
12363                         {
12364 #ifdef MULTIPLE_HEAPS
12365                             if (!commit_failed_p)
12366                             {
12367                                 // some other threads already grabbed the more space lock and allocated
12368                                 // so we should attempt an ephemeral GC again.
12369                                 assert (heap_segment_allocated (ephemeral_heap_segment) < alloc_allocated);
12370                                 soh_alloc_state = a_state_trigger_ephemeral_gc;
12371                             }
12372                             else
12373 #endif //MULTIPLE_HEAPS
12374                             {
12375                                 soh_alloc_state = a_state_trigger_full_compact_gc;
12376                                 if (fgn_maxgen_percent)
12377                                 {
12378                                     dprintf (2, ("FGN: failed to commit, doing full compacting GC"));
12379                                     send_full_gc_notification (max_generation, FALSE);
12380                                 }
12381                             }
12382                         }
12383                     }
12384                 }
12385                 break;
12386             }
12387             case a_state_trigger_2nd_ephemeral_gc:
12388             {
12389                 BOOL commit_failed_p = FALSE;
12390                 BOOL can_use_existing_p = FALSE;
12391                 BOOL short_seg_end_p = FALSE;
12392                 BOOL did_full_compacting_gc = FALSE;
12393 
12394 
12395                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12396 
12397                 if (did_full_compacting_gc)
12398                 {
12399                     soh_alloc_state = a_state_try_fit_after_cg;
12400                 }
12401                 else
12402                 {
12403                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12404                                                       align_const, &commit_failed_p,
12405                                                       &short_seg_end_p);
12406                     if (short_seg_end_p || commit_failed_p)
12407                     {
12408                         soh_alloc_state = a_state_trigger_full_compact_gc;
12409                     }
12410                     else
12411                     {
12412                         assert (can_use_existing_p);
12413                         soh_alloc_state = a_state_can_allocate;
12414                     }
12415                 }
12416                 break;
12417             }
12418             case a_state_trigger_full_compact_gc:
12419             {
12420                 BOOL got_full_compacting_gc = FALSE;
12421 
12422                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
12423                 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12424                 break;
12425             }
12426             default:
12427             {
12428                 assert (!"Invalid state!");
12429                 break;
12430             }
12431         }
12432     }
12433 
12434 exit:
12435     if (soh_alloc_state == a_state_cant_allocate)
12436     {
12437         assert (oom_r != oom_no_failure);
12438         handle_oom (heap_number,
12439                     oom_r,
12440                     size,
12441                     heap_segment_allocated (ephemeral_heap_segment),
12442                     heap_segment_reserved (ephemeral_heap_segment));
12443 
12444         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for oom", heap_number));
12445         add_saved_spinlock_info (me_release, mt_alloc_small_cant);
12446         leave_spin_lock (&more_space_lock);
12447     }
12448 
12449     return (soh_alloc_state == a_state_can_allocate);
12450 }
12451 
12452 #ifdef BACKGROUND_GC
12453 inline
wait_for_background_planning(alloc_wait_reason awr)12454 void gc_heap::wait_for_background_planning (alloc_wait_reason awr)
12455 {
12456     while (current_c_gc_state == c_gc_state_planning)
12457     {
12458         dprintf (3, ("lh state planning, cannot allocate"));
12459 
12460         dprintf (SPINLOCK_LOG, ("[%d]Lmsl to wait for bgc plan", heap_number));
12461         add_saved_spinlock_info (me_release, mt_wait_bgc_plan);
12462         leave_spin_lock (&more_space_lock);
12463         background_gc_wait_lh (awr);
12464         enter_spin_lock (&more_space_lock);
12465         add_saved_spinlock_info (me_acquire, mt_wait_bgc_plan);
12466         dprintf (SPINLOCK_LOG, ("[%d]Emsl after waiting for bgc plan", heap_number));
12467     }
12468     assert ((current_c_gc_state == c_gc_state_free) ||
12469             (current_c_gc_state == c_gc_state_marking));
12470 }
12471 
bgc_loh_should_allocate()12472 BOOL gc_heap::bgc_loh_should_allocate()
12473 {
12474     size_t min_gc_size = dd_min_gc_size(dynamic_data_of (max_generation + 1));
12475 
12476     if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
12477     {
12478         return TRUE;
12479     }
12480 
12481     if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
12482     {
12483         if ((bgc_begin_loh_size / end_loh_size) > 2)
12484         {
12485             dprintf (3, ("alloc-ed too much before bgc started"));
12486         }
12487         else
12488         {
12489             dprintf (3, ("alloc-ed too much after bgc started"));
12490         }
12491         return FALSE;
12492     }
12493     else
12494     {
12495         bgc_alloc_spin_loh = (uint32_t)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
12496         return TRUE;
12497     }
12498 }
12499 #endif //BACKGROUND_GC
12500 
get_large_seg_size(size_t size)12501 size_t gc_heap::get_large_seg_size (size_t size)
12502 {
12503     size_t default_seg_size = get_valid_segment_size(TRUE);
12504 #ifdef SEG_MAPPING_TABLE
12505     size_t align_size =  default_seg_size;
12506 #else //SEG_MAPPING_TABLE
12507     size_t align_size =  default_seg_size / 2;
12508 #endif //SEG_MAPPING_TABLE
12509     int align_const = get_alignment_constant (FALSE);
12510     size_t large_seg_size = align_on_page (
12511         max (default_seg_size,
12512             ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE +
12513             align_size) / align_size * align_size)));
12514     return large_seg_size;
12515 }
12516 
loh_get_new_seg(generation * gen,size_t size,int align_const,BOOL * did_full_compact_gc,oom_reason * oom_r)12517 BOOL gc_heap::loh_get_new_seg (generation* gen,
12518                                size_t size,
12519                                int align_const,
12520                                BOOL* did_full_compact_gc,
12521                                oom_reason* oom_r)
12522 {
12523     UNREFERENCED_PARAMETER(gen);
12524     UNREFERENCED_PARAMETER(align_const);
12525 
12526     *did_full_compact_gc = FALSE;
12527 
12528     size_t seg_size = get_large_seg_size (size);
12529 
12530     heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
12531 
12532     if (new_seg)
12533     {
12534         loh_alloc_since_cg += seg_size;
12535     }
12536     else
12537     {
12538         *oom_r = oom_loh;
12539     }
12540 
12541     return (new_seg != 0);
12542 }
12543 
retry_full_compact_gc(size_t size)12544 BOOL gc_heap::retry_full_compact_gc (size_t size)
12545 {
12546     size_t seg_size = get_large_seg_size (size);
12547 
12548     if (loh_alloc_since_cg >= (2 * (uint64_t)seg_size))
12549     {
12550         return TRUE;
12551     }
12552 
12553 #ifdef MULTIPLE_HEAPS
12554     uint64_t total_alloc_size = 0;
12555     for (int i = 0; i < n_heaps; i++)
12556     {
12557         total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12558     }
12559 
12560     if (total_alloc_size >= (2 * (uint64_t)seg_size))
12561     {
12562         return TRUE;
12563     }
12564 #endif //MULTIPLE_HEAPS
12565 
12566     return FALSE;
12567 }
12568 
check_and_wait_for_bgc(alloc_wait_reason awr,BOOL * did_full_compact_gc)12569 BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12570                                       BOOL* did_full_compact_gc)
12571 {
12572     BOOL bgc_in_progress = FALSE;
12573     *did_full_compact_gc = FALSE;
12574 #ifdef BACKGROUND_GC
12575     if (recursive_gc_sync::background_running_p())
12576     {
12577         bgc_in_progress = TRUE;
12578         size_t last_full_compact_gc_count = get_full_compact_gc_count();
12579         wait_for_background (awr);
12580         size_t current_full_compact_gc_count = get_full_compact_gc_count();
12581         if (current_full_compact_gc_count > last_full_compact_gc_count)
12582         {
12583             *did_full_compact_gc = TRUE;
12584         }
12585     }
12586 #endif //BACKGROUND_GC
12587 
12588     return bgc_in_progress;
12589 }
12590 
loh_try_fit(int gen_number,size_t size,alloc_context * acontext,int align_const,BOOL * commit_failed_p,oom_reason * oom_r)12591 BOOL gc_heap::loh_try_fit (int gen_number,
12592                            size_t size,
12593                            alloc_context* acontext,
12594                            int align_const,
12595                            BOOL* commit_failed_p,
12596                            oom_reason* oom_r)
12597 {
12598     BOOL can_allocate = TRUE;
12599 
12600     if (!a_fit_free_list_large_p (size, acontext, align_const))
12601     {
12602         can_allocate = loh_a_fit_segment_end_p (gen_number, size,
12603                                                 acontext, align_const,
12604                                                 commit_failed_p, oom_r);
12605 
12606 #ifdef BACKGROUND_GC
12607         if (can_allocate && recursive_gc_sync::background_running_p())
12608         {
12609             bgc_loh_size_increased += size;
12610         }
12611 #endif //BACKGROUND_GC
12612     }
12613 #ifdef BACKGROUND_GC
12614     else
12615     {
12616         if (recursive_gc_sync::background_running_p())
12617         {
12618             bgc_loh_allocated_in_free += size;
12619         }
12620     }
12621 #endif //BACKGROUND_GC
12622 
12623     return can_allocate;
12624 }
12625 
trigger_full_compact_gc(gc_reason gr,oom_reason * oom_r)12626 BOOL gc_heap::trigger_full_compact_gc (gc_reason gr,
12627                                        oom_reason* oom_r)
12628 {
12629     BOOL did_full_compact_gc = FALSE;
12630 
12631     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12632 
12633     // Set this so the next GC will be a full compacting GC.
12634     if (!last_gc_before_oom)
12635     {
12636         last_gc_before_oom = TRUE;
12637     }
12638 
12639 #ifdef BACKGROUND_GC
12640     if (recursive_gc_sync::background_running_p())
12641     {
12642         wait_for_background ((gr == reason_oos_soh) ? awr_gen0_oos_bgc : awr_loh_oos_bgc);
12643         dprintf (2, ("waited for BGC - done"));
12644     }
12645 #endif //BACKGROUND_GC
12646 
12647     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12648     if (current_full_compact_gc_count > last_full_compact_gc_count)
12649     {
12650         dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
12651         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12652         did_full_compact_gc = TRUE;
12653         goto exit;
12654     }
12655 
12656     dprintf (3, ("h%d full GC", heap_number));
12657     vm_heap->GarbageCollectGeneration(max_generation, gr);
12658 
12659 #ifdef MULTIPLE_HEAPS
12660     enter_spin_lock (&more_space_lock);
12661     dprintf (SPINLOCK_LOG, ("[%d]Emsl after full gc", heap_number));
12662     add_saved_spinlock_info (me_acquire, mt_t_full_gc);
12663 #endif //MULTIPLE_HEAPS
12664 
12665     current_full_compact_gc_count = get_full_compact_gc_count();
12666 
12667     if (current_full_compact_gc_count == last_full_compact_gc_count)
12668     {
12669         dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
12670         // We requested a full GC but didn't get because of the elevation logic
12671         // which means we should fail.
12672         *oom_r = oom_unproductive_full_gc;
12673     }
12674     else
12675     {
12676         dprintf (3, ("h%d: T full compacting GC (%d->%d)",
12677             heap_number,
12678             last_full_compact_gc_count,
12679             current_full_compact_gc_count));
12680 
12681         assert (current_full_compact_gc_count > last_full_compact_gc_count);
12682         did_full_compact_gc = TRUE;
12683     }
12684 
12685 exit:
12686     return did_full_compact_gc;
12687 }
12688 
12689 #ifdef RECORD_LOH_STATE
add_saved_loh_state(allocation_state loh_state_to_save,EEThreadId thread_id)12690 void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, EEThreadId thread_id)
12691 {
12692     // When the state is can_allocate we already have released the more
12693     // space lock. So we are not logging states here since this code
12694     // is not thread safe.
12695     if (loh_state_to_save != a_state_can_allocate)
12696     {
12697         last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
12698         last_loh_states[loh_state_index].thread_id = thread_id;
12699         loh_state_index++;
12700 
12701         if (loh_state_index == max_saved_loh_states)
12702         {
12703             loh_state_index = 0;
12704         }
12705 
12706         assert (loh_state_index < max_saved_loh_states);
12707     }
12708 }
12709 #endif //RECORD_LOH_STATE
12710 
allocate_large(int gen_number,size_t size,alloc_context * acontext,int align_const)12711 BOOL gc_heap::allocate_large (int gen_number,
12712                               size_t size,
12713                               alloc_context* acontext,
12714                               int align_const)
12715 {
12716 #ifdef BACKGROUND_GC
12717     if (recursive_gc_sync::background_running_p() && (current_c_gc_state != c_gc_state_planning))
12718     {
12719         background_loh_alloc_count++;
12720         //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
12721         {
12722             if (bgc_loh_should_allocate())
12723             {
12724                 if (!bgc_alloc_spin_loh)
12725                 {
12726                     Thread* current_thread = GetThread();
12727                     add_saved_spinlock_info (me_release, mt_alloc_large);
12728                     dprintf (SPINLOCK_LOG, ("[%d]spin Lmsl loh", heap_number));
12729                     leave_spin_lock (&more_space_lock);
12730                     BOOL cooperative_mode = enable_preemptive (current_thread);
12731                     GCToOSInterface::YieldThread (bgc_alloc_spin_loh);
12732                     disable_preemptive (current_thread, cooperative_mode);
12733                     enter_spin_lock (&more_space_lock);
12734                     add_saved_spinlock_info (me_acquire, mt_alloc_large);
12735                     dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
12736                 }
12737             }
12738             else
12739             {
12740                 wait_for_background (awr_loh_alloc_during_bgc);
12741             }
12742         }
12743     }
12744 #endif //BACKGROUND_GC
12745 
12746     gc_reason gr = reason_oos_loh;
12747     generation* gen = generation_of (gen_number);
12748     oom_reason oom_r = oom_no_failure;
12749     size_t current_full_compact_gc_count = 0;
12750 
12751     // No variable values should be "carried over" from one state to the other.
12752     // That's why there are local variable for each state
12753     allocation_state loh_alloc_state = a_state_start;
12754 #ifdef RECORD_LOH_STATE
12755     EEThreadId current_thread_id;
12756     current_thread_id.SetToCurrentThread();
12757 #endif //RECORD_LOH_STATE
12758 
12759     // If we can get a new seg it means allocation will succeed.
12760     while (1)
12761     {
12762         dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
12763 
12764 #ifdef RECORD_LOH_STATE
12765         add_saved_loh_state (loh_alloc_state, current_thread_id);
12766 #endif //RECORD_LOH_STATE
12767         switch (loh_alloc_state)
12768         {
12769             case a_state_can_allocate:
12770             case a_state_cant_allocate:
12771             {
12772                 goto exit;
12773             }
12774             case a_state_start:
12775             {
12776                 loh_alloc_state = a_state_try_fit;
12777                 break;
12778             }
12779             case a_state_try_fit:
12780             {
12781                 BOOL commit_failed_p = FALSE;
12782                 BOOL can_use_existing_p = FALSE;
12783 
12784                 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12785                                                   align_const, &commit_failed_p, &oom_r);
12786                 loh_alloc_state = (can_use_existing_p ?
12787                                         a_state_can_allocate :
12788                                         (commit_failed_p ?
12789                                             a_state_trigger_full_compact_gc :
12790                                             a_state_acquire_seg));
12791                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12792                 break;
12793             }
12794             case a_state_try_fit_new_seg:
12795             {
12796                 BOOL commit_failed_p = FALSE;
12797                 BOOL can_use_existing_p = FALSE;
12798 
12799                 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12800                                                   align_const, &commit_failed_p, &oom_r);
12801                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12802                 // another LOH allocating thread could have beat us to acquire the msl so
12803                 // we need to try again.
12804                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
12805                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12806                 break;
12807             }
12808             case a_state_try_fit_new_seg_after_cg:
12809             {
12810                 BOOL commit_failed_p = FALSE;
12811                 BOOL can_use_existing_p = FALSE;
12812 
12813                 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12814                                                   align_const, &commit_failed_p, &oom_r);
12815                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
12816                 // another LOH allocating thread could have beat us to acquire the msl so
12817                 // we need to try again. However, if we failed to commit, which means we
12818                 // did have space on the seg, we bail right away 'cause we already did a
12819                 // full compacting GC.
12820                 loh_alloc_state = (can_use_existing_p ?
12821                                         a_state_can_allocate :
12822                                         (commit_failed_p ?
12823                                             a_state_cant_allocate :
12824                                             a_state_acquire_seg_after_cg));
12825                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12826                 break;
12827             }
12828             case a_state_try_fit_no_seg:
12829             {
12830                 BOOL commit_failed_p = FALSE;
12831                 BOOL can_use_existing_p = FALSE;
12832 
12833                 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12834                                                   align_const, &commit_failed_p, &oom_r);
12835                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_cant_allocate);
12836                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12837                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12838                 break;
12839             }
12840             case a_state_try_fit_after_cg:
12841             {
12842                 BOOL commit_failed_p = FALSE;
12843                 BOOL can_use_existing_p = FALSE;
12844 
12845                 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12846                                                   align_const, &commit_failed_p, &oom_r);
12847                 loh_alloc_state = (can_use_existing_p ?
12848                                         a_state_can_allocate :
12849                                         (commit_failed_p ?
12850                                             a_state_cant_allocate :
12851                                             a_state_acquire_seg_after_cg));
12852                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12853                 break;
12854             }
12855             case a_state_try_fit_after_bgc:
12856             {
12857                 BOOL commit_failed_p = FALSE;
12858                 BOOL can_use_existing_p = FALSE;
12859 
12860                 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
12861                                                   align_const, &commit_failed_p, &oom_r);
12862                 loh_alloc_state = (can_use_existing_p ?
12863                                         a_state_can_allocate :
12864                                         (commit_failed_p ?
12865                                             a_state_trigger_full_compact_gc :
12866                                             a_state_acquire_seg_after_bgc));
12867                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
12868                 break;
12869             }
12870             case a_state_acquire_seg:
12871             {
12872                 BOOL can_get_new_seg_p = FALSE;
12873                 BOOL did_full_compacting_gc = FALSE;
12874 
12875                 current_full_compact_gc_count = get_full_compact_gc_count();
12876 
12877                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12878                 loh_alloc_state = (can_get_new_seg_p ?
12879                                         a_state_try_fit_new_seg :
12880                                         (did_full_compacting_gc ?
12881                                             a_state_check_retry_seg :
12882                                             a_state_check_and_wait_for_bgc));
12883                 break;
12884             }
12885             case a_state_acquire_seg_after_cg:
12886             {
12887                 BOOL can_get_new_seg_p = FALSE;
12888                 BOOL did_full_compacting_gc = FALSE;
12889 
12890                 current_full_compact_gc_count = get_full_compact_gc_count();
12891 
12892                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12893                 // Since we release the msl before we try to allocate a seg, other
12894                 // threads could have allocated a bunch of segments before us so
12895                 // we might need to retry.
12896                 loh_alloc_state = (can_get_new_seg_p ?
12897                                         a_state_try_fit_new_seg_after_cg :
12898                                         a_state_check_retry_seg);
12899                 break;
12900             }
12901             case a_state_acquire_seg_after_bgc:
12902             {
12903                 BOOL can_get_new_seg_p = FALSE;
12904                 BOOL did_full_compacting_gc = FALSE;
12905 
12906                 current_full_compact_gc_count = get_full_compact_gc_count();
12907 
12908                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
12909                 loh_alloc_state = (can_get_new_seg_p ?
12910                                         a_state_try_fit_new_seg :
12911                                         (did_full_compacting_gc ?
12912                                             a_state_check_retry_seg :
12913                                             a_state_trigger_full_compact_gc));
12914                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12915                 break;
12916             }
12917             case a_state_check_and_wait_for_bgc:
12918             {
12919                 BOOL bgc_in_progress_p = FALSE;
12920                 BOOL did_full_compacting_gc = FALSE;
12921 
12922                 if (fgn_maxgen_percent)
12923                 {
12924                     dprintf (2, ("FGN: failed to acquire seg, may need to do a full blocking GC"));
12925                     send_full_gc_notification (max_generation, FALSE);
12926                 }
12927 
12928                 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc);
12929                 loh_alloc_state = (!bgc_in_progress_p ?
12930                                         a_state_trigger_full_compact_gc :
12931                                         (did_full_compacting_gc ?
12932                                             a_state_try_fit_after_cg :
12933                                             a_state_try_fit_after_bgc));
12934                 break;
12935             }
12936             case a_state_trigger_full_compact_gc:
12937             {
12938                 BOOL got_full_compacting_gc = FALSE;
12939 
12940                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r);
12941                 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12942                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12943                 break;
12944             }
12945             case a_state_check_retry_seg:
12946             {
12947                 BOOL should_retry_gc = retry_full_compact_gc (size);
12948                 BOOL should_retry_get_seg = FALSE;
12949                 if (!should_retry_gc)
12950                 {
12951                     size_t last_full_compact_gc_count = current_full_compact_gc_count;
12952                     current_full_compact_gc_count = get_full_compact_gc_count();
12953 
12954                     if (current_full_compact_gc_count > (last_full_compact_gc_count + 1))
12955                     {
12956                         should_retry_get_seg = TRUE;
12957                     }
12958                 }
12959 
12960                 loh_alloc_state = (should_retry_gc ?
12961                                         a_state_trigger_full_compact_gc :
12962                                         (should_retry_get_seg ?
12963                                             a_state_acquire_seg_after_cg :
12964                                             a_state_cant_allocate));
12965                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
12966                 break;
12967             }
12968             default:
12969             {
12970                 assert (!"Invalid state!");
12971                 break;
12972             }
12973         }
12974     }
12975 
12976 exit:
12977     if (loh_alloc_state == a_state_cant_allocate)
12978     {
12979         assert (oom_r != oom_no_failure);
12980         handle_oom (heap_number,
12981                     oom_r,
12982                     size,
12983                     0,
12984                     0);
12985 
12986         add_saved_spinlock_info (me_release, mt_alloc_large_cant);
12987         dprintf (SPINLOCK_LOG, ("[%d]Lmsl for loh oom", heap_number));
12988         leave_spin_lock (&more_space_lock);
12989     }
12990 
12991     return (loh_alloc_state == a_state_can_allocate);
12992 }
12993 
try_allocate_more_space(alloc_context * acontext,size_t size,int gen_number)12994 int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
12995                                    int gen_number)
12996 {
12997     if (gc_heap::gc_started)
12998     {
12999         wait_for_gc_done();
13000         return -1;
13001     }
13002 
13003 #ifdef SYNCHRONIZATION_STATS
13004     unsigned int msl_acquire_start = GetCycleCount32();
13005 #endif //SYNCHRONIZATION_STATS
13006     enter_spin_lock (&more_space_lock);
13007     add_saved_spinlock_info (me_acquire, mt_try_alloc);
13008     dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
13009 #ifdef SYNCHRONIZATION_STATS
13010     unsigned int msl_acquire = GetCycleCount32() - msl_acquire_start;
13011     total_msl_acquire += msl_acquire;
13012     num_msl_acquired++;
13013     if (msl_acquire > 200)
13014     {
13015         num_high_msl_acquire++;
13016     }
13017     else
13018     {
13019         num_low_msl_acquire++;
13020     }
13021 #endif //SYNCHRONIZATION_STATS
13022 
13023     /*
13024     // We are commenting this out 'cause we don't see the point - we already
13025     // have checked gc_started when we were acquiring the msl - no need to check
13026     // again. This complicates the logic in bgc_suspend_EE 'cause that one would
13027     // need to release msl which causes all sorts of trouble.
13028     if (gc_heap::gc_started)
13029     {
13030 #ifdef SYNCHRONIZATION_STATS
13031         good_suspension++;
13032 #endif //SYNCHRONIZATION_STATS
13033         BOOL fStress = (g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_TRANSITION) != 0;
13034         if (!fStress)
13035         {
13036             //Rendez vous early (MP scaling issue)
13037             //dprintf (1, ("[%d]waiting for gc", heap_number));
13038             wait_for_gc_done();
13039 #ifdef MULTIPLE_HEAPS
13040             return -1;
13041 #endif //MULTIPLE_HEAPS
13042         }
13043     }
13044     */
13045 
13046     dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
13047 
13048     int align_const = get_alignment_constant (gen_number != (max_generation+1));
13049 
13050     if (fgn_maxgen_percent)
13051     {
13052         check_for_full_gc (gen_number, size);
13053     }
13054 
13055     if (!(new_allocation_allowed (gen_number)))
13056     {
13057         if (fgn_maxgen_percent && (gen_number == 0))
13058         {
13059             // We only check gen0 every so often, so take this opportunity to check again.
13060             check_for_full_gc (gen_number, size);
13061         }
13062 
13063 #ifdef BACKGROUND_GC
13064         wait_for_bgc_high_memory (awr_gen0_alloc);
13065 #endif //BACKGROUND_GC
13066 
13067 #ifdef SYNCHRONIZATION_STATS
13068         bad_suspension++;
13069 #endif //SYNCHRONIZATION_STATS
13070         dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
13071 
13072         if (!settings.concurrent || (gen_number == 0))
13073         {
13074             vm_heap->GarbageCollectGeneration (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh));
13075 #ifdef MULTIPLE_HEAPS
13076             enter_spin_lock (&more_space_lock);
13077             add_saved_spinlock_info (me_acquire, mt_try_budget);
13078             dprintf (SPINLOCK_LOG, ("[%d]Emsl out budget", heap_number));
13079 #endif //MULTIPLE_HEAPS
13080         }
13081     }
13082 
13083     BOOL can_allocate = ((gen_number == 0) ?
13084         allocate_small (gen_number, size, acontext, align_const) :
13085         allocate_large (gen_number, size, acontext, align_const));
13086 
13087     if (can_allocate)
13088     {
13089         size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
13090         int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
13091 
13092         etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
13093 
13094 
13095         if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
13096         {
13097 #ifdef FEATURE_REDHAWK
13098             FireEtwGCAllocationTick_V1((uint32_t)etw_allocation_running_amount[etw_allocation_index],
13099                                     ((gen_number == 0) ? ETW::GCLog::ETW_GC_INFO::AllocationSmall : ETW::GCLog::ETW_GC_INFO::AllocationLarge),
13100                                     GetClrInstanceId());
13101 #else
13102             // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
13103             // The ones that do are much less efficient.
13104 #if defined(FEATURE_EVENT_TRACE)
13105             if (EventEnabledGCAllocationTick_V2())
13106             {
13107                 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
13108             }
13109 #endif //FEATURE_EVENT_TRACE
13110 #endif //FEATURE_REDHAWK
13111             etw_allocation_running_amount[etw_allocation_index] = 0;
13112         }
13113     }
13114 
13115     return (int)can_allocate;
13116 }
13117 
13118 #ifdef MULTIPLE_HEAPS
balance_heaps(alloc_context * acontext)13119 void gc_heap::balance_heaps (alloc_context* acontext)
13120 {
13121 
13122     if (acontext->alloc_count < 4)
13123     {
13124         if (acontext->alloc_count == 0)
13125         {
13126             acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, 0) ));
13127             gc_heap* hp = acontext->get_home_heap()->pGenGCHeap;
13128             dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
13129             acontext->set_alloc_heap(acontext->get_home_heap());
13130             hp->alloc_context_count++;
13131         }
13132     }
13133     else
13134     {
13135         BOOL set_home_heap = FALSE;
13136         int hint = 0;
13137 
13138         if (heap_select::can_find_heap_fast())
13139         {
13140             if (acontext->get_home_heap() != NULL)
13141                 hint = acontext->get_home_heap()->pGenGCHeap->heap_number;
13142             if (acontext->get_home_heap() != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
13143             {
13144                 set_home_heap = TRUE;
13145             }
13146         }
13147         else
13148         {
13149             // can't use gdt
13150             if ((acontext->alloc_count & 3) == 0)
13151                 set_home_heap = TRUE;
13152         }
13153 
13154         if (set_home_heap)
13155         {
13156 /*
13157             // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
13158             if (n_heaps > MAX_SUPPORTED_CPUS)
13159             {
13160                 // on machines with many processors cache affinity is really king, so don't even try
13161                 // to balance on these.
13162                 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
13163                 acontext->alloc_heap = acontext->home_heap;
13164             }
13165             else
13166 */
13167             {
13168                 gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13169 
13170                 dynamic_data* dd = org_hp->dynamic_data_of (0);
13171                 ptrdiff_t org_size = dd_new_allocation (dd);
13172                 int org_alloc_context_count;
13173                 int max_alloc_context_count;
13174                 gc_heap* max_hp;
13175                 ptrdiff_t max_size;
13176                 size_t delta = dd_min_size (dd)/4;
13177 
13178                 int start, end, finish;
13179                 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13180                 finish = start + n_heaps;
13181 
13182 try_again:
13183                 do
13184                 {
13185                     max_hp = org_hp;
13186                     max_size = org_size + delta;
13187                     acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, hint) ));
13188 
13189                     if (org_hp == acontext->get_home_heap()->pGenGCHeap)
13190                         max_size = max_size + delta;
13191 
13192                     org_alloc_context_count = org_hp->alloc_context_count;
13193                     max_alloc_context_count = org_alloc_context_count;
13194                     if (max_alloc_context_count > 1)
13195                         max_size /= max_alloc_context_count;
13196 
13197                     for (int i = start; i < end; i++)
13198                     {
13199                         gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13200                         dd = hp->dynamic_data_of (0);
13201                         ptrdiff_t size = dd_new_allocation (dd);
13202                         if (hp == acontext->get_home_heap()->pGenGCHeap)
13203                             size = size + delta;
13204                         int hp_alloc_context_count = hp->alloc_context_count;
13205                         if (hp_alloc_context_count > 0)
13206                             size /= (hp_alloc_context_count + 1);
13207                         if (size > max_size)
13208                         {
13209                             max_hp = hp;
13210                             max_size = size;
13211                             max_alloc_context_count = hp_alloc_context_count;
13212                         }
13213                     }
13214                 }
13215                 while (org_alloc_context_count != org_hp->alloc_context_count ||
13216                        max_alloc_context_count != max_hp->alloc_context_count);
13217 
13218                 if ((max_hp == org_hp) && (end < finish))
13219                 {
13220                     start = end; end = finish;
13221                     delta = dd_min_size(dd)/2; // Make it twice as hard to balance to remote nodes on NUMA.
13222                     goto try_again;
13223                 }
13224 
13225                 if (max_hp != org_hp)
13226                 {
13227                     org_hp->alloc_context_count--;
13228                     max_hp->alloc_context_count++;
13229                     acontext->set_alloc_heap(GCHeap::GetHeap(max_hp->heap_number));
13230 #if !defined(FEATURE_PAL)
13231                     if (CPUGroupInfo::CanEnableGCCPUGroups())
13232                     {   //only set ideal processor when max_hp and org_hp are in the same cpu
13233                         //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
13234                         uint8_t org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
13235                         uint8_t max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
13236                         if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
13237                         {
13238                             uint8_t group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
13239 
13240                             GCThreadAffinity affinity;
13241                             affinity.Processor = group_proc_no;
13242                             affinity.Group = org_gn;
13243                             if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13244                             {
13245                                 dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
13246                                             org_hp->heap_number));
13247                             }
13248                         }
13249                     }
13250                     else
13251                     {
13252                         uint8_t proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
13253 
13254                         GCThreadAffinity affinity;
13255                         affinity.Processor = proc_no;
13256                         affinity.Group = GCThreadAffinity::None;
13257 
13258                         if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13259                         {
13260                             dprintf (3, ("Failed to set the ideal processor for heap %d.",
13261                                         org_hp->heap_number));
13262                         }
13263                     }
13264 #endif // !FEATURE_PAL
13265                     dprintf (3, ("Switching context %p (home heap %d) ",
13266                                  acontext,
13267                         acontext->get_home_heap()->pGenGCHeap->heap_number));
13268                     dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ",
13269                                  org_hp->heap_number,
13270                                  org_size,
13271                                  org_alloc_context_count));
13272                     dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n",
13273                                  max_hp->heap_number,
13274                                  dd_new_allocation(max_hp->dynamic_data_of(0)),
13275                                                    max_alloc_context_count));
13276                 }
13277             }
13278         }
13279     }
13280     acontext->alloc_count++;
13281 }
13282 
balance_heaps_loh(alloc_context * acontext,size_t)13283 gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t /*size*/)
13284 {
13285     gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13286     //dprintf (1, ("LA: %Id", size));
13287 
13288     //if (size > 128*1024)
13289     if (1)
13290     {
13291         dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
13292 
13293         ptrdiff_t org_size = dd_new_allocation (dd);
13294         gc_heap* max_hp;
13295         ptrdiff_t max_size;
13296         size_t delta = dd_min_size (dd) * 4;
13297 
13298         int start, end, finish;
13299         heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13300         finish = start + n_heaps;
13301 
13302 try_again:
13303         {
13304             max_hp = org_hp;
13305             max_size = org_size + delta;
13306             dprintf (3, ("orig hp: %d, max size: %d",
13307                 org_hp->heap_number,
13308                 max_size));
13309 
13310             for (int i = start; i < end; i++)
13311             {
13312                 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13313                 dd = hp->dynamic_data_of (max_generation + 1);
13314                 ptrdiff_t size = dd_new_allocation (dd);
13315                 dprintf (3, ("hp: %d, size: %d",
13316                     hp->heap_number,
13317                     size));
13318                 if (size > max_size)
13319                 {
13320                     max_hp = hp;
13321                     max_size = size;
13322                     dprintf (3, ("max hp: %d, max size: %d",
13323                         max_hp->heap_number,
13324                         max_size));
13325                 }
13326             }
13327         }
13328 
13329         if ((max_hp == org_hp) && (end < finish))
13330         {
13331             start = end; end = finish;
13332             delta = dd_min_size(dd) * 4;   // Need to tuning delta
13333             goto try_again;
13334         }
13335 
13336         if (max_hp != org_hp)
13337         {
13338             dprintf (3, ("loh: %d(%Id)->%d(%Id)",
13339                 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
13340                 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
13341         }
13342 
13343         return max_hp;
13344     }
13345     else
13346     {
13347         return org_hp;
13348     }
13349 }
13350 #endif //MULTIPLE_HEAPS
13351 
allocate_more_space(alloc_context * acontext,size_t size,int alloc_generation_number)13352 BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
13353                                   int alloc_generation_number)
13354 {
13355     int status;
13356     do
13357     {
13358 #ifdef MULTIPLE_HEAPS
13359         if (alloc_generation_number == 0)
13360         {
13361             balance_heaps (acontext);
13362             status = acontext->get_alloc_heap()->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
13363         }
13364         else
13365         {
13366             gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
13367             status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
13368         }
13369 #else
13370         status = try_allocate_more_space (acontext, size, alloc_generation_number);
13371 #endif //MULTIPLE_HEAPS
13372     }
13373     while (status == -1);
13374 
13375     return (status != 0);
13376 }
13377 
13378 inline
allocate(size_t jsize,alloc_context * acontext)13379 CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
13380 {
13381     size_t size = Align (jsize);
13382     assert (size >= Align (min_obj_size));
13383     {
13384     retry:
13385         uint8_t*  result = acontext->alloc_ptr;
13386         acontext->alloc_ptr+=size;
13387         if (acontext->alloc_ptr <= acontext->alloc_limit)
13388         {
13389             CObjectHeader* obj = (CObjectHeader*)result;
13390             assert (obj != 0);
13391             return obj;
13392         }
13393         else
13394         {
13395             acontext->alloc_ptr -= size;
13396 
13397 #ifdef _MSC_VER
13398 #pragma inline_depth(0)
13399 #endif //_MSC_VER
13400 
13401             if (! allocate_more_space (acontext, size, 0))
13402                 return 0;
13403 
13404 #ifdef _MSC_VER
13405 #pragma inline_depth(20)
13406 #endif //_MSC_VER
13407 
13408             goto retry;
13409         }
13410     }
13411 }
13412 
13413 inline
try_fast_alloc(size_t jsize)13414 CObjectHeader* gc_heap::try_fast_alloc (size_t jsize)
13415 {
13416     size_t size = Align (jsize);
13417     assert (size >= Align (min_obj_size));
13418     generation* gen = generation_of (0);
13419     uint8_t*  result = generation_allocation_pointer (gen);
13420     generation_allocation_pointer (gen) += size;
13421     if (generation_allocation_pointer (gen) <=
13422         generation_allocation_limit (gen))
13423     {
13424         return (CObjectHeader*)result;
13425     }
13426     else
13427     {
13428         generation_allocation_pointer (gen) -= size;
13429         return 0;
13430     }
13431 }
leave_allocation_segment(generation * gen)13432 void  gc_heap::leave_allocation_segment (generation* gen)
13433 {
13434     adjust_limit (0, 0, gen, max_generation);
13435 }
13436 
init_free_and_plug()13437 void gc_heap::init_free_and_plug()
13438 {
13439 #ifdef FREE_USAGE_STATS
13440     for (int i = 0; i <= settings.condemned_generation; i++)
13441     {
13442         generation* gen = generation_of (i);
13443         memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
13444         memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13445         memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
13446     }
13447 
13448     if (settings.condemned_generation != max_generation)
13449     {
13450         for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
13451         {
13452             generation* gen = generation_of (i);
13453             memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13454         }
13455     }
13456 #endif //FREE_USAGE_STATS
13457 }
13458 
print_free_and_plug(const char * msg)13459 void gc_heap::print_free_and_plug (const char* msg)
13460 {
13461 #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
13462     int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
13463     for (int i = 0; i <= older_gen; i++)
13464     {
13465         generation* gen = generation_of (i);
13466         for (int j = 0; j < NUM_GEN_POWER2; j++)
13467         {
13468             if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
13469             {
13470                 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id",
13471                     msg,
13472                     heap_number,
13473                     (settings.concurrent ? "BGC" : "GC"),
13474                     settings.gc_index,
13475                     i,
13476                     (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
13477             }
13478         }
13479     }
13480 #else
13481     UNREFERENCED_PARAMETER(msg);
13482 #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
13483 }
13484 
add_gen_plug(int gen_number,size_t plug_size)13485 void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
13486 {
13487 #ifdef FREE_USAGE_STATS
13488     dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
13489     generation* gen = generation_of (gen_number);
13490     size_t sz = BASE_GEN_SIZE;
13491     int i = 0;
13492 
13493     for (; i < NUM_GEN_POWER2; i++)
13494     {
13495         if (plug_size < sz)
13496         {
13497             break;
13498         }
13499         sz = sz * 2;
13500     }
13501 
13502     (gen->gen_plugs[i])++;
13503 #else
13504     UNREFERENCED_PARAMETER(gen_number);
13505     UNREFERENCED_PARAMETER(plug_size);
13506 #endif //FREE_USAGE_STATS
13507 }
13508 
add_item_to_current_pinned_free(int gen_number,size_t free_size)13509 void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
13510 {
13511 #ifdef FREE_USAGE_STATS
13512     generation* gen = generation_of (gen_number);
13513     size_t sz = BASE_GEN_SIZE;
13514     int i = 0;
13515 
13516     for (; i < NUM_GEN_POWER2; i++)
13517     {
13518         if (free_size < sz)
13519         {
13520             break;
13521         }
13522         sz = sz * 2;
13523     }
13524 
13525     (gen->gen_current_pinned_free_spaces[i])++;
13526     generation_pinned_free_obj_space (gen) += free_size;
13527     dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)",
13528         free_size, (i + 10), gen_number,
13529         generation_pinned_free_obj_space (gen),
13530         gen->gen_current_pinned_free_spaces[i]));
13531 #else
13532     UNREFERENCED_PARAMETER(gen_number);
13533     UNREFERENCED_PARAMETER(free_size);
13534 #endif //FREE_USAGE_STATS
13535 }
13536 
add_gen_free(int gen_number,size_t free_size)13537 void gc_heap::add_gen_free (int gen_number, size_t free_size)
13538 {
13539 #ifdef FREE_USAGE_STATS
13540     dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13541     generation* gen = generation_of (gen_number);
13542     size_t sz = BASE_GEN_SIZE;
13543     int i = 0;
13544 
13545     for (; i < NUM_GEN_POWER2; i++)
13546     {
13547         if (free_size < sz)
13548         {
13549             break;
13550         }
13551         sz = sz * 2;
13552     }
13553 
13554     (gen->gen_free_spaces[i])++;
13555 #else
13556     UNREFERENCED_PARAMETER(gen_number);
13557     UNREFERENCED_PARAMETER(free_size);
13558 #endif //FREE_USAGE_STATS
13559 }
13560 
remove_gen_free(int gen_number,size_t free_size)13561 void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13562 {
13563 #ifdef FREE_USAGE_STATS
13564     dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13565     generation* gen = generation_of (gen_number);
13566     size_t sz = BASE_GEN_SIZE;
13567     int i = 0;
13568 
13569     for (; i < NUM_GEN_POWER2; i++)
13570     {
13571         if (free_size < sz)
13572         {
13573             break;
13574         }
13575         sz = sz * 2;
13576     }
13577 
13578     (gen->gen_free_spaces[i])--;
13579 #else
13580     UNREFERENCED_PARAMETER(gen_number);
13581     UNREFERENCED_PARAMETER(free_size);
13582 #endif //FREE_USAGE_STATS
13583 }
13584 
allocate_in_older_generation(generation * gen,size_t size,int from_gen_number,uint8_t * old_loc REQD_ALIGN_AND_OFFSET_DCL)13585 uint8_t* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13586                                              int from_gen_number,
13587                                              uint8_t* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13588 {
13589     size = Align (size);
13590     assert (size >= Align (min_obj_size));
13591     assert (from_gen_number < max_generation);
13592     assert (from_gen_number >= 0);
13593     assert (generation_of (from_gen_number + 1) == gen);
13594 
13595     allocator* gen_allocator = generation_allocator (gen);
13596     BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13597     int pad_in_front = (old_loc != 0)? USE_PADDING_FRONT : 0;
13598 
13599     size_t real_size = size + Align (min_obj_size);
13600     if (pad_in_front)
13601         real_size += Align (min_obj_size);
13602 
13603     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13604                        generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13605     {
13606         size_t sz_list = gen_allocator->first_bucket_size();
13607         for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13608         {
13609             if ((real_size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13610             {
13611                 uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13612                 uint8_t* prev_free_item = 0;
13613                 while (free_list != 0)
13614                 {
13615                     dprintf (3, ("considering free list %Ix", (size_t)free_list));
13616 
13617                     size_t free_list_size = unused_array_size (free_list);
13618 
13619                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
13620                                     old_loc, USE_PADDING_TAIL | pad_in_front))
13621                     {
13622                         dprintf (4, ("F:%Ix-%Id",
13623                                      (size_t)free_list, free_list_size));
13624 
13625                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
13626                         generation_free_list_space (gen) -= free_list_size;
13627                         remove_gen_free (gen->gen_num, free_list_size);
13628 
13629                         adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
13630                         goto finished;
13631                     }
13632                     // We do first fit on bucket 0 because we are not guaranteed to find a fit there.
13633                     else if (discard_p || (a_l_idx == 0))
13634                     {
13635                         dprintf (3, ("couldn't use this free area, discarding"));
13636                         generation_free_obj_space (gen) += free_list_size;
13637 
13638                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
13639                         generation_free_list_space (gen) -= free_list_size;
13640                         remove_gen_free (gen->gen_num, free_list_size);
13641                     }
13642                     else
13643                     {
13644                         prev_free_item = free_list;
13645                     }
13646                     free_list = free_list_slot (free_list);
13647                 }
13648             }
13649             sz_list = sz_list * 2;
13650         }
13651         //go back to the beginning of the segment list
13652         generation_allocate_end_seg_p (gen) = TRUE;
13653         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
13654         if (seg != generation_allocation_segment (gen))
13655         {
13656             leave_allocation_segment (gen);
13657             generation_allocation_segment (gen) = seg;
13658         }
13659         while (seg != ephemeral_heap_segment)
13660         {
13661             if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13662                            heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
13663             {
13664                 dprintf (3, ("using what's left in committed"));
13665                 adjust_limit (heap_segment_plan_allocated (seg),
13666                               heap_segment_committed (seg) -
13667                               heap_segment_plan_allocated (seg),
13668                               gen, from_gen_number+1);
13669                 // dformat (t, 3, "Expanding segment allocation");
13670                 heap_segment_plan_allocated (seg) =
13671                     heap_segment_committed (seg);
13672                 goto finished;
13673             }
13674             else
13675             {
13676                 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13677                                 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13678                     grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
13679                 {
13680                     dprintf (3, ("using what's left in reserved"));
13681                     adjust_limit (heap_segment_plan_allocated (seg),
13682                                   heap_segment_committed (seg) -
13683                                   heap_segment_plan_allocated (seg),
13684                                   gen, from_gen_number+1);
13685                     heap_segment_plan_allocated (seg) =
13686                         heap_segment_committed (seg);
13687 
13688                     goto finished;
13689                 }
13690                 else
13691                 {
13692                     leave_allocation_segment (gen);
13693                     heap_segment*   next_seg = heap_segment_next_rw (seg);
13694                     if (next_seg)
13695                     {
13696                         dprintf (3, ("getting next segment"));
13697                         generation_allocation_segment (gen) = next_seg;
13698                         generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13699                         generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13700                     }
13701                     else
13702                     {
13703                         size = 0;
13704                         goto finished;
13705                     }
13706                 }
13707             }
13708             seg = generation_allocation_segment (gen);
13709         }
13710         //No need to fix the last region. Will be done later
13711         size = 0;
13712         goto finished;
13713     }
13714     finished:
13715     if (0 == size)
13716     {
13717         return 0;
13718     }
13719     else
13720     {
13721         uint8_t*  result = generation_allocation_pointer (gen);
13722         size_t pad = 0;
13723 
13724 #ifdef SHORT_PLUGS
13725         if ((pad_in_front & USE_PADDING_FRONT) &&
13726             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13727              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13728         {
13729             pad = Align (min_obj_size);
13730             set_plug_padded (old_loc);
13731         }
13732 #endif //SHORT_PLUGS
13733 
13734 #ifdef FEATURE_STRUCTALIGN
13735         _ASSERTE(!old_loc || alignmentOffset != 0);
13736         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13737         if (old_loc != 0)
13738         {
13739             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13740             set_node_aligninfo (old_loc, requiredAlignment, pad1);
13741             pad += pad1;
13742         }
13743 #else // FEATURE_STRUCTALIGN
13744         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13745         {
13746             pad += switch_alignment_size (is_plug_padded (old_loc));
13747             set_node_realigned (old_loc);
13748             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
13749                          (size_t)old_loc, (size_t)(result+pad)));
13750             assert (same_large_alignment_p (result + pad, old_loc));
13751         }
13752 #endif // FEATURE_STRUCTALIGN
13753         dprintf (3, ("Allocate %Id bytes", size));
13754 
13755         if ((old_loc == 0) || (pad != 0))
13756         {
13757             //allocating a non plug or a gap, so reset the start region
13758             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13759         }
13760 
13761         generation_allocation_pointer (gen) += size + pad;
13762         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
13763         if (generation_allocate_end_seg_p (gen))
13764         {
13765             generation_end_seg_allocated (gen) += size;
13766         }
13767         else
13768         {
13769             generation_free_list_allocated (gen) += size;
13770         }
13771         generation_allocation_size (gen) += size;
13772 
13773         dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix",
13774             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13775             generation_allocation_context_start_region (gen)));
13776 
13777         return result + pad;;
13778     }
13779 }
13780 
repair_allocation_in_expanded_heap(generation * consing_gen)13781 void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
13782 {
13783     //make sure that every generation has a planned allocation start
13784     int  gen_number = max_generation - 1;
13785     while (gen_number>= 0)
13786     {
13787         generation* gen = generation_of (gen_number);
13788         if (0 == generation_plan_allocation_start (gen))
13789         {
13790             realloc_plan_generation_start (gen, consing_gen);
13791 
13792             assert (generation_plan_allocation_start (gen));
13793         }
13794         gen_number--;
13795     }
13796 
13797     // now we know the planned allocation size
13798     size_t  size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
13799     heap_segment* seg = generation_allocation_segment (consing_gen);
13800     if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
13801     {
13802         if (size != 0)
13803         {
13804             heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
13805         }
13806     }
13807     else
13808     {
13809         assert (settings.condemned_generation == max_generation);
13810         uint8_t* first_address = generation_allocation_limit (consing_gen);
13811         //look through the pinned plugs for relevant ones.
13812         //Look for the right pinned plug to start from.
13813         size_t mi = 0;
13814         mark* m = 0;
13815         while (mi != mark_stack_tos)
13816         {
13817             m = pinned_plug_of (mi);
13818             if ((pinned_plug (m) == first_address))
13819                 break;
13820             else
13821                 mi++;
13822         }
13823         assert (mi != mark_stack_tos);
13824         pinned_len (m) = size;
13825     }
13826 }
13827 
13828 //tododefrag optimize for new segment (plan_allocated == mem)
allocate_in_expanded_heap(generation * gen,size_t size,BOOL & adjacentp,uint8_t * old_loc,BOOL set_padding_on_saved_p,mark * pinned_plug_entry,BOOL consider_bestfit,int active_new_gen_number REQD_ALIGN_AND_OFFSET_DCL)13829 uint8_t* gc_heap::allocate_in_expanded_heap (generation* gen,
13830                                           size_t size,
13831                                           BOOL& adjacentp,
13832                                           uint8_t* old_loc,
13833 #ifdef SHORT_PLUGS
13834                                           BOOL set_padding_on_saved_p,
13835                                           mark* pinned_plug_entry,
13836 #endif //SHORT_PLUGS
13837                                           BOOL consider_bestfit,
13838                                           int active_new_gen_number
13839                                           REQD_ALIGN_AND_OFFSET_DCL)
13840 {
13841     UNREFERENCED_PARAMETER(active_new_gen_number);
13842     dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
13843 
13844     size = Align (size);
13845     assert (size >= Align (min_obj_size));
13846     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
13847 
13848     if (consider_bestfit && use_bestfit)
13849     {
13850         assert (bestfit_seg);
13851         dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id",
13852                     old_loc, size));
13853         return bestfit_seg->fit (old_loc,
13854 #ifdef SHORT_PLUGS
13855                                  set_padding_on_saved_p,
13856                                  pinned_plug_entry,
13857 #endif //SHORT_PLUGS
13858                                  size REQD_ALIGN_AND_OFFSET_ARG);
13859     }
13860 
13861     heap_segment* seg = generation_allocation_segment (gen);
13862 
13863     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13864                        generation_allocation_limit (gen), old_loc,
13865                        ((generation_allocation_limit (gen) !=
13866                           heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
13867     {
13868         dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
13869             generation_allocation_limit (gen)));
13870 
13871         adjacentp = FALSE;
13872         uint8_t* first_address = (generation_allocation_limit (gen) ?
13873                                generation_allocation_limit (gen) :
13874                                heap_segment_mem (seg));
13875         assert (in_range_for_segment (first_address, seg));
13876 
13877         uint8_t* end_address   = heap_segment_reserved (seg);
13878 
13879         dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
13880             first_address, generation_allocation_limit (gen), end_address));
13881 
13882         size_t mi = 0;
13883         mark* m = 0;
13884 
13885         if (heap_segment_allocated (seg) != heap_segment_mem (seg))
13886         {
13887             assert (settings.condemned_generation == max_generation);
13888             //look through the pinned plugs for relevant ones.
13889             //Look for the right pinned plug to start from.
13890             while (mi != mark_stack_tos)
13891             {
13892                 m = pinned_plug_of (mi);
13893                 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
13894                 {
13895                     dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
13896                     break;
13897                 }
13898                 else
13899                     mi++;
13900             }
13901             if (mi != mark_stack_tos)
13902             {
13903                 //fix old free list.
13904                 size_t  hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
13905                 {
13906                     dprintf(3,("gc filling up hole"));
13907                     ptrdiff_t mi1 = (ptrdiff_t)mi;
13908                     while ((mi1 >= 0) &&
13909                            (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
13910                     {
13911                         dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
13912                         mi1--;
13913                     }
13914                     if (mi1 >= 0)
13915                     {
13916                         size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
13917                         pinned_len (pinned_plug_of(mi1)) = hsize;
13918                         dprintf (3, ("changing %Ix len %Ix->%Ix",
13919                             pinned_plug (pinned_plug_of(mi1)),
13920                             saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
13921                     }
13922                 }
13923             }
13924         }
13925         else
13926         {
13927             assert (generation_allocation_limit (gen) ==
13928                     generation_allocation_pointer (gen));
13929             mi = mark_stack_tos;
13930         }
13931 
13932         while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
13933         {
13934             size_t len = pinned_len (m);
13935             uint8_t*  free_list = (pinned_plug (m) - len);
13936             dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)",
13937                 free_list, (free_list + len), len));
13938             if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
13939             {
13940                 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
13941                             (size_t)free_list, len));
13942                 {
13943                     generation_allocation_pointer (gen) = free_list;
13944                     generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13945                     generation_allocation_limit (gen) = (free_list + len);
13946                 }
13947                 goto allocate_in_free;
13948             }
13949             mi++;
13950             m = pinned_plug_of (mi);
13951         }
13952 
13953         //switch to the end of the segment.
13954         generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
13955         generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
13956         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
13957         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
13958         dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)",
13959             generation_allocation_pointer (gen), generation_allocation_limit (gen),
13960             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
13961 
13962         if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13963                          generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
13964         {
13965             dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
13966                 generation_allocation_limit (gen)));
13967             assert (!"Can't allocate if no free space");
13968             return 0;
13969         }
13970     }
13971     else
13972     {
13973         adjacentp = TRUE;
13974     }
13975 
13976 allocate_in_free:
13977     {
13978         uint8_t*  result = generation_allocation_pointer (gen);
13979         size_t pad = 0;
13980 
13981 #ifdef SHORT_PLUGS
13982         if ((pad_in_front & USE_PADDING_FRONT) &&
13983             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13984              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13985 
13986         {
13987             pad = Align (min_obj_size);
13988             set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
13989         }
13990 #endif //SHORT_PLUGS
13991 
13992 #ifdef FEATURE_STRUCTALIGN
13993         _ASSERTE(!old_loc || alignmentOffset != 0);
13994         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13995         if (old_loc != 0)
13996         {
13997             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13998             set_node_aligninfo (old_loc, requiredAlignment, pad1);
13999             pad += pad1;
14000             adjacentp = FALSE;
14001         }
14002 #else // FEATURE_STRUCTALIGN
14003         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14004         {
14005             pad += switch_alignment_size (is_plug_padded (old_loc));
14006             set_node_realigned (old_loc);
14007             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14008                          (size_t)old_loc, (size_t)(result+pad)));
14009             assert (same_large_alignment_p (result + pad, old_loc));
14010             adjacentp = FALSE;
14011         }
14012 #endif // FEATURE_STRUCTALIGN
14013 
14014         if ((old_loc == 0) || (pad != 0))
14015         {
14016             //allocating a non plug or a gap, so reset the start region
14017             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14018         }
14019 
14020         generation_allocation_pointer (gen) += size + pad;
14021         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14022         dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
14023 
14024         dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix",
14025             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14026             generation_allocation_context_start_region (gen)));
14027 
14028         return result + pad;
14029     }
14030 }
14031 
ensure_ephemeral_heap_segment(generation * consing_gen)14032 generation*  gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
14033 {
14034     heap_segment* seg = generation_allocation_segment (consing_gen);
14035     if (seg != ephemeral_heap_segment)
14036     {
14037         assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
14038         assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
14039 
14040         //fix the allocated size of the segment.
14041         heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14042 
14043         generation* new_consing_gen = generation_of (max_generation - 1);
14044         generation_allocation_pointer (new_consing_gen) =
14045                 heap_segment_mem (ephemeral_heap_segment);
14046         generation_allocation_limit (new_consing_gen) =
14047             generation_allocation_pointer (new_consing_gen);
14048         generation_allocation_context_start_region (new_consing_gen) =
14049             generation_allocation_pointer (new_consing_gen);
14050         generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
14051 
14052         return new_consing_gen;
14053     }
14054     else
14055         return consing_gen;
14056 }
14057 
allocate_in_condemned_generations(generation * gen,size_t size,int from_gen_number,BOOL * convert_to_pinned_p,uint8_t * next_pinned_plug,heap_segment * current_seg,uint8_t * old_loc REQD_ALIGN_AND_OFFSET_DCL)14058 uint8_t* gc_heap::allocate_in_condemned_generations (generation* gen,
14059                                                   size_t size,
14060                                                   int from_gen_number,
14061 #ifdef SHORT_PLUGS
14062                                                   BOOL* convert_to_pinned_p,
14063                                                   uint8_t* next_pinned_plug,
14064                                                   heap_segment* current_seg,
14065 #endif //SHORT_PLUGS
14066                                                   uint8_t* old_loc
14067                                                   REQD_ALIGN_AND_OFFSET_DCL)
14068 {
14069     // Make sure that the youngest generation gap hasn't been allocated
14070     if (settings.promotion)
14071     {
14072         assert (generation_plan_allocation_start (youngest_generation) == 0);
14073     }
14074 
14075     size = Align (size);
14076     assert (size >= Align (min_obj_size));
14077     int to_gen_number = from_gen_number;
14078     if (from_gen_number != (int)max_generation)
14079     {
14080         to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
14081     }
14082 
14083     dprintf (3, ("aic gen%d: s: %Id", gen->gen_num, size));
14084 
14085     int pad_in_front = (old_loc != 0) ? USE_PADDING_FRONT : 0;
14086 
14087     if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
14088     {
14089         generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14090         generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14091     }
14092 retry:
14093     {
14094         heap_segment* seg = generation_allocation_segment (gen);
14095         if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14096                            generation_allocation_limit (gen), old_loc,
14097                            ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
14098         {
14099             if ((! (pinned_plug_que_empty_p()) &&
14100                  (generation_allocation_limit (gen) ==
14101                   pinned_plug (oldest_pin()))))
14102             {
14103                 size_t entry = deque_pinned_plug();
14104                 mark* pinned_plug_entry = pinned_plug_of (entry);
14105                 size_t len = pinned_len (pinned_plug_entry);
14106                 uint8_t* plug = pinned_plug (pinned_plug_entry);
14107                 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
14108 
14109 #ifdef FREE_USAGE_STATS
14110                 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
14111                 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id",
14112                     generation_allocated_since_last_pin (gen),
14113                     plug,
14114                     generation_allocated_in_pinned_free (gen)));
14115                 generation_allocated_since_last_pin (gen) = 0;
14116 
14117                 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
14118 #endif //FREE_USAGE_STATS
14119 
14120                 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix",
14121                     mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
14122 
14123                 assert(mark_stack_array[entry].len == 0 ||
14124                        mark_stack_array[entry].len >= Align(min_obj_size));
14125                 generation_allocation_pointer (gen) = plug + len;
14126                 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14127                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14128                 set_allocator_next_pin (gen);
14129 
14130                 //Add the size of the pinned plug to the right pinned allocations
14131                 //find out which gen this pinned plug came from
14132                 int frgn = object_gennum (plug);
14133                 if ((frgn != (int)max_generation) && settings.promotion)
14134                 {
14135                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
14136                     int togn = object_gennum_plan (plug);
14137                     if (frgn < togn)
14138                     {
14139                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
14140                     }
14141                 }
14142                 goto retry;
14143             }
14144 
14145             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
14146             {
14147                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14148                 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
14149             }
14150             else
14151             {
14152                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
14153                 {
14154                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14155                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14156                     dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
14157                 }
14158                 else
14159                 {
14160 #ifndef RESPECT_LARGE_ALIGNMENT
14161                     assert (gen != youngest_generation);
14162 #endif //RESPECT_LARGE_ALIGNMENT
14163 
14164                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14165                                     heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14166                         (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
14167                                             size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
14168                     {
14169                         dprintf (3, ("Expanded segment allocation by committing more memory"));
14170                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14171                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14172                     }
14173                     else
14174                     {
14175                         heap_segment*   next_seg = heap_segment_next (seg);
14176                         assert (generation_allocation_pointer (gen)>=
14177                                 heap_segment_mem (seg));
14178                         // Verify that all pinned plugs for this segment are consumed
14179                         if (!pinned_plug_que_empty_p() &&
14180                             ((pinned_plug (oldest_pin()) <
14181                               heap_segment_allocated (seg)) &&
14182                              (pinned_plug (oldest_pin()) >=
14183                               generation_allocation_pointer (gen))))
14184                         {
14185                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
14186                                          pinned_plug (oldest_pin())));
14187                             FATAL_GC_ERROR();
14188                         }
14189                         assert (generation_allocation_pointer (gen)>=
14190                                 heap_segment_mem (seg));
14191                         assert (generation_allocation_pointer (gen)<=
14192                                 heap_segment_committed (seg));
14193                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
14194 
14195                         if (next_seg)
14196                         {
14197                             generation_allocation_segment (gen) = next_seg;
14198                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14199                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14200                             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14201                         }
14202                         else
14203                         {
14204                             return 0; //should only happen during allocation of generation 0 gap
14205                             // in that case we are going to grow the heap anyway
14206                         }
14207                     }
14208                 }
14209             }
14210             set_allocator_next_pin (gen);
14211 
14212             goto retry;
14213         }
14214     }
14215 
14216     {
14217         assert (generation_allocation_pointer (gen)>=
14218                 heap_segment_mem (generation_allocation_segment (gen)));
14219         uint8_t* result = generation_allocation_pointer (gen);
14220         size_t pad = 0;
14221 #ifdef SHORT_PLUGS
14222         if ((pad_in_front & USE_PADDING_FRONT) &&
14223             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14224              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14225         {
14226             ptrdiff_t dist = old_loc - result;
14227             if (dist == 0)
14228             {
14229                 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
14230                 pad = 0;
14231             }
14232             else
14233             {
14234                 if ((dist > 0) && (dist < (ptrdiff_t)Align (min_obj_size)))
14235                 {
14236                     dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
14237                     FATAL_GC_ERROR();
14238                 }
14239 
14240                 pad = Align (min_obj_size);
14241                 set_plug_padded (old_loc);
14242             }
14243         }
14244 #endif //SHORT_PLUGS
14245 #ifdef FEATURE_STRUCTALIGN
14246         _ASSERTE(!old_loc || alignmentOffset != 0);
14247         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14248         if ((old_loc != 0))
14249         {
14250             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14251             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14252             pad += pad1;
14253         }
14254 #else // FEATURE_STRUCTALIGN
14255         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14256         {
14257             pad += switch_alignment_size (is_plug_padded (old_loc));
14258             set_node_realigned(old_loc);
14259             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14260                          (size_t)old_loc, (size_t)(result+pad)));
14261             assert (same_large_alignment_p (result + pad, old_loc));
14262         }
14263 #endif // FEATURE_STRUCTALIGN
14264 
14265 #ifdef SHORT_PLUGS
14266         if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
14267         {
14268             assert (old_loc != 0);
14269             ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
14270             assert (dist_to_next_pin >= 0);
14271 
14272             if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
14273             {
14274                 dprintf (3, ("%Ix->(%Ix,%Ix),%Ix(%Ix)(%Ix),NP->PP",
14275                     old_loc,
14276                     generation_allocation_pointer (gen),
14277                     generation_allocation_limit (gen),
14278                     next_pinned_plug,
14279                     size,
14280                     dist_to_next_pin));
14281                 clear_plug_padded (old_loc);
14282                 pad = 0;
14283                 *convert_to_pinned_p = TRUE;
14284                 record_interesting_data_point (idp_converted_pin);
14285 
14286                 return 0;
14287             }
14288         }
14289 #endif //SHORT_PLUGS
14290 
14291         if ((old_loc == 0) || (pad != 0))
14292         {
14293             //allocating a non plug or a gap, so reset the start region
14294             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14295         }
14296 
14297         generation_allocation_pointer (gen) += size + pad;
14298         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14299 
14300 #ifdef FREE_USAGE_STATS
14301         generation_allocated_since_last_pin (gen) += size;
14302 #endif //FREE_USAGE_STATS
14303 
14304         dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix",
14305             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14306             generation_allocation_context_start_region (gen)));
14307 
14308         assert (result + pad);
14309         return result + pad;
14310     }
14311 }
14312 
power(int x,int y)14313 inline int power (int x, int y)
14314 {
14315     int z = 1;
14316     for (int i = 0; i < y; i++)
14317     {
14318         z = z*x;
14319     }
14320     return z;
14321 }
14322 
joined_generation_to_condemn(BOOL should_evaluate_elevation,int n_initial,BOOL * blocking_collection_p STRESS_HEAP_ARG (int n_original))14323 int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
14324                                            int n_initial,
14325                                            BOOL* blocking_collection_p
14326                                            STRESS_HEAP_ARG(int n_original))
14327 {
14328     int n = n_initial;
14329 #ifdef MULTIPLE_HEAPS
14330     BOOL blocking_p = *blocking_collection_p;
14331     if (!blocking_p)
14332     {
14333         for (int i = 0; i < n_heaps; i++)
14334         {
14335             if (g_heaps[i]->last_gc_before_oom)
14336             {
14337                 dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
14338                 *blocking_collection_p = TRUE;
14339                 break;
14340             }
14341         }
14342     }
14343 #endif //MULTIPLE_HEAPS
14344 
14345     if (should_evaluate_elevation && (n == max_generation))
14346     {
14347         dprintf (GTC_LOG, ("lock: %d(%d)",
14348             (settings.should_lock_elevation ? 1 : 0),
14349             settings.elevation_locked_count));
14350 
14351         if (settings.should_lock_elevation)
14352         {
14353             settings.elevation_locked_count++;
14354             if (settings.elevation_locked_count == 6)
14355             {
14356                 settings.elevation_locked_count = 0;
14357             }
14358             else
14359             {
14360                 n = max_generation - 1;
14361                 settings.elevation_reduced = TRUE;
14362             }
14363         }
14364         else
14365         {
14366             settings.elevation_locked_count = 0;
14367         }
14368     }
14369     else
14370     {
14371         settings.should_lock_elevation = FALSE;
14372         settings.elevation_locked_count = 0;
14373     }
14374 
14375 #ifdef STRESS_HEAP
14376 #ifdef BACKGROUND_GC
14377     // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14378     // generations to be collected,
14379 
14380     if (n_original != max_generation &&
14381         g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
14382     {
14383 #ifndef FEATURE_REDHAWK
14384         // for the GC stress mix mode throttle down gen2 collections
14385         if (g_pConfig->IsGCStressMix())
14386         {
14387             size_t current_gc_count = 0;
14388 
14389 #ifdef MULTIPLE_HEAPS
14390             current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
14391 #else
14392             current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
14393 #endif //MULTIPLE_HEAPS
14394             // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
14395             if ((current_gc_count % 10) == 0)
14396             {
14397                 n = max_generation;
14398             }
14399         }
14400         // for traditional GC stress
14401         else
14402 #endif // !FEATURE_REDHAWK
14403         if (*blocking_collection_p)
14404         {
14405             // We call StressHeap() a lot for Concurrent GC Stress. However,
14406             // if we can not do a concurrent collection, no need to stress anymore.
14407             // @TODO: Enable stress when the memory pressure goes down again
14408             GCStressPolicy::GlobalDisable();
14409         }
14410         else
14411         {
14412             n = max_generation;
14413         }
14414     }
14415 #endif //BACKGROUND_GC
14416 #endif //STRESS_HEAP
14417 
14418     return n;
14419 }
14420 
14421 inline
get_survived_size(gc_history_per_heap * hist)14422 size_t get_survived_size (gc_history_per_heap* hist)
14423 {
14424     size_t surv_size = 0;
14425     gc_generation_data* gen_data;
14426 
14427     for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14428     {
14429         gen_data = &(hist->gen_data[gen_number]);
14430         surv_size += (gen_data->size_after -
14431                       gen_data->free_list_space_after -
14432                       gen_data->free_obj_space_after);
14433     }
14434 
14435     return surv_size;
14436 }
14437 
get_total_survived_size()14438 size_t gc_heap::get_total_survived_size()
14439 {
14440     size_t total_surv_size = 0;
14441 #ifdef MULTIPLE_HEAPS
14442     for (int i = 0; i < gc_heap::n_heaps; i++)
14443     {
14444         gc_heap* hp = gc_heap::g_heaps[i];
14445         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
14446         total_surv_size += get_survived_size (current_gc_data_per_heap);
14447     }
14448 #else
14449     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14450     total_surv_size = get_survived_size (current_gc_data_per_heap);
14451 #endif //MULTIPLE_HEAPS
14452     return total_surv_size;
14453 }
14454 
14455 // Gets what's allocated on both SOH and LOH that hasn't been collected.
get_current_allocated()14456 size_t gc_heap::get_current_allocated()
14457 {
14458     dynamic_data* dd = dynamic_data_of (0);
14459     size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
14460     dd = dynamic_data_of (max_generation + 1);
14461     current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
14462 
14463     return current_alloc;
14464 }
14465 
get_total_allocated()14466 size_t gc_heap::get_total_allocated()
14467 {
14468     size_t total_current_allocated = 0;
14469 #ifdef MULTIPLE_HEAPS
14470     for (int i = 0; i < gc_heap::n_heaps; i++)
14471     {
14472         gc_heap* hp = gc_heap::g_heaps[i];
14473         total_current_allocated += hp->get_current_allocated();
14474     }
14475 #else
14476     total_current_allocated = get_current_allocated();
14477 #endif //MULTIPLE_HEAPS
14478     return total_current_allocated;
14479 }
14480 
current_generation_size(int gen_number)14481 size_t gc_heap::current_generation_size (int gen_number)
14482 {
14483     dynamic_data* dd = dynamic_data_of (gen_number);
14484     size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
14485                         - dd_new_allocation (dd));
14486 
14487     return gen_size;
14488 }
14489 
14490 #ifdef _PREFAST_
14491 #pragma warning(push)
14492 #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
14493 #endif //_PREFAST_
14494 
14495 /*
14496     This is called by when we are actually doing a GC, or when we are just checking whether
14497     we would do a full blocking GC, in which case check_only_p is TRUE.
14498 
14499     The difference between calling this with check_only_p TRUE and FALSE is that when it's
14500     TRUE:
14501             settings.reason is ignored
14502             budgets are not checked (since they are checked before this is called)
14503             it doesn't change anything non local like generation_skip_ratio
14504 */
generation_to_condemn(int n_initial,BOOL * blocking_collection_p,BOOL * elevation_requested_p,BOOL check_only_p)14505 int gc_heap::generation_to_condemn (int n_initial,
14506                                     BOOL* blocking_collection_p,
14507                                     BOOL* elevation_requested_p,
14508                                     BOOL check_only_p)
14509 {
14510     gc_mechanisms temp_settings = settings;
14511     gen_to_condemn_tuning temp_condemn_reasons;
14512     gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
14513     gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
14514     if (!check_only_p)
14515     {
14516         if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
14517         {
14518             assert (n_initial >= 1);
14519         }
14520 
14521         assert (settings.reason != reason_empty);
14522     }
14523 
14524     local_condemn_reasons->init();
14525 
14526     int n = n_initial;
14527     int n_alloc = n;
14528     if (heap_number == 0)
14529     {
14530         dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
14531     }
14532     int i = 0;
14533     int temp_gen = 0;
14534     BOOL low_memory_detected = g_low_memory_status;
14535     uint32_t memory_load = 0;
14536     uint64_t available_physical = 0;
14537     uint64_t available_page_file = 0;
14538     BOOL check_memory = FALSE;
14539     BOOL high_fragmentation  = FALSE;
14540     BOOL v_high_memory_load  = FALSE;
14541     BOOL high_memory_load    = FALSE;
14542     BOOL low_ephemeral_space = FALSE;
14543     BOOL evaluate_elevation  = TRUE;
14544     *elevation_requested_p   = FALSE;
14545     *blocking_collection_p   = FALSE;
14546 
14547     BOOL check_max_gen_alloc = TRUE;
14548 
14549 #ifdef STRESS_HEAP
14550     int orig_gen = n;
14551 #endif //STRESS_HEAP
14552 
14553     if (!check_only_p)
14554     {
14555         dd_fragmentation (dynamic_data_of (0)) =
14556             generation_free_list_space (youngest_generation) +
14557             generation_free_obj_space (youngest_generation);
14558 
14559         dd_fragmentation (dynamic_data_of (max_generation + 1)) =
14560             generation_free_list_space (large_object_generation) +
14561             generation_free_obj_space (large_object_generation);
14562 
14563         //save new_allocation
14564         for (i = 0; i <= max_generation+1; i++)
14565         {
14566             dynamic_data* dd = dynamic_data_of (i);
14567             dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)",
14568                             heap_number, i,
14569                             dd_new_allocation (dd),
14570                             dd_desired_allocation (dd)));
14571             dd_gc_new_allocation (dd) = dd_new_allocation (dd);
14572         }
14573 
14574         local_condemn_reasons->set_gen (gen_initial, n);
14575         temp_gen = n;
14576 
14577 #ifdef BACKGROUND_GC
14578         if (recursive_gc_sync::background_running_p())
14579         {
14580             dprintf (GTC_LOG, ("bgc in prog, 1"));
14581             check_max_gen_alloc = FALSE;
14582         }
14583 #endif //BACKGROUND_GC
14584 
14585         if (check_max_gen_alloc)
14586         {
14587             //figure out if large objects need to be collected.
14588             if (get_new_allocation (max_generation+1) <= 0)
14589             {
14590                 n = max_generation;
14591                 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14592             }
14593         }
14594 
14595         //figure out which generation ran out of allocation
14596         for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
14597         {
14598             if (get_new_allocation (i) <= 0)
14599             {
14600                 n = i;
14601             }
14602             else
14603                 break;
14604         }
14605     }
14606 
14607     if (n > temp_gen)
14608     {
14609         local_condemn_reasons->set_gen (gen_alloc_budget, n);
14610     }
14611 
14612     dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
14613 
14614     n_alloc = n;
14615 
14616 #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
14617     //time based tuning
14618     // if enough time has elapsed since the last gc
14619     // and the number of gc is too low (1/10 of lower gen) then collect
14620     // This should also be enabled if we have memory concerns
14621     int n_time_max = max_generation;
14622 
14623     if (!check_only_p)
14624     {
14625         if (recursive_gc_sync::background_running_p())
14626         {
14627             n_time_max = max_generation - 1;
14628         }
14629     }
14630 
14631     if ((local_settings->pause_mode == pause_interactive) ||
14632         (local_settings->pause_mode == pause_sustained_low_latency))
14633     {
14634         dynamic_data* dd0 = dynamic_data_of (0);
14635         size_t now = GetHighPrecisionTimeStamp();
14636         temp_gen = n;
14637         for (i = (temp_gen+1); i <= n_time_max; i++)
14638         {
14639             dynamic_data* dd = dynamic_data_of (i);
14640             if ((now > dd_time_clock(dd) + power (10, i)*1000) &&
14641                 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + (power (10, i)))) &&
14642                 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
14643             {
14644                 n = min (i, n_time_max);
14645                 dprintf (GTC_LOG, ("time %d", n));
14646             }
14647         }
14648         if (n > temp_gen)
14649         {
14650             local_condemn_reasons->set_gen (gen_time_tuning, n);
14651         }
14652     }
14653 
14654     if (n != n_alloc)
14655     {
14656         dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
14657     }
14658 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
14659 
14660     if (n < (max_generation - 1))
14661     {
14662         if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
14663         {
14664             n = max (n, max_generation - 1);
14665             local_settings->promotion = TRUE;
14666             dprintf (GTC_LOG, ("h%d: skip %d, c %d",
14667                         heap_number, generation_skip_ratio, n));
14668             local_condemn_reasons->set_condition (gen_low_card_p);
14669         }
14670     }
14671 
14672     if (!check_only_p)
14673     {
14674         generation_skip_ratio = 100;
14675     }
14676 
14677     if (dt_low_ephemeral_space_p (check_only_p ?
14678                                   tuning_deciding_full_gc :
14679                                   tuning_deciding_condemned_gen))
14680     {
14681         low_ephemeral_space = TRUE;
14682 
14683         n = max (n, max_generation - 1);
14684         local_condemn_reasons->set_condition (gen_low_ephemeral_p);
14685         dprintf (GTC_LOG, ("h%d: low eph", heap_number));
14686 
14687 #ifdef BACKGROUND_GC
14688         if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
14689 #endif //BACKGROUND_GC
14690         {
14691             //It is better to defragment first if we are running out of space for
14692             //the ephemeral generation but we have enough fragmentation to make up for it
14693             //in the non ephemeral generation. Essentially we are trading a gen2 for
14694             // having to expand heap in ephemeral collections.
14695             if (dt_high_frag_p (tuning_deciding_condemned_gen,
14696                                 max_generation - 1,
14697                                 TRUE))
14698             {
14699                 high_fragmentation = TRUE;
14700                 local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
14701                 dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
14702             }
14703         }
14704     }
14705 
14706     //figure out which ephemeral generation is too fragramented
14707     temp_gen = n;
14708     for (i = n+1; i < max_generation; i++)
14709     {
14710         if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
14711         {
14712             dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
14713             n = i;
14714         }
14715         else
14716             break;
14717     }
14718 
14719     if (low_ephemeral_space)
14720     {
14721         //enable promotion
14722         local_settings->promotion = TRUE;
14723     }
14724 
14725     if (n > temp_gen)
14726     {
14727         local_condemn_reasons->set_condition (gen_eph_high_frag_p);
14728     }
14729 
14730     if (!check_only_p)
14731     {
14732         if (settings.pause_mode == pause_low_latency)
14733         {
14734             if (!is_induced (settings.reason))
14735             {
14736                 n = min (n, max_generation - 1);
14737                 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
14738                 evaluate_elevation = FALSE;
14739                 goto exit;
14740             }
14741         }
14742     }
14743 
14744     // It's hard to catch when we get to the point that the memory load is so high
14745     // we get an induced GC from the finalizer thread so we are checking the memory load
14746     // for every gen0 GC.
14747     check_memory = (check_only_p ?
14748                     (n >= 0) :
14749                     ((n >= 1) || low_memory_detected));
14750 
14751     if (check_memory)
14752     {
14753         //find out if we are short on memory
14754         get_memory_info (&memory_load, &available_physical, &available_page_file);
14755         if (heap_number == 0)
14756         {
14757             dprintf (GTC_LOG, ("ml: %d", memory_load));
14758         }
14759 
14760         // Need to get it early enough for all heaps to use.
14761         entry_available_physical_mem = available_physical;
14762         local_settings->entry_memory_load = memory_load;
14763 
14764         // @TODO: Force compaction more often under GCSTRESS
14765         if (memory_load >= high_memory_load_th || low_memory_detected)
14766         {
14767 #ifdef SIMPLE_DPRINTF
14768             // stress log can't handle any parameter that's bigger than a void*.
14769             if (heap_number == 0)
14770             {
14771                 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d", total_physical_mem, available_physical));
14772             }
14773 #endif //SIMPLE_DPRINTF
14774 
14775             high_memory_load = TRUE;
14776 
14777             if (memory_load >= v_high_memory_load_th || low_memory_detected)
14778             {
14779                 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
14780                 // gen1/gen0 may take a lot more memory than gen2.
14781                 if (!high_fragmentation)
14782                 {
14783                     high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation);
14784                 }
14785                 v_high_memory_load = TRUE;
14786             }
14787             else
14788             {
14789                 if (!high_fragmentation)
14790                 {
14791                     high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, available_physical);
14792                 }
14793             }
14794 
14795             if (high_fragmentation)
14796             {
14797                 if (high_memory_load)
14798                 {
14799                     local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
14800                 }
14801                 else if (v_high_memory_load)
14802                 {
14803                     local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
14804                 }
14805             }
14806         }
14807     }
14808 
14809     dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
14810                  heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
14811                  high_fragmentation));
14812 
14813     if (should_expand_in_full_gc)
14814     {
14815         dprintf (GTC_LOG, ("h%d: expand_in_full - BLOCK", heap_number));
14816         *blocking_collection_p = TRUE;
14817         if (!check_only_p)
14818         {
14819             should_expand_in_full_gc = FALSE;
14820         }
14821         evaluate_elevation = FALSE;
14822         n = max_generation;
14823         local_condemn_reasons->set_condition (gen_expand_fullgc_p);
14824     }
14825 
14826     if (last_gc_before_oom)
14827     {
14828         dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
14829         n = max_generation;
14830         *blocking_collection_p = TRUE;
14831         if ((local_settings->reason == reason_oos_loh) ||
14832             (local_settings->reason == reason_alloc_loh))
14833             evaluate_elevation = FALSE;
14834 
14835         local_condemn_reasons->set_condition (gen_before_oom);
14836     }
14837 
14838     if (!check_only_p)
14839     {
14840         if (is_induced_blocking (settings.reason) &&
14841             n_initial == max_generation
14842             IN_STRESS_HEAP( && !settings.stress_induced ))
14843         {
14844             if (heap_number == 0)
14845             {
14846                 dprintf (GTC_LOG, ("induced - BLOCK"));
14847             }
14848 
14849             *blocking_collection_p = TRUE;
14850             local_condemn_reasons->set_condition (gen_induced_fullgc_p);
14851             evaluate_elevation = FALSE;
14852         }
14853 
14854         if (settings.reason == reason_induced_noforce)
14855         {
14856             local_condemn_reasons->set_condition (gen_induced_noforce_p);
14857             evaluate_elevation = FALSE;
14858         }
14859     }
14860 
14861     if (evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
14862     {
14863         *elevation_requested_p = TRUE;
14864 #ifdef BIT64
14865         // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
14866         if (high_memory_load || v_high_memory_load)
14867         {
14868             dynamic_data* dd_max = dynamic_data_of (max_generation);
14869             if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
14870             {
14871                 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)",
14872                     dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
14873                 n = max_generation;
14874                 local_condemn_reasons->set_condition (gen_almost_max_alloc);
14875             }
14876         }
14877 
14878         if (n <= max_generation)
14879         {
14880 #endif // BIT64
14881             if (high_fragmentation)
14882             {
14883                 //elevate to max_generation
14884                 n = max_generation;
14885                 dprintf (GTC_LOG, ("h%d: f full", heap_number));
14886 
14887 #ifdef BACKGROUND_GC
14888                 if (high_memory_load || v_high_memory_load)
14889                 {
14890                     // For background GC we want to do blocking collections more eagerly because we don't
14891                     // want to get into the situation where the memory load becomes high while we are in
14892                     // a background GC and we'd have to wait for the background GC to finish to start
14893                     // a blocking collection (right now the implemenation doesn't handle converting
14894                     // a background GC to a blocking collection midway.
14895                     dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
14896                     *blocking_collection_p = TRUE;
14897                 }
14898 #else
14899                 if (v_high_memory_load)
14900                 {
14901                     dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
14902                     *blocking_collection_p = TRUE;
14903                 }
14904 #endif //BACKGROUND_GC
14905             }
14906             else
14907             {
14908                 n = max (n, max_generation - 1);
14909                 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
14910             }
14911 #ifdef BIT64
14912         }
14913 #endif // BIT64
14914     }
14915 
14916     if ((n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
14917     {
14918         dprintf (GTC_LOG, ("h%d: budget %d, check 2",
14919                       heap_number, n_alloc));
14920         if (get_new_allocation (max_generation) <= 0)
14921         {
14922             dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
14923             n = max_generation;
14924             local_condemn_reasons->set_condition (gen_max_gen1);
14925         }
14926     }
14927 
14928     //figure out if max_generation is too fragmented -> blocking collection
14929     if (n == max_generation)
14930     {
14931         if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
14932         {
14933             dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
14934             local_condemn_reasons->set_condition (gen_max_high_frag_p);
14935             if (local_settings->pause_mode != pause_sustained_low_latency)
14936             {
14937                 *blocking_collection_p = TRUE;
14938             }
14939         }
14940     }
14941 
14942 #ifdef BACKGROUND_GC
14943     if (n == max_generation)
14944     {
14945         if (heap_number == 0)
14946         {
14947             BOOL bgc_heap_too_small = TRUE;
14948             size_t gen2size = 0;
14949             size_t gen3size = 0;
14950 #ifdef MULTIPLE_HEAPS
14951             for (int i = 0; i < n_heaps; i++)
14952             {
14953                 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) ||
14954                     ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
14955                 {
14956                     bgc_heap_too_small = FALSE;
14957                     break;
14958                 }
14959             }
14960 #else //MULTIPLE_HEAPS
14961             if ((current_generation_size (max_generation) > bgc_min_per_heap) ||
14962                 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
14963             {
14964                 bgc_heap_too_small = FALSE;
14965             }
14966 #endif //MULTIPLE_HEAPS
14967 
14968             if (bgc_heap_too_small)
14969             {
14970                 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
14971 
14972 #ifdef STRESS_HEAP
14973                 // do not turn stress-induced collections into blocking GCs
14974                 if (!settings.stress_induced)
14975 #endif //STRESS_HEAP
14976                 {
14977                     *blocking_collection_p = TRUE;
14978                 }
14979 
14980                 local_condemn_reasons->set_condition (gen_gen2_too_small);
14981             }
14982         }
14983     }
14984 #endif //BACKGROUND_GC
14985 
14986 exit:
14987     if (!check_only_p)
14988     {
14989 #ifdef STRESS_HEAP
14990 #ifdef BACKGROUND_GC
14991         // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14992         // generations to be collected,
14993 
14994         if (orig_gen != max_generation &&
14995             g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
14996         {
14997             *elevation_requested_p = FALSE;
14998         }
14999 #endif //BACKGROUND_GC
15000 #endif //STRESS_HEAP
15001 
15002         if (check_memory)
15003         {
15004             fgm_result.available_pagefile_mb = (size_t)(available_page_file / (1024 * 1024));
15005         }
15006 
15007         local_condemn_reasons->set_gen (gen_final_per_heap, n);
15008         get_gc_data_per_heap()->gen_to_condemn_reasons.init (local_condemn_reasons);
15009 
15010 #ifdef DT_LOG
15011         local_condemn_reasons->print (heap_number);
15012 #endif //DT_LOG
15013 
15014         if ((local_settings->reason == reason_oos_soh) ||
15015             (local_settings->reason == reason_oos_loh))
15016         {
15017             assert (n >= 1);
15018         }
15019     }
15020 
15021 #ifndef FEATURE_REDHAWK
15022     if (n == max_generation)
15023     {
15024         if (SystemDomain::System()->RequireAppDomainCleanup())
15025         {
15026 #ifdef BACKGROUND_GC
15027             // do not turn stress-induced collections into blocking GCs, unless there
15028             // have already been more full BGCs than full NGCs
15029 #if 0
15030             // This exposes DevDiv 94129, so we'll leave this out for now
15031             if (!settings.stress_induced ||
15032                 full_gc_counts[gc_type_blocking] <= full_gc_counts[gc_type_background])
15033 #endif // 0
15034 #endif // BACKGROUND_GC
15035             {
15036                 *blocking_collection_p = TRUE;
15037             }
15038         }
15039     }
15040 #endif //!FEATURE_REDHAWK
15041 
15042     return n;
15043 }
15044 
15045 #ifdef _PREFAST_
15046 #pragma warning(pop)
15047 #endif //_PREFAST_
15048 
15049 inline
min_reclaim_fragmentation_threshold(uint32_t num_heaps)15050 size_t gc_heap::min_reclaim_fragmentation_threshold (uint32_t num_heaps)
15051 {
15052     // if the memory load is higher, the threshold we'd want to collect gets lower.
15053     size_t min_mem_based_on_available =
15054         (500 - (settings.entry_memory_load - high_memory_load_th) * 40) * 1024 * 1024 / num_heaps;
15055     size_t ten_percent_size = (size_t)((float)generation_size (max_generation) * 0.10);
15056     uint64_t three_percent_mem = mem_one_percent * 3 / num_heaps;
15057 
15058 #ifdef SIMPLE_DPRINTF
15059     dprintf (GTC_LOG, ("min av: %Id, 10%% gen2: %Id, 3%% mem: %I64d",
15060         min_mem_based_on_available, ten_percent_size, three_percent_mem));
15061 #endif //SIMPLE_DPRINTF
15062     return (size_t)(min (min_mem_based_on_available, min (ten_percent_size, three_percent_mem)));
15063 }
15064 
15065 inline
min_high_fragmentation_threshold(uint64_t available_mem,uint32_t num_heaps)15066 uint64_t gc_heap::min_high_fragmentation_threshold(uint64_t available_mem, uint32_t num_heaps)
15067 {
15068     return min (available_mem, (256*1024*1024)) / num_heaps;
15069 }
15070 
15071 enum {
15072 CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
15073 };
15074 
15075 
15076 #ifdef BACKGROUND_GC
init_background_gc()15077 void gc_heap::init_background_gc ()
15078 {
15079     //reset the allocation so foreground gc can allocate into older (max_generation) generation
15080     generation* gen = generation_of (max_generation);
15081     generation_allocation_pointer (gen)= 0;
15082     generation_allocation_limit (gen) = 0;
15083     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
15084 
15085     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
15086 
15087     //reset the plan allocation for each segment
15088     for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
15089         seg = heap_segment_next_rw (seg))
15090     {
15091         heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
15092     }
15093 
15094     if (heap_number == 0)
15095     {
15096         dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix",
15097             heap_number,
15098             background_saved_lowest_address,
15099             background_saved_highest_address));
15100     }
15101 
15102     gc_lh_block_event.Reset();
15103 }
15104 
15105 #endif //BACKGROUND_GC
15106 
15107 #define fire_bgc_event(x) { FireEtw##x(GetClrInstanceId()); }
15108 
15109 inline
fire_drain_mark_list_event(size_t mark_list_objects)15110 void fire_drain_mark_list_event (size_t mark_list_objects)
15111 {
15112     FireEtwBGCDrainMark (mark_list_objects, GetClrInstanceId());
15113 }
15114 
15115 inline
fire_revisit_event(size_t dirtied_pages,size_t marked_objects,BOOL large_objects_p)15116 void fire_revisit_event (size_t dirtied_pages,
15117                          size_t marked_objects,
15118                          BOOL large_objects_p)
15119 {
15120     FireEtwBGCRevisit (dirtied_pages, marked_objects, large_objects_p, GetClrInstanceId());
15121 }
15122 
15123 inline
fire_overflow_event(uint8_t * overflow_min,uint8_t * overflow_max,size_t marked_objects,int large_objects_p)15124 void fire_overflow_event (uint8_t* overflow_min,
15125                           uint8_t* overflow_max,
15126                           size_t marked_objects,
15127                           int large_objects_p)
15128 {
15129     FireEtwBGCOverflow ((uint64_t)overflow_min, (uint64_t)overflow_max,
15130                         marked_objects, large_objects_p,
15131                         GetClrInstanceId());
15132 }
15133 
concurrent_print_time_delta(const char * msg)15134 void gc_heap::concurrent_print_time_delta (const char* msg)
15135 {
15136 #ifdef TRACE_GC
15137     size_t current_time = GetHighPrecisionTimeStamp();
15138     size_t elapsed_time = current_time - time_bgc_last;
15139     time_bgc_last = current_time;
15140 
15141     dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
15142 #else
15143     UNREFERENCED_PARAMETER(msg);
15144 #endif //TRACE_GC
15145 }
15146 
free_list_info(int gen_num,const char * msg)15147 void gc_heap::free_list_info (int gen_num, const char* msg)
15148 {
15149     UNREFERENCED_PARAMETER(gen_num);
15150 #if defined (BACKGROUND_GC) && defined (TRACE_GC)
15151     dprintf (3, ("h%d: %s", heap_number, msg));
15152     for (int i = 0; i <= (max_generation + 1); i++)
15153     {
15154         generation* gen = generation_of (i);
15155         if ((generation_allocation_size (gen) == 0) &&
15156             (generation_free_list_space (gen) == 0) &&
15157             (generation_free_obj_space (gen) == 0))
15158         {
15159             // don't print if everything is 0.
15160         }
15161         else
15162         {
15163             dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
15164                 heap_number, i,
15165                 generation_allocation_size (gen),
15166                 generation_free_list_space (gen),
15167                 generation_free_obj_space (gen)));
15168         }
15169     }
15170 #else
15171     UNREFERENCED_PARAMETER(msg);
15172 #endif // BACKGROUND_GC && TRACE_GC
15173 }
15174 
update_collection_counts_for_no_gc()15175 void gc_heap::update_collection_counts_for_no_gc()
15176 {
15177     assert (settings.pause_mode == pause_no_gc);
15178 
15179     settings.condemned_generation = max_generation;
15180 #ifdef MULTIPLE_HEAPS
15181     for (int i = 0; i < n_heaps; i++)
15182         g_heaps[i]->update_collection_counts();
15183 #else //MULTIPLE_HEAPS
15184     update_collection_counts();
15185 #endif //MULTIPLE_HEAPS
15186 
15187     full_gc_counts[gc_type_blocking]++;
15188 }
15189 
should_proceed_with_gc()15190 BOOL gc_heap::should_proceed_with_gc()
15191 {
15192     if (gc_heap::settings.pause_mode == pause_no_gc)
15193     {
15194         if (current_no_gc_region_info.started)
15195         {
15196             // The no_gc mode was already in progress yet we triggered another GC,
15197             // this effectively exits the no_gc mode.
15198             restore_data_for_no_gc();
15199         }
15200         else
15201             return should_proceed_for_no_gc();
15202     }
15203 
15204     return TRUE;
15205 }
15206 
15207 //internal part of gc used by the serial and concurrent version
gc1()15208 void gc_heap::gc1()
15209 {
15210 #ifdef BACKGROUND_GC
15211     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15212 #endif //BACKGROUND_GC
15213 
15214 #ifdef TIME_GC
15215     mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
15216 #endif //TIME_GC
15217 
15218     verify_soh_segment_list();
15219 
15220     int n = settings.condemned_generation;
15221 
15222     update_collection_counts ();
15223 
15224 #ifdef BACKGROUND_GC
15225     bgc_alloc_lock->check();
15226 #endif //BACKGROUND_GC
15227 
15228     free_list_info (max_generation, "beginning");
15229 
15230     vm_heap->GcCondemnedGeneration = settings.condemned_generation;
15231 
15232     assert (g_gc_card_table == card_table);
15233 
15234     {
15235         if (n == max_generation)
15236         {
15237             gc_low = lowest_address;
15238             gc_high = highest_address;
15239         }
15240         else
15241         {
15242             gc_low = generation_allocation_start (generation_of (n));
15243             gc_high = heap_segment_reserved (ephemeral_heap_segment);
15244         }
15245 #ifdef BACKGROUND_GC
15246         if (settings.concurrent)
15247         {
15248 #ifdef TRACE_GC
15249             time_bgc_last = GetHighPrecisionTimeStamp();
15250 #endif //TRACE_GC
15251 
15252             fire_bgc_event (BGCBegin);
15253 
15254             concurrent_print_time_delta ("BGC");
15255 
15256 //#ifdef WRITE_WATCH
15257             //reset_write_watch (FALSE);
15258 //#endif //WRITE_WATCH
15259 
15260             concurrent_print_time_delta ("RW");
15261             background_mark_phase();
15262             free_list_info (max_generation, "after mark phase");
15263 
15264             background_sweep();
15265             free_list_info (max_generation, "after sweep phase");
15266         }
15267         else
15268 #endif //BACKGROUND_GC
15269         {
15270             mark_phase (n, FALSE);
15271 
15272             GCScan::GcRuntimeStructuresValid (FALSE);
15273             plan_phase (n);
15274             GCScan::GcRuntimeStructuresValid (TRUE);
15275         }
15276     }
15277 
15278     size_t end_gc_time = GetHighPrecisionTimeStamp();
15279 //    printf ("generation: %d, elapsed time: %Id\n", n,  end_gc_time - dd_time_clock (dynamic_data_of (0)));
15280 
15281     //adjust the allocation size from the pinned quantities.
15282     for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
15283     {
15284         generation* gn = generation_of (gen_number);
15285         if (settings.compaction)
15286         {
15287             generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
15288             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
15289         }
15290         else
15291         {
15292             generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
15293             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
15294         }
15295         generation_pinned_allocation_sweep_size (gn) = 0;
15296         generation_pinned_allocation_compact_size (gn) = 0;
15297     }
15298 
15299 #ifdef BACKGROUND_GC
15300     if (settings.concurrent)
15301     {
15302         dynamic_data* dd = dynamic_data_of (n);
15303         dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15304 
15305         free_list_info (max_generation, "after computing new dynamic data");
15306 
15307         gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
15308 
15309         for (int gen_number = 0; gen_number < max_generation; gen_number++)
15310         {
15311             dprintf (2, ("end of BGC: gen%d new_alloc: %Id",
15312                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15313             current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
15314             current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15315             current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15316         }
15317     }
15318     else
15319 #endif //BACKGROUND_GC
15320     {
15321         free_list_info (max_generation, "end");
15322         for (int gen_number = 0; gen_number <= n; gen_number++)
15323         {
15324             dynamic_data* dd = dynamic_data_of (gen_number);
15325             dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15326             compute_new_dynamic_data (gen_number);
15327         }
15328 
15329         if (n != max_generation)
15330         {
15331             int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1));
15332             for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++)
15333             {
15334                 get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number);
15335                 get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15336                 get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15337             }
15338         }
15339 
15340         get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100);
15341 
15342         free_list_info (max_generation, "after computing new dynamic data");
15343 
15344         if (heap_number == 0)
15345         {
15346             dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms",
15347                 dd_collection_count (dynamic_data_of (0)),
15348                 settings.condemned_generation,
15349                 dd_gc_elapsed_time (dynamic_data_of (0))));
15350         }
15351 
15352         for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
15353         {
15354             dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id",
15355                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15356         }
15357     }
15358 
15359     if (n < max_generation)
15360     {
15361         compute_promoted_allocation (1 + n);
15362 
15363         dynamic_data* dd = dynamic_data_of (1 + n);
15364         size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) +
15365                                    generation_free_obj_space (generation_of (1 + n));
15366 
15367 #ifdef BACKGROUND_GC
15368         if (current_c_gc_state != c_gc_state_planning)
15369 #endif //BACKGROUND_GC
15370         {
15371             if (settings.promotion)
15372             {
15373                 dd_fragmentation (dd) = new_fragmentation;
15374             }
15375             else
15376             {
15377                 //assert (dd_fragmentation (dd) == new_fragmentation);
15378             }
15379         }
15380     }
15381 
15382 #ifdef BACKGROUND_GC
15383     if (!settings.concurrent)
15384 #endif //BACKGROUND_GC
15385     {
15386 #ifndef FEATURE_REDHAWK
15387         // IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
15388         assert(!!IsGCThread());
15389 #endif // FEATURE_REDHAWK
15390         adjust_ephemeral_limits();
15391     }
15392 
15393 #ifdef BACKGROUND_GC
15394     assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
15395     assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
15396 #endif //BACKGROUND_GC
15397 
15398     if (fgn_maxgen_percent)
15399     {
15400         if (settings.condemned_generation == (max_generation - 1))
15401         {
15402             check_for_full_gc (max_generation - 1, 0);
15403         }
15404         else if (settings.condemned_generation == max_generation)
15405         {
15406             if (full_gc_approach_event_set
15407 #ifdef MULTIPLE_HEAPS
15408                 && (heap_number == 0)
15409 #endif //MULTIPLE_HEAPS
15410                 )
15411             {
15412                 dprintf (2, ("FGN-GC: setting gen2 end event"));
15413 
15414                 full_gc_approach_event.Reset();
15415 #ifdef BACKGROUND_GC
15416                 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
15417                 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
15418 #endif //BACKGROUND_GC
15419                 full_gc_end_event.Set();
15420                 full_gc_approach_event_set = false;
15421             }
15422         }
15423     }
15424 
15425 #ifdef BACKGROUND_GC
15426     if (!settings.concurrent)
15427 #endif //BACKGROUND_GC
15428     {
15429         //decide on the next allocation quantum
15430         if (alloc_contexts_used >= 1)
15431         {
15432             allocation_quantum = Align (min ((size_t)CLR_SIZE,
15433                                             (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
15434                                             get_alignment_constant(FALSE));
15435             dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
15436         }
15437     }
15438 #ifdef NO_WRITE_BARRIER
15439     reset_write_watch(FALSE);
15440 #endif //NO_WRITE_BARRIER
15441 
15442     descr_generations (FALSE);
15443     descr_card_table();
15444 
15445     verify_soh_segment_list();
15446 
15447 #ifdef BACKGROUND_GC
15448     add_to_history_per_heap();
15449     if (heap_number == 0)
15450     {
15451         add_to_history();
15452     }
15453 #endif // BACKGROUND_GC
15454 
15455 #ifdef GC_STATS
15456     if (GCStatistics::Enabled() && heap_number == 0)
15457         g_GCStatistics.AddGCStats(settings,
15458             dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
15459 #endif // GC_STATS
15460 
15461 #ifdef TIME_GC
15462     fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
15463              n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
15464 #endif //TIME_GC
15465 
15466 #ifdef BACKGROUND_GC
15467     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15468 #endif //BACKGROUND_GC
15469 
15470 #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15471     if (FALSE
15472 #ifdef VERIFY_HEAP
15473         // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same
15474         // value. If we ever allow randomly adjusting this as the process runs,
15475         // we cannot call it this way as joins need to match - we must have the same
15476         // value for all heaps like we do with bgc_heap_walk_for_etw_p.
15477         || (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
15478 #endif
15479 #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
15480         || (bgc_heap_walk_for_etw_p && settings.concurrent)
15481 #endif
15482         )
15483     {
15484 #ifdef BACKGROUND_GC
15485         Thread* current_thread = GetThread();
15486         BOOL cooperative_mode = TRUE;
15487 
15488         if (settings.concurrent)
15489         {
15490             cooperative_mode = enable_preemptive (current_thread);
15491 
15492 #ifdef MULTIPLE_HEAPS
15493             bgc_t_join.join(this, gc_join_suspend_ee_verify);
15494             if (bgc_t_join.joined())
15495             {
15496                 bgc_threads_sync_event.Reset();
15497 
15498                 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
15499                 bgc_t_join.restart();
15500             }
15501             if (heap_number == 0)
15502             {
15503                 suspend_EE();
15504                 bgc_threads_sync_event.Set();
15505             }
15506             else
15507             {
15508                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15509                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15510             }
15511 #else
15512             suspend_EE();
15513 #endif //MULTIPLE_HEAPS
15514 
15515             //fix the allocation area so verify_heap can proceed.
15516             fix_allocation_contexts (FALSE);
15517         }
15518 #endif //BACKGROUND_GC
15519 
15520 #ifdef BACKGROUND_GC
15521         assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15522 #ifdef FEATURE_EVENT_TRACE
15523         if (bgc_heap_walk_for_etw_p && settings.concurrent)
15524         {
15525             GCToEEInterface::DiagWalkBGCSurvivors(__this);
15526 
15527 #ifdef MULTIPLE_HEAPS
15528             bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
15529             if (bgc_t_join.joined())
15530             {
15531                 bgc_t_join.restart();
15532             }
15533 #endif // MULTIPLE_HEAPS
15534         }
15535 #endif // FEATURE_EVENT_TRACE
15536 #endif //BACKGROUND_GC
15537 
15538 #ifdef VERIFY_HEAP
15539         if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
15540             verify_heap (FALSE);
15541 #endif // VERIFY_HEAP
15542 
15543 #ifdef BACKGROUND_GC
15544         if (settings.concurrent)
15545         {
15546             repair_allocation_contexts (TRUE);
15547 
15548 #ifdef MULTIPLE_HEAPS
15549             bgc_t_join.join(this, gc_join_restart_ee_verify);
15550             if (bgc_t_join.joined())
15551             {
15552                 bgc_threads_sync_event.Reset();
15553 
15554                 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
15555                 bgc_t_join.restart();
15556             }
15557             if (heap_number == 0)
15558             {
15559                 restart_EE();
15560                 bgc_threads_sync_event.Set();
15561             }
15562             else
15563             {
15564                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15565                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15566             }
15567 #else
15568             restart_EE();
15569 #endif //MULTIPLE_HEAPS
15570 
15571             disable_preemptive (current_thread, cooperative_mode);
15572         }
15573 #endif //BACKGROUND_GC
15574     }
15575 #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15576 
15577 #ifdef MULTIPLE_HEAPS
15578     if (!settings.concurrent)
15579     {
15580         gc_t_join.join(this, gc_join_done);
15581         if (gc_t_join.joined ())
15582         {
15583             gc_heap::internal_gc_done = false;
15584 
15585             //equalize the new desired size of the generations
15586             int limit = settings.condemned_generation;
15587             if (limit == max_generation)
15588             {
15589                 limit = max_generation+1;
15590             }
15591             for (int gen = 0; gen <= limit; gen++)
15592             {
15593                 size_t total_desired = 0;
15594 
15595                 for (int i = 0; i < gc_heap::n_heaps; i++)
15596                 {
15597                     gc_heap* hp = gc_heap::g_heaps[i];
15598                     dynamic_data* dd = hp->dynamic_data_of (gen);
15599                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
15600                     if (temp_total_desired < total_desired)
15601                     {
15602                         // we overflowed.
15603                         total_desired = (size_t)MAX_PTR;
15604                         break;
15605                     }
15606                     total_desired = temp_total_desired;
15607                 }
15608 
15609                 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
15610                                                     get_alignment_constant ((gen != (max_generation+1))));
15611 
15612                 if (gen == 0)
15613                 {
15614 #if 1 //subsumed by the linear allocation model
15615                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15616                     // apply some smoothing.
15617                     static size_t smoothed_desired_per_heap = 0;
15618                     size_t smoothing = 3; // exponential smoothing factor
15619                     if (smoothing  > VolatileLoad(&settings.gc_index))
15620                         smoothing  = VolatileLoad(&settings.gc_index);
15621                     smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
15622                     dprintf (1, ("sn = %Id  n = %Id", smoothed_desired_per_heap, desired_per_heap));
15623                     desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
15624 #endif //0
15625 
15626                     // if desired_per_heap is close to min_gc_size, trim it
15627                     // down to min_gc_size to stay in the cache
15628                     gc_heap* hp = gc_heap::g_heaps[0];
15629                     dynamic_data* dd = hp->dynamic_data_of (gen);
15630                     size_t min_gc_size = dd_min_gc_size(dd);
15631                     // if min GC size larger than true on die cache, then don't bother
15632                     // limiting the desired size
15633                     if ((min_gc_size <= GCToOSInterface::GetLargestOnDieCacheSize(TRUE) / GCToOSInterface::GetLogicalCpuCount()) &&
15634                         desired_per_heap <= 2*min_gc_size)
15635                     {
15636                         desired_per_heap = min_gc_size;
15637                     }
15638 #ifdef BIT64
15639                     desired_per_heap = joined_youngest_desired (desired_per_heap);
15640                     dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
15641 #endif // BIT64
15642 
15643                     gc_data_global.final_youngest_desired = desired_per_heap;
15644                 }
15645 #if 1 //subsumed by the linear allocation model
15646                 if (gen == (max_generation + 1))
15647                 {
15648                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15649                     // apply some smoothing.
15650                     static size_t smoothed_desired_per_heap_loh = 0;
15651                     size_t smoothing = 3; // exponential smoothing factor
15652                     size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
15653                     if (smoothing  > loh_count)
15654                         smoothing  = loh_count;
15655                     smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
15656                     dprintf( 2, ("smoothed_desired_per_heap_loh  = %Id  desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
15657                     desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
15658                 }
15659 #endif //0
15660                 for (int i = 0; i < gc_heap::n_heaps; i++)
15661                 {
15662                     gc_heap* hp = gc_heap::g_heaps[i];
15663                     dynamic_data* dd = hp->dynamic_data_of (gen);
15664                     dd_desired_allocation (dd) = desired_per_heap;
15665                     dd_gc_new_allocation (dd) = desired_per_heap;
15666                     dd_new_allocation (dd) = desired_per_heap;
15667 
15668                     if (gen == 0)
15669                     {
15670                         hp->fgn_last_alloc = desired_per_heap;
15671                     }
15672                 }
15673             }
15674 
15675 #ifdef FEATURE_LOH_COMPACTION
15676             BOOL all_heaps_compacted_p = TRUE;
15677 #endif //FEATURE_LOH_COMPACTION
15678             for (int i = 0; i < gc_heap::n_heaps; i++)
15679             {
15680                 gc_heap* hp = gc_heap::g_heaps[i];
15681                 hp->decommit_ephemeral_segment_pages();
15682                 hp->rearrange_large_heap_segments();
15683 #ifdef FEATURE_LOH_COMPACTION
15684                 all_heaps_compacted_p &= hp->loh_compacted_p;
15685 #endif //FEATURE_LOH_COMPACTION
15686             }
15687 
15688 #ifdef FEATURE_LOH_COMPACTION
15689             check_loh_compact_mode (all_heaps_compacted_p);
15690 #endif //FEATURE_LOH_COMPACTION
15691 
15692             fire_pevents();
15693 
15694             gc_t_join.restart();
15695         }
15696         alloc_context_count = 0;
15697         heap_select::mark_heap (heap_number);
15698     }
15699 
15700 #else
15701     gc_data_global.final_youngest_desired =
15702         dd_desired_allocation (dynamic_data_of (0));
15703 
15704     check_loh_compact_mode (loh_compacted_p);
15705 
15706     decommit_ephemeral_segment_pages();
15707     fire_pevents();
15708 
15709     if (!(settings.concurrent))
15710     {
15711         rearrange_large_heap_segments();
15712         do_post_gc();
15713     }
15714 
15715 #ifdef BACKGROUND_GC
15716     recover_bgc_settings();
15717 #endif //BACKGROUND_GC
15718 #endif //MULTIPLE_HEAPS
15719 }
15720 
save_data_for_no_gc()15721 void gc_heap::save_data_for_no_gc()
15722 {
15723     current_no_gc_region_info.saved_pause_mode = settings.pause_mode;
15724 #ifdef MULTIPLE_HEAPS
15725     // This is to affect heap balancing.
15726     for (int i = 0; i < n_heaps; i++)
15727     {
15728         current_no_gc_region_info.saved_gen0_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (0));
15729         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = min_balance_threshold;
15730         current_no_gc_region_info.saved_gen3_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1));
15731         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = 0;
15732     }
15733 #endif //MULTIPLE_HEAPS
15734 }
15735 
restore_data_for_no_gc()15736 void gc_heap::restore_data_for_no_gc()
15737 {
15738     gc_heap::settings.pause_mode = current_no_gc_region_info.saved_pause_mode;
15739 #ifdef MULTIPLE_HEAPS
15740     for (int i = 0; i < n_heaps; i++)
15741     {
15742         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = current_no_gc_region_info.saved_gen0_min_size;
15743         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = current_no_gc_region_info.saved_gen3_min_size;
15744     }
15745 #endif //MULTIPLE_HEAPS
15746 }
15747 
prepare_for_no_gc_region(uint64_t total_size,BOOL loh_size_known,uint64_t loh_size,BOOL disallow_full_blocking)15748 start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size,
15749                                                              BOOL loh_size_known,
15750                                                              uint64_t loh_size,
15751                                                              BOOL disallow_full_blocking)
15752 {
15753     if (current_no_gc_region_info.started)
15754     {
15755         return start_no_gc_in_progress;
15756     }
15757 
15758     start_no_gc_region_status status = start_no_gc_success;
15759 
15760     save_data_for_no_gc();
15761     settings.pause_mode = pause_no_gc;
15762     current_no_gc_region_info.start_status = start_no_gc_success;
15763 
15764     uint64_t allocation_no_gc_loh = 0;
15765     uint64_t allocation_no_gc_soh = 0;
15766     assert(total_size != 0);
15767     if (loh_size_known)
15768     {
15769         assert(loh_size != 0);
15770         assert(loh_size <= total_size);
15771         allocation_no_gc_loh = loh_size;
15772         allocation_no_gc_soh = total_size - loh_size;
15773     }
15774     else
15775     {
15776         allocation_no_gc_soh = total_size;
15777         allocation_no_gc_loh = total_size;
15778     }
15779 
15780     size_t soh_segment_size = get_valid_segment_size();
15781     int soh_align_const = get_alignment_constant (TRUE);
15782     size_t max_soh_allocated = soh_segment_size - OS_PAGE_SIZE - eph_gen_starts_size;
15783     size_t size_per_heap = 0;
15784     const double scale_factor = 1.05;
15785 
15786     int num_heaps = 1;
15787 #ifdef MULTIPLE_HEAPS
15788     num_heaps = n_heaps;
15789 #endif // MULTIPLE_HEAPS
15790 
15791     uint64_t total_allowed_soh_allocation = (uint64_t)max_soh_allocated * num_heaps;
15792     // [LOCALGC TODO]
15793     // In theory, the upper limit here is the physical memory of the machine, not
15794     // SIZE_T_MAX. This is not true today because total_physical_mem can be
15795     // larger than SIZE_T_MAX if running in wow64 on a machine with more than
15796     // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing
15797     // more freely between branches, it would be good to clean this up to use
15798     // total_physical_mem instead of SIZE_T_MAX.
15799     assert(total_allowed_soh_allocation <= SIZE_T_MAX);
15800     uint64_t total_allowed_loh_allocation = SIZE_T_MAX;
15801     uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast<uint64_t>(total_allowed_soh_allocation / scale_factor) : 0;
15802     uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast<uint64_t>(total_allowed_loh_allocation / scale_factor) : 0;
15803 
15804     if (allocation_no_gc_soh > total_allowed_soh_alloc_scaled ||
15805         allocation_no_gc_loh > total_allowed_loh_alloc_scaled)
15806     {
15807         status = start_no_gc_too_large;
15808         goto done;
15809     }
15810 
15811     if (allocation_no_gc_soh > 0)
15812     {
15813         allocation_no_gc_soh = static_cast<uint64_t>(allocation_no_gc_soh * scale_factor);
15814         allocation_no_gc_soh = min (allocation_no_gc_soh, total_allowed_soh_alloc_scaled);
15815     }
15816 
15817     if (allocation_no_gc_loh > 0)
15818     {
15819         allocation_no_gc_loh = static_cast<uint64_t>(allocation_no_gc_loh * scale_factor);
15820         allocation_no_gc_loh = min (allocation_no_gc_loh, total_allowed_loh_alloc_scaled);
15821     }
15822 
15823     if (disallow_full_blocking)
15824         current_no_gc_region_info.minimal_gc_p = TRUE;
15825 
15826     if (allocation_no_gc_soh != 0)
15827     {
15828         current_no_gc_region_info.soh_allocation_size = (size_t)allocation_no_gc_soh;
15829         size_per_heap = current_no_gc_region_info.soh_allocation_size;
15830 #ifdef MULTIPLE_HEAPS
15831         size_per_heap /= n_heaps;
15832         for (int i = 0; i < n_heaps; i++)
15833         {
15834             // due to heap balancing we need to allow some room before we even look to balance to another heap.
15835             g_heaps[i]->soh_allocation_no_gc = min (Align ((size_per_heap + min_balance_threshold), soh_align_const), max_soh_allocated);
15836         }
15837 #else //MULTIPLE_HEAPS
15838         soh_allocation_no_gc = min (Align (size_per_heap, soh_align_const), max_soh_allocated);
15839 #endif //MULTIPLE_HEAPS
15840     }
15841 
15842     if (allocation_no_gc_loh != 0)
15843     {
15844         current_no_gc_region_info.loh_allocation_size = (size_t)allocation_no_gc_loh;
15845         size_per_heap = current_no_gc_region_info.loh_allocation_size;
15846 #ifdef MULTIPLE_HEAPS
15847         size_per_heap /= n_heaps;
15848         for (int i = 0; i < n_heaps; i++)
15849             g_heaps[i]->loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
15850 #else //MULTIPLE_HEAPS
15851         loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
15852 #endif //MULTIPLE_HEAPS
15853     }
15854 
15855 done:
15856     if (status != start_no_gc_success)
15857         restore_data_for_no_gc();
15858     return status;
15859 }
15860 
handle_failure_for_no_gc()15861 void gc_heap::handle_failure_for_no_gc()
15862 {
15863     gc_heap::restore_data_for_no_gc();
15864     // sets current_no_gc_region_info.started to FALSE here.
15865     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
15866 }
15867 
get_start_no_gc_region_status()15868 start_no_gc_region_status gc_heap::get_start_no_gc_region_status()
15869 {
15870     return current_no_gc_region_info.start_status;
15871 }
15872 
record_gcs_during_no_gc()15873 void gc_heap::record_gcs_during_no_gc()
15874 {
15875     if (current_no_gc_region_info.started)
15876     {
15877         current_no_gc_region_info.num_gcs++;
15878         if (is_induced (settings.reason))
15879             current_no_gc_region_info.num_gcs_induced++;
15880     }
15881 }
15882 
find_loh_free_for_no_gc()15883 BOOL gc_heap::find_loh_free_for_no_gc()
15884 {
15885     allocator* loh_allocator = generation_allocator (generation_of (max_generation + 1));
15886     size_t sz_list = loh_allocator->first_bucket_size();
15887     size_t size = loh_allocation_no_gc;
15888     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
15889     {
15890         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
15891         {
15892             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
15893             while (free_list)
15894             {
15895                 size_t free_list_size = unused_array_size(free_list);
15896 
15897                 if (free_list_size > loh_allocation_no_gc)
15898                 {
15899                     dprintf (3, ("free item %Ix(%Id) for no gc", (size_t)free_list, free_list_size));
15900                     return TRUE;
15901                 }
15902 
15903                 free_list = free_list_slot (free_list);
15904             }
15905         }
15906         sz_list = sz_list * 2;
15907     }
15908 
15909     return FALSE;
15910 }
15911 
find_loh_space_for_no_gc()15912 BOOL gc_heap::find_loh_space_for_no_gc()
15913 {
15914     saved_loh_segment_no_gc = 0;
15915 
15916     if (find_loh_free_for_no_gc())
15917         return TRUE;
15918 
15919     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
15920 
15921     while (seg)
15922     {
15923         size_t remaining = heap_segment_reserved (seg) - heap_segment_allocated (seg);
15924         if (remaining >= loh_allocation_no_gc)
15925         {
15926             saved_loh_segment_no_gc = seg;
15927             break;
15928         }
15929         seg = heap_segment_next (seg);
15930     }
15931 
15932     if (!saved_loh_segment_no_gc && current_no_gc_region_info.minimal_gc_p)
15933     {
15934         // If no full GC is allowed, we try to get a new seg right away.
15935         saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc)
15936 #ifdef MULTIPLE_HEAPS
15937                                                       , this
15938 #endif //MULTIPLE_HEAPS
15939                                                       );
15940     }
15941 
15942     return (saved_loh_segment_no_gc != 0);
15943 }
15944 
loh_allocated_for_no_gc()15945 BOOL gc_heap::loh_allocated_for_no_gc()
15946 {
15947     if (!saved_loh_segment_no_gc)
15948         return FALSE;
15949 
15950     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
15951     do
15952     {
15953         if (seg == saved_loh_segment_no_gc)
15954         {
15955             return FALSE;
15956         }
15957         seg = heap_segment_next (seg);
15958     } while (seg);
15959 
15960     return TRUE;
15961 }
15962 
commit_loh_for_no_gc(heap_segment * seg)15963 BOOL gc_heap::commit_loh_for_no_gc (heap_segment* seg)
15964 {
15965     uint8_t* end_committed = heap_segment_allocated (seg) + loh_allocation_no_gc;
15966     assert (end_committed <= heap_segment_reserved (seg));
15967     return (grow_heap_segment (seg, end_committed));
15968 }
15969 
thread_no_gc_loh_segments()15970 void gc_heap::thread_no_gc_loh_segments()
15971 {
15972 #ifdef MULTIPLE_HEAPS
15973     for (int i = 0; i < n_heaps; i++)
15974     {
15975         gc_heap* hp = g_heaps[i];
15976         if (hp->loh_allocated_for_no_gc())
15977         {
15978             hp->thread_loh_segment (hp->saved_loh_segment_no_gc);
15979             hp->saved_loh_segment_no_gc = 0;
15980         }
15981     }
15982 #else //MULTIPLE_HEAPS
15983     if (loh_allocated_for_no_gc())
15984     {
15985         thread_loh_segment (saved_loh_segment_no_gc);
15986         saved_loh_segment_no_gc = 0;
15987     }
15988 #endif //MULTIPLE_HEAPS
15989 }
15990 
set_loh_allocations_for_no_gc()15991 void gc_heap::set_loh_allocations_for_no_gc()
15992 {
15993     if (current_no_gc_region_info.loh_allocation_size != 0)
15994     {
15995         dynamic_data* dd = dynamic_data_of (max_generation + 1);
15996         dd_new_allocation (dd) = loh_allocation_no_gc;
15997         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
15998     }
15999 }
16000 
set_soh_allocations_for_no_gc()16001 void gc_heap::set_soh_allocations_for_no_gc()
16002 {
16003     if (current_no_gc_region_info.soh_allocation_size != 0)
16004     {
16005         dynamic_data* dd = dynamic_data_of (0);
16006         dd_new_allocation (dd) = soh_allocation_no_gc;
16007         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16008 #ifdef MULTIPLE_HEAPS
16009         alloc_context_count = 0;
16010 #endif //MULTIPLE_HEAPS
16011     }
16012 }
16013 
set_allocations_for_no_gc()16014 void gc_heap::set_allocations_for_no_gc()
16015 {
16016 #ifdef MULTIPLE_HEAPS
16017     for (int i = 0; i < n_heaps; i++)
16018     {
16019         gc_heap* hp = g_heaps[i];
16020         hp->set_loh_allocations_for_no_gc();
16021         hp->set_soh_allocations_for_no_gc();
16022     }
16023 #else //MULTIPLE_HEAPS
16024     set_loh_allocations_for_no_gc();
16025     set_soh_allocations_for_no_gc();
16026 #endif //MULTIPLE_HEAPS
16027 }
16028 
should_proceed_for_no_gc()16029 BOOL gc_heap::should_proceed_for_no_gc()
16030 {
16031     BOOL gc_requested = FALSE;
16032     BOOL loh_full_gc_requested = FALSE;
16033     BOOL soh_full_gc_requested = FALSE;
16034     BOOL no_gc_requested = FALSE;
16035     BOOL get_new_loh_segments = FALSE;
16036 
16037     if (current_no_gc_region_info.soh_allocation_size)
16038     {
16039 #ifdef MULTIPLE_HEAPS
16040         for (int i = 0; i < n_heaps; i++)
16041         {
16042             gc_heap* hp = g_heaps[i];
16043             if ((size_t)(heap_segment_reserved (hp->ephemeral_heap_segment) - hp->alloc_allocated) < hp->soh_allocation_no_gc)
16044             {
16045                 gc_requested = TRUE;
16046                 break;
16047             }
16048         }
16049 #else //MULTIPLE_HEAPS
16050         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated) < soh_allocation_no_gc)
16051             gc_requested = TRUE;
16052 #endif //MULTIPLE_HEAPS
16053 
16054         if (!gc_requested)
16055         {
16056 #ifdef MULTIPLE_HEAPS
16057             for (int i = 0; i < n_heaps; i++)
16058             {
16059                 gc_heap* hp = g_heaps[i];
16060                 if (!(hp->grow_heap_segment (hp->ephemeral_heap_segment, (hp->alloc_allocated + hp->soh_allocation_no_gc))))
16061                 {
16062                     soh_full_gc_requested = TRUE;
16063                     break;
16064                 }
16065             }
16066 #else //MULTIPLE_HEAPS
16067             if (!grow_heap_segment (ephemeral_heap_segment, (alloc_allocated + soh_allocation_no_gc)))
16068                 soh_full_gc_requested = TRUE;
16069 #endif //MULTIPLE_HEAPS
16070         }
16071     }
16072 
16073     if (!current_no_gc_region_info.minimal_gc_p && gc_requested)
16074     {
16075         soh_full_gc_requested = TRUE;
16076     }
16077 
16078     no_gc_requested = !(soh_full_gc_requested || gc_requested);
16079 
16080     if (soh_full_gc_requested && current_no_gc_region_info.minimal_gc_p)
16081     {
16082         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16083         goto done;
16084     }
16085 
16086     if (!soh_full_gc_requested && current_no_gc_region_info.loh_allocation_size)
16087     {
16088         // Check to see if we have enough reserved space.
16089 #ifdef MULTIPLE_HEAPS
16090         for (int i = 0; i < n_heaps; i++)
16091         {
16092             gc_heap* hp = g_heaps[i];
16093             if (!hp->find_loh_space_for_no_gc())
16094             {
16095                 loh_full_gc_requested = TRUE;
16096                 break;
16097             }
16098         }
16099 #else //MULTIPLE_HEAPS
16100         if (!find_loh_space_for_no_gc())
16101             loh_full_gc_requested = TRUE;
16102 #endif //MULTIPLE_HEAPS
16103 
16104         // Check to see if we have committed space.
16105         if (!loh_full_gc_requested)
16106         {
16107 #ifdef MULTIPLE_HEAPS
16108             for (int i = 0; i < n_heaps; i++)
16109             {
16110                 gc_heap* hp = g_heaps[i];
16111                 if (hp->saved_loh_segment_no_gc &&!hp->commit_loh_for_no_gc (hp->saved_loh_segment_no_gc))
16112                 {
16113                     loh_full_gc_requested = TRUE;
16114                     break;
16115                 }
16116             }
16117 #else //MULTIPLE_HEAPS
16118             if (saved_loh_segment_no_gc && !commit_loh_for_no_gc (saved_loh_segment_no_gc))
16119                 loh_full_gc_requested = TRUE;
16120 #endif //MULTIPLE_HEAPS
16121         }
16122     }
16123 
16124     if (loh_full_gc_requested || soh_full_gc_requested)
16125     {
16126         if (current_no_gc_region_info.minimal_gc_p)
16127             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16128     }
16129 
16130     no_gc_requested = !(loh_full_gc_requested || soh_full_gc_requested || gc_requested);
16131 
16132     if (current_no_gc_region_info.start_status == start_no_gc_success)
16133     {
16134         if (no_gc_requested)
16135             set_allocations_for_no_gc();
16136     }
16137 
16138 done:
16139 
16140     if ((current_no_gc_region_info.start_status == start_no_gc_success) && !no_gc_requested)
16141         return TRUE;
16142     else
16143     {
16144         // We are done with starting the no gc region.
16145         current_no_gc_region_info.started = TRUE;
16146         return FALSE;
16147     }
16148 }
16149 
end_no_gc_region()16150 end_no_gc_region_status gc_heap::end_no_gc_region()
16151 {
16152     dprintf (1, ("end no gc called"));
16153 
16154     end_no_gc_region_status status = end_no_gc_success;
16155 
16156     if (!(current_no_gc_region_info.started))
16157         status = end_no_gc_not_in_progress;
16158     if (current_no_gc_region_info.num_gcs_induced)
16159         status = end_no_gc_induced;
16160     else if (current_no_gc_region_info.num_gcs)
16161         status = end_no_gc_alloc_exceeded;
16162 
16163     if (settings.pause_mode == pause_no_gc)
16164         restore_data_for_no_gc();
16165 
16166     // sets current_no_gc_region_info.started to FALSE here.
16167     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16168 
16169     return status;
16170 }
16171 
16172 //update counters
update_collection_counts()16173 void gc_heap::update_collection_counts ()
16174 {
16175     dynamic_data* dd0 = dynamic_data_of (0);
16176     dd_gc_clock (dd0) += 1;
16177 
16178     size_t now = GetHighPrecisionTimeStamp();
16179 
16180     for (int i = 0; i <= settings.condemned_generation;i++)
16181     {
16182         dynamic_data* dd = dynamic_data_of (i);
16183         dd_collection_count (dd)++;
16184         //this is needed by the linear allocation model
16185         if (i == max_generation)
16186             dd_collection_count (dynamic_data_of (max_generation+1))++;
16187         dd_gc_clock (dd) = dd_gc_clock (dd0);
16188         dd_time_clock (dd) = now;
16189     }
16190 }
16191 
16192 #ifdef HEAP_ANALYZE
16193 inline
AnalyzeSurvivorsRequested(int condemnedGeneration)16194 BOOL AnalyzeSurvivorsRequested(int condemnedGeneration)
16195 {
16196     // Is the list active?
16197     GcNotifications gn(g_pGcNotificationTable);
16198     if (gn.IsActive())
16199     {
16200         GcEvtArgs gea = { GC_MARK_END, { (1<<condemnedGeneration) } };
16201         if (gn.GetNotification(gea) != 0)
16202         {
16203             return TRUE;
16204         }
16205     }
16206     return FALSE;
16207 }
16208 
DACNotifyGcMarkEnd(int condemnedGeneration)16209 void DACNotifyGcMarkEnd(int condemnedGeneration)
16210 {
16211     // Is the list active?
16212     GcNotifications gn(g_pGcNotificationTable);
16213     if (gn.IsActive())
16214     {
16215         GcEvtArgs gea = { GC_MARK_END, { (1<<condemnedGeneration) } };
16216         if (gn.GetNotification(gea) != 0)
16217         {
16218             DACNotify::DoGCNotification(gea);
16219         }
16220     }
16221 }
16222 #endif // HEAP_ANALYZE
16223 
expand_soh_with_minimal_gc()16224 BOOL gc_heap::expand_soh_with_minimal_gc()
16225 {
16226     if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) >= soh_allocation_no_gc)
16227         return TRUE;
16228 
16229     heap_segment* new_seg = soh_get_segment_to_expand();
16230     if (new_seg)
16231     {
16232         if (g_gc_card_table != card_table)
16233             copy_brick_card_table();
16234 
16235         settings.promotion = TRUE;
16236         settings.demotion = FALSE;
16237         ephemeral_promotion = TRUE;
16238         int condemned_gen_number = max_generation - 1;
16239 
16240         generation* gen = 0;
16241         int align_const = get_alignment_constant (TRUE);
16242 
16243         for (int i = 0; i <= condemned_gen_number; i++)
16244         {
16245             gen = generation_of (i);
16246             saved_ephemeral_plan_start[i] = generation_allocation_start (gen);
16247             saved_ephemeral_plan_start_size[i] = Align (size (generation_allocation_start (gen)), align_const);
16248         }
16249 
16250         // We do need to clear the bricks here as we are converting a bunch of ephemeral objects to gen2
16251         // and need to make sure that there are no left over bricks from the previous GCs for the space
16252         // we just used for gen0 allocation. We will need to go through the bricks for these objects for
16253         // ephemeral GCs later.
16254         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
16255              b < brick_of (align_on_brick (heap_segment_allocated (ephemeral_heap_segment)));
16256              b++)
16257         {
16258             set_brick (b, -1);
16259         }
16260 
16261         size_t ephemeral_size = (heap_segment_allocated (ephemeral_heap_segment) -
16262                                 generation_allocation_start (generation_of (max_generation - 1)));
16263         heap_segment_next (ephemeral_heap_segment) = new_seg;
16264         ephemeral_heap_segment = new_seg;
16265         uint8_t*  start = heap_segment_mem (ephemeral_heap_segment);
16266 
16267         for (int i = condemned_gen_number; i >= 0; i--)
16268         {
16269             gen = generation_of (i);
16270             size_t gen_start_size = Align (min_obj_size);
16271             make_generation (generation_table[i], ephemeral_heap_segment, start, 0);
16272             generation_plan_allocation_start (gen) = start;
16273             generation_plan_allocation_start_size (gen) = gen_start_size;
16274             start += gen_start_size;
16275         }
16276         heap_segment_used (ephemeral_heap_segment) = start - plug_skew;
16277         heap_segment_plan_allocated (ephemeral_heap_segment) = start;
16278 
16279         fix_generation_bounds (condemned_gen_number, generation_of (0));
16280 
16281         dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
16282         dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));
16283 
16284         adjust_ephemeral_limits();
16285         return TRUE;
16286     }
16287     else
16288         return FALSE;
16289 }
16290 
16291 // Only to be done on the thread that calls restart in a join for server GC
16292 // and reset the oom status per heap.
check_and_set_no_gc_oom()16293 void gc_heap::check_and_set_no_gc_oom()
16294 {
16295 #ifdef MULTIPLE_HEAPS
16296     for (int i = 0; i < n_heaps; i++)
16297     {
16298         gc_heap* hp = g_heaps[i];
16299         if (hp->no_gc_oom_p)
16300         {
16301             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16302             hp->no_gc_oom_p = false;
16303         }
16304     }
16305 #else
16306     if (no_gc_oom_p)
16307     {
16308         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16309         no_gc_oom_p = false;
16310     }
16311 #endif //MULTIPLE_HEAPS
16312 }
16313 
allocate_for_no_gc_after_gc()16314 void gc_heap::allocate_for_no_gc_after_gc()
16315 {
16316     if (current_no_gc_region_info.minimal_gc_p)
16317         repair_allocation_contexts (TRUE);
16318 
16319     no_gc_oom_p = false;
16320 
16321     if (current_no_gc_region_info.start_status != start_no_gc_no_memory)
16322     {
16323         if (current_no_gc_region_info.soh_allocation_size != 0)
16324         {
16325             if (((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) < soh_allocation_no_gc) ||
16326                 (!grow_heap_segment (ephemeral_heap_segment, (heap_segment_allocated (ephemeral_heap_segment) + soh_allocation_no_gc))))
16327             {
16328                 no_gc_oom_p = true;
16329             }
16330 
16331 #ifdef MULTIPLE_HEAPS
16332             gc_t_join.join(this, gc_join_after_commit_soh_no_gc);
16333             if (gc_t_join.joined())
16334             {
16335 #endif //MULTIPLE_HEAPS
16336 
16337                 check_and_set_no_gc_oom();
16338 
16339 #ifdef MULTIPLE_HEAPS
16340                 gc_t_join.restart();
16341             }
16342 #endif //MULTIPLE_HEAPS
16343         }
16344 
16345         if ((current_no_gc_region_info.start_status == start_no_gc_success) &&
16346             !(current_no_gc_region_info.minimal_gc_p) &&
16347             (current_no_gc_region_info.loh_allocation_size != 0))
16348         {
16349             gc_policy = policy_compact;
16350             saved_loh_segment_no_gc = 0;
16351 
16352             if (!find_loh_free_for_no_gc())
16353             {
16354                 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16355                 BOOL found_seg_p = FALSE;
16356                 while (seg)
16357                 {
16358                     if ((size_t)(heap_segment_reserved (seg) - heap_segment_allocated (seg)) >= loh_allocation_no_gc)
16359                     {
16360                         found_seg_p = TRUE;
16361                         if (!commit_loh_for_no_gc (seg))
16362                         {
16363                             no_gc_oom_p = true;
16364                             break;
16365                         }
16366                     }
16367                     seg = heap_segment_next (seg);
16368                 }
16369 
16370                 if (!found_seg_p)
16371                     gc_policy = policy_expand;
16372             }
16373 
16374 #ifdef MULTIPLE_HEAPS
16375             gc_t_join.join(this, gc_join_expand_loh_no_gc);
16376             if (gc_t_join.joined())
16377             {
16378                 check_and_set_no_gc_oom();
16379 
16380                 if (current_no_gc_region_info.start_status == start_no_gc_success)
16381                 {
16382                     for (int i = 0; i < n_heaps; i++)
16383                     {
16384                         gc_heap* hp = g_heaps[i];
16385                         if (hp->gc_policy == policy_expand)
16386                         {
16387                             hp->saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc), hp);
16388                             if (!(hp->saved_loh_segment_no_gc))
16389                             {
16390                                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16391                                 break;
16392                             }
16393                         }
16394                     }
16395                 }
16396 
16397                 gc_t_join.restart();
16398             }
16399 #else //MULTIPLE_HEAPS
16400             check_and_set_no_gc_oom();
16401 
16402             if ((current_no_gc_region_info.start_status == start_no_gc_success) && (gc_policy == policy_expand))
16403             {
16404                 saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc));
16405                 if (!saved_loh_segment_no_gc)
16406                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16407             }
16408 #endif //MULTIPLE_HEAPS
16409 
16410             if ((current_no_gc_region_info.start_status == start_no_gc_success) && saved_loh_segment_no_gc)
16411             {
16412                 if (!commit_loh_for_no_gc (saved_loh_segment_no_gc))
16413                 {
16414                     no_gc_oom_p = true;
16415                 }
16416             }
16417         }
16418     }
16419 
16420 #ifdef MULTIPLE_HEAPS
16421     gc_t_join.join(this, gc_join_final_no_gc);
16422     if (gc_t_join.joined())
16423     {
16424 #endif //MULTIPLE_HEAPS
16425 
16426         check_and_set_no_gc_oom();
16427 
16428         if (current_no_gc_region_info.start_status == start_no_gc_success)
16429         {
16430             set_allocations_for_no_gc();
16431             current_no_gc_region_info.started = TRUE;
16432         }
16433 
16434 #ifdef MULTIPLE_HEAPS
16435         gc_t_join.restart();
16436     }
16437 #endif //MULTIPLE_HEAPS
16438 }
16439 
init_records()16440 void gc_heap::init_records()
16441 {
16442     memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
16443     gc_data_per_heap.heap_index = heap_number;
16444     if (heap_number == 0)
16445         memset (&gc_data_global, 0, sizeof (gc_data_global));
16446 
16447 #ifdef GC_CONFIG_DRIVEN
16448     memset (interesting_data_per_gc, 0, sizeof (interesting_data_per_gc));
16449 #endif //GC_CONFIG_DRIVEN
16450 }
16451 
garbage_collect(int n)16452 int gc_heap::garbage_collect (int n)
16453 {
16454     //reset the number of alloc contexts
16455     alloc_contexts_used = 0;
16456 
16457     fix_allocation_contexts (TRUE);
16458 #ifdef MULTIPLE_HEAPS
16459     clear_gen0_bricks();
16460 #endif //MULTIPLE_HEAPS
16461 
16462     if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p)
16463     {
16464 #ifdef MULTIPLE_HEAPS
16465         gc_t_join.join(this, gc_join_minimal_gc);
16466         if (gc_t_join.joined())
16467         {
16468 #endif //MULTIPLE_HEAPS
16469 
16470 #ifdef MULTIPLE_HEAPS
16471             // this is serialized because we need to get a segment
16472             for (int i = 0; i < n_heaps; i++)
16473             {
16474                 if (!(g_heaps[i]->expand_soh_with_minimal_gc()))
16475                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16476             }
16477 #else
16478             if (!expand_soh_with_minimal_gc())
16479                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16480 #endif //MULTIPLE_HEAPS
16481 
16482             update_collection_counts_for_no_gc();
16483 
16484 #ifdef MULTIPLE_HEAPS
16485             gc_t_join.restart();
16486         }
16487 #endif //MULTIPLE_HEAPS
16488 
16489         goto done;
16490     }
16491 
16492     init_records();
16493     memset (&fgm_result, 0, sizeof (fgm_result));
16494 
16495     settings.reason = gc_trigger_reason;
16496     verify_pinned_queue_p = FALSE;
16497 
16498 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
16499         num_pinned_objects = 0;
16500 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
16501 
16502 #ifdef STRESS_HEAP
16503     if (settings.reason == reason_gcstress)
16504     {
16505         settings.reason = reason_induced;
16506         settings.stress_induced = TRUE;
16507     }
16508 #endif // STRESS_HEAP
16509 
16510 #ifdef MULTIPLE_HEAPS
16511     //align all heaps on the max generation to condemn
16512     dprintf (3, ("Joining for max generation to condemn"));
16513     condemned_generation_num = generation_to_condemn (n,
16514                                                       &blocking_collection,
16515                                                       &elevation_requested,
16516                                                       FALSE);
16517     gc_t_join.join(this, gc_join_generation_determined);
16518     if (gc_t_join.joined())
16519 #endif //MULTIPLE_HEAPS
16520     {
16521 #ifdef TRACE_GC
16522         int gc_count = (int)dd_collection_count (dynamic_data_of (0));
16523         if (gc_count >= g_pConfig->GetGCtraceStart())
16524             trace_gc = 1;
16525         if (gc_count >=  g_pConfig->GetGCtraceEnd())
16526             trace_gc = 0;
16527 #endif //TRACE_GC
16528 
16529 #ifdef MULTIPLE_HEAPS
16530 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16531         //delete old slots from the segment table
16532         seg_table->delete_old_slots();
16533 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16534         for (int i = 0; i < n_heaps; i++)
16535         {
16536             //copy the card and brick tables
16537             if (g_gc_card_table != g_heaps[i]->card_table)
16538             {
16539                 g_heaps[i]->copy_brick_card_table();
16540             }
16541 
16542             g_heaps[i]->rearrange_large_heap_segments();
16543             if (!recursive_gc_sync::background_running_p())
16544             {
16545                 g_heaps[i]->rearrange_small_heap_segments();
16546             }
16547         }
16548 #else //MULTIPLE_HEAPS
16549 #ifdef BACKGROUND_GC
16550             //delete old slots from the segment table
16551 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16552             seg_table->delete_old_slots();
16553 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16554             rearrange_large_heap_segments();
16555             if (!recursive_gc_sync::background_running_p())
16556             {
16557                 rearrange_small_heap_segments();
16558             }
16559 #endif //BACKGROUND_GC
16560         // check for card table growth
16561         if (g_gc_card_table != card_table)
16562             copy_brick_card_table();
16563 
16564 #endif //MULTIPLE_HEAPS
16565 
16566         BOOL should_evaluate_elevation = FALSE;
16567         BOOL should_do_blocking_collection = FALSE;
16568 
16569 #ifdef MULTIPLE_HEAPS
16570         int gen_max = condemned_generation_num;
16571         for (int i = 0; i < n_heaps; i++)
16572         {
16573             if (gen_max < g_heaps[i]->condemned_generation_num)
16574                 gen_max = g_heaps[i]->condemned_generation_num;
16575             if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested))
16576                 should_evaluate_elevation = TRUE;
16577             if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
16578                 should_do_blocking_collection = TRUE;
16579         }
16580 
16581         settings.condemned_generation = gen_max;
16582 #else //MULTIPLE_HEAPS
16583         settings.condemned_generation = generation_to_condemn (n,
16584                                                             &blocking_collection,
16585                                                             &elevation_requested,
16586                                                             FALSE);
16587         should_evaluate_elevation = elevation_requested;
16588         should_do_blocking_collection = blocking_collection;
16589 #endif //MULTIPLE_HEAPS
16590 
16591         settings.condemned_generation = joined_generation_to_condemn (
16592                                             should_evaluate_elevation,
16593                                             settings.condemned_generation,
16594                                             &should_do_blocking_collection
16595                                             STRESS_HEAP_ARG(n)
16596                                             );
16597 
16598         STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10,
16599                 "condemned generation num: %d\n", settings.condemned_generation);
16600 
16601         record_gcs_during_no_gc();
16602 
16603         if (settings.condemned_generation > 1)
16604             settings.promotion = TRUE;
16605 
16606 #ifdef HEAP_ANALYZE
16607         // At this point we've decided what generation is condemned
16608         // See if we've been requested to analyze survivors after the mark phase
16609         if (AnalyzeSurvivorsRequested(settings.condemned_generation))
16610         {
16611             heap_analyze_enabled = TRUE;
16612         }
16613 #endif // HEAP_ANALYZE
16614 
16615         GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced);
16616 
16617 #ifdef BACKGROUND_GC
16618         if ((settings.condemned_generation == max_generation) &&
16619             (recursive_gc_sync::background_running_p()))
16620         {
16621             //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work
16622             // because we have to collect 0 and 1 properly
16623             // in particular, the allocation contexts are gone.
16624             // For now, it is simpler to collect max_generation-1
16625             settings.condemned_generation = max_generation - 1;
16626             dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
16627         }
16628 
16629         if ((settings.condemned_generation == max_generation) &&
16630             (should_do_blocking_collection == FALSE) &&
16631             gc_can_use_concurrent &&
16632             !temp_disable_concurrent_p &&
16633             ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
16634         {
16635             keep_bgc_threads_p = TRUE;
16636             c_write (settings.concurrent,  TRUE);
16637         }
16638 #endif //BACKGROUND_GC
16639 
16640         settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1;
16641 
16642         // Call the EE for start of GC work
16643         // just one thread for MP GC
16644         GCToEEInterface::GcStartWork (settings.condemned_generation,
16645                                  max_generation);
16646 
16647         // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
16648         // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
16649         // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
16650         // fired in gc1.
16651         do_pre_gc();
16652 
16653 #ifdef MULTIPLE_HEAPS
16654         gc_start_event.Reset();
16655         //start all threads on the roots.
16656         dprintf(3, ("Starting all gc threads for gc"));
16657         gc_t_join.restart();
16658 #endif //MULTIPLE_HEAPS
16659     }
16660 
16661     {
16662         int gen_num_for_data = max_generation + 1;
16663         for (int i = 0; i <= gen_num_for_data; i++)
16664         {
16665             gc_data_per_heap.gen_data[i].size_before = generation_size (i);
16666             generation* gen = generation_of (i);
16667             gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
16668             gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
16669         }
16670     }
16671     descr_generations (TRUE);
16672 //    descr_card_table();
16673 
16674 #ifdef NO_WRITE_BARRIER
16675     fix_card_table();
16676 #endif //NO_WRITE_BARRIER
16677 
16678 #ifdef VERIFY_HEAP
16679     if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC) &&
16680        !(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_POST_GC_ONLY))
16681     {
16682         verify_heap (TRUE);
16683     }
16684     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)
16685         checkGCWriteBarrier();
16686 
16687 #endif // VERIFY_HEAP
16688 
16689 #ifdef BACKGROUND_GC
16690     if (settings.concurrent)
16691     {
16692         // We need to save the settings because we'll need to restore it after each FGC.
16693         assert (settings.condemned_generation == max_generation);
16694         settings.compaction = FALSE;
16695         saved_bgc_settings = settings;
16696 
16697 #ifdef MULTIPLE_HEAPS
16698         if (heap_number == 0)
16699         {
16700             for (int i = 0; i < n_heaps; i++)
16701             {
16702                 prepare_bgc_thread (g_heaps[i]);
16703             }
16704             dprintf (2, ("setting bgc_threads_sync_event"));
16705             bgc_threads_sync_event.Set();
16706         }
16707         else
16708         {
16709             bgc_threads_sync_event.Wait(INFINITE, FALSE);
16710             dprintf (2, ("bgc_threads_sync_event is signalled"));
16711         }
16712 #else
16713         prepare_bgc_thread(0);
16714 #endif //MULTIPLE_HEAPS
16715 
16716 #ifdef MULTIPLE_HEAPS
16717         gc_t_join.join(this, gc_join_start_bgc);
16718         if (gc_t_join.joined())
16719 #endif //MULTIPLE_HEAPS
16720         {
16721             do_concurrent_p = TRUE;
16722             do_ephemeral_gc_p = FALSE;
16723 #ifdef MULTIPLE_HEAPS
16724             dprintf(2, ("Joined to perform a background GC"));
16725 
16726             for (int i = 0; i < n_heaps; i++)
16727             {
16728                 gc_heap* hp = g_heaps[i];
16729                 if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
16730                 {
16731                     do_concurrent_p = FALSE;
16732                     break;
16733                 }
16734                 else
16735                 {
16736                     hp->background_saved_lowest_address = hp->lowest_address;
16737                     hp->background_saved_highest_address = hp->highest_address;
16738                 }
16739             }
16740 #else
16741             do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
16742             if (do_concurrent_p)
16743             {
16744                 background_saved_lowest_address = lowest_address;
16745                 background_saved_highest_address = highest_address;
16746             }
16747 #endif //MULTIPLE_HEAPS
16748 
16749             if (do_concurrent_p)
16750             {
16751 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
16752                 SoftwareWriteWatch::EnableForGCHeap();
16753 #endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
16754 
16755 #ifdef MULTIPLE_HEAPS
16756                 for (int i = 0; i < n_heaps; i++)
16757                     g_heaps[i]->current_bgc_state = bgc_initialized;
16758 #else
16759                 current_bgc_state = bgc_initialized;
16760 #endif //MULTIPLE_HEAPS
16761 
16762                 int gen = check_for_ephemeral_alloc();
16763                 // always do a gen1 GC before we start BGC.
16764                 // This is temporary for testing purpose.
16765                 //int gen = max_generation - 1;
16766                 dont_restart_ee_p = TRUE;
16767                 if (gen == -1)
16768                 {
16769                     // If we decide to not do a GC before the BGC we need to
16770                     // restore the gen0 alloc context.
16771 #ifdef MULTIPLE_HEAPS
16772                     for (int i = 0; i < n_heaps; i++)
16773                     {
16774                         generation_allocation_pointer (g_heaps[i]->generation_of (0)) =  0;
16775                         generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
16776                     }
16777 #else
16778                     generation_allocation_pointer (youngest_generation) =  0;
16779                     generation_allocation_limit (youngest_generation) = 0;
16780 #endif //MULTIPLE_HEAPS
16781                 }
16782                 else
16783                 {
16784                     do_ephemeral_gc_p = TRUE;
16785 
16786                     settings.init_mechanisms();
16787                     settings.condemned_generation = gen;
16788                     settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2;
16789                     do_pre_gc();
16790 
16791                     // TODO BACKGROUND_GC need to add the profiling stuff here.
16792                     dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
16793                 }
16794 
16795                 //clear the cards so they don't bleed in gen 1 during collection
16796                 // shouldn't this always be done at the beginning of any GC?
16797                 //clear_card_for_addresses (
16798                 //    generation_allocation_start (generation_of (0)),
16799                 //    heap_segment_allocated (ephemeral_heap_segment));
16800 
16801                 if (!do_ephemeral_gc_p)
16802                 {
16803                     do_background_gc();
16804                 }
16805             }
16806             else
16807             {
16808                 settings.compaction = TRUE;
16809                 c_write (settings.concurrent, FALSE);
16810             }
16811 
16812 #ifdef MULTIPLE_HEAPS
16813             gc_t_join.restart();
16814 #endif //MULTIPLE_HEAPS
16815         }
16816 
16817         if (do_concurrent_p)
16818         {
16819             // At this point we are sure we'll be starting a BGC, so save its per heap data here.
16820             // global data is only calculated at the end of the GC so we don't need to worry about
16821             // FGCs overwriting it.
16822             memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap));
16823             memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
16824 
16825             if (do_ephemeral_gc_p)
16826             {
16827                 dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
16828 
16829                 gen_to_condemn_reasons.init();
16830                 gen_to_condemn_reasons.set_condition (gen_before_bgc);
16831                 gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
16832                 gc1();
16833 #ifdef MULTIPLE_HEAPS
16834                 gc_t_join.join(this, gc_join_bgc_after_ephemeral);
16835                 if (gc_t_join.joined())
16836 #endif //MULTIPLE_HEAPS
16837                 {
16838 #ifdef MULTIPLE_HEAPS
16839                     do_post_gc();
16840 #endif //MULTIPLE_HEAPS
16841                     settings = saved_bgc_settings;
16842                     assert (settings.concurrent);
16843 
16844                     do_background_gc();
16845 
16846 #ifdef MULTIPLE_HEAPS
16847                     gc_t_join.restart();
16848 #endif //MULTIPLE_HEAPS
16849                 }
16850             }
16851         }
16852         else
16853         {
16854             dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
16855             gc1();
16856         }
16857     }
16858     else
16859 #endif //BACKGROUND_GC
16860     {
16861         gc1();
16862     }
16863 #ifndef MULTIPLE_HEAPS
16864     allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp();
16865     allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
16866     fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
16867 #endif //MULTIPLE_HEAPS
16868 
16869 done:
16870     if (settings.pause_mode == pause_no_gc)
16871         allocate_for_no_gc_after_gc();
16872 
16873     int gn = settings.condemned_generation;
16874     return gn;
16875 }
16876 
16877 #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
16878 
16879 inline
promoted_bytes(int thread)16880 size_t& gc_heap::promoted_bytes(int thread)
16881 {
16882 #ifdef MULTIPLE_HEAPS
16883     return g_promoted [thread*16];
16884 #else //MULTIPLE_HEAPS
16885     UNREFERENCED_PARAMETER(thread);
16886     return g_promoted;
16887 #endif //MULTIPLE_HEAPS
16888 }
16889 
16890 #ifdef INTERIOR_POINTERS
find_segment(uint8_t * interior,BOOL small_segment_only_p)16891 heap_segment* gc_heap::find_segment (uint8_t* interior, BOOL small_segment_only_p)
16892 {
16893 #ifdef SEG_MAPPING_TABLE
16894     heap_segment* seg = seg_mapping_table_segment_of (interior);
16895     if (seg)
16896     {
16897         if (small_segment_only_p && heap_segment_loh_p (seg))
16898             return 0;
16899     }
16900     return seg;
16901 #else //SEG_MAPPING_TABLE
16902 #ifdef MULTIPLE_HEAPS
16903     for (int i = 0; i < gc_heap::n_heaps; i++)
16904     {
16905         gc_heap* h = gc_heap::g_heaps [i];
16906         hs = h->find_segment_per_heap (o, small_segment_only_p);
16907         if (hs)
16908         {
16909             break;
16910         }
16911     }
16912 #else
16913     {
16914         gc_heap* h = pGenGCHeap;
16915         hs = h->find_segment_per_heap (o, small_segment_only_p);
16916     }
16917 #endif //MULTIPLE_HEAPS
16918 #endif //SEG_MAPPING_TABLE
16919 }
16920 
find_segment_per_heap(uint8_t * interior,BOOL small_segment_only_p)16921 heap_segment* gc_heap::find_segment_per_heap (uint8_t* interior, BOOL small_segment_only_p)
16922 {
16923 #ifdef SEG_MAPPING_TABLE
16924     return find_segment (interior, small_segment_only_p);
16925 #else //SEG_MAPPING_TABLE
16926     if (in_range_for_segment (interior, ephemeral_heap_segment))
16927     {
16928         return ephemeral_heap_segment;
16929     }
16930     else
16931     {
16932         heap_segment* found_seg = 0;
16933 
16934         {
16935             heap_segment* seg = generation_start_segment (generation_of (max_generation));
16936             do
16937             {
16938                 if (in_range_for_segment (interior, seg))
16939                 {
16940                     found_seg = seg;
16941                     goto end_find_segment;
16942                 }
16943 
16944             } while ((seg = heap_segment_next (seg)) != 0);
16945         }
16946         if (!small_segment_only_p)
16947         {
16948 #ifdef BACKGROUND_GC
16949             {
16950                 ptrdiff_t delta = 0;
16951                 heap_segment* seg = segment_of (interior, delta);
16952                 if (seg && in_range_for_segment (interior, seg))
16953                 {
16954                     found_seg = seg;
16955                 }
16956                 goto end_find_segment;
16957             }
16958 #else //BACKGROUND_GC
16959             heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
16960             do
16961             {
16962                 if (in_range_for_segment(interior, seg))
16963                 {
16964                     found_seg = seg;
16965                     goto end_find_segment;
16966                 }
16967 
16968             } while ((seg = heap_segment_next (seg)) != 0);
16969 #endif //BACKGROUND_GC
16970         }
16971 end_find_segment:
16972 
16973         return found_seg;
16974     }
16975 #endif //SEG_MAPPING_TABLE
16976 }
16977 #endif //INTERIOR_POINTERS
16978 
16979 #if !defined(_DEBUG) && !defined(__GNUC__)
16980 inline // This causes link errors if global optimization is off
16981 #endif //!_DEBUG && !__GNUC__
heap_of(uint8_t * o)16982 gc_heap* gc_heap::heap_of (uint8_t* o)
16983 {
16984 #ifdef MULTIPLE_HEAPS
16985     if (o == 0)
16986         return g_heaps [0];
16987 #ifdef SEG_MAPPING_TABLE
16988     gc_heap* hp = seg_mapping_table_heap_of (o);
16989     return (hp ? hp : g_heaps[0]);
16990 #else //SEG_MAPPING_TABLE
16991     ptrdiff_t delta = 0;
16992     heap_segment* seg = segment_of (o, delta);
16993     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
16994 #endif //SEG_MAPPING_TABLE
16995 #else //MULTIPLE_HEAPS
16996     UNREFERENCED_PARAMETER(o);
16997     return __this;
16998 #endif //MULTIPLE_HEAPS
16999 }
17000 
17001 inline
heap_of_gc(uint8_t * o)17002 gc_heap* gc_heap::heap_of_gc (uint8_t* o)
17003 {
17004 #ifdef MULTIPLE_HEAPS
17005     if (o == 0)
17006         return g_heaps [0];
17007 #ifdef SEG_MAPPING_TABLE
17008     gc_heap* hp = seg_mapping_table_heap_of_gc (o);
17009     return (hp ? hp : g_heaps[0]);
17010 #else //SEG_MAPPING_TABLE
17011     ptrdiff_t delta = 0;
17012     heap_segment* seg = segment_of (o, delta);
17013     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17014 #endif //SEG_MAPPING_TABLE
17015 #else //MULTIPLE_HEAPS
17016     UNREFERENCED_PARAMETER(o);
17017     return __this;
17018 #endif //MULTIPLE_HEAPS
17019 }
17020 
17021 #ifdef INTERIOR_POINTERS
17022 // will find all heap objects (large and small)
find_object(uint8_t * interior,uint8_t * low)17023 uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
17024 {
17025     if (!gen0_bricks_cleared)
17026     {
17027 #ifdef MULTIPLE_HEAPS
17028         assert (!"Should have already been done in server GC");
17029 #endif //MULTIPLE_HEAPS
17030         gen0_bricks_cleared = TRUE;
17031         //initialize brick table for gen 0
17032         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
17033              b < brick_of (align_on_brick
17034                            (heap_segment_allocated (ephemeral_heap_segment)));
17035              b++)
17036         {
17037             set_brick (b, -1);
17038         }
17039     }
17040 #ifdef FFIND_OBJECT
17041     //indicate that in the future this needs to be done during allocation
17042 #ifdef MULTIPLE_HEAPS
17043     gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
17044 #else
17045     gen0_must_clear_bricks = FFIND_DECAY;
17046 #endif //MULTIPLE_HEAPS
17047 #endif //FFIND_OBJECT
17048 
17049     int brick_entry = brick_table [brick_of (interior)];
17050     if (brick_entry == 0)
17051     {
17052         // this is a pointer to a large object
17053         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17054         if (seg
17055 #ifdef FEATURE_CONSERVATIVE_GC
17056             && (!g_pConfig->GetGCConservative() || interior <= heap_segment_allocated(seg))
17057 #endif
17058             )
17059         {
17060             // If interior falls within the first free object at the beginning of a generation,
17061             // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
17062             int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
17063 #ifdef FEATURE_CONSERVATIVE_GC
17064                                                        || (g_pConfig->GetGCConservative() && !heap_segment_loh_p (seg))
17065 #endif
17066                                                       );
17067             //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
17068             assert (interior < heap_segment_allocated (seg));
17069 
17070             uint8_t* o = heap_segment_mem (seg);
17071             while (o < heap_segment_allocated (seg))
17072             {
17073                 uint8_t* next_o = o + Align (size (o), align_const);
17074                 assert (next_o > o);
17075                 if ((o <= interior) && (interior < next_o))
17076                 return o;
17077                 o = next_o;
17078             }
17079             return 0;
17080         }
17081         else
17082         {
17083             return 0;
17084         }
17085     }
17086     else if (interior >= low)
17087     {
17088         heap_segment* seg = find_segment_per_heap (interior, TRUE);
17089         if (seg)
17090         {
17091 #ifdef FEATURE_CONSERVATIVE_GC
17092             if (interior >= heap_segment_allocated (seg))
17093                 return 0;
17094 #else
17095             assert (interior < heap_segment_allocated (seg));
17096 #endif
17097             uint8_t* o = find_first_object (interior, heap_segment_mem (seg));
17098             return o;
17099         }
17100         else
17101             return 0;
17102     }
17103     else
17104         return 0;
17105 }
17106 
17107 uint8_t*
find_object_for_relocation(uint8_t * interior,uint8_t * low,uint8_t * high)17108 gc_heap::find_object_for_relocation (uint8_t* interior, uint8_t* low, uint8_t* high)
17109 {
17110     uint8_t* old_address = interior;
17111     if (!((old_address >= low) && (old_address < high)))
17112         return 0;
17113     uint8_t* plug = 0;
17114     size_t  brick = brick_of (old_address);
17115     int    brick_entry =  brick_table [ brick ];
17116     if (brick_entry != 0)
17117     {
17118     retry:
17119         {
17120             while (brick_entry < 0)
17121             {
17122                 brick = (brick + brick_entry);
17123                 brick_entry =  brick_table [ brick ];
17124             }
17125             uint8_t* old_loc = old_address;
17126             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
17127                                       old_loc);
17128             if (node <= old_loc)
17129                 plug = node;
17130             else
17131             {
17132                 brick = brick - 1;
17133                 brick_entry =  brick_table [ brick ];
17134                 goto retry;
17135             }
17136 
17137         }
17138         assert (plug);
17139         //find the object by going along the plug
17140         uint8_t* o = plug;
17141         while (o <= interior)
17142         {
17143             uint8_t* next_o = o + Align (size (o));
17144             assert (next_o > o);
17145             if (next_o > interior)
17146             {
17147                 break;
17148             }
17149             o = next_o;
17150         }
17151         assert ((o <= interior) && ((o + Align (size (o))) > interior));
17152         return o;
17153     }
17154     else
17155     {
17156         // this is a pointer to a large object
17157         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17158         if (seg)
17159         {
17160             assert (interior < heap_segment_allocated (seg));
17161 
17162             uint8_t* o = heap_segment_mem (seg);
17163             while (o < heap_segment_allocated (seg))
17164             {
17165                 uint8_t* next_o = o + Align (size (o));
17166                 assert (next_o > o);
17167                 if ((o < interior) && (interior < next_o))
17168                 return o;
17169                 o = next_o;
17170             }
17171             return 0;
17172         }
17173         else
17174             {
17175             return 0;
17176         }
17177     }
17178 }
17179 #else //INTERIOR_POINTERS
17180 inline
find_object(uint8_t * o,uint8_t * low)17181 uint8_t* gc_heap::find_object (uint8_t* o, uint8_t* low)
17182 {
17183     return o;
17184 }
17185 #endif //INTERIOR_POINTERS
17186 
17187 #ifdef MARK_LIST
17188 #ifdef GC_CONFIG_DRIVEN
17189 #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;} else {mark_list_index++;} if (slow > o) slow = o; if (shigh < o) shigh = o;}
17190 #else
17191 #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;}
17192 #endif //GC_CONFIG_DRIVEN
17193 #else //MARK_LIST
17194 #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17195 #endif //MARK_LIST
17196 
17197 #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17198 
17199 #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
17200 
17201 inline
gc_mark1(uint8_t * o)17202 BOOL gc_heap::gc_mark1 (uint8_t* o)
17203 {
17204     BOOL marked = !marked (o);
17205     set_marked (o);
17206     dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
17207     return marked;
17208 }
17209 
17210 inline
gc_mark(uint8_t * o,uint8_t * low,uint8_t * high)17211 BOOL gc_heap::gc_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17212 {
17213     BOOL marked = FALSE;
17214     if ((o >= low) && (o < high))
17215         marked = gc_mark1 (o);
17216 #ifdef MULTIPLE_HEAPS
17217     else if (o)
17218     {
17219         //find the heap
17220         gc_heap* hp = heap_of_gc (o);
17221         assert (hp);
17222         if ((o >= hp->gc_low) && (o < hp->gc_high))
17223             marked = gc_mark1 (o);
17224     }
17225 #ifdef SNOOP_STATS
17226     snoop_stat.objects_checked_count++;
17227 
17228     if (marked)
17229     {
17230         snoop_stat.objects_marked_count++;
17231     }
17232     if (!o)
17233     {
17234         snoop_stat.zero_ref_count++;
17235     }
17236 
17237 #endif //SNOOP_STATS
17238 #endif //MULTIPLE_HEAPS
17239     return marked;
17240 }
17241 
17242 #ifdef BACKGROUND_GC
17243 
17244 inline
background_marked(uint8_t * o)17245 BOOL gc_heap::background_marked (uint8_t* o)
17246 {
17247     return mark_array_marked (o);
17248 }
17249 inline
background_mark1(uint8_t * o)17250 BOOL gc_heap::background_mark1 (uint8_t* o)
17251 {
17252     BOOL to_mark = !mark_array_marked (o);
17253 
17254     dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
17255     if (to_mark)
17256     {
17257         mark_array_set_marked (o);
17258         dprintf (4, ("n*%Ix*n", (size_t)o));
17259         return TRUE;
17260     }
17261     else
17262         return FALSE;
17263 }
17264 
17265 // TODO: we could consider filtering out NULL's here instead of going to
17266 // look for it on other heaps
17267 inline
background_mark(uint8_t * o,uint8_t * low,uint8_t * high)17268 BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17269 {
17270     BOOL marked = FALSE;
17271     if ((o >= low) && (o < high))
17272         marked = background_mark1 (o);
17273 #ifdef MULTIPLE_HEAPS
17274     else if (o)
17275     {
17276         //find the heap
17277         gc_heap* hp = heap_of (o);
17278         assert (hp);
17279         if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
17280             marked = background_mark1 (o);
17281     }
17282 #endif //MULTIPLE_HEAPS
17283     return marked;
17284 }
17285 
17286 #endif //BACKGROUND_GC
17287 
17288 inline
next_end(heap_segment * seg,uint8_t * f)17289 uint8_t* gc_heap::next_end (heap_segment* seg, uint8_t* f)
17290 {
17291     if (seg == ephemeral_heap_segment)
17292         return  f;
17293     else
17294         return  heap_segment_allocated (seg);
17295 }
17296 
17297 #define new_start() {if (ppstop <= start) {break;} else {parm = start}}
17298 #define ignore_start 0
17299 #define use_start 1
17300 
17301 #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp)      \
17302 {                                                                           \
17303     CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt));           \
17304     CGCDescSeries* cur = map->GetHighestSeries();                           \
17305     ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries();                        \
17306                                                                             \
17307     if (cnt >= 0)                                                           \
17308     {                                                                       \
17309         CGCDescSeries* last = map->GetLowestSeries();                       \
17310         uint8_t** parm = 0;                                                 \
17311         do                                                                  \
17312         {                                                                   \
17313             assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset()));     \
17314             parm = (uint8_t**)((o) + cur->GetSeriesOffset());               \
17315             uint8_t** ppstop =                                              \
17316                 (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\
17317             if (!start_useful || (uint8_t*)ppstop > (start))                \
17318             {                                                               \
17319                 if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\
17320                 while (parm < ppstop)                                       \
17321                 {                                                           \
17322                    {exp}                                                    \
17323                    parm++;                                                  \
17324                 }                                                           \
17325             }                                                               \
17326             cur--;                                                          \
17327                                                                             \
17328         } while (cur >= last);                                              \
17329     }                                                                       \
17330     else                                                                    \
17331     {                                                                       \
17332         /* Handle the repeating case - array of valuetypes */               \
17333         uint8_t** parm = (uint8_t**)((o) + cur->startoffset);               \
17334         if (start_useful && start > (uint8_t*)parm)                         \
17335         {                                                                   \
17336             ptrdiff_t cs = mt->RawGetComponentSize();                         \
17337             parm = (uint8_t**)((uint8_t*)parm + (((start) - (uint8_t*)parm)/cs)*cs); \
17338         }                                                                   \
17339         while ((uint8_t*)parm < ((o)+(size)-plug_skew))                     \
17340         {                                                                   \
17341             for (ptrdiff_t __i = 0; __i > cnt; __i--)                         \
17342             {                                                               \
17343                 HALF_SIZE_T skip =  cur->val_serie[__i].skip;               \
17344                 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs;              \
17345                 uint8_t** ppstop = parm + nptrs;                            \
17346                 if (!start_useful || (uint8_t*)ppstop > (start))            \
17347                 {                                                           \
17348                     if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);      \
17349                     do                                                      \
17350                     {                                                       \
17351                        {exp}                                                \
17352                        parm++;                                              \
17353                     } while (parm < ppstop);                                \
17354                 }                                                           \
17355                 parm = (uint8_t**)((uint8_t*)ppstop + skip);                \
17356             }                                                               \
17357         }                                                                   \
17358     }                                                                       \
17359 }
17360 
17361 #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
17362 
17363 // 1 thing to note about this macro:
17364 // 1) you can use *parm safely but in general you don't want to use parm
17365 // because for the collectible types it's not an address on the managed heap.
17366 #ifndef COLLECTIBLE_CLASS
17367 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17368 {                                                                           \
17369     if (header(o)->ContainsPointers())                                      \
17370     {                                                                       \
17371         go_through_object_nostart(mt,o,size,parm,exp);                      \
17372     }                                                                       \
17373 }
17374 #else //COLLECTIBLE_CLASS
17375 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17376 {                                                                           \
17377     if (header(o)->Collectible())                                           \
17378     {                                                                       \
17379         uint8_t* class_obj = get_class_object (o);                             \
17380         uint8_t** parm = &class_obj;                                           \
17381         do {exp} while (false);                                             \
17382     }                                                                       \
17383     if (header(o)->ContainsPointers())                                      \
17384     {                                                                       \
17385         go_through_object_nostart(mt,o,size,parm,exp);                      \
17386     }                                                                       \
17387 }
17388 #endif //COLLECTIBLE_CLASS
17389 
17390 // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
enque_pinned_plug(uint8_t * plug,BOOL save_pre_plug_info_p,uint8_t * last_object_in_last_plug)17391 void gc_heap::enque_pinned_plug (uint8_t* plug,
17392                                  BOOL save_pre_plug_info_p,
17393                                  uint8_t* last_object_in_last_plug)
17394 {
17395     if (mark_stack_array_length <= mark_stack_tos)
17396     {
17397         if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
17398         {
17399             // we don't want to continue here due to security
17400             // risks. This happens very rarely and fixing it in the
17401             // way so that we can continue is a bit involved and will
17402             // not be done in Dev10.
17403             EEPOLICY_HANDLE_FATAL_ERROR(CORINFO_EXCEPTION_GC);
17404         }
17405     }
17406 
17407     dprintf (3, ("enquing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d",
17408         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)));
17409     mark& m = mark_stack_array[mark_stack_tos];
17410     m.first = plug;
17411     // Must be set now because if we have a short object we'll need the value of saved_pre_p.
17412     m.saved_pre_p = save_pre_plug_info_p;
17413 
17414     if (save_pre_plug_info_p)
17415     {
17416 #ifdef SHORT_PLUGS
17417         BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17418         if (is_padded)
17419             clear_plug_padded (last_object_in_last_plug);
17420 #endif //SHORT_PLUGS
17421         memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17422 #ifdef SHORT_PLUGS
17423         if (is_padded)
17424             set_plug_padded (last_object_in_last_plug);
17425 #endif //SHORT_PLUGS
17426 
17427         memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17428 
17429         // If the last object in the last plug is too short, it requires special handling.
17430         size_t last_obj_size = plug - last_object_in_last_plug;
17431         if (last_obj_size < min_pre_pin_obj_size)
17432         {
17433             record_interesting_data_point (idp_pre_short);
17434 #ifdef SHORT_PLUGS
17435             if (is_padded)
17436                 record_interesting_data_point (idp_pre_short_padded);
17437 #endif //SHORT_PLUGS
17438             dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!",
17439                          last_object_in_last_plug, plug));
17440             // Need to set the short bit regardless of having refs or not because we need to
17441             // indicate that this object is not walkable.
17442             m.set_pre_short();
17443 
17444 #ifdef COLLECTIBLE_CLASS
17445             if (is_collectible (last_object_in_last_plug))
17446             {
17447                 m.set_pre_short_collectible();
17448             }
17449 #endif //COLLECTIBLE_CLASS
17450 
17451             if (contain_pointers (last_object_in_last_plug))
17452             {
17453                 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17454 
17455                 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17456                     {
17457                         size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17458                         dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17459                         m.set_pre_short_bit (gap_offset);
17460                     }
17461                 );
17462             }
17463         }
17464     }
17465 
17466     m.saved_post_p = FALSE;
17467 }
17468 
save_post_plug_info(uint8_t * last_pinned_plug,uint8_t * last_object_in_last_plug,uint8_t * post_plug)17469 void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_object_in_last_plug, uint8_t* post_plug)
17470 {
17471     UNREFERENCED_PARAMETER(last_pinned_plug);
17472 
17473     mark& m = mark_stack_array[mark_stack_tos - 1];
17474     assert (last_pinned_plug == m.first);
17475     m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]);
17476 
17477 #ifdef SHORT_PLUGS
17478     BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17479     if (is_padded)
17480         clear_plug_padded (last_object_in_last_plug);
17481 #endif //SHORT_PLUGS
17482     memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17483 #ifdef SHORT_PLUGS
17484     if (is_padded)
17485         set_plug_padded (last_object_in_last_plug);
17486 #endif //SHORT_PLUGS
17487 
17488     memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17489 
17490     // This is important - we need to clear all bits here except the last one.
17491     m.saved_post_p = TRUE;
17492 
17493 #ifdef _DEBUG
17494     m.saved_post_plug_debug.gap = 1;
17495 #endif //_DEBUG
17496 
17497     dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
17498 
17499     size_t last_obj_size = post_plug - last_object_in_last_plug;
17500     if (last_obj_size < min_pre_pin_obj_size)
17501     {
17502         dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
17503         record_interesting_data_point (idp_post_short);
17504 #ifdef SHORT_PLUGS
17505         if (is_padded)
17506             record_interesting_data_point (idp_post_short_padded);
17507 #endif //SHORT_PLUGS
17508         m.set_post_short();
17509         verify_pinned_queue_p = TRUE;
17510 
17511 #ifdef COLLECTIBLE_CLASS
17512         if (is_collectible (last_object_in_last_plug))
17513         {
17514             m.set_post_short_collectible();
17515         }
17516 #endif //COLLECTIBLE_CLASS
17517 
17518         if (contain_pointers (last_object_in_last_plug))
17519         {
17520             dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17521 
17522             // TODO: since we won't be able to walk this object in relocation, we still need to
17523             // take care of collectible assemblies here.
17524             go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17525                 {
17526                     size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17527                     dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17528                     m.set_post_short_bit (gap_offset);
17529                 }
17530             );
17531         }
17532     }
17533 }
17534 
17535 //#define PREFETCH
17536 #ifdef PREFETCH
Prefetch(void * addr)17537 __declspec(naked) void __fastcall Prefetch(void* addr)
17538 {
17539    __asm {
17540        PREFETCHT0 [ECX]
17541         ret
17542     };
17543 }
17544 #else //PREFETCH
Prefetch(void * addr)17545 inline void Prefetch (void* addr)
17546 {
17547     UNREFERENCED_PARAMETER(addr);
17548 }
17549 #endif //PREFETCH
17550 #ifdef MH_SC_MARK
17551 inline
VOLATILE(uint8_t *)17552 VOLATILE(uint8_t*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
17553 {
17554     return ((VOLATILE(uint8_t*)*)(hp->mark_stack_array))[index];
17555 }
17556 
17557 #endif //MH_SC_MARK
17558 
17559 #define stolen 2
17560 #define partial 1
17561 #define partial_object 3
17562 inline
ref_from_slot(uint8_t * r)17563 uint8_t* ref_from_slot (uint8_t* r)
17564 {
17565     return (uint8_t*)((size_t)r & ~(stolen | partial));
17566 }
17567 inline
stolen_p(uint8_t * r)17568 BOOL stolen_p (uint8_t* r)
17569 {
17570     return (((size_t)r&2) && !((size_t)r&1));
17571 }
17572 inline
ready_p(uint8_t * r)17573 BOOL ready_p (uint8_t* r)
17574 {
17575     return ((size_t)r != 1);
17576 }
17577 inline
partial_p(uint8_t * r)17578 BOOL partial_p (uint8_t* r)
17579 {
17580     return (((size_t)r&1) && !((size_t)r&2));
17581 }
17582 inline
straight_ref_p(uint8_t * r)17583 BOOL straight_ref_p (uint8_t* r)
17584 {
17585     return (!stolen_p (r) && !partial_p (r));
17586 }
17587 inline
partial_object_p(uint8_t * r)17588 BOOL partial_object_p (uint8_t* r)
17589 {
17590     return (((size_t)r & partial_object) == partial_object);
17591 }
17592 inline
ref_p(uint8_t * r)17593 BOOL ref_p (uint8_t* r)
17594 {
17595     return (straight_ref_p (r) || partial_object_p (r));
17596 }
17597 
mark_object_simple1(uint8_t * oo,uint8_t * start THREAD_NUMBER_DCL)17598 void gc_heap::mark_object_simple1 (uint8_t* oo, uint8_t* start THREAD_NUMBER_DCL)
17599 {
17600     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)mark_stack_array;
17601     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)&mark_stack_array[mark_stack_array_length];
17602     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_base = mark_stack_tos;
17603 #ifdef SORT_MARK_STACK
17604     SERVER_SC_MARK_VOLATILE(uint8_t*)* sorted_tos = mark_stack_base;
17605 #endif //SORT_MARK_STACK
17606 
17607     // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't
17608     // update mark list.
17609     BOOL  full_p = (settings.condemned_generation == max_generation);
17610 
17611     assert ((start >= oo) && (start < oo+size(oo)));
17612 
17613 #ifndef MH_SC_MARK
17614     *mark_stack_tos = oo;
17615 #endif //!MH_SC_MARK
17616 
17617     while (1)
17618     {
17619 #ifdef MULTIPLE_HEAPS
17620 #else  //MULTIPLE_HEAPS
17621         const int thread = 0;
17622 #endif //MULTIPLE_HEAPS
17623 
17624         if (oo && ((size_t)oo != 4))
17625         {
17626             size_t s = 0;
17627             if (stolen_p (oo))
17628             {
17629                 --mark_stack_tos;
17630                 goto next_level;
17631             }
17632             else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
17633             {
17634                 BOOL overflow_p = FALSE;
17635 
17636                 if (mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit  - 1))
17637                 {
17638                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
17639                     if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
17640                     {
17641                         overflow_p = TRUE;
17642                     }
17643                 }
17644 
17645                 if (overflow_p == FALSE)
17646                 {
17647                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17648 
17649                     go_through_object_cl (method_table(oo), oo, s, ppslot,
17650                                           {
17651                                               uint8_t* o = *ppslot;
17652                                               Prefetch(o);
17653                                               if (gc_mark (o, gc_low, gc_high))
17654                                               {
17655                                                   if (full_p)
17656                                                   {
17657                                                       m_boundary_fullgc (o);
17658                                                   }
17659                                                   else
17660                                                   {
17661                                                       m_boundary (o);
17662                                                   }
17663                                                   size_t obj_size = size (o);
17664                                                   promoted_bytes (thread) += obj_size;
17665                                                   if (contain_pointers_or_collectible (o))
17666                                                   {
17667                                                       *(mark_stack_tos++) = o;
17668                                                   }
17669                                               }
17670                                           }
17671                         );
17672                 }
17673                 else
17674                 {
17675                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17676                     min_overflow_address = min (min_overflow_address, oo);
17677                     max_overflow_address = max (max_overflow_address, oo);
17678                 }
17679             }
17680             else
17681             {
17682                 if (partial_p (oo))
17683                 {
17684                     start = ref_from_slot (oo);
17685                     oo = ref_from_slot (*(--mark_stack_tos));
17686                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
17687                     assert ((oo < start) && (start < (oo + size (oo))));
17688                 }
17689 #ifdef COLLECTIBLE_CLASS
17690                 else
17691                 {
17692                     // If there's a class object, push it now. We are guaranteed to have the slot since
17693                     // we just popped one object off.
17694                     if (is_collectible (oo))
17695                     {
17696                         uint8_t* class_obj = get_class_object (oo);
17697                         if (gc_mark (class_obj, gc_low, gc_high))
17698                         {
17699                             if (full_p)
17700                             {
17701                                 m_boundary_fullgc (class_obj);
17702                             }
17703                             else
17704                             {
17705                                 m_boundary (class_obj);
17706                             }
17707 
17708                             size_t obj_size = size (class_obj);
17709                             promoted_bytes (thread) += obj_size;
17710                             *(mark_stack_tos++) = class_obj;
17711                         }
17712                     }
17713                 }
17714 #endif //COLLECTIBLE_CLASS
17715 
17716                 s = size (oo);
17717 
17718                 BOOL overflow_p = FALSE;
17719 
17720                 if (mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
17721                 {
17722                     overflow_p = TRUE;
17723                 }
17724                 if (overflow_p == FALSE)
17725                 {
17726                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17727 
17728                     //push the object and its current
17729                     SERVER_SC_MARK_VOLATILE(uint8_t*)* place = ++mark_stack_tos;
17730                     mark_stack_tos++;
17731 #ifdef MH_SC_MARK
17732                     *(place-1) = 0;
17733                     *(place) = (uint8_t*)partial;
17734 #endif //MH_SC_MARK
17735                     int i = num_partial_refs;
17736                     uint8_t* ref_to_continue = 0;
17737 
17738                     go_through_object (method_table(oo), oo, s, ppslot,
17739                                        start, use_start, (oo + s),
17740                                        {
17741                                            uint8_t* o = *ppslot;
17742                                            Prefetch(o);
17743                                            if (gc_mark (o, gc_low, gc_high))
17744                                            {
17745                                                 if (full_p)
17746                                                 {
17747                                                     m_boundary_fullgc (o);
17748                                                 }
17749                                                 else
17750                                                 {
17751                                                     m_boundary (o);
17752                                                 }
17753                                                 size_t obj_size = size (o);
17754                                                 promoted_bytes (thread) += obj_size;
17755                                                 if (contain_pointers_or_collectible (o))
17756                                                 {
17757                                                     *(mark_stack_tos++) = o;
17758                                                     if (--i == 0)
17759                                                     {
17760                                                         ref_to_continue = (uint8_t*)((size_t)(ppslot+1) | partial);
17761                                                         goto more_to_do;
17762                                                     }
17763 
17764                                                 }
17765                                            }
17766 
17767                                        }
17768                         );
17769                     //we are finished with this object
17770                     assert (ref_to_continue == 0);
17771 #ifdef MH_SC_MARK
17772                     assert ((*(place-1)) == (uint8_t*)0);
17773 #else //MH_SC_MARK
17774                     *(place-1) = 0;
17775 #endif //MH_SC_MARK
17776                     *place = 0;
17777                     // shouldn't we decrease tos by 2 here??
17778 
17779 more_to_do:
17780                     if (ref_to_continue)
17781                     {
17782                         //update the start
17783 #ifdef MH_SC_MARK
17784                         assert ((*(place-1)) == (uint8_t*)0);
17785                         *(place-1) = (uint8_t*)((size_t)oo | partial_object);
17786                         assert (((*place) == (uint8_t*)1) || ((*place) == (uint8_t*)2));
17787 #endif //MH_SC_MARK
17788                         *place = ref_to_continue;
17789                     }
17790                 }
17791                 else
17792                 {
17793                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17794                     min_overflow_address = min (min_overflow_address, oo);
17795                     max_overflow_address = max (max_overflow_address, oo);
17796                 }
17797             }
17798 #ifdef SORT_MARK_STACK
17799             if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
17800             {
17801                 rqsort1 (sorted_tos, mark_stack_tos-1);
17802                 sorted_tos = mark_stack_tos-1;
17803             }
17804 #endif //SORT_MARK_STACK
17805         }
17806     next_level:
17807         if (!(mark_stack_empty_p()))
17808         {
17809             oo = *(--mark_stack_tos);
17810             start = oo;
17811 
17812 #ifdef SORT_MARK_STACK
17813             sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
17814 #endif //SORT_MARK_STACK
17815         }
17816         else
17817             break;
17818     }
17819 }
17820 
17821 #ifdef MH_SC_MARK
same_numa_node_p(int hn1,int hn2)17822 BOOL same_numa_node_p (int hn1, int hn2)
17823 {
17824     return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
17825 }
17826 
find_next_buddy_heap(int this_heap_number,int current_buddy,int n_heaps)17827 int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
17828 {
17829     int hn = (current_buddy+1)%n_heaps;
17830     while (hn != current_buddy)
17831     {
17832         if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
17833             return hn;
17834         hn = (hn+1)%n_heaps;
17835     }
17836     return current_buddy;
17837 }
17838 
17839 void
mark_steal()17840 gc_heap::mark_steal()
17841 {
17842     mark_stack_busy() = 0;
17843     //clear the mark stack in the snooping range
17844     for (int i = 0; i < max_snoop_level; i++)
17845     {
17846         ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
17847     }
17848 
17849     //pick the next heap as our buddy
17850     int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
17851 
17852 #ifdef SNOOP_STATS
17853         dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
17854         uint32_t begin_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
17855 #endif //SNOOP_STATS
17856 
17857     int idle_loop_count = 0;
17858     int first_not_ready_level = 0;
17859 
17860     while (1)
17861     {
17862         gc_heap* hp = g_heaps [thpn];
17863         int level = first_not_ready_level;
17864         first_not_ready_level = 0;
17865 
17866         while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
17867         {
17868             idle_loop_count = 0;
17869 #ifdef SNOOP_STATS
17870             snoop_stat.busy_count++;
17871             dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix",
17872                                  heap_number, level, (int)((uint8_t**)(hp->mark_stack_array))[level]));
17873 #endif //SNOOP_STATS
17874 
17875             uint8_t* o = ref_mark_stack (hp, level);
17876 
17877             uint8_t* start = o;
17878             if (ref_p (o))
17879             {
17880                 mark_stack_busy() = 1;
17881 
17882                 BOOL success = TRUE;
17883                 uint8_t* next = (ref_mark_stack (hp, level+1));
17884                 if (ref_p (next))
17885                 {
17886                     if (((size_t)o > 4) && !partial_object_p (o))
17887                     {
17888                         //this is a normal object, not a partial mark tuple
17889                         //success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
17890                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), (uint8_t*)4, o)==o);
17891 #ifdef SNOOP_STATS
17892                         snoop_stat.interlocked_count++;
17893                         if (success)
17894                             snoop_stat.normal_count++;
17895 #endif //SNOOP_STATS
17896                     }
17897                     else
17898                     {
17899                         //it is a stolen entry, or beginning/ending of a partial mark
17900                         level++;
17901 #ifdef SNOOP_STATS
17902                         snoop_stat.stolen_or_pm_count++;
17903 #endif //SNOOP_STATS
17904                         success = FALSE;
17905                     }
17906                 }
17907                 else if (stolen_p (next))
17908                 {
17909                     //ignore the stolen guy and go to the next level
17910                     success = FALSE;
17911                     level+=2;
17912 #ifdef SNOOP_STATS
17913                     snoop_stat.stolen_entry_count++;
17914 #endif //SNOOP_STATS
17915                 }
17916                 else
17917                 {
17918                     assert (partial_p (next));
17919                     start = ref_from_slot (next);
17920                     //re-read the object
17921                     o = ref_from_slot (ref_mark_stack (hp, level));
17922                     if (o && start)
17923                     {
17924                         //steal the object
17925                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level+1), (uint8_t*)stolen, next)==next);
17926 #ifdef SNOOP_STATS
17927                         snoop_stat.interlocked_count++;
17928                         if (success)
17929                         {
17930                             snoop_stat.partial_mark_parent_count++;
17931                         }
17932 #endif //SNOOP_STATS
17933                     }
17934                     else
17935                     {
17936                         // stack is not ready, or o is completely different from the last time we read from this stack level.
17937                         // go up 2 levels to steal children or totally unrelated objects.
17938                         success = FALSE;
17939                         if (first_not_ready_level == 0)
17940                         {
17941                             first_not_ready_level = level;
17942                         }
17943                         level+=2;
17944 #ifdef SNOOP_STATS
17945                         snoop_stat.pm_not_ready_count++;
17946 #endif //SNOOP_STATS
17947                     }
17948                 }
17949                 if (success)
17950                 {
17951 
17952 #ifdef SNOOP_STATS
17953                     dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
17954                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
17955                             (GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
17956                     uint32_t start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
17957 #endif //SNOOP_STATS
17958 
17959                     mark_object_simple1 (o, start, heap_number);
17960 
17961 #ifdef SNOOP_STATS
17962                     dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
17963                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
17964                             (GCToOSInterface::GetLowPrecisionTimeStamp()-start_tick),(GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
17965 #endif //SNOOP_STATS
17966 
17967                     mark_stack_busy() = 0;
17968 
17969                     //clear the mark stack in snooping range
17970                     for (int i = 0; i < max_snoop_level; i++)
17971                     {
17972                         if (((uint8_t**)mark_stack_array)[i] != 0)
17973                         {
17974                             ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
17975 #ifdef SNOOP_STATS
17976                             snoop_stat.stack_bottom_clear_count++;
17977 #endif //SNOOP_STATS
17978                         }
17979                     }
17980 
17981                     level = 0;
17982                 }
17983                 mark_stack_busy() = 0;
17984             }
17985             else
17986             {
17987                 //slot is either partial or stolen
17988                 level++;
17989             }
17990         }
17991         if ((first_not_ready_level != 0) && hp->mark_stack_busy())
17992         {
17993             continue;
17994         }
17995         if (!hp->mark_stack_busy())
17996         {
17997             first_not_ready_level = 0;
17998             idle_loop_count++;
17999 
18000             if ((idle_loop_count % (6) )==1)
18001             {
18002 #ifdef SNOOP_STATS
18003                 snoop_stat.switch_to_thread_count++;
18004 #endif //SNOOP_STATS
18005                 GCToOSInterface::Sleep(1);
18006             }
18007             int free_count = 1;
18008 #ifdef SNOOP_STATS
18009             snoop_stat.stack_idle_count++;
18010             //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
18011 #endif //SNOOP_STATS
18012             for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
18013             {
18014                 if (!((g_heaps [hpn])->mark_stack_busy()))
18015                 {
18016                     free_count++;
18017 #ifdef SNOOP_STATS
18018                 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
18019 #endif //SNOOP_STATS
18020                 }
18021                 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
18022                 {
18023                     thpn = hpn;
18024                     break;
18025                 }
18026                 hpn = (hpn+1)%n_heaps;
18027                 YieldProcessor();
18028             }
18029             if (free_count == n_heaps)
18030             {
18031                 break;
18032             }
18033         }
18034     }
18035 }
18036 
18037 inline
check_next_mark_stack(gc_heap * next_heap)18038 BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
18039 {
18040 #ifdef SNOOP_STATS
18041     snoop_stat.check_level_count++;
18042 #endif //SNOOP_STATS
18043     return (next_heap->mark_stack_busy()>=1);
18044 }
18045 #endif //MH_SC_MARK
18046 
18047 #ifdef SNOOP_STATS
print_snoop_stat()18048 void gc_heap::print_snoop_stat()
18049 {
18050     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s",
18051         "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
18052     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
18053         snoop_stat.heap_index,
18054         snoop_stat.objects_checked_count,
18055         snoop_stat.zero_ref_count,
18056         snoop_stat.objects_marked_count,
18057         snoop_stat.stolen_stack_count,
18058         snoop_stat.partial_stack_count,
18059         snoop_stat.normal_stack_count,
18060         snoop_stat.non_stack_count));
18061     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s",
18062         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
18063     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18064         snoop_stat.heap_index,
18065         snoop_stat.check_level_count,
18066         snoop_stat.busy_count,
18067         snoop_stat.interlocked_count,
18068         snoop_stat.partial_mark_parent_count,
18069         snoop_stat.stolen_or_pm_count,
18070         snoop_stat.stolen_entry_count,
18071         snoop_stat.pm_not_ready_count,
18072         snoop_stat.normal_count,
18073         snoop_stat.stack_bottom_clear_count));
18074 
18075     printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n",
18076         "heap", "check", "zero", "mark", "idle", "switch");
18077     printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
18078         snoop_stat.heap_index,
18079         snoop_stat.objects_checked_count,
18080         snoop_stat.zero_ref_count,
18081         snoop_stat.objects_marked_count,
18082         snoop_stat.stack_idle_count,
18083         snoop_stat.switch_to_thread_count);
18084     printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n",
18085         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18086     printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18087         snoop_stat.heap_index,
18088         snoop_stat.check_level_count,
18089         snoop_stat.busy_count,
18090         snoop_stat.interlocked_count,
18091         snoop_stat.partial_mark_parent_count,
18092         snoop_stat.stolen_or_pm_count,
18093         snoop_stat.stolen_entry_count,
18094         snoop_stat.pm_not_ready_count,
18095         snoop_stat.normal_count,
18096         snoop_stat.stack_bottom_clear_count);
18097 }
18098 #endif //SNOOP_STATS
18099 
18100 #ifdef HEAP_ANALYZE
18101 void
ha_mark_object_simple(uint8_t ** po THREAD_NUMBER_DCL)18102 gc_heap::ha_mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18103 {
18104     if (!internal_root_array)
18105     {
18106         internal_root_array = new (nothrow) uint8_t* [internal_root_array_length];
18107         if (!internal_root_array)
18108         {
18109             heap_analyze_success = FALSE;
18110         }
18111     }
18112 
18113     if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
18114     {
18115         size_t new_size = 2*internal_root_array_length;
18116 
18117         uint64_t available_physical = 0;
18118         get_memory_info (NULL, &available_physical);
18119         if (new_size > (size_t)(available_physical / 10))
18120         {
18121             heap_analyze_success = FALSE;
18122         }
18123         else
18124         {
18125             uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18126             if (tmp)
18127             {
18128                 memcpy (tmp, internal_root_array,
18129                         internal_root_array_length*sizeof (uint8_t*));
18130                 delete[] internal_root_array;
18131                 internal_root_array = tmp;
18132                 internal_root_array_length = new_size;
18133             }
18134             else
18135             {
18136                 heap_analyze_success = FALSE;
18137             }
18138         }
18139     }
18140 
18141     if (heap_analyze_success)
18142     {
18143         PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
18144 
18145         uint8_t* ref = (uint8_t*)po;
18146         if (!current_obj ||
18147             !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
18148         {
18149             gc_heap* hp = gc_heap::heap_of (ref);
18150             current_obj = hp->find_object (ref, hp->lowest_address);
18151             current_obj_size = size (current_obj);
18152 
18153             internal_root_array[internal_root_array_index] = current_obj;
18154             internal_root_array_index++;
18155         }
18156     }
18157 
18158     mark_object_simple (po THREAD_NUMBER_ARG);
18159 }
18160 #endif //HEAP_ANALYZE
18161 
18162 //this method assumes that *po is in the [low. high[ range
18163 void
mark_object_simple(uint8_t ** po THREAD_NUMBER_DCL)18164 gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18165 {
18166     uint8_t* o = *po;
18167 #ifdef MULTIPLE_HEAPS
18168 #else  //MULTIPLE_HEAPS
18169     const int thread = 0;
18170 #endif //MULTIPLE_HEAPS
18171     {
18172 #ifdef SNOOP_STATS
18173         snoop_stat.objects_checked_count++;
18174 #endif //SNOOP_STATS
18175 
18176         if (gc_mark1 (o))
18177         {
18178             m_boundary (o);
18179             size_t s = size (o);
18180             promoted_bytes (thread) += s;
18181             {
18182                 go_through_object_cl (method_table(o), o, s, poo,
18183                                         {
18184                                             uint8_t* oo = *poo;
18185                                             if (gc_mark (oo, gc_low, gc_high))
18186                                             {
18187                                                 m_boundary (oo);
18188                                                 size_t obj_size = size (oo);
18189                                                 promoted_bytes (thread) += obj_size;
18190 
18191                                                 if (contain_pointers_or_collectible (oo))
18192                                                     mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
18193                                             }
18194                                         }
18195                     );
18196             }
18197         }
18198     }
18199 }
18200 
18201 inline
mark_object(uint8_t * o THREAD_NUMBER_DCL)18202 uint8_t* gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL)
18203 {
18204     if ((o >= gc_low) && (o < gc_high))
18205         mark_object_simple (&o THREAD_NUMBER_ARG);
18206 #ifdef MULTIPLE_HEAPS
18207     else if (o)
18208     {
18209         //find the heap
18210         gc_heap* hp = heap_of (o);
18211         assert (hp);
18212         if ((o >= hp->gc_low) && (o < hp->gc_high))
18213             mark_object_simple (&o THREAD_NUMBER_ARG);
18214     }
18215 #endif //MULTIPLE_HEAPS
18216 
18217     return o;
18218 }
18219 
18220 #ifdef BACKGROUND_GC
18221 
background_mark_simple1(uint8_t * oo THREAD_NUMBER_DCL)18222 void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL)
18223 {
18224     uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
18225 
18226 #ifdef SORT_MARK_STACK
18227     uint8_t** sorted_tos = background_mark_stack_array;
18228 #endif //SORT_MARK_STACK
18229 
18230     background_mark_stack_tos = background_mark_stack_array;
18231 
18232     while (1)
18233     {
18234 #ifdef MULTIPLE_HEAPS
18235 #else  //MULTIPLE_HEAPS
18236         const int thread = 0;
18237 #endif //MULTIPLE_HEAPS
18238         if (oo)
18239         {
18240             size_t s = 0;
18241             if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18242             {
18243                 BOOL overflow_p = FALSE;
18244 
18245                 if (background_mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1))
18246                 {
18247                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18248                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18249                     if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
18250                     {
18251                         dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs",
18252                             heap_number,
18253                             (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
18254                             method_table(oo),
18255                             num_pointers));
18256 
18257                         bgc_overflow_count++;
18258                         overflow_p = TRUE;
18259                     }
18260                 }
18261 
18262                 if (overflow_p == FALSE)
18263                 {
18264                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18265 
18266                     go_through_object_cl (method_table(oo), oo, s, ppslot,
18267                     {
18268                         uint8_t* o = *ppslot;
18269                         Prefetch(o);
18270                         if (background_mark (o,
18271                                              background_saved_lowest_address,
18272                                              background_saved_highest_address))
18273                         {
18274                             //m_boundary (o);
18275                             size_t obj_size = size (o);
18276                             bpromoted_bytes (thread) += obj_size;
18277                             if (contain_pointers_or_collectible (o))
18278                             {
18279                                 *(background_mark_stack_tos++) = o;
18280 
18281                             }
18282                         }
18283                     }
18284                         );
18285                 }
18286                 else
18287                 {
18288                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18289                     background_min_overflow_address = min (background_min_overflow_address, oo);
18290                     background_max_overflow_address = max (background_max_overflow_address, oo);
18291                 }
18292             }
18293             else
18294             {
18295                 uint8_t* start = oo;
18296                 if ((size_t)oo & 1)
18297                 {
18298                     oo = (uint8_t*)((size_t)oo & ~1);
18299                     start = *(--background_mark_stack_tos);
18300                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18301                 }
18302 #ifdef COLLECTIBLE_CLASS
18303                 else
18304                 {
18305                     // If there's a class object, push it now. We are guaranteed to have the slot since
18306                     // we just popped one object off.
18307                     if (is_collectible (oo))
18308                     {
18309                         uint8_t* class_obj = get_class_object (oo);
18310                         if (background_mark (class_obj,
18311                                             background_saved_lowest_address,
18312                                             background_saved_highest_address))
18313                         {
18314                             size_t obj_size = size (class_obj);
18315                             bpromoted_bytes (thread) += obj_size;
18316 
18317                             *(background_mark_stack_tos++) = class_obj;
18318                         }
18319                     }
18320                 }
18321 #endif //COLLECTIBLE_CLASS
18322 
18323                 s = size (oo);
18324 
18325                 BOOL overflow_p = FALSE;
18326 
18327                 if (background_mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
18328                 {
18329                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18330                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18331 
18332                     dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id",
18333                         heap_number,
18334                         (size_t)(mark_stack_limit - background_mark_stack_tos),
18335                         oo,
18336                         method_table(oo),
18337                         start,
18338                         num_pointers));
18339 
18340                     bgc_overflow_count++;
18341                     overflow_p = TRUE;
18342                 }
18343                 if (overflow_p == FALSE)
18344                 {
18345                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18346 
18347                     //push the object and its current
18348                     uint8_t** place = background_mark_stack_tos++;
18349                     *(place) = start;
18350                     *(background_mark_stack_tos++) = (uint8_t*)((size_t)oo | 1);
18351 
18352                     int i = num_partial_refs;
18353 
18354                     go_through_object (method_table(oo), oo, s, ppslot,
18355                                        start, use_start, (oo + s),
18356                     {
18357                         uint8_t* o = *ppslot;
18358                         Prefetch(o);
18359 
18360                         if (background_mark (o,
18361                                             background_saved_lowest_address,
18362                                             background_saved_highest_address))
18363                         {
18364                             //m_boundary (o);
18365                             size_t obj_size = size (o);
18366                             bpromoted_bytes (thread) += obj_size;
18367                             if (contain_pointers_or_collectible (o))
18368                             {
18369                                 *(background_mark_stack_tos++) = o;
18370                                 if (--i == 0)
18371                                 {
18372                                     //update the start
18373                                     *place = (uint8_t*)(ppslot+1);
18374                                     goto more_to_do;
18375                                 }
18376 
18377                             }
18378                         }
18379 
18380                     }
18381                         );
18382                     //we are finished with this object
18383                     *place = 0;
18384                     *(place+1) = 0;
18385 
18386                 more_to_do:;
18387                 }
18388                 else
18389                 {
18390                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18391                     background_min_overflow_address = min (background_min_overflow_address, oo);
18392                     background_max_overflow_address = max (background_max_overflow_address, oo);
18393                 }
18394             }
18395         }
18396 #ifdef SORT_MARK_STACK
18397         if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18398         {
18399             rqsort1 (sorted_tos, background_mark_stack_tos-1);
18400             sorted_tos = background_mark_stack_tos-1;
18401         }
18402 #endif //SORT_MARK_STACK
18403 
18404         allow_fgc();
18405 
18406         if (!(background_mark_stack_tos == background_mark_stack_array))
18407         {
18408             oo = *(--background_mark_stack_tos);
18409 
18410 #ifdef SORT_MARK_STACK
18411             sorted_tos = (uint8_t**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
18412 #endif //SORT_MARK_STACK
18413         }
18414         else
18415             break;
18416     }
18417 
18418     assert (background_mark_stack_tos == background_mark_stack_array);
18419 
18420 
18421 }
18422 
18423 //this version is different than the foreground GC because
18424 //it can't keep pointers to the inside of an object
18425 //while calling background_mark_simple1. The object could be moved
18426 //by an intervening foreground gc.
18427 //this method assumes that *po is in the [low. high[ range
18428 void
background_mark_simple(uint8_t * o THREAD_NUMBER_DCL)18429 gc_heap::background_mark_simple (uint8_t* o THREAD_NUMBER_DCL)
18430 {
18431 #ifdef MULTIPLE_HEAPS
18432 #else  //MULTIPLE_HEAPS
18433     const int thread = 0;
18434 #endif //MULTIPLE_HEAPS
18435     {
18436         dprintf (3, ("bmarking %Ix", o));
18437 
18438         if (background_mark1 (o))
18439         {
18440             //m_boundary (o);
18441             size_t s = size (o);
18442             bpromoted_bytes (thread) += s;
18443 
18444             if (contain_pointers_or_collectible (o))
18445             {
18446                 background_mark_simple1 (o THREAD_NUMBER_ARG);
18447             }
18448         }
18449     }
18450 }
18451 
18452 inline
background_mark_object(uint8_t * o THREAD_NUMBER_DCL)18453 uint8_t* gc_heap::background_mark_object (uint8_t* o THREAD_NUMBER_DCL)
18454 {
18455     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
18456     {
18457         background_mark_simple (o THREAD_NUMBER_ARG);
18458     }
18459     else
18460     {
18461         if (o)
18462         {
18463             dprintf (3, ("or-%Ix", o));
18464         }
18465     }
18466     return o;
18467 }
18468 
background_verify_mark(Object * & object,ScanContext * sc,uint32_t flags)18469 void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, uint32_t flags)
18470 {
18471     UNREFERENCED_PARAMETER(sc);
18472 
18473     assert (settings.concurrent);
18474     uint8_t* o = (uint8_t*)object;
18475 
18476     gc_heap* hp = gc_heap::heap_of (o);
18477 #ifdef INTERIOR_POINTERS
18478     if (flags & GC_CALL_INTERIOR)
18479     {
18480         o = hp->find_object (o, background_saved_lowest_address);
18481     }
18482 #endif //INTERIOR_POINTERS
18483 
18484     if (!background_object_marked (o, FALSE))
18485     {
18486         FATAL_GC_ERROR();
18487     }
18488 }
18489 
background_promote(Object ** ppObject,ScanContext * sc,uint32_t flags)18490 void gc_heap::background_promote (Object** ppObject, ScanContext* sc, uint32_t flags)
18491 {
18492     UNREFERENCED_PARAMETER(sc);
18493     //in order to save space on the array, mark the object,
18494     //knowing that it will be visited later
18495     assert (settings.concurrent);
18496 
18497     THREAD_NUMBER_FROM_CONTEXT;
18498 #ifndef MULTIPLE_HEAPS
18499     const int thread = 0;
18500 #endif //!MULTIPLE_HEAPS
18501 
18502     uint8_t* o = (uint8_t*)*ppObject;
18503 
18504     if (o == 0)
18505         return;
18506 
18507 #ifdef DEBUG_DestroyedHandleValue
18508     // we can race with destroy handle during concurrent scan
18509     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
18510         return;
18511 #endif //DEBUG_DestroyedHandleValue
18512 
18513     HEAP_FROM_THREAD;
18514 
18515     gc_heap* hp = gc_heap::heap_of (o);
18516 
18517     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
18518     {
18519         return;
18520     }
18521 
18522 #ifdef INTERIOR_POINTERS
18523     if (flags & GC_CALL_INTERIOR)
18524     {
18525         o = hp->find_object (o, hp->background_saved_lowest_address);
18526         if (o == 0)
18527             return;
18528     }
18529 #endif //INTERIOR_POINTERS
18530 
18531 #ifdef FEATURE_CONSERVATIVE_GC
18532     // For conservative GC, a value on stack may point to middle of a free object.
18533     // In this case, we don't need to promote the pointer.
18534     if (g_pConfig->GetGCConservative() && ((CObjectHeader*)o)->IsFree())
18535     {
18536         return;
18537     }
18538 #endif //FEATURE_CONSERVATIVE_GC
18539 
18540 #ifdef _DEBUG
18541     ((CObjectHeader*)o)->Validate();
18542 #endif //_DEBUG
18543 
18544     dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
18545 
18546     //needs to be called before the marking because it is possible for a foreground
18547     //gc to take place during the mark and move the object
18548     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
18549 
18550     hpt->background_mark_simple (o THREAD_NUMBER_ARG);
18551 }
18552 
18553 //used by the ephemeral collection to scan the local background structures
18554 //containing references.
18555 void
scan_background_roots(promote_func * fn,int hn,ScanContext * pSC)18556 gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
18557 {
18558     ScanContext sc;
18559     if (pSC == 0)
18560         pSC = &sc;
18561 
18562     pSC->thread_number = hn;
18563 
18564 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
18565     pSC->pCurrentDomain = 0;
18566 #endif
18567 
18568     BOOL relocate_p = (fn == &GCHeap::Relocate);
18569 
18570     dprintf (3, ("Scanning background mark list"));
18571 
18572     //scan mark_list
18573     size_t mark_list_finger = 0;
18574     while (mark_list_finger < c_mark_list_index)
18575     {
18576         uint8_t** o = &c_mark_list [mark_list_finger];
18577         if (!relocate_p)
18578         {
18579             // We may not be able to calculate the size during relocate as POPO
18580             // may have written over the object.
18581             size_t s = size (*o);
18582             assert (Align (s) >= Align (min_obj_size));
18583             dprintf(3,("background root %Ix", (size_t)*o));
18584         }
18585         (*fn) ((Object**)o, pSC, 0);
18586         mark_list_finger++;
18587     }
18588 
18589     //scan the mark stack
18590     dprintf (3, ("Scanning background mark stack"));
18591 
18592     uint8_t** finger = background_mark_stack_array;
18593     while (finger < background_mark_stack_tos)
18594     {
18595         if ((finger + 1) < background_mark_stack_tos)
18596         {
18597             // We need to check for the partial mark case here.
18598             uint8_t* parent_obj = *(finger + 1);
18599             if ((size_t)parent_obj & 1)
18600             {
18601                 uint8_t* place = *finger;
18602                 size_t place_offset = 0;
18603                 uint8_t* real_parent_obj = (uint8_t*)((size_t)parent_obj & ~1);
18604 
18605                 if (relocate_p)
18606                 {
18607                     *(finger + 1) = real_parent_obj;
18608                     place_offset = place - real_parent_obj;
18609                     dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
18610                     (*fn) ((Object**)(finger + 1), pSC, 0);
18611                     real_parent_obj = *(finger + 1);
18612                     *finger = real_parent_obj + place_offset;
18613                     *(finger + 1) = (uint8_t*)((size_t)real_parent_obj | 1);
18614                     dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
18615                 }
18616                 else
18617                 {
18618                     uint8_t** temp = &real_parent_obj;
18619                     dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
18620                     (*fn) ((Object**)temp, pSC, 0);
18621                 }
18622 
18623                 finger += 2;
18624                 continue;
18625             }
18626         }
18627         dprintf(3,("background root %Ix", (size_t)*finger));
18628         (*fn) ((Object**)finger, pSC, 0);
18629         finger++;
18630     }
18631 }
18632 
18633 #endif //BACKGROUND_GC
18634 
18635 
fix_card_table()18636 void gc_heap::fix_card_table ()
18637 {
18638 #ifdef WRITE_WATCH
18639     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
18640 
18641     PREFIX_ASSUME(seg != NULL);
18642 
18643 #ifdef BACKGROUND_GC
18644     bool reset_watch_state = !!settings.concurrent;
18645 #else //BACKGROUND_GC
18646     bool reset_watch_state = false;
18647 #endif //BACKGROUND_GC
18648     BOOL small_object_segments = TRUE;
18649     while (1)
18650     {
18651         if (seg == 0)
18652         {
18653             if (small_object_segments)
18654             {
18655                 small_object_segments = FALSE;
18656                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
18657 
18658                 PREFIX_ASSUME(seg != NULL);
18659 
18660                 continue;
18661             }
18662             else
18663             {
18664                 break;
18665             }
18666         }
18667 
18668         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
18669         uint8_t* high_address =  align_on_page (
18670             (seg != ephemeral_heap_segment) ?
18671             heap_segment_allocated (seg) :
18672             generation_allocation_start (generation_of (0))
18673             );
18674         uintptr_t bcount = array_size;
18675         do
18676         {
18677             if(high_address <= base_address)
18678                 break;
18679 
18680             size_t region_size = high_address - base_address;
18681             assert (region_size > 0);
18682             dprintf (3,("Probing pages [%Ix, %Ix[", (size_t)base_address, (size_t)high_address));
18683 
18684 #ifdef TIME_WRITE_WATCH
18685             unsigned int time_start = GetCycleCount32();
18686 #endif //TIME_WRITE_WATCH
18687             get_write_watch_for_gc_heap(reset_watch_state, base_address, region_size,
18688                                         (void**)g_addresses,
18689                                         &bcount, true);
18690 
18691 #ifdef TIME_WRITE_WATCH
18692             unsigned int time_stop = GetCycleCount32();
18693             tot_cycles += time_stop - time_start;
18694             printf ("get_write_watch_for_gc_heap Duration: %d, total: %d\n",
18695                     time_stop - time_start, tot_cycles);
18696 #endif //TIME_WRITE_WATCH
18697 
18698             assert( ((card_size * card_word_width)&(OS_PAGE_SIZE-1))==0 );
18699             //printf ("%Ix written into\n", bcount);
18700             dprintf (3,("Found %Id pages written", bcount));
18701             for (unsigned  i = 0; i < bcount; i++)
18702             {
18703                 for (unsigned j = 0; j< (card_size*card_word_width)/OS_PAGE_SIZE; j++)
18704                 {
18705                     card_table [card_word (card_of (g_addresses [i]))+j] = ~0u;
18706                 }
18707                 dprintf (2,("Set Cards [%Ix:%Ix, %Ix:%Ix[",
18708                       card_of (g_addresses [i]), (size_t)g_addresses [i],
18709                       card_of (g_addresses [i]+OS_PAGE_SIZE), (size_t)g_addresses [i]+OS_PAGE_SIZE));
18710             }
18711             if (bcount >= array_size){
18712                 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
18713                 bcount = array_size;
18714             }
18715         } while (bcount >= array_size);
18716         seg = heap_segment_next_rw (seg);
18717     }
18718 
18719 #ifdef BACKGROUND_GC
18720     if (settings.concurrent)
18721     {
18722         //reset the ephemeral page allocated by generation_of (0)
18723         uint8_t* base_address =
18724             align_on_page (generation_allocation_start (generation_of (0)));
18725         size_t region_size =
18726             heap_segment_allocated (ephemeral_heap_segment) - base_address;
18727         reset_write_watch_for_gc_heap(base_address, region_size);
18728     }
18729 #endif //BACKGROUND_GC
18730 #endif //WRITE_WATCH
18731 }
18732 
18733 #ifdef BACKGROUND_GC
18734 inline
background_mark_through_object(uint8_t * oo THREAD_NUMBER_DCL)18735 void gc_heap::background_mark_through_object (uint8_t* oo THREAD_NUMBER_DCL)
18736 {
18737     if (contain_pointers (oo))
18738     {
18739         size_t total_refs = 0;
18740         size_t s = size (oo);
18741         go_through_object_nostart (method_table(oo), oo, s, po,
18742                           {
18743                             uint8_t* o = *po;
18744                             total_refs++;
18745                             background_mark_object (o THREAD_NUMBER_ARG);
18746                           }
18747             );
18748 
18749         dprintf (3,("Background marking through %Ix went through %Id refs",
18750                           (size_t)oo,
18751                            total_refs));
18752     }
18753 }
18754 
background_seg_end(heap_segment * seg,BOOL concurrent_p)18755 uint8_t* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
18756 {
18757     if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
18758     {
18759         // for now we stop at where gen1 started when we started processing
18760         return background_min_soh_overflow_address;
18761     }
18762     else
18763     {
18764         return heap_segment_allocated (seg);
18765     }
18766 }
18767 
background_first_overflow(uint8_t * min_add,heap_segment * seg,BOOL concurrent_p,BOOL small_object_p)18768 uint8_t* gc_heap::background_first_overflow (uint8_t* min_add,
18769                                           heap_segment* seg,
18770                                           BOOL concurrent_p,
18771                                           BOOL small_object_p)
18772 {
18773     uint8_t* o = 0;
18774 
18775     if (small_object_p)
18776     {
18777         if (in_range_for_segment (min_add, seg))
18778         {
18779             // min_add was the beginning of gen1 when we did the concurrent
18780             // overflow. Now we could be in a situation where min_add is
18781             // actually the same as allocated for that segment (because
18782             // we expanded heap), in which case we can not call
18783             // find first on this address or we will AV.
18784             if (min_add >= heap_segment_allocated (seg))
18785             {
18786                 return min_add;
18787             }
18788             else
18789             {
18790                 if (concurrent_p &&
18791                     ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
18792                 {
18793                     return background_min_soh_overflow_address;
18794                 }
18795                 else
18796                 {
18797                     o = find_first_object (min_add, heap_segment_mem (seg));
18798                     return o;
18799                 }
18800             }
18801         }
18802     }
18803 
18804     o = max (heap_segment_mem (seg), min_add);
18805     return o;
18806 }
18807 
background_process_mark_overflow_internal(int condemned_gen_number,uint8_t * min_add,uint8_t * max_add,BOOL concurrent_p)18808 void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
18809                                                          uint8_t* min_add, uint8_t* max_add,
18810                                                          BOOL concurrent_p)
18811 {
18812     if (concurrent_p)
18813     {
18814         current_bgc_state = bgc_overflow_soh;
18815     }
18816 
18817     size_t total_marked_objects = 0;
18818 
18819 #ifdef MULTIPLE_HEAPS
18820     int thread = heap_number;
18821 #endif //MULTIPLE_HEAPS
18822 
18823     exclusive_sync* loh_alloc_lock = 0;
18824 
18825     dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
18826 #ifdef MULTIPLE_HEAPS
18827     // We don't have each heap scan all heaps concurrently because we are worried about
18828     // multiple threads calling things like find_first_object.
18829     int h_start = (concurrent_p ? heap_number : 0);
18830     int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
18831     for (int hi = h_start; hi < h_end; hi++)
18832     {
18833         gc_heap*  hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
18834 
18835 #else
18836     {
18837         gc_heap*  hp = 0;
18838 
18839 #endif //MULTIPLE_HEAPS
18840         BOOL small_object_segments = TRUE;
18841         int align_const = get_alignment_constant (small_object_segments);
18842         generation* gen = hp->generation_of (condemned_gen_number);
18843         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
18844         PREFIX_ASSUME(seg != NULL);
18845         loh_alloc_lock = hp->bgc_alloc_lock;
18846 
18847         uint8_t* o = hp->background_first_overflow (min_add,
18848                                                     seg,
18849                                                     concurrent_p,
18850                                                     small_object_segments);
18851 
18852         while (1)
18853         {
18854             while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
18855             {
18856                 dprintf (3, ("considering %Ix", (size_t)o));
18857 
18858                 size_t s;
18859 
18860                 if (concurrent_p && !small_object_segments)
18861                 {
18862                     loh_alloc_lock->bgc_mark_set (o);
18863 
18864                     if (((CObjectHeader*)o)->IsFree())
18865                     {
18866                         s = unused_array_size (o);
18867                     }
18868                     else
18869                     {
18870                         s = size (o);
18871                     }
18872                 }
18873                 else
18874                 {
18875                     s = size (o);
18876                 }
18877 
18878                 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
18879                 {
18880                     total_marked_objects++;
18881                     go_through_object_cl (method_table(o), o, s, poo,
18882                                           uint8_t* oo = *poo;
18883                                           background_mark_object (oo THREAD_NUMBER_ARG);
18884                                          );
18885                 }
18886 
18887                 if (concurrent_p && !small_object_segments)
18888                 {
18889                     loh_alloc_lock->bgc_mark_done ();
18890                 }
18891 
18892                 o = o + Align (s, align_const);
18893 
18894                 if (concurrent_p)
18895                 {
18896                     allow_fgc();
18897                 }
18898             }
18899 
18900             dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)",
18901                 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
18902 
18903             if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
18904                 (seg = heap_segment_next_in_range (seg)) == 0)
18905             {
18906                 if (small_object_segments)
18907                 {
18908                     if (concurrent_p)
18909                     {
18910                         current_bgc_state = bgc_overflow_loh;
18911                     }
18912 
18913                     dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
18914                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
18915                     concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
18916                     total_marked_objects = 0;
18917                     small_object_segments = FALSE;
18918                     align_const = get_alignment_constant (small_object_segments);
18919                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
18920 
18921                     PREFIX_ASSUME(seg != NULL);
18922 
18923                     o = max (heap_segment_mem (seg), min_add);
18924                     continue;
18925                 }
18926                 else
18927                 {
18928                     dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
18929                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
18930                     break;
18931                 }
18932             }
18933             else
18934             {
18935                 o = hp->background_first_overflow (min_add,
18936                                                    seg,
18937                                                    concurrent_p,
18938                                                    small_object_segments);
18939                 continue;
18940             }
18941         }
18942     }
18943 }
18944 
18945 BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
18946 {
18947     BOOL grow_mark_array_p = TRUE;
18948 
18949     if (concurrent_p)
18950     {
18951         assert (!processed_soh_overflow_p);
18952 
18953         if ((background_max_overflow_address != 0) &&
18954             (background_min_overflow_address != MAX_PTR))
18955         {
18956             // We have overflow to process but we know we can't process the ephemeral generations
18957             // now (we actually could process till the current gen1 start but since we are going to
18958             // make overflow per segment, for now I'll just stop at the saved gen1 start.
18959             saved_overflow_ephemeral_seg = ephemeral_heap_segment;
18960             background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
18961             background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
18962         }
18963     }
18964     else
18965     {
18966         assert ((saved_overflow_ephemeral_seg == 0) ||
18967                 ((background_max_soh_overflow_address != 0) &&
18968                  (background_min_soh_overflow_address != MAX_PTR)));
18969 
18970         if (!processed_soh_overflow_p)
18971         {
18972             // if there was no more overflow we just need to process what we didn't process
18973             // on the saved ephemeral segment.
18974             if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
18975             {
18976                 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
18977                 grow_mark_array_p = FALSE;
18978             }
18979 
18980             background_min_overflow_address = min (background_min_overflow_address,
18981                                                 background_min_soh_overflow_address);
18982             background_max_overflow_address = max (background_max_overflow_address,
18983                                                 background_max_soh_overflow_address);
18984             processed_soh_overflow_p = TRUE;
18985         }
18986     }
18987 
18988     BOOL  overflow_p = FALSE;
18989 recheck:
18990     if ((! ((background_max_overflow_address == 0)) ||
18991          ! ((background_min_overflow_address == MAX_PTR))))
18992     {
18993         overflow_p = TRUE;
18994 
18995         if (grow_mark_array_p)
18996         {
18997             // Try to grow the array.
18998             size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
18999 
19000             if ((new_size * sizeof(mark)) > 100*1024)
19001             {
19002                 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19003 
19004                 new_size = min(new_max_size, new_size);
19005             }
19006 
19007             if ((background_mark_stack_array_length < new_size) &&
19008                 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
19009             {
19010                 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
19011 
19012                 uint8_t** tmp = new (nothrow) uint8_t* [new_size];
19013                 if (tmp)
19014                 {
19015                     delete background_mark_stack_array;
19016                     background_mark_stack_array = tmp;
19017                     background_mark_stack_array_length = new_size;
19018                     background_mark_stack_tos = background_mark_stack_array;
19019                 }
19020             }
19021         }
19022         else
19023         {
19024             grow_mark_array_p = TRUE;
19025         }
19026 
19027         uint8_t*  min_add = background_min_overflow_address;
19028         uint8_t*  max_add = background_max_overflow_address;
19029 
19030         background_max_overflow_address = 0;
19031         background_min_overflow_address = MAX_PTR;
19032 
19033         background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
19034         if (!concurrent_p)
19035         {
19036             goto recheck;
19037         }
19038     }
19039 
19040     return overflow_p;
19041 }
19042 
19043 #endif //BACKGROUND_GC
19044 
19045 inline
19046 void gc_heap::mark_through_object (uint8_t* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
19047 {
19048 #ifndef COLLECTIBLE_CLASS
19049     UNREFERENCED_PARAMETER(mark_class_object_p);
19050     BOOL to_mark_class_object = FALSE;
19051 #else //COLLECTIBLE_CLASS
19052     BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
19053 #endif //COLLECTIBLE_CLASS
19054     if (contain_pointers (oo) || to_mark_class_object)
19055     {
19056         dprintf(3,( "Marking through %Ix", (size_t)oo));
19057         size_t s = size (oo);
19058 
19059 #ifdef COLLECTIBLE_CLASS
19060         if (to_mark_class_object)
19061         {
19062             uint8_t* class_obj = get_class_object (oo);
19063             mark_object (class_obj THREAD_NUMBER_ARG);
19064         }
19065 #endif //COLLECTIBLE_CLASS
19066 
19067         if (contain_pointers (oo))
19068         {
19069             go_through_object_nostart (method_table(oo), oo, s, po,
19070                                 uint8_t* o = *po;
19071                                 mark_object (o THREAD_NUMBER_ARG);
19072                                 );
19073         }
19074     }
19075 }
19076 
19077 size_t gc_heap::get_total_heap_size()
19078 {
19079     size_t total_heap_size = 0;
19080 
19081 #ifdef MULTIPLE_HEAPS
19082     int hn = 0;
19083 
19084     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19085     {
19086         gc_heap* hp2 = gc_heap::g_heaps [hn];
19087         total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
19088     }
19089 #else
19090     total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
19091 #endif //MULTIPLE_HEAPS
19092 
19093     return total_heap_size;
19094 }
19095 
19096 size_t gc_heap::committed_size()
19097 {
19098     generation* gen = generation_of (max_generation);
19099     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19100     size_t total_committed = 0;
19101 
19102     while (1)
19103     {
19104         total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19105 
19106         seg = heap_segment_next (seg);
19107         if (!seg)
19108         {
19109             if (gen != large_object_generation)
19110             {
19111                 gen = generation_of (max_generation + 1);
19112                 seg = generation_start_segment (gen);
19113             }
19114             else
19115                 break;
19116         }
19117     }
19118 
19119     return total_committed;
19120 }
19121 
19122 size_t gc_heap::get_total_committed_size()
19123 {
19124     size_t total_committed = 0;
19125 
19126 #ifdef MULTIPLE_HEAPS
19127     int hn = 0;
19128 
19129     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19130     {
19131         gc_heap* hp = gc_heap::g_heaps [hn];
19132         total_committed += hp->committed_size();
19133     }
19134 #else
19135     total_committed = committed_size();
19136 #endif //MULTIPLE_HEAPS
19137 
19138     return total_committed;
19139 }
19140 
19141 void gc_heap::get_memory_info (uint32_t* memory_load,
19142                                uint64_t* available_physical,
19143                                uint64_t* available_page_file)
19144 {
19145     GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file);
19146 }
19147 
19148 void fire_mark_event (int heap_num, int root_type, size_t bytes_marked)
19149 {
19150     dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id", heap_num, root_type, bytes_marked));
19151     FireEtwGCMarkWithType (heap_num, GetClrInstanceId(), root_type, bytes_marked);
19152 }
19153 
19154 //returns TRUE is an overflow happened.
19155 BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
19156 {
19157     size_t last_promoted_bytes = promoted_bytes (heap_number);
19158     BOOL  overflow_p = FALSE;
19159 recheck:
19160     if ((! (max_overflow_address == 0) ||
19161          ! (min_overflow_address == MAX_PTR)))
19162     {
19163         overflow_p = TRUE;
19164         // Try to grow the array.
19165         size_t new_size =
19166             max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
19167 
19168         if ((new_size * sizeof(mark)) > 100*1024)
19169         {
19170             size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19171 
19172             new_size = min(new_max_size, new_size);
19173         }
19174 
19175         if ((mark_stack_array_length < new_size) &&
19176             ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
19177         {
19178             mark* tmp = new (nothrow) mark [new_size];
19179             if (tmp)
19180             {
19181                 delete mark_stack_array;
19182                 mark_stack_array = tmp;
19183                 mark_stack_array_length = new_size;
19184             }
19185         }
19186 
19187         uint8_t*  min_add = min_overflow_address;
19188         uint8_t*  max_add = max_overflow_address;
19189         max_overflow_address = 0;
19190         min_overflow_address = MAX_PTR;
19191         process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
19192         goto recheck;
19193     }
19194 
19195     size_t current_promoted_bytes = promoted_bytes (heap_number);
19196 
19197     if (current_promoted_bytes != last_promoted_bytes)
19198         fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes));
19199     return overflow_p;
19200 }
19201 
19202 void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
19203                                               uint8_t* min_add, uint8_t* max_add)
19204 {
19205 #ifdef MULTIPLE_HEAPS
19206     int thread = heap_number;
19207 #endif //MULTIPLE_HEAPS
19208     BOOL  full_p = (condemned_gen_number == max_generation);
19209 
19210         dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19211 #ifdef MULTIPLE_HEAPS
19212             for (int hi = 0; hi < n_heaps; hi++)
19213             {
19214                 gc_heap*  hp = g_heaps [(heap_number + hi) % n_heaps];
19215 
19216 #else
19217             {
19218                 gc_heap*  hp = 0;
19219 
19220 #endif //MULTIPLE_HEAPS
19221         BOOL small_object_segments = TRUE;
19222         int align_const = get_alignment_constant (small_object_segments);
19223         generation* gen = hp->generation_of (condemned_gen_number);
19224         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19225 
19226         PREFIX_ASSUME(seg != NULL);
19227         uint8_t*  o = max (heap_segment_mem (seg), min_add);
19228         while (1)
19229         {
19230             uint8_t*  end = heap_segment_allocated (seg);
19231 
19232             while ((o < end) && (o <= max_add))
19233             {
19234                 assert ((min_add <= o) && (max_add >= o));
19235                 dprintf (3, ("considering %Ix", (size_t)o));
19236                 if (marked (o))
19237                 {
19238                     mark_through_object (o, TRUE THREAD_NUMBER_ARG);
19239                 }
19240 
19241                 o = o + Align (size (o), align_const);
19242             }
19243 
19244             if (( seg = heap_segment_next_in_range (seg)) == 0)
19245             {
19246                 if (small_object_segments && full_p)
19247                 {
19248                     small_object_segments = FALSE;
19249                     align_const = get_alignment_constant (small_object_segments);
19250                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19251 
19252                     PREFIX_ASSUME(seg != NULL);
19253 
19254                     o = max (heap_segment_mem (seg), min_add);
19255                     continue;
19256                 }
19257                 else
19258                 {
19259                     break;
19260                 }
19261             }
19262             else
19263             {
19264                 o = max (heap_segment_mem (seg), min_add);
19265                 continue;
19266             }
19267         }
19268     }
19269 }
19270 
19271 // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
19272 // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
19273 // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
19274 // promotion scan multiple times.
19275 // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
19276 // also has the effect of processing any mark stack overflow.
19277 
19278 #ifdef MULTIPLE_HEAPS
19279 // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
19280 // worker threads synchronized. The algorithms are sufficiently divergent that we have different
19281 // implementations based on whether MULTIPLE_HEAPS is defined or not.
19282 //
19283 // Define some static variables used for synchronization in the method below. These should really be defined
19284 // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
19285 //
19286 // A note about the synchronization used within this method. Communication between the worker threads is
19287 // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
19288 // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
19289 // protection of a join.
19290 static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
19291 static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
19292 static VOLATILE(BOOL) s_fScanRequired;
19293 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19294 {
19295     // Whenever we call this method there may have been preceding object promotions. So set
19296     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19297     // based on the how the scanning proceeded).
19298     s_fUnscannedPromotions = TRUE;
19299 
19300     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
19301     // the state of this thread's portion of the dependent handle table. That's because promotions on other
19302     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
19303     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
19304     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
19305     // as all the others or they'll get out of step).
19306     while (true)
19307     {
19308         // The various worker threads are all currently racing in this code. We need to work out if at least
19309         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
19310         // dependent handle table when both of the following conditions apply:
19311         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
19312         //     object happens to correspond to a primary in one of our handles we might potentially have to
19313         //     promote the associated secondary).
19314         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
19315         //
19316         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
19317         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
19318         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
19319         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
19320         // follows below. Note that we can't read this outside of the join since on any iteration apart from
19321         // the first threads will be racing between reading this value and completing their previous
19322         // iteration's table scan.
19323         //
19324         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
19325         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
19326         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
19327         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
19328         // we're safely joined.
19329         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19330             s_fUnpromotedHandles = TRUE;
19331 
19332         // Synchronize all the threads so we can read our state variables safely. The shared variable
19333         // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
19334         // a single thread inside the join.
19335         gc_t_join.join(this, gc_join_scan_dependent_handles);
19336         if (gc_t_join.joined())
19337         {
19338             // We're synchronized so it's safe to read our shared state variables. We update another shared
19339             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
19340             // the loop. We scan if there has been at least one object promotion since last time and at least
19341             // one thread has a dependent handle table with a potential handle promotion possible.
19342             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
19343 
19344             // Reset our shared state variables (ready to be set again on this scan or with a good initial
19345             // value for the next call if we're terminating the loop).
19346             s_fUnscannedPromotions = FALSE;
19347             s_fUnpromotedHandles = FALSE;
19348 
19349             if (!s_fScanRequired)
19350             {
19351                 // We're terminating the loop. Perform any last operations that require single threaded access.
19352                 if (!initial_scan_p)
19353                 {
19354                     // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
19355                     // load balance if some of the heaps have an abnormally large workload.
19356                     uint8_t* all_heaps_max = 0;
19357                     uint8_t* all_heaps_min = MAX_PTR;
19358                     int i;
19359                     for (i = 0; i < n_heaps; i++)
19360                     {
19361                         if (all_heaps_max < g_heaps[i]->max_overflow_address)
19362                             all_heaps_max = g_heaps[i]->max_overflow_address;
19363                         if (all_heaps_min > g_heaps[i]->min_overflow_address)
19364                             all_heaps_min = g_heaps[i]->min_overflow_address;
19365                     }
19366                     for (i = 0; i < n_heaps; i++)
19367                     {
19368                         g_heaps[i]->max_overflow_address = all_heaps_max;
19369                         g_heaps[i]->min_overflow_address = all_heaps_min;
19370                     }
19371                 }
19372             }
19373 
19374             // Restart all the workers.
19375             dprintf(3, ("Starting all gc thread mark stack overflow processing"));
19376             gc_t_join.restart();
19377         }
19378 
19379         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19380         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
19381         // global flag indicating that at least one object promotion may have occurred (the usual comment
19382         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
19383         // exit the method since we unconditionally set this variable on method entry anyway).
19384         if (process_mark_overflow(condemned_gen_number))
19385             s_fUnscannedPromotions = TRUE;
19386 
19387         // If we decided that no scan was required we can terminate the loop now.
19388         if (!s_fScanRequired)
19389             break;
19390 
19391         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
19392         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
19393         // could miss noting the promotion of some primary objects).
19394         gc_t_join.join(this, gc_join_rescan_dependent_handles);
19395         if (gc_t_join.joined())
19396         {
19397             // Restart all the workers.
19398             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
19399             gc_t_join.restart();
19400         }
19401 
19402         // If the portion of the dependent handle table managed by this worker has handles that could still be
19403         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
19404         // could require a rescan of handles on this or other workers.
19405         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19406             if (GCScan::GcDhReScan(sc))
19407                 s_fUnscannedPromotions = TRUE;
19408     }
19409 }
19410 #else //MULTIPLE_HEAPS
19411 // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
19412 // threads synchronized.
19413 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19414 {
19415     UNREFERENCED_PARAMETER(initial_scan_p);
19416 
19417     // Whenever we call this method there may have been preceding object promotions. So set
19418     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19419     // based on the how the scanning proceeded).
19420     bool fUnscannedPromotions = true;
19421 
19422     // Loop until there are either no more dependent handles that can have their secondary promoted or we've
19423     // managed to perform a scan without promoting anything new.
19424     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
19425     {
19426         // On each iteration of the loop start with the assumption that no further objects have been promoted.
19427         fUnscannedPromotions = false;
19428 
19429         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19430         // being visible. If there was an overflow (process_mark_overflow returned true) then additional
19431         // objects now appear to be promoted and we should set the flag.
19432         if (process_mark_overflow(condemned_gen_number))
19433             fUnscannedPromotions = true;
19434 
19435         // Perform the scan and set the flag if any promotions resulted.
19436         if (GCScan::GcDhReScan(sc))
19437             fUnscannedPromotions = true;
19438     }
19439 
19440     // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
19441     // scan any handles at all this is the processing of overflows that may have occurred prior to this method
19442     // invocation).
19443     process_mark_overflow(condemned_gen_number);
19444 }
19445 #endif //MULTIPLE_HEAPS
19446 
19447 void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
19448 {
19449     assert (settings.concurrent == FALSE);
19450 
19451     ScanContext sc;
19452     sc.thread_number = heap_number;
19453     sc.promotion = TRUE;
19454     sc.concurrent = FALSE;
19455 
19456     dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
19457     BOOL  full_p = (condemned_gen_number == max_generation);
19458 
19459 #ifdef TIME_GC
19460     unsigned start;
19461     unsigned finish;
19462     start = GetCycleCount32();
19463 #endif //TIME_GC
19464 
19465     int gen_to_init = condemned_gen_number;
19466     if (condemned_gen_number == max_generation)
19467     {
19468         gen_to_init = max_generation + 1;
19469     }
19470     for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
19471     {
19472         dynamic_data* dd = dynamic_data_of (gen_idx);
19473         dd_begin_data_size (dd) = generation_size (gen_idx) -
19474                                    dd_fragmentation (dd) -
19475                                    Align (size (generation_allocation_start (generation_of (gen_idx))));
19476         dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
19477         dd_survived_size (dd) = 0;
19478         dd_pinned_survived_size (dd) = 0;
19479         dd_artificial_pinned_survived_size (dd) = 0;
19480         dd_added_pinned_size (dd) = 0;
19481 #ifdef SHORT_PLUGS
19482         dd_padding_size (dd) = 0;
19483 #endif //SHORT_PLUGS
19484 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
19485         dd_num_npinned_plugs (dd) = 0;
19486 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
19487     }
19488 
19489 #ifdef FFIND_OBJECT
19490     if (gen0_must_clear_bricks > 0)
19491         gen0_must_clear_bricks--;
19492 #endif //FFIND_OBJECT
19493 
19494     size_t last_promoted_bytes = 0;
19495 
19496     promoted_bytes (heap_number) = 0;
19497     reset_mark_stack();
19498 
19499 #ifdef SNOOP_STATS
19500     memset (&snoop_stat, 0, sizeof(snoop_stat));
19501     snoop_stat.heap_index = heap_number;
19502 #endif //SNOOP_STATS
19503 
19504 #ifdef MH_SC_MARK
19505     if (full_p)
19506     {
19507         //initialize the mark stack
19508         for (int i = 0; i < max_snoop_level; i++)
19509         {
19510             ((uint8_t**)(mark_stack_array))[i] = 0;
19511         }
19512 
19513         mark_stack_busy() = 1;
19514     }
19515 #endif //MH_SC_MARK
19516 
19517     static uint32_t num_sizedrefs = 0;
19518 
19519 #ifdef MH_SC_MARK
19520     static BOOL do_mark_steal_p = FALSE;
19521 #endif //MH_SC_MARK
19522 
19523 #ifdef MULTIPLE_HEAPS
19524     gc_t_join.join(this, gc_join_begin_mark_phase);
19525     if (gc_t_join.joined())
19526     {
19527 #endif //MULTIPLE_HEAPS
19528 
19529         num_sizedrefs = SystemDomain::System()->GetTotalNumSizedRefHandles();
19530 
19531 #ifdef MULTIPLE_HEAPS
19532 
19533 #ifdef MH_SC_MARK
19534         if (full_p)
19535         {
19536             size_t total_heap_size = get_total_heap_size();
19537 
19538             if (total_heap_size > (100 * 1024 * 1024))
19539             {
19540                 do_mark_steal_p = TRUE;
19541             }
19542             else
19543             {
19544                 do_mark_steal_p = FALSE;
19545             }
19546         }
19547         else
19548         {
19549             do_mark_steal_p = FALSE;
19550         }
19551 #endif //MH_SC_MARK
19552 
19553         gc_t_join.restart();
19554     }
19555 #endif //MULTIPLE_HEAPS
19556 
19557     {
19558 
19559 #ifdef MARK_LIST
19560         //set up the mark lists from g_mark_list
19561         assert (g_mark_list);
19562 #ifdef MULTIPLE_HEAPS
19563         mark_list = &g_mark_list [heap_number*mark_list_size];
19564 #else
19565         mark_list = g_mark_list;
19566 #endif //MULTIPLE_HEAPS
19567         //dont use the mark list for full gc
19568         //because multiple segments are more complex to handle and the list
19569         //is likely to overflow
19570         if (condemned_gen_number != max_generation)
19571             mark_list_end = &mark_list [mark_list_size-1];
19572         else
19573             mark_list_end = &mark_list [0];
19574         mark_list_index = &mark_list [0];
19575 #endif //MARK_LIST
19576 
19577         shigh = (uint8_t*) 0;
19578         slow  = MAX_PTR;
19579 
19580         //%type%  category = quote (mark);
19581 
19582         if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
19583         {
19584             GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19585             fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes));
19586             last_promoted_bytes = promoted_bytes (heap_number);
19587 
19588 #ifdef MULTIPLE_HEAPS
19589             gc_t_join.join(this, gc_join_scan_sizedref_done);
19590             if (gc_t_join.joined())
19591             {
19592                 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
19593                 gc_t_join.restart();
19594             }
19595 #endif //MULTIPLE_HEAPS
19596         }
19597 
19598         dprintf(3,("Marking Roots"));
19599 
19600         GCScan::GcScanRoots(GCHeap::Promote,
19601                                 condemned_gen_number, max_generation,
19602                                 &sc);
19603 
19604         fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes));
19605         last_promoted_bytes = promoted_bytes (heap_number);
19606 
19607 #ifdef BACKGROUND_GC
19608         if (recursive_gc_sync::background_running_p())
19609         {
19610             scan_background_roots (GCHeap::Promote, heap_number, &sc);
19611         }
19612 #endif //BACKGROUND_GC
19613 
19614 #ifdef FEATURE_PREMORTEM_FINALIZATION
19615         dprintf(3, ("Marking finalization data"));
19616         finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
19617 #endif // FEATURE_PREMORTEM_FINALIZATION
19618 
19619         fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes));
19620         last_promoted_bytes = promoted_bytes (heap_number);
19621 
19622 // MTHTS
19623         {
19624 
19625             dprintf(3,("Marking handle table"));
19626             GCScan::GcScanHandles(GCHeap::Promote,
19627                                       condemned_gen_number, max_generation,
19628                                       &sc);
19629             fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes));
19630             last_promoted_bytes = promoted_bytes (heap_number);
19631         }
19632 
19633 #ifdef TRACE_GC
19634         size_t promoted_before_cards = promoted_bytes (heap_number);
19635 #endif //TRACE_GC
19636 
19637         dprintf (3, ("before cards: %Id", promoted_before_cards));
19638         if (!full_p)
19639         {
19640 #ifdef CARD_BUNDLE
19641 #ifdef MULTIPLE_HEAPS
19642             if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
19643             {
19644 #endif //MULTIPLE_HEAPS
19645 
19646                 update_card_table_bundle ();
19647 
19648 #ifdef MULTIPLE_HEAPS
19649                 gc_t_join.r_restart();
19650             }
19651 #endif //MULTIPLE_HEAPS
19652 #endif //CARD_BUNDLE
19653 
19654             card_fn mark_object_fn = &gc_heap::mark_object_simple;
19655 #ifdef HEAP_ANALYZE
19656             heap_analyze_success = TRUE;
19657             if (heap_analyze_enabled)
19658             {
19659                 internal_root_array_index = 0;
19660                 current_obj = 0;
19661                 current_obj_size = 0;
19662                 mark_object_fn = &gc_heap::ha_mark_object_simple;
19663             }
19664 #endif //HEAP_ANALYZE
19665 
19666             dprintf(3,("Marking cross generation pointers"));
19667             mark_through_cards_for_segments (mark_object_fn, FALSE);
19668 
19669             dprintf(3,("Marking cross generation pointers for large objects"));
19670             mark_through_cards_for_large_objects (mark_object_fn, FALSE);
19671 
19672             dprintf (3, ("marked by cards: %Id",
19673                 (promoted_bytes (heap_number) - promoted_before_cards)));
19674             fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes));
19675             last_promoted_bytes = promoted_bytes (heap_number);
19676         }
19677     }
19678 
19679 #ifdef MH_SC_MARK
19680     if (do_mark_steal_p)
19681     {
19682         mark_steal();
19683     }
19684 #endif //MH_SC_MARK
19685 
19686     // Dependent handles need to be scanned with a special algorithm (see the header comment on
19687     // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
19688     // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
19689     // but in a common case (where there are no dependent handles that are due to be collected) it allows us
19690     // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
19691     // iterations if required and will also perform processing of any mark stack overflow once the dependent
19692     // handle table has been fully promoted.
19693     GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19694     scan_dependent_handles(condemned_gen_number, &sc, true);
19695 
19696 #ifdef MULTIPLE_HEAPS
19697     dprintf(3, ("Joining for short weak handle scan"));
19698     gc_t_join.join(this, gc_join_null_dead_short_weak);
19699     if (gc_t_join.joined())
19700 #endif //MULTIPLE_HEAPS
19701     {
19702 #ifdef HEAP_ANALYZE
19703         heap_analyze_enabled = FALSE;
19704         DACNotifyGcMarkEnd(condemned_gen_number);
19705 #endif // HEAP_ANALYZE
19706         GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
19707 
19708 #ifdef MULTIPLE_HEAPS
19709         if (!full_p)
19710         {
19711             // we used r_join and need to reinitialize states for it here.
19712             gc_t_join.r_init();
19713         }
19714 
19715         //start all threads on the roots.
19716         dprintf(3, ("Starting all gc thread for short weak handle scan"));
19717         gc_t_join.restart();
19718 #endif //MULTIPLE_HEAPS
19719 
19720     }
19721 
19722     // null out the target of short weakref that were not promoted.
19723     GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
19724 
19725 // MTHTS: keep by single thread
19726 #ifdef MULTIPLE_HEAPS
19727     dprintf(3, ("Joining for finalization"));
19728     gc_t_join.join(this, gc_join_scan_finalization);
19729     if (gc_t_join.joined())
19730 #endif //MULTIPLE_HEAPS
19731 
19732     {
19733 #ifdef MULTIPLE_HEAPS
19734         //start all threads on the roots.
19735         dprintf(3, ("Starting all gc thread for Finalization"));
19736         gc_t_join.restart();
19737 #endif //MULTIPLE_HEAPS
19738     }
19739 
19740     //Handle finalization.
19741     size_t promoted_bytes_live = promoted_bytes (heap_number);
19742 
19743 #ifdef FEATURE_PREMORTEM_FINALIZATION
19744     dprintf (3, ("Finalize marking"));
19745     finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
19746 
19747     GCToEEInterface::DiagWalkFReachableObjects(__this);
19748 #endif // FEATURE_PREMORTEM_FINALIZATION
19749 
19750     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
19751     // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
19752     scan_dependent_handles(condemned_gen_number, &sc, false);
19753 
19754 #ifdef MULTIPLE_HEAPS
19755     dprintf(3, ("Joining for weak pointer deletion"));
19756     gc_t_join.join(this, gc_join_null_dead_long_weak);
19757     if (gc_t_join.joined())
19758     {
19759         //start all threads on the roots.
19760         dprintf(3, ("Starting all gc thread for weak pointer deletion"));
19761         gc_t_join.restart();
19762     }
19763 #endif //MULTIPLE_HEAPS
19764 
19765     // null out the target of long weakref that were not promoted.
19766     GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19767 
19768 // MTHTS: keep by single thread
19769 #ifdef MULTIPLE_HEAPS
19770 #ifdef MARK_LIST
19771 #ifdef PARALLEL_MARK_LIST_SORT
19772 //    unsigned long start = GetCycleCount32();
19773     sort_mark_list();
19774 //    printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
19775 #endif //PARALLEL_MARK_LIST_SORT
19776 #endif //MARK_LIST
19777 
19778     dprintf (3, ("Joining for sync block cache entry scanning"));
19779     gc_t_join.join(this, gc_join_null_dead_syncblk);
19780     if (gc_t_join.joined())
19781 #endif //MULTIPLE_HEAPS
19782     {
19783         // scan for deleted entries in the syncblk cache
19784         GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
19785 
19786 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
19787         if (g_fEnableARM)
19788         {
19789             size_t promoted_all_heaps = 0;
19790 #ifdef MULTIPLE_HEAPS
19791             for (int i = 0; i < n_heaps; i++)
19792             {
19793                 promoted_all_heaps += promoted_bytes (i);
19794             }
19795 #else
19796             promoted_all_heaps = promoted_bytes (heap_number);
19797 #endif //MULTIPLE_HEAPS
19798             SystemDomain::RecordTotalSurvivedBytes (promoted_all_heaps);
19799         }
19800 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
19801 
19802 #ifdef MULTIPLE_HEAPS
19803 
19804 #ifdef MARK_LIST
19805 #ifndef PARALLEL_MARK_LIST_SORT
19806         //compact g_mark_list and sort it.
19807         combine_mark_lists();
19808 #endif //PARALLEL_MARK_LIST_SORT
19809 #endif //MARK_LIST
19810 
19811         //decide on promotion
19812         if (!settings.promotion)
19813         {
19814             size_t m = 0;
19815             for (int n = 0; n <= condemned_gen_number;n++)
19816             {
19817                 m +=  (size_t)(dd_min_gc_size (dynamic_data_of (n))*(n+1)*0.1);
19818             }
19819 
19820             for (int i = 0; i < n_heaps; i++)
19821             {
19822                 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
19823                                                                      max_generation));
19824                 size_t older_gen_size = (dd_current_size (dd) +
19825                                          (dd_desired_allocation (dd) -
19826                                          dd_new_allocation (dd)));
19827 
19828                 if ((m > (older_gen_size)) ||
19829                     (promoted_bytes (i) > m))
19830                 {
19831                     settings.promotion = TRUE;
19832                 }
19833             }
19834         }
19835 
19836 #ifdef SNOOP_STATS
19837         if (do_mark_steal_p)
19838         {
19839             size_t objects_checked_count = 0;
19840             size_t zero_ref_count = 0;
19841             size_t objects_marked_count = 0;
19842             size_t check_level_count = 0;
19843             size_t busy_count = 0;
19844             size_t interlocked_count = 0;
19845             size_t partial_mark_parent_count = 0;
19846             size_t stolen_or_pm_count = 0;
19847             size_t stolen_entry_count = 0;
19848             size_t pm_not_ready_count = 0;
19849             size_t normal_count = 0;
19850             size_t stack_bottom_clear_count = 0;
19851 
19852             for (int i = 0; i < n_heaps; i++)
19853             {
19854                 gc_heap* hp = g_heaps[i];
19855                 hp->print_snoop_stat();
19856                 objects_checked_count += hp->snoop_stat.objects_checked_count;
19857                 zero_ref_count += hp->snoop_stat.zero_ref_count;
19858                 objects_marked_count += hp->snoop_stat.objects_marked_count;
19859                 check_level_count += hp->snoop_stat.check_level_count;
19860                 busy_count += hp->snoop_stat.busy_count;
19861                 interlocked_count += hp->snoop_stat.interlocked_count;
19862                 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
19863                 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
19864                 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
19865                 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
19866                 normal_count += hp->snoop_stat.normal_count;
19867                 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
19868             }
19869 
19870             fflush (stdout);
19871 
19872             printf ("-------total stats-------\n");
19873             printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n",
19874                 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
19875             printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
19876                 objects_checked_count,
19877                 zero_ref_count,
19878                 objects_marked_count,
19879                 check_level_count,
19880                 busy_count,
19881                 interlocked_count,
19882                 partial_mark_parent_count,
19883                 stolen_or_pm_count,
19884                 stolen_entry_count,
19885                 pm_not_ready_count,
19886                 normal_count,
19887                 stack_bottom_clear_count);
19888         }
19889 #endif //SNOOP_STATS
19890 
19891         //start all threads.
19892         dprintf(3, ("Starting all threads for end of mark phase"));
19893         gc_t_join.restart();
19894 #else //MULTIPLE_HEAPS
19895 
19896         //decide on promotion
19897         if (!settings.promotion)
19898         {
19899             size_t m = 0;
19900             for (int n = 0; n <= condemned_gen_number;n++)
19901             {
19902                 m +=  (size_t)(dd_min_gc_size (dynamic_data_of (n))*(n+1)*0.06);
19903             }
19904             dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
19905                                                      max_generation));
19906             size_t older_gen_size = (dd_current_size (dd) +
19907                                      (dd_desired_allocation (dd) -
19908                                      dd_new_allocation (dd)));
19909 
19910             dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
19911                          m, promoted_bytes (heap_number), older_gen_size));
19912 
19913             if ((m > older_gen_size) ||
19914                     (promoted_bytes (heap_number) > m))
19915             {
19916                 settings.promotion = TRUE;
19917             }
19918         }
19919 
19920 #endif //MULTIPLE_HEAPS
19921     }
19922 
19923 #ifdef MULTIPLE_HEAPS
19924 #ifdef MARK_LIST
19925 #ifdef PARALLEL_MARK_LIST_SORT
19926 //    start = GetCycleCount32();
19927     merge_mark_lists();
19928 //    printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
19929 #endif //PARALLEL_MARK_LIST_SORT
19930 #endif //MARK_LIST
19931 #endif //MULTIPLE_HEAPS
19932 
19933 #ifdef BACKGROUND_GC
19934     total_promoted_bytes = promoted_bytes (heap_number);
19935 #endif //BACKGROUND_GC
19936 
19937     promoted_bytes (heap_number) -= promoted_bytes_live;
19938 
19939 #ifdef TIME_GC
19940         finish = GetCycleCount32();
19941         mark_time = finish - start;
19942 #endif //TIME_GC
19943 
19944     dprintf(2,("---- End of mark phase ----"));
19945 }
19946 
19947 inline
19948 void gc_heap::pin_object (uint8_t* o, uint8_t** ppObject, uint8_t* low, uint8_t* high)
19949 {
19950     dprintf (3, ("Pinning %Ix", (size_t)o));
19951     if ((o >= low) && (o < high))
19952     {
19953         dprintf(3,("^%Ix^", (size_t)o));
19954         set_pinned (o);
19955 
19956 #ifdef FEATURE_EVENT_TRACE
19957         if(EventEnabledPinObjectAtGCTime())
19958         {
19959             fire_etw_pin_object_event(o, ppObject);
19960         }
19961 #endif // FEATURE_EVENT_TRACE
19962 
19963 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
19964         num_pinned_objects++;
19965 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
19966     }
19967 }
19968 
19969 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
19970 size_t gc_heap::get_total_pinned_objects()
19971 {
19972 #ifdef MULTIPLE_HEAPS
19973     size_t total_num_pinned_objects = 0;
19974     for (int i = 0; i < gc_heap::n_heaps; i++)
19975     {
19976         gc_heap* hp = gc_heap::g_heaps[i];
19977         total_num_pinned_objects += hp->num_pinned_objects;
19978     }
19979     return total_num_pinned_objects;
19980 #else //MULTIPLE_HEAPS
19981     return num_pinned_objects;
19982 #endif //MULTIPLE_HEAPS
19983 }
19984 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
19985 
19986 void gc_heap::reset_mark_stack ()
19987 {
19988     reset_pinned_queue();
19989     max_overflow_address = 0;
19990     min_overflow_address = MAX_PTR;
19991 }
19992 
19993 #ifdef FEATURE_STRUCTALIGN
19994 //
19995 // The word with left child, right child, and align info is laid out as follows:
19996 //
19997 //      |   upper short word   |   lower short word   |
19998 //      |<------------> <----->|<------------> <----->|
19999 //      |  left child   info hi| right child   info lo|
20000 // x86: |    10 bits     6 bits|   10 bits      6 bits|
20001 //
20002 // where left/right child are signed values and concat(info hi, info lo) is unsigned.
20003 //
20004 // The "align info" encodes two numbers: the required alignment (a power of two)
20005 // and the misalignment (the number of machine words the destination address needs
20006 // to be adjusted by to provide alignment - so this number is always smaller than
20007 // the required alignment).  Thus, the two can be represented as the "logical or"
20008 // of the two numbers.  Note that the actual pad is computed from the misalignment
20009 // by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
20010 //
20011 
20012 // The number of bits in a brick.
20013 #if defined (_TARGET_AMD64_)
20014 #define brick_bits (12)
20015 #else
20016 #define brick_bits (11)
20017 #endif //_TARGET_AMD64_
20018 C_ASSERT(brick_size == (1 << brick_bits));
20019 
20020 // The number of bits needed to represent the offset to a child node.
20021 // "brick_bits + 1" allows us to represent a signed offset within a brick.
20022 #define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
20023 
20024 // The number of bits in each of the pad hi, pad lo fields.
20025 #define pad_bits (sizeof(short) * 8 - child_bits)
20026 
20027 #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
20028 #define pad_mask ((1 << pad_bits) - 1)
20029 #define pad_from_short(w) ((size_t)(w) & pad_mask)
20030 #else // FEATURE_STRUCTALIGN
20031 #define child_from_short(w) (w)
20032 #endif // FEATURE_STRUCTALIGN
20033 
20034 inline
20035 short node_left_child(uint8_t* node)
20036 {
20037     return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20038 }
20039 
20040 inline
20041 void set_node_left_child(uint8_t* node, ptrdiff_t val)
20042 {
20043     assert (val > -(ptrdiff_t)brick_size);
20044     assert (val < (ptrdiff_t)brick_size);
20045     assert (Aligned (val));
20046 #ifdef FEATURE_STRUCTALIGN
20047     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20048     ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20049 #else // FEATURE_STRUCTALIGN
20050     ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
20051 #endif // FEATURE_STRUCTALIGN
20052     assert (node_left_child (node) == val);
20053 }
20054 
20055 inline
20056 short node_right_child(uint8_t* node)
20057 {
20058     return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20059 }
20060 
20061 inline
20062 void set_node_right_child(uint8_t* node, ptrdiff_t val)
20063 {
20064     assert (val > -(ptrdiff_t)brick_size);
20065     assert (val < (ptrdiff_t)brick_size);
20066     assert (Aligned (val));
20067 #ifdef FEATURE_STRUCTALIGN
20068     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20069     ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20070 #else // FEATURE_STRUCTALIGN
20071     ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
20072 #endif // FEATURE_STRUCTALIGN
20073     assert (node_right_child (node) == val);
20074 }
20075 
20076 #ifdef FEATURE_STRUCTALIGN
20077 void node_aligninfo (uint8_t* node, int& requiredAlignment, ptrdiff_t& pad)
20078 {
20079     // Extract the single-number aligninfo from the fields.
20080     short left = ((plug_and_pair*)node)[-1].m_pair.left;
20081     short right = ((plug_and_pair*)node)[-1].m_pair.right;
20082     ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
20083     ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
20084 
20085     // Replicate the topmost bit into all lower bits.
20086     ptrdiff_t x = aligninfo;
20087     x |= x >> 8;
20088     x |= x >> 4;
20089     x |= x >> 2;
20090     x |= x >> 1;
20091 
20092     // Clear all bits but the highest.
20093     requiredAlignment = (int)(x ^ (x >> 1));
20094     pad = aligninfo - requiredAlignment;
20095     pad += AdjustmentForMinPadSize(pad, requiredAlignment);
20096 }
20097 
20098 inline
20099 ptrdiff_t node_alignpad (uint8_t* node)
20100 {
20101     int requiredAlignment;
20102     ptrdiff_t alignpad;
20103     node_aligninfo (node, requiredAlignment, alignpad);
20104     return alignpad;
20105 }
20106 
20107 void clear_node_aligninfo (uint8_t* node)
20108 {
20109     ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
20110     ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
20111 }
20112 
20113 void set_node_aligninfo (uint8_t* node, int requiredAlignment, ptrdiff_t pad)
20114 {
20115     // Encode the alignment requirement and alignment offset as a single number
20116     // as described above.
20117     ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
20118     assert (Aligned (aligninfo));
20119     ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
20120     assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
20121 
20122     ptrdiff_t hi = aligninfo_shifted >> pad_bits;
20123     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
20124     ((plug_and_pair*)node)[-1].m_pair.left |= hi;
20125 
20126     ptrdiff_t lo = aligninfo_shifted & pad_mask;
20127     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
20128     ((plug_and_pair*)node)[-1].m_pair.right |= lo;
20129 
20130 #ifdef _DEBUG
20131     int requiredAlignment2;
20132     ptrdiff_t pad2;
20133     node_aligninfo (node, requiredAlignment2, pad2);
20134     assert (requiredAlignment == requiredAlignment2);
20135     assert (pad == pad2);
20136 #endif // _DEBUG
20137 }
20138 #endif // FEATURE_STRUCTALIGN
20139 
20140 inline
20141 void loh_set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20142 {
20143     ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
20144     *place = val;
20145 }
20146 
20147 inline
20148 ptrdiff_t loh_node_relocation_distance(uint8_t* node)
20149 {
20150     return (((loh_obj_and_pad*)node)[-1].reloc);
20151 }
20152 
20153 inline
20154 ptrdiff_t node_relocation_distance (uint8_t* node)
20155 {
20156     return (((plug_and_reloc*)(node))[-1].reloc & ~3);
20157 }
20158 
20159 inline
20160 void set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20161 {
20162     assert (val == (val & ~3));
20163     ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
20164     //clear the left bit and the relocation field
20165     *place &= 1;
20166     // store the value
20167     *place |= val;
20168 }
20169 
20170 #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
20171 
20172 #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
20173 
20174 #ifndef FEATURE_STRUCTALIGN
20175 void set_node_realigned(uint8_t* node)
20176 {
20177     ((plug_and_reloc*)(node))[-1].reloc |= 1;
20178 }
20179 
20180 void clear_node_realigned(uint8_t* node)
20181 {
20182 #ifdef RESPECT_LARGE_ALIGNMENT
20183     ((plug_and_reloc*)(node))[-1].reloc &= ~1;
20184 #else
20185     UNREFERENCED_PARAMETER(node);
20186 #endif //RESPECT_LARGE_ALIGNMENT
20187 }
20188 #endif // FEATURE_STRUCTALIGN
20189 
20190 inline
20191 size_t  node_gap_size (uint8_t* node)
20192 {
20193     return ((plug_and_gap *)node)[-1].gap;
20194 }
20195 
20196 void set_gap_size (uint8_t* node, size_t size)
20197 {
20198     assert (Aligned (size));
20199 
20200     // clear the 2 uint32_t used by the node.
20201     ((plug_and_gap *)node)[-1].reloc = 0;
20202     ((plug_and_gap *)node)[-1].lr =0;
20203     ((plug_and_gap *)node)[-1].gap = size;
20204 
20205     assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
20206 
20207 }
20208 
20209 uint8_t* gc_heap::insert_node (uint8_t* new_node, size_t sequence_number,
20210                    uint8_t* tree, uint8_t* last_node)
20211 {
20212     dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
20213                  (size_t)new_node, brick_of(new_node),
20214                  (size_t)tree, brick_of(tree),
20215                  (size_t)last_node, brick_of(last_node),
20216                  sequence_number));
20217     if (power_of_two_p (sequence_number))
20218     {
20219         set_node_left_child (new_node, (tree - new_node));
20220         dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
20221         tree = new_node;
20222     }
20223     else
20224     {
20225         if (oddp (sequence_number))
20226         {
20227             set_node_right_child (last_node, (new_node - last_node));
20228             dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
20229         }
20230         else
20231         {
20232             uint8_t*  earlier_node = tree;
20233             size_t imax = logcount(sequence_number) - 2;
20234             for (size_t i = 0; i != imax; i++)
20235             {
20236                 earlier_node = earlier_node + node_right_child (earlier_node);
20237             }
20238             int tmp_offset = node_right_child (earlier_node);
20239             assert (tmp_offset); // should never be empty
20240             set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
20241             set_node_right_child (earlier_node, (new_node - earlier_node));
20242 
20243             dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix",
20244                 new_node, ((earlier_node + tmp_offset ) - new_node),
20245                 earlier_node, (new_node - earlier_node)));
20246         }
20247     }
20248     return tree;
20249 }
20250 
20251 size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick,
20252                                     uint8_t* x, uint8_t* plug_end)
20253 {
20254     dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
20255         tree, current_brick, x, plug_end));
20256 
20257     if (tree != NULL)
20258     {
20259         dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix",
20260             current_brick, (size_t)(tree - brick_address (current_brick)), tree));
20261         set_brick (current_brick, (tree - brick_address (current_brick)));
20262     }
20263     else
20264     {
20265         dprintf (3, ("b- %Ix->-1", current_brick));
20266         set_brick (current_brick, -1);
20267     }
20268     size_t  b = 1 + current_brick;
20269     ptrdiff_t  offset = 0;
20270     size_t last_br = brick_of (plug_end-1);
20271     current_brick = brick_of (x-1);
20272     dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
20273     while (b <= current_brick)
20274     {
20275         if (b <= last_br)
20276         {
20277             set_brick (b, --offset);
20278         }
20279         else
20280         {
20281             set_brick (b,-1);
20282         }
20283         b++;
20284     }
20285     return brick_of (x);
20286 }
20287 
20288 void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate)
20289 {
20290 #ifdef BIT64
20291     // We should never demote big plugs to gen0.
20292     if (gen == youngest_generation)
20293     {
20294         heap_segment* seg = ephemeral_heap_segment;
20295         size_t mark_stack_large_bos = mark_stack_bos;
20296         size_t large_plug_pos = 0;
20297         while (mark_stack_large_bos < mark_stack_tos)
20298         {
20299             if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
20300             {
20301                 while (mark_stack_bos <= mark_stack_large_bos)
20302                 {
20303                     size_t entry = deque_pinned_plug();
20304                     size_t len = pinned_len (pinned_plug_of (entry));
20305                     uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20306                     if (len > demotion_plug_len_th)
20307                     {
20308                         dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
20309                     }
20310                     pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
20311                     assert(mark_stack_array[entry].len == 0 ||
20312                             mark_stack_array[entry].len >= Align(min_obj_size));
20313                     generation_allocation_pointer (consing_gen) = plug + len;
20314                     generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
20315                     set_allocator_next_pin (consing_gen);
20316                 }
20317             }
20318 
20319             mark_stack_large_bos++;
20320         }
20321     }
20322 #endif // BIT64
20323 
20324     generation_plan_allocation_start (gen) =
20325         allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
20326     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20327     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20328     if (next_plug_to_allocate)
20329     {
20330         size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
20331         if (allocation_left > dist_to_next_plug)
20332         {
20333             allocation_left = dist_to_next_plug;
20334         }
20335     }
20336     if (allocation_left < Align (min_obj_size))
20337     {
20338         generation_plan_allocation_start_size (gen) += allocation_left;
20339         generation_allocation_pointer (consing_gen) += allocation_left;
20340     }
20341 
20342     dprintf (1, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)", gen->gen_num,
20343         generation_plan_allocation_start (gen),
20344         generation_plan_allocation_start_size (gen),
20345         generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen),
20346         next_plug_to_allocate));
20347 }
20348 
20349 void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
20350 {
20351     BOOL adjacentp = FALSE;
20352 
20353     generation_plan_allocation_start (gen) =
20354         allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0,
20355 #ifdef SHORT_PLUGS
20356                                    FALSE, NULL,
20357 #endif //SHORT_PLUGS
20358                                    FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
20359 
20360     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20361     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20362     if ((allocation_left < Align (min_obj_size)) &&
20363          (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
20364     {
20365         generation_plan_allocation_start_size (gen) += allocation_left;
20366         generation_allocation_pointer (consing_gen) += allocation_left;
20367     }
20368 
20369     dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num,
20370         generation_plan_allocation_start (consing_gen),
20371         generation_allocation_pointer (consing_gen),
20372         generation_allocation_limit (consing_gen)));
20373 }
20374 
20375 void gc_heap::plan_generation_starts (generation*& consing_gen)
20376 {
20377     //make sure that every generation has a planned allocation start
20378     int  gen_number = settings.condemned_generation;
20379     while (gen_number >= 0)
20380     {
20381         if (gen_number < max_generation)
20382         {
20383             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20384         }
20385         generation* gen = generation_of (gen_number);
20386         if (0 == generation_plan_allocation_start (gen))
20387         {
20388             plan_generation_start (gen, consing_gen, 0);
20389             assert (generation_plan_allocation_start (gen));
20390         }
20391         gen_number--;
20392     }
20393     // now we know the planned allocation size
20394     heap_segment_plan_allocated (ephemeral_heap_segment) =
20395         generation_allocation_pointer (consing_gen);
20396 }
20397 
20398 void gc_heap::advance_pins_for_demotion (generation* gen)
20399 {
20400     uint8_t* original_youngest_start = generation_allocation_start (youngest_generation);
20401     heap_segment* seg = ephemeral_heap_segment;
20402 
20403     if ((!(pinned_plug_que_empty_p())))
20404     {
20405         size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
20406         size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
20407         size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
20408         float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
20409         float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
20410         if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
20411         {
20412             while (!pinned_plug_que_empty_p() &&
20413                     (pinned_plug (oldest_pin()) < original_youngest_start))
20414             {
20415                 size_t entry = deque_pinned_plug();
20416                 size_t len = pinned_len (pinned_plug_of (entry));
20417                 uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20418                 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
20419                 assert(mark_stack_array[entry].len == 0 ||
20420                         mark_stack_array[entry].len >= Align(min_obj_size));
20421                 generation_allocation_pointer (gen) = plug + len;
20422                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20423                 set_allocator_next_pin (gen);
20424 
20425                 //Add the size of the pinned plug to the right pinned allocations
20426                 //find out which gen this pinned plug came from
20427                 int frgn = object_gennum (plug);
20428                 if ((frgn != (int)max_generation) && settings.promotion)
20429                 {
20430                     int togn = object_gennum_plan (plug);
20431                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20432                     if (frgn < togn)
20433                     {
20434                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
20435                     }
20436                 }
20437 
20438                 dprintf (2, ("skipping gap %d, pin %Ix (%Id)",
20439                     pinned_len (pinned_plug_of (entry)), plug, len));
20440             }
20441         }
20442         dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d",
20443             gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
20444     }
20445 }
20446 
20447 void gc_heap::process_ephemeral_boundaries (uint8_t* x,
20448                                             int& active_new_gen_number,
20449                                             int& active_old_gen_number,
20450                                             generation*& consing_gen,
20451                                             BOOL& allocate_in_condemned)
20452 {
20453 retry:
20454     if ((active_old_gen_number > 0) &&
20455         (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
20456     {
20457         dprintf (1, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
20458 
20459         if (!pinned_plug_que_empty_p())
20460         {
20461             dprintf (1, ("oldest pin: %Ix(%Id)",
20462                 pinned_plug (oldest_pin()),
20463                 (x - pinned_plug (oldest_pin()))));
20464         }
20465 
20466         if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
20467         {
20468             active_new_gen_number--;
20469         }
20470 
20471         active_old_gen_number--;
20472         assert ((!settings.promotion) || (active_new_gen_number>0));
20473 
20474         if (active_new_gen_number == (max_generation - 1))
20475         {
20476 #ifdef FREE_USAGE_STATS
20477             if (settings.condemned_generation == max_generation)
20478             {
20479                 // We need to do this before we skip the rest of the pinned plugs.
20480                 generation* gen_2 = generation_of (max_generation);
20481                 generation* gen_1 = generation_of (max_generation - 1);
20482 
20483                 size_t total_num_pinned_free_spaces_left = 0;
20484 
20485                 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
20486                 for (int j = 0; j < NUM_GEN_POWER2; j++)
20487                 {
20488                     dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)",
20489                         heap_number,
20490                         settings.gc_index,
20491                         (j + 10),
20492                         gen_2->gen_current_pinned_free_spaces[j],
20493                         gen_2->gen_plugs[j], gen_1->gen_plugs[j],
20494                         (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
20495 
20496                     total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
20497                 }
20498 
20499                 float pinned_free_list_efficiency = 0;
20500                 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
20501                 if (total_pinned_free_space != 0)
20502                 {
20503                     pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
20504                 }
20505 
20506                 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
20507                             heap_number,
20508                             generation_allocated_in_pinned_free (gen_2),
20509                             total_pinned_free_space,
20510                             (int)(pinned_free_list_efficiency * 100),
20511                             generation_pinned_free_obj_space (gen_2),
20512                             total_num_pinned_free_spaces_left));
20513             }
20514 #endif //FREE_USAGE_STATS
20515 
20516             //Go past all of the pinned plugs for this generation.
20517             while (!pinned_plug_que_empty_p() &&
20518                    (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
20519             {
20520                 size_t  entry = deque_pinned_plug();
20521                 mark*  m = pinned_plug_of (entry);
20522                 uint8_t*  plug = pinned_plug (m);
20523                 size_t  len = pinned_len (m);
20524                 // detect pinned block in different segment (later) than
20525                 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
20526                 // adjust the allocation segment along the way (at the end it will
20527                 // be the ephemeral segment.
20528                 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
20529 
20530                 PREFIX_ASSUME(nseg != NULL);
20531 
20532                 while (!((plug >= generation_allocation_pointer (consing_gen))&&
20533                         (plug < heap_segment_allocated (nseg))))
20534                 {
20535                     //adjust the end of the segment to be the end of the plug
20536                     assert (generation_allocation_pointer (consing_gen)>=
20537                             heap_segment_mem (nseg));
20538                     assert (generation_allocation_pointer (consing_gen)<=
20539                             heap_segment_committed (nseg));
20540 
20541                     heap_segment_plan_allocated (nseg) =
20542                         generation_allocation_pointer (consing_gen);
20543                     //switch allocation segment
20544                     nseg = heap_segment_next_rw (nseg);
20545                     generation_allocation_segment (consing_gen) = nseg;
20546                     //reset the allocation pointer and limits
20547                     generation_allocation_pointer (consing_gen) =
20548                         heap_segment_mem (nseg);
20549                 }
20550                 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
20551                 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
20552                 generation_allocation_pointer (consing_gen) = plug + len;
20553                 generation_allocation_limit (consing_gen) =
20554                     generation_allocation_pointer (consing_gen);
20555             }
20556             allocate_in_condemned = TRUE;
20557             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20558         }
20559 
20560         if (active_new_gen_number != max_generation)
20561         {
20562             if (active_new_gen_number == (max_generation - 1))
20563             {
20564                 maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
20565                 if (!demote_gen1_p)
20566                     advance_pins_for_demotion (consing_gen);
20567             }
20568 
20569             plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
20570 
20571             dprintf (1, ("process eph: allocated gen%d start at %Ix",
20572                 active_new_gen_number,
20573                 generation_plan_allocation_start (generation_of (active_new_gen_number))));
20574 
20575             if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
20576             {
20577                 uint8_t* pplug = pinned_plug (oldest_pin());
20578                 if (object_gennum (pplug) > 0)
20579                 {
20580                     demotion_low = pplug;
20581                     dprintf (3, ("process eph: dlow->%Ix", demotion_low));
20582                 }
20583             }
20584 
20585             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
20586         }
20587 
20588         goto retry;
20589     }
20590 }
20591 
20592 inline
20593 void gc_heap::seg_clear_mark_bits (heap_segment* seg)
20594 {
20595     uint8_t* o = heap_segment_mem (seg);
20596     while (o < heap_segment_allocated (seg))
20597     {
20598         if (marked (o))
20599         {
20600             clear_marked (o);
20601         }
20602         o = o  + Align (size (o));
20603     }
20604 }
20605 
20606 #ifdef FEATURE_BASICFREEZE
20607 void gc_heap::sweep_ro_segments (heap_segment* start_seg)
20608 {
20609     //go through all of the segment in range and reset the mark bit
20610     //TODO works only on small object segments
20611 
20612     heap_segment* seg = start_seg;
20613 
20614     while (seg)
20615     {
20616         if (heap_segment_read_only_p (seg) &&
20617             heap_segment_in_range_p (seg))
20618         {
20619 #ifdef BACKGROUND_GC
20620             if (settings.concurrent)
20621             {
20622                 seg_clear_mark_array_bits_soh (seg);
20623             }
20624             else
20625             {
20626                 seg_clear_mark_bits (seg);
20627             }
20628 #else //BACKGROUND_GC
20629 
20630 #ifdef MARK_ARRAY
20631             if(gc_can_use_concurrent)
20632             {
20633                 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
20634                               min (heap_segment_allocated (seg), highest_address),
20635                               FALSE); // read_only segments need the mark clear
20636             }
20637 #else //MARK_ARRAY
20638             seg_clear_mark_bits (seg);
20639 #endif //MARK_ARRAY
20640 
20641 #endif //BACKGROUND_GC
20642         }
20643         seg = heap_segment_next (seg);
20644     }
20645 }
20646 #endif // FEATURE_BASICFREEZE
20647 
20648 #ifdef FEATURE_LOH_COMPACTION
20649 inline
20650 BOOL gc_heap::loh_pinned_plug_que_empty_p()
20651 {
20652     return (loh_pinned_queue_bos == loh_pinned_queue_tos);
20653 }
20654 
20655 void gc_heap::loh_set_allocator_next_pin()
20656 {
20657     if (!(loh_pinned_plug_que_empty_p()))
20658     {
20659         mark*  oldest_entry = loh_oldest_pin();
20660         uint8_t* plug = pinned_plug (oldest_entry);
20661         generation* gen = large_object_generation;
20662         if ((plug >= generation_allocation_pointer (gen)) &&
20663             (plug <  generation_allocation_limit (gen)))
20664         {
20665             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
20666         }
20667         else
20668             assert (!((plug < generation_allocation_pointer (gen)) &&
20669                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
20670     }
20671 }
20672 
20673 size_t gc_heap::loh_deque_pinned_plug ()
20674 {
20675     size_t m = loh_pinned_queue_bos;
20676     loh_pinned_queue_bos++;
20677     return m;
20678 }
20679 
20680 inline
20681 mark* gc_heap::loh_pinned_plug_of (size_t bos)
20682 {
20683     return &loh_pinned_queue[bos];
20684 }
20685 
20686 inline
20687 mark* gc_heap::loh_oldest_pin()
20688 {
20689     return loh_pinned_plug_of (loh_pinned_queue_bos);
20690 }
20691 
20692 // If we can't grow the queue, then don't compact.
20693 BOOL gc_heap::loh_enque_pinned_plug (uint8_t* plug, size_t len)
20694 {
20695     assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
20696 
20697     if (loh_pinned_queue_length <= loh_pinned_queue_tos)
20698     {
20699         if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
20700         {
20701             return FALSE;
20702         }
20703     }
20704     dprintf (3, (" P: %Ix(%Id)", plug, len));
20705     mark& m = loh_pinned_queue[loh_pinned_queue_tos];
20706     m.first = plug;
20707     m.len = len;
20708     loh_pinned_queue_tos++;
20709     loh_set_allocator_next_pin();
20710     return TRUE;
20711 }
20712 
20713 inline
20714 BOOL gc_heap::loh_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit)
20715 {
20716     dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)",
20717         size,
20718         (2* AlignQword (loh_padding_obj_size) +  size),
20719         alloc_pointer,
20720         alloc_limit,
20721         (alloc_limit - alloc_pointer)));
20722 
20723     return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) +  size) <= alloc_limit);
20724 }
20725 
20726 uint8_t* gc_heap::loh_allocate_in_condemned (uint8_t* old_loc, size_t size)
20727 {
20728     UNREFERENCED_PARAMETER(old_loc);
20729 
20730     generation* gen = large_object_generation;
20731     dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id",
20732         generation_allocation_pointer (gen),
20733         generation_allocation_limit (gen),
20734         size));
20735 
20736 retry:
20737     {
20738         heap_segment* seg = generation_allocation_segment (gen);
20739         if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
20740         {
20741             if ((!(loh_pinned_plug_que_empty_p()) &&
20742                  (generation_allocation_limit (gen) ==
20743                   pinned_plug (loh_oldest_pin()))))
20744             {
20745                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
20746                 size_t len = pinned_len (m);
20747                 uint8_t* plug = pinned_plug (m);
20748                 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
20749                 pinned_len (m) = plug - generation_allocation_pointer (gen);
20750                 generation_allocation_pointer (gen) = plug + len;
20751 
20752                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20753                 loh_set_allocator_next_pin();
20754                 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)",
20755                     generation_allocation_pointer (gen),
20756                     generation_allocation_limit (gen),
20757                     (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20758 
20759                 goto retry;
20760             }
20761 
20762             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
20763             {
20764                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20765                 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
20766             }
20767             else
20768             {
20769                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
20770                 {
20771                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
20772                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20773                     dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
20774                 }
20775                 else
20776                 {
20777                     if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
20778                         (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
20779                     {
20780                         dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
20781                                          (generation_allocation_pointer (gen) + size)));
20782 
20783                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
20784                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20785 
20786                         dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)",
20787                             generation_allocation_pointer (gen),
20788                             generation_allocation_limit (gen),
20789                             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20790                     }
20791                     else
20792                     {
20793                         heap_segment* next_seg = heap_segment_next (seg);
20794                         assert (generation_allocation_pointer (gen)>=
20795                                 heap_segment_mem (seg));
20796                         // Verify that all pinned plugs for this segment are consumed
20797                         if (!loh_pinned_plug_que_empty_p() &&
20798                             ((pinned_plug (loh_oldest_pin()) <
20799                               heap_segment_allocated (seg)) &&
20800                              (pinned_plug (loh_oldest_pin()) >=
20801                               generation_allocation_pointer (gen))))
20802                         {
20803                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
20804                                          pinned_plug (loh_oldest_pin())));
20805                             dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
20806                             FATAL_GC_ERROR();
20807                         }
20808                         assert (generation_allocation_pointer (gen)>=
20809                                 heap_segment_mem (seg));
20810                         assert (generation_allocation_pointer (gen)<=
20811                                 heap_segment_committed (seg));
20812                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
20813 
20814                         if (next_seg)
20815                         {
20816                             // for LOH do we want to try starting from the first LOH every time though?
20817                             generation_allocation_segment (gen) = next_seg;
20818                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
20819                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
20820 
20821                             dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)",
20822                                 generation_allocation_pointer (gen),
20823                                 generation_allocation_limit (gen),
20824                                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20825                         }
20826                         else
20827                         {
20828                             dprintf (1, ("We ran out of space compacting, shouldn't happen"));
20829                             FATAL_GC_ERROR();
20830                         }
20831                     }
20832                 }
20833             }
20834             loh_set_allocator_next_pin();
20835 
20836             dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)",
20837                 generation_allocation_pointer (gen),
20838                 generation_allocation_limit (gen),
20839                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20840 
20841             goto retry;
20842         }
20843     }
20844 
20845     {
20846         assert (generation_allocation_pointer (gen)>=
20847                 heap_segment_mem (generation_allocation_segment (gen)));
20848         uint8_t* result = generation_allocation_pointer (gen);
20849         size_t loh_pad = AlignQword (loh_padding_obj_size);
20850 
20851         generation_allocation_pointer (gen) += size + loh_pad;
20852         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
20853 
20854         dprintf (1235, ("p: %Ix, l: %Ix (%Id)",
20855             generation_allocation_pointer (gen),
20856             generation_allocation_limit (gen),
20857             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
20858 
20859         assert (result + loh_pad);
20860         return result + loh_pad;
20861     }
20862 }
20863 
20864 BOOL gc_heap::should_compact_loh()
20865 {
20866     return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
20867 }
20868 
20869 inline
20870 void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
20871 {
20872     if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
20873     {
20874         if (all_heaps_compacted_p)
20875         {
20876             // If the compaction mode says to compact once and we are going to compact LOH,
20877             // we need to revert it back to no compaction.
20878             loh_compaction_mode = loh_compaction_default;
20879         }
20880     }
20881 }
20882 
20883 BOOL gc_heap::plan_loh()
20884 {
20885     if (!loh_pinned_queue)
20886     {
20887         loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
20888         if (!loh_pinned_queue)
20889         {
20890             dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction",
20891                          LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
20892             return FALSE;
20893         }
20894 
20895         loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
20896     }
20897 
20898     if (heap_number == 0)
20899         loh_pinned_queue_decay = LOH_PIN_DECAY;
20900 
20901     loh_pinned_queue_tos = 0;
20902     loh_pinned_queue_bos = 0;
20903 
20904     generation* gen        = large_object_generation;
20905     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
20906     PREFIX_ASSUME(start_seg != NULL);
20907     heap_segment* seg      = start_seg;
20908     uint8_t* o             = generation_allocation_start (gen);
20909 
20910     dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n",
20911         generation_size (max_generation + 1),
20912         generation_free_list_space (gen),
20913         generation_free_obj_space (gen)));
20914 
20915     while (seg)
20916     {
20917         heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
20918         seg = heap_segment_next (seg);
20919     }
20920 
20921     seg = start_seg;
20922 
20923     //Skip the generation gap object
20924     o = o + AlignQword (size (o));
20925     // We don't need to ever realloc gen3 start so don't touch it.
20926     heap_segment_plan_allocated (seg) = o;
20927     generation_allocation_pointer (gen) = o;
20928     generation_allocation_limit (gen) = generation_allocation_pointer (gen);
20929     generation_allocation_segment (gen) = start_seg;
20930 
20931     uint8_t* free_space_start = o;
20932     uint8_t* free_space_end = o;
20933     uint8_t* new_address = 0;
20934 
20935     while (1)
20936     {
20937         if (o >= heap_segment_allocated (seg))
20938         {
20939             seg = heap_segment_next (seg);
20940             if (seg == 0)
20941             {
20942                 break;
20943             }
20944 
20945             o = heap_segment_mem (seg);
20946         }
20947 
20948         if (marked (o))
20949         {
20950             free_space_end = o;
20951             size_t size = AlignQword (size (o));
20952             dprintf (1235, ("%Ix(%Id) M", o, size));
20953 
20954             if (pinned (o))
20955             {
20956                 // We don't clear the pinned bit yet so we can check in
20957                 // compact phase how big a free object we should allocate
20958                 // in front of the pinned object. We use the reloc address
20959                 // field to store this.
20960                 if (!loh_enque_pinned_plug (o, size))
20961                 {
20962                     return FALSE;
20963                 }
20964                 new_address = o;
20965             }
20966             else
20967             {
20968                 new_address = loh_allocate_in_condemned (o, size);
20969             }
20970 
20971             loh_set_node_relocation_distance (o, (new_address - o));
20972             dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
20973 
20974             o = o + size;
20975             free_space_start = o;
20976             if (o < heap_segment_allocated (seg))
20977             {
20978                 assert (!marked (o));
20979             }
20980         }
20981         else
20982         {
20983             while (o < heap_segment_allocated (seg) && !marked (o))
20984             {
20985                 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_pFreeObjectMethodTable) ? 1 : 0)));
20986                 o = o + AlignQword (size (o));
20987             }
20988         }
20989     }
20990 
20991     while (!loh_pinned_plug_que_empty_p())
20992     {
20993         mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
20994         size_t len = pinned_len (m);
20995         uint8_t* plug = pinned_plug (m);
20996 
20997         // detect pinned block in different segment (later) than
20998         // allocation segment
20999         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
21000 
21001         while ((plug < generation_allocation_pointer (gen)) ||
21002                (plug >= heap_segment_allocated (nseg)))
21003         {
21004             assert ((plug < heap_segment_mem (nseg)) ||
21005                     (plug > heap_segment_reserved (nseg)));
21006             //adjust the end of the segment to be the end of the plug
21007             assert (generation_allocation_pointer (gen)>=
21008                     heap_segment_mem (nseg));
21009             assert (generation_allocation_pointer (gen)<=
21010                     heap_segment_committed (nseg));
21011 
21012             heap_segment_plan_allocated (nseg) =
21013                 generation_allocation_pointer (gen);
21014             //switch allocation segment
21015             nseg = heap_segment_next_rw (nseg);
21016             generation_allocation_segment (gen) = nseg;
21017             //reset the allocation pointer and limits
21018             generation_allocation_pointer (gen) =
21019                 heap_segment_mem (nseg);
21020         }
21021 
21022         dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21023         pinned_len (m) = plug - generation_allocation_pointer (gen);
21024         generation_allocation_pointer (gen) = plug + len;
21025     }
21026 
21027     heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
21028     generation_allocation_pointer (gen) = 0;
21029     generation_allocation_limit (gen) = 0;
21030 
21031     return TRUE;
21032 }
21033 
21034 void gc_heap::compact_loh()
21035 {
21036     assert (should_compact_loh());
21037 
21038     generation* gen        = large_object_generation;
21039     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21040     PREFIX_ASSUME(start_seg != NULL);
21041     heap_segment* seg      = start_seg;
21042     heap_segment* prev_seg = 0;
21043     uint8_t* o             = generation_allocation_start (gen);
21044 
21045     //Skip the generation gap object
21046     o = o + AlignQword (size (o));
21047     // We don't need to ever realloc gen3 start so don't touch it.
21048     uint8_t* free_space_start = o;
21049     uint8_t* free_space_end = o;
21050     generation_allocator (gen)->clear();
21051     generation_free_list_space (gen) = 0;
21052     generation_free_obj_space (gen) = 0;
21053 
21054     loh_pinned_queue_bos = 0;
21055 
21056     while (1)
21057     {
21058         if (o >= heap_segment_allocated (seg))
21059         {
21060             heap_segment* next_seg = heap_segment_next (seg);
21061 
21062             if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
21063                 (seg != start_seg) && !heap_segment_read_only_p (seg))
21064             {
21065                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
21066                 assert (prev_seg);
21067                 heap_segment_next (prev_seg) = next_seg;
21068                 heap_segment_next (seg) = freeable_large_heap_segment;
21069                 freeable_large_heap_segment = seg;
21070             }
21071             else
21072             {
21073                 if (!heap_segment_read_only_p (seg))
21074                 {
21075                     // We grew the segment to accommondate allocations.
21076                     if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
21077                     {
21078                         if ((heap_segment_plan_allocated (seg) - plug_skew)  > heap_segment_used (seg))
21079                         {
21080                             heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
21081                         }
21082                     }
21083 
21084                     heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
21085                     dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
21086                     decommit_heap_segment_pages (seg, 0);
21087                     dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
21088                         seg,
21089                         heap_segment_allocated (seg),
21090                         heap_segment_used (seg),
21091                         heap_segment_committed (seg)));
21092                     //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
21093                     dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
21094                 }
21095                 prev_seg = seg;
21096             }
21097 
21098             seg = next_seg;
21099             if (seg == 0)
21100                 break;
21101             else
21102             {
21103                 o = heap_segment_mem (seg);
21104             }
21105         }
21106 
21107         if (marked (o))
21108         {
21109             free_space_end = o;
21110             size_t size = AlignQword (size (o));
21111 
21112             size_t loh_pad;
21113             uint8_t* reloc = o;
21114             clear_marked (o);
21115 
21116             if (pinned (o))
21117             {
21118                 // We are relying on the fact the pinned objects are always looked at in the same order
21119                 // in plan phase and in compact phase.
21120                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21121                 uint8_t* plug = pinned_plug (m);
21122                 assert (plug == o);
21123 
21124                 loh_pad = pinned_len (m);
21125                 clear_pinned (o);
21126             }
21127             else
21128             {
21129                 loh_pad = AlignQword (loh_padding_obj_size);
21130 
21131                 reloc += loh_node_relocation_distance (o);
21132                 gcmemcopy (reloc, o, size, TRUE);
21133             }
21134 
21135             thread_gap ((reloc - loh_pad), loh_pad, gen);
21136 
21137             o = o + size;
21138             free_space_start = o;
21139             if (o < heap_segment_allocated (seg))
21140             {
21141                 assert (!marked (o));
21142             }
21143         }
21144         else
21145         {
21146             while (o < heap_segment_allocated (seg) && !marked (o))
21147             {
21148                 o = o + AlignQword (size (o));
21149             }
21150         }
21151     }
21152 
21153     assert (loh_pinned_plug_que_empty_p());
21154 
21155     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n",
21156         generation_size (max_generation + 1),
21157         generation_free_list_space (gen),
21158         generation_free_obj_space (gen)));
21159 }
21160 
21161 void gc_heap::relocate_in_loh_compact()
21162 {
21163     generation* gen        = large_object_generation;
21164     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21165     uint8_t* o             = generation_allocation_start (gen);
21166 
21167     //Skip the generation gap object
21168     o = o + AlignQword (size (o));
21169 
21170     relocate_args args;
21171     args.low = gc_low;
21172     args.high = gc_high;
21173     args.last_plug = 0;
21174 
21175     while (1)
21176     {
21177         if (o >= heap_segment_allocated (seg))
21178         {
21179             seg = heap_segment_next (seg);
21180             if (seg == 0)
21181             {
21182                 break;
21183             }
21184 
21185             o = heap_segment_mem (seg);
21186         }
21187 
21188         if (marked (o))
21189         {
21190             size_t size = AlignQword (size (o));
21191 
21192             check_class_object_demotion (o);
21193             if (contain_pointers (o))
21194             {
21195                 go_through_object_nostart (method_table (o), o, size(o), pval,
21196                 {
21197                     reloc_survivor_helper (pval);
21198                 });
21199             }
21200 
21201             o = o + size;
21202             if (o < heap_segment_allocated (seg))
21203             {
21204                 assert (!marked (o));
21205             }
21206         }
21207         else
21208         {
21209             while (o < heap_segment_allocated (seg) && !marked (o))
21210             {
21211                 o = o + AlignQword (size (o));
21212             }
21213         }
21214     }
21215 
21216     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n",
21217         generation_size (max_generation + 1),
21218         generation_free_list_space (gen),
21219         generation_free_obj_space (gen)));
21220 }
21221 
21222 void gc_heap::walk_relocation_for_loh (size_t profiling_context, record_surv_fn fn)
21223 {
21224     generation* gen        = large_object_generation;
21225     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21226     uint8_t* o             = generation_allocation_start (gen);
21227 
21228     //Skip the generation gap object
21229     o = o + AlignQword (size (o));
21230 
21231     while (1)
21232     {
21233         if (o >= heap_segment_allocated (seg))
21234         {
21235             seg = heap_segment_next (seg);
21236             if (seg == 0)
21237             {
21238                 break;
21239             }
21240 
21241             o = heap_segment_mem (seg);
21242         }
21243 
21244         if (marked (o))
21245         {
21246             size_t size = AlignQword (size (o));
21247 
21248             ptrdiff_t reloc = loh_node_relocation_distance (o);
21249 
21250             STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
21251 
21252             fn (o, (o + size), reloc, profiling_context, settings.compaction, FALSE);
21253 
21254             o = o + size;
21255             if (o < heap_segment_allocated (seg))
21256             {
21257                 assert (!marked (o));
21258             }
21259         }
21260         else
21261         {
21262             while (o < heap_segment_allocated (seg) && !marked (o))
21263             {
21264                 o = o + AlignQword (size (o));
21265             }
21266         }
21267     }
21268 }
21269 
21270 BOOL gc_heap::loh_object_p (uint8_t* o)
21271 {
21272 #ifdef MULTIPLE_HEAPS
21273     gc_heap* hp = gc_heap::g_heaps [0];
21274     int brick_entry = hp->brick_table[hp->brick_of (o)];
21275 #else //MULTIPLE_HEAPS
21276     int brick_entry = brick_table[brick_of (o)];
21277 #endif //MULTIPLE_HEAPS
21278 
21279     return (brick_entry == 0);
21280 }
21281 #endif //FEATURE_LOH_COMPACTION
21282 
21283 void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p,
21284                                       BOOL& last_pinned_plug_p,
21285                                       BOOL& pinned_plug_p,
21286                                       size_t ps,
21287                                       size_t& artificial_pinned_size)
21288 {
21289     last_npinned_plug_p = FALSE;
21290     last_pinned_plug_p = TRUE;
21291     pinned_plug_p = TRUE;
21292     artificial_pinned_size = ps;
21293 }
21294 
21295 // Because we have the artifical pinning, we can't gaurantee that pinned and npinned
21296 // plugs are always interleaved.
21297 void gc_heap::store_plug_gap_info (uint8_t* plug_start,
21298                                    uint8_t* plug_end,
21299                                    BOOL& last_npinned_plug_p,
21300                                    BOOL& last_pinned_plug_p,
21301                                    uint8_t*& last_pinned_plug,
21302                                    BOOL& pinned_plug_p,
21303                                    uint8_t* last_object_in_last_plug,
21304                                    BOOL& merge_with_last_pin_p,
21305                                    // this is only for verification purpose
21306                                    size_t last_plug_len)
21307 {
21308     UNREFERENCED_PARAMETER(last_plug_len);
21309 
21310     if (!last_npinned_plug_p && !last_pinned_plug_p)
21311     {
21312         //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
21313         dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
21314         assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
21315         set_gap_size (plug_start, plug_start - plug_end);
21316     }
21317 
21318     if (pinned (plug_start))
21319     {
21320         BOOL save_pre_plug_info_p = FALSE;
21321 
21322         if (last_npinned_plug_p || last_pinned_plug_p)
21323         {
21324             //if (last_plug_len == Align (min_obj_size))
21325             //{
21326             //    dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
21327             //    GCToOSInterface::DebugBreak();
21328             //}
21329             save_pre_plug_info_p = TRUE;
21330         }
21331 
21332         pinned_plug_p = TRUE;
21333         last_npinned_plug_p = FALSE;
21334 
21335         if (last_pinned_plug_p)
21336         {
21337             dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
21338             merge_with_last_pin_p = TRUE;
21339         }
21340         else
21341         {
21342             last_pinned_plug_p = TRUE;
21343             last_pinned_plug = plug_start;
21344 
21345             enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
21346 
21347             if (save_pre_plug_info_p)
21348             {
21349                 set_gap_size (plug_start, sizeof (gap_reloc_pair));
21350             }
21351         }
21352     }
21353     else
21354     {
21355         if (last_pinned_plug_p)
21356         {
21357             //if (Align (last_plug_len) < min_pre_pin_obj_size)
21358             //{
21359             //    dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
21360             //    GCToOSInterface::DebugBreak();
21361             //}
21362 
21363             save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
21364             set_gap_size (plug_start, sizeof (gap_reloc_pair));
21365 
21366             verify_pins_with_post_plug_info("after saving post plug info");
21367         }
21368         last_npinned_plug_p = TRUE;
21369         last_pinned_plug_p = FALSE;
21370     }
21371 }
21372 
21373 void gc_heap::record_interesting_data_point (interesting_data_point idp)
21374 {
21375 #ifdef GC_CONFIG_DRIVEN
21376     (interesting_data_per_gc[idp])++;
21377 #else
21378     UNREFERENCED_PARAMETER(idp);
21379 #endif //GC_CONFIG_DRIVEN
21380 }
21381 
21382 #ifdef _PREFAST_
21383 #pragma warning(push)
21384 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
21385 #endif //_PREFAST_
21386 void gc_heap::plan_phase (int condemned_gen_number)
21387 {
21388     size_t old_gen2_allocated = 0;
21389     size_t old_gen2_size = 0;
21390 
21391     if (condemned_gen_number == (max_generation - 1))
21392     {
21393         old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
21394         old_gen2_size = generation_size (max_generation);
21395     }
21396 
21397     assert (settings.concurrent == FALSE);
21398 
21399     // %type%  category = quote (plan);
21400 #ifdef TIME_GC
21401     unsigned start;
21402     unsigned finish;
21403     start = GetCycleCount32();
21404 #endif //TIME_GC
21405 
21406     dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
21407                 condemned_gen_number, settings.promotion ? 1 : 0));
21408 
21409     generation*  condemned_gen1 = generation_of (condemned_gen_number);
21410 
21411 #ifdef MARK_LIST
21412     BOOL use_mark_list = FALSE;
21413     uint8_t** mark_list_next = &mark_list[0];
21414 #ifdef GC_CONFIG_DRIVEN
21415     dprintf (3, ("total number of marked objects: %Id (%Id)",
21416                  (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0]))));
21417 #else
21418     dprintf (3, ("mark_list length: %Id",
21419                  (mark_list_index - &mark_list[0])));
21420 #endif //GC_CONFIG_DRIVEN
21421 
21422     if ((condemned_gen_number < max_generation) &&
21423         (mark_list_index <= mark_list_end)
21424 #ifdef BACKGROUND_GC
21425         && (!recursive_gc_sync::background_running_p())
21426 #endif //BACKGROUND_GC
21427         )
21428     {
21429 #ifndef MULTIPLE_HEAPS
21430         _sort (&mark_list[0], mark_list_index-1, 0);
21431         //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
21432         //verify_qsort_array (&mark_list[0], mark_list_index-1);
21433 #endif //!MULTIPLE_HEAPS
21434         use_mark_list = TRUE;
21435         get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit);
21436     }
21437     else
21438     {
21439         dprintf (3, ("mark_list not used"));
21440     }
21441 
21442 #endif //MARK_LIST
21443 
21444 #ifdef FEATURE_BASICFREEZE
21445     if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
21446         ro_segments_in_range)
21447     {
21448         sweep_ro_segments (generation_start_segment (condemned_gen1));
21449     }
21450 #endif // FEATURE_BASICFREEZE
21451 
21452 #ifndef MULTIPLE_HEAPS
21453     if (shigh != (uint8_t*)0)
21454     {
21455         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21456 
21457         PREFIX_ASSUME(seg != NULL);
21458 
21459         heap_segment* fseg = seg;
21460         do
21461         {
21462             if (slow > heap_segment_mem (seg) &&
21463                 slow < heap_segment_reserved (seg))
21464             {
21465                 if (seg == fseg)
21466                 {
21467                     uint8_t* o = generation_allocation_start (condemned_gen1) +
21468                         Align (size (generation_allocation_start (condemned_gen1)));
21469                     if (slow > o)
21470                     {
21471                         assert ((slow - o) >= (int)Align (min_obj_size));
21472 #ifdef BACKGROUND_GC
21473                         if (current_c_gc_state == c_gc_state_marking)
21474                         {
21475                             bgc_clear_batch_mark_array_bits (o, slow);
21476                         }
21477 #endif //BACKGROUND_GC
21478                         make_unused_array (o, slow - o);
21479                     }
21480                 }
21481                 else
21482                 {
21483                     assert (condemned_gen_number == max_generation);
21484                     make_unused_array (heap_segment_mem (seg),
21485                                        slow - heap_segment_mem (seg));
21486                 }
21487             }
21488             if (in_range_for_segment (shigh, seg))
21489             {
21490 #ifdef BACKGROUND_GC
21491                 if (current_c_gc_state == c_gc_state_marking)
21492                 {
21493                     bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
21494                 }
21495 #endif //BACKGROUND_GC
21496                 heap_segment_allocated (seg) = shigh + Align (size (shigh));
21497             }
21498             // test if the segment is in the range of [slow, shigh]
21499             if (!((heap_segment_reserved (seg) >= slow) &&
21500                   (heap_segment_mem (seg) <= shigh)))
21501             {
21502                 // shorten it to minimum
21503                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
21504             }
21505             seg = heap_segment_next_rw (seg);
21506         } while (seg);
21507     }
21508     else
21509     {
21510         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21511 
21512         PREFIX_ASSUME(seg != NULL);
21513 
21514         heap_segment* sseg = seg;
21515         do
21516         {
21517             // shorten it to minimum
21518             if (seg == sseg)
21519             {
21520                 // no survivors make all generations look empty
21521                 uint8_t* o = generation_allocation_start (condemned_gen1) +
21522                     Align (size (generation_allocation_start (condemned_gen1)));
21523 #ifdef BACKGROUND_GC
21524                 if (current_c_gc_state == c_gc_state_marking)
21525                 {
21526                     bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
21527                 }
21528 #endif //BACKGROUND_GC
21529                 heap_segment_allocated (seg) = o;
21530             }
21531             else
21532             {
21533                 assert (condemned_gen_number == max_generation);
21534 #ifdef BACKGROUND_GC
21535                 if (current_c_gc_state == c_gc_state_marking)
21536                 {
21537                     bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
21538                 }
21539 #endif //BACKGROUND_GC
21540                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
21541             }
21542             seg = heap_segment_next_rw (seg);
21543         } while (seg);
21544     }
21545 
21546 #endif //MULTIPLE_HEAPS
21547 
21548     heap_segment*  seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
21549 
21550     PREFIX_ASSUME(seg1 != NULL);
21551 
21552     uint8_t*  end = heap_segment_allocated (seg1);
21553     uint8_t*  first_condemned_address = generation_allocation_start (condemned_gen1);
21554     uint8_t*  x = first_condemned_address;
21555 
21556     assert (!marked (x));
21557     uint8_t*  plug_end = x;
21558     uint8_t*  tree = 0;
21559     size_t  sequence_number = 0;
21560     uint8_t*  last_node = 0;
21561     size_t  current_brick = brick_of (x);
21562     BOOL  allocate_in_condemned = ((condemned_gen_number == max_generation)||
21563                                    (settings.promotion == FALSE));
21564     int  active_old_gen_number = condemned_gen_number;
21565     int  active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
21566                                   (1 + condemned_gen_number));
21567     generation*  older_gen = 0;
21568     generation* consing_gen = condemned_gen1;
21569     alloc_list  r_free_list [MAX_BUCKET_COUNT];
21570 
21571     size_t r_free_list_space = 0;
21572     size_t r_free_obj_space = 0;
21573     size_t r_older_gen_free_list_allocated = 0;
21574     size_t r_older_gen_condemned_allocated = 0;
21575     size_t r_older_gen_end_seg_allocated = 0;
21576     uint8_t*  r_allocation_pointer = 0;
21577     uint8_t*  r_allocation_limit = 0;
21578     uint8_t* r_allocation_start_region = 0;
21579     heap_segment*  r_allocation_segment = 0;
21580 #ifdef FREE_USAGE_STATS
21581     size_t r_older_gen_free_space[NUM_GEN_POWER2];
21582 #endif //FREE_USAGE_STATS
21583 
21584     if ((condemned_gen_number < max_generation))
21585     {
21586         older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
21587         generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
21588 
21589         r_free_list_space = generation_free_list_space (older_gen);
21590         r_free_obj_space = generation_free_obj_space (older_gen);
21591 #ifdef FREE_USAGE_STATS
21592         memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
21593 #endif //FREE_USAGE_STATS
21594         generation_allocate_end_seg_p (older_gen) = FALSE;
21595         r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
21596         r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
21597         r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
21598         r_allocation_limit = generation_allocation_limit (older_gen);
21599         r_allocation_pointer = generation_allocation_pointer (older_gen);
21600         r_allocation_start_region = generation_allocation_context_start_region (older_gen);
21601         r_allocation_segment = generation_allocation_segment (older_gen);
21602         heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
21603 
21604         PREFIX_ASSUME(start_seg != NULL);
21605 
21606         if (start_seg != ephemeral_heap_segment)
21607         {
21608             assert (condemned_gen_number == (max_generation - 1));
21609             while (start_seg && (start_seg != ephemeral_heap_segment))
21610             {
21611                 assert (heap_segment_allocated (start_seg) >=
21612                         heap_segment_mem (start_seg));
21613                 assert (heap_segment_allocated (start_seg) <=
21614                         heap_segment_reserved (start_seg));
21615                 heap_segment_plan_allocated (start_seg) =
21616                     heap_segment_allocated (start_seg);
21617                 start_seg = heap_segment_next_rw (start_seg);
21618             }
21619         }
21620     }
21621 
21622     //reset all of the segment allocated sizes
21623     {
21624         heap_segment*  seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
21625 
21626         PREFIX_ASSUME(seg2 != NULL);
21627 
21628         while (seg2)
21629         {
21630             heap_segment_plan_allocated (seg2) =
21631                 heap_segment_mem (seg2);
21632             seg2 = heap_segment_next_rw (seg2);
21633         }
21634     }
21635     int  condemned_gn = condemned_gen_number;
21636 
21637     int bottom_gen = 0;
21638     init_free_and_plug();
21639 
21640     while (condemned_gn >= bottom_gen)
21641     {
21642         generation*  condemned_gen2 = generation_of (condemned_gn);
21643         generation_allocator (condemned_gen2)->clear();
21644         generation_free_list_space (condemned_gen2) = 0;
21645         generation_free_obj_space (condemned_gen2) = 0;
21646         generation_allocation_size (condemned_gen2) = 0;
21647         generation_condemned_allocated (condemned_gen2) = 0;
21648         generation_pinned_allocated (condemned_gen2) = 0;
21649         generation_free_list_allocated(condemned_gen2) = 0;
21650         generation_end_seg_allocated (condemned_gen2) = 0;
21651         generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
21652         generation_pinned_allocation_compact_size (condemned_gen2) = 0;
21653 #ifdef FREE_USAGE_STATS
21654         generation_pinned_free_obj_space (condemned_gen2) = 0;
21655         generation_allocated_in_pinned_free (condemned_gen2) = 0;
21656         generation_allocated_since_last_pin (condemned_gen2) = 0;
21657 #endif //FREE_USAGE_STATS
21658         generation_plan_allocation_start (condemned_gen2) = 0;
21659         generation_allocation_segment (condemned_gen2) =
21660             heap_segment_rw (generation_start_segment (condemned_gen2));
21661 
21662         PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
21663 
21664         if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
21665         {
21666             generation_allocation_pointer (condemned_gen2) =
21667                 heap_segment_mem (generation_allocation_segment (condemned_gen2));
21668         }
21669         else
21670         {
21671             generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
21672         }
21673 
21674         generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21675         generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21676 
21677         condemned_gn--;
21678     }
21679 
21680     BOOL allocate_first_generation_start = FALSE;
21681 
21682     if (allocate_in_condemned)
21683     {
21684         allocate_first_generation_start = TRUE;
21685     }
21686 
21687     dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21688 
21689     demotion_low = MAX_PTR;
21690     demotion_high = heap_segment_allocated (ephemeral_heap_segment);
21691 
21692     // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
21693     // from gen1. They should get promoted to gen2.
21694     demote_gen1_p = !(settings.promotion &&
21695                       (settings.condemned_generation == (max_generation - 1)) &&
21696                       gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
21697 
21698     total_ephemeral_size = 0;
21699 
21700     print_free_and_plug ("BP");
21701 
21702     for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
21703     {
21704         generation* temp_gen = generation_of (gen_idx);
21705 
21706         dprintf (2, ("gen%d start %Ix, plan start %Ix",
21707             gen_idx,
21708             generation_allocation_start (temp_gen),
21709             generation_plan_allocation_start (temp_gen)));
21710     }
21711 
21712     BOOL fire_pinned_plug_events_p = ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, PinPlugAtGCTime);
21713     size_t last_plug_len = 0;
21714 
21715     while (1)
21716     {
21717         if (x >= end)
21718         {
21719             assert (x == end);
21720             assert (heap_segment_allocated (seg1) == end);
21721             heap_segment_allocated (seg1) = plug_end;
21722 
21723             current_brick = update_brick_table (tree, current_brick, x, plug_end);
21724             dprintf (3, ("end of seg: new tree, sequence# 0"));
21725             sequence_number = 0;
21726             tree = 0;
21727 
21728             if (heap_segment_next_rw (seg1))
21729             {
21730                 seg1 = heap_segment_next_rw (seg1);
21731                 end = heap_segment_allocated (seg1);
21732                 plug_end = x = heap_segment_mem (seg1);
21733                 current_brick = brick_of (x);
21734                 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21735                 continue;
21736             }
21737             else
21738             {
21739                 break;
21740             }
21741         }
21742 
21743         BOOL last_npinned_plug_p = FALSE;
21744         BOOL last_pinned_plug_p = FALSE;
21745 
21746         // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
21747         // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
21748         // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
21749         uint8_t* last_pinned_plug = 0;
21750         size_t num_pinned_plugs_in_plug = 0;
21751 
21752         uint8_t* last_object_in_plug = 0;
21753 
21754         while ((x < end) && marked (x))
21755         {
21756             uint8_t*  plug_start = x;
21757             uint8_t*  saved_plug_end = plug_end;
21758             BOOL   pinned_plug_p = FALSE;
21759             BOOL   npin_before_pin_p = FALSE;
21760             BOOL   saved_last_npinned_plug_p = last_npinned_plug_p;
21761             uint8_t*  saved_last_object_in_plug = last_object_in_plug;
21762             BOOL   merge_with_last_pin_p = FALSE;
21763 
21764             size_t added_pinning_size = 0;
21765             size_t artificial_pinned_size = 0;
21766 
21767             store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p,
21768                                  last_pinned_plug, pinned_plug_p, last_object_in_plug,
21769                                  merge_with_last_pin_p, last_plug_len);
21770 
21771 #ifdef FEATURE_STRUCTALIGN
21772             int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
21773             size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
21774 #endif // FEATURE_STRUCTALIGN
21775 
21776             {
21777                 uint8_t* xl = x;
21778                 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
21779                 {
21780                     assert (xl < end);
21781                     if (pinned(xl))
21782                     {
21783                         clear_pinned (xl);
21784                     }
21785 #ifdef FEATURE_STRUCTALIGN
21786                     else
21787                     {
21788                         int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
21789                         if (obj_requiredAlignment > requiredAlignment)
21790                         {
21791                             requiredAlignment = obj_requiredAlignment;
21792                             alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
21793                         }
21794                     }
21795 #endif // FEATURE_STRUCTALIGN
21796 
21797                     clear_marked (xl);
21798 
21799                     dprintf(4, ("+%Ix+", (size_t)xl));
21800                     assert ((size (xl) > 0));
21801                     assert ((size (xl) <= LARGE_OBJECT_SIZE));
21802 
21803                     last_object_in_plug = xl;
21804 
21805                     xl = xl + Align (size (xl));
21806                     Prefetch (xl);
21807                 }
21808 
21809                 BOOL next_object_marked_p = ((xl < end) && marked (xl));
21810 
21811                 if (pinned_plug_p)
21812                 {
21813                     // If it is pinned we need to extend to the next marked object as we can't use part of
21814                     // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
21815                     // references but for now I am just using the next non pinned object for that).
21816                     if (next_object_marked_p)
21817                     {
21818                         clear_marked (xl);
21819                         last_object_in_plug = xl;
21820                         size_t extra_size = Align (size (xl));
21821                         xl = xl + extra_size;
21822                         added_pinning_size = extra_size;
21823                     }
21824                 }
21825                 else
21826                 {
21827                     if (next_object_marked_p)
21828                         npin_before_pin_p = TRUE;
21829                 }
21830 
21831                 assert (xl <= end);
21832                 x = xl;
21833             }
21834             dprintf (3, ( "%Ix[", (size_t)x));
21835             plug_end = x;
21836             size_t ps = plug_end - plug_start;
21837             last_plug_len = ps;
21838             dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
21839             uint8_t*  new_address = 0;
21840 
21841             if (!pinned_plug_p)
21842             {
21843                 if (allocate_in_condemned &&
21844                     (settings.condemned_generation == max_generation) &&
21845                     (ps > (OS_PAGE_SIZE)))
21846                 {
21847                     ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
21848                     //reloc should >=0 except when we relocate
21849                     //across segments and the dest seg is higher then the src
21850 
21851                     if ((ps > (8*OS_PAGE_SIZE)) &&
21852                         (reloc > 0) &&
21853                         ((size_t)reloc < (ps/16)))
21854                     {
21855                         dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
21856                                      (size_t)plug_start, reloc));
21857                         // The last plug couldn't have been a npinned plug or it would have
21858                         // included this plug.
21859                         assert (!saved_last_npinned_plug_p);
21860 
21861                         if (last_pinned_plug)
21862                         {
21863                             dprintf (3, ("artificially pinned plug merged with last pinned plug"));
21864                             merge_with_last_pin_p = TRUE;
21865                         }
21866                         else
21867                         {
21868                             enque_pinned_plug (plug_start, FALSE, 0);
21869                             last_pinned_plug = plug_start;
21870                         }
21871 
21872                         convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
21873                                                 ps, artificial_pinned_size);
21874                     }
21875                 }
21876             }
21877 
21878             if (allocate_first_generation_start)
21879             {
21880                 allocate_first_generation_start = FALSE;
21881                 plan_generation_start (condemned_gen1, consing_gen, plug_start);
21882                 assert (generation_plan_allocation_start (condemned_gen1));
21883             }
21884 
21885             if (seg1 == ephemeral_heap_segment)
21886             {
21887                 process_ephemeral_boundaries (plug_start, active_new_gen_number,
21888                                               active_old_gen_number,
21889                                               consing_gen,
21890                                               allocate_in_condemned);
21891             }
21892 
21893             dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
21894 
21895             dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
21896             dd_survived_size (dd_active_old) += ps;
21897 
21898             BOOL convert_to_pinned_p = FALSE;
21899 
21900             if (!pinned_plug_p)
21901             {
21902 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
21903                 dd_num_npinned_plugs (dd_active_old)++;
21904 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
21905 
21906                 add_gen_plug (active_old_gen_number, ps);
21907 
21908                 if (allocate_in_condemned)
21909                 {
21910                     verify_pins_with_post_plug_info("before aic");
21911 
21912                     new_address =
21913                         allocate_in_condemned_generations (consing_gen,
21914                                                            ps,
21915                                                            active_old_gen_number,
21916 #ifdef SHORT_PLUGS
21917                                                            &convert_to_pinned_p,
21918                                                            (npin_before_pin_p ? plug_end : 0),
21919                                                            seg1,
21920 #endif //SHORT_PLUGS
21921                                                            plug_start REQD_ALIGN_AND_OFFSET_ARG);
21922                     verify_pins_with_post_plug_info("after aic");
21923                 }
21924                 else
21925                 {
21926                     new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
21927 
21928                     if (new_address != 0)
21929                     {
21930                         if (settings.condemned_generation == (max_generation - 1))
21931                         {
21932                             dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
21933                                 plug_start, plug_end,
21934                                 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
21935                                 (size_t)(plug_end - plug_start)));
21936                         }
21937                     }
21938                     else
21939                     {
21940                         allocate_in_condemned = TRUE;
21941 
21942                         new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number,
21943 #ifdef SHORT_PLUGS
21944                                                                          &convert_to_pinned_p,
21945                                                                          (npin_before_pin_p ? plug_end : 0),
21946                                                                          seg1,
21947 #endif //SHORT_PLUGS
21948                                                                          plug_start REQD_ALIGN_AND_OFFSET_ARG);
21949                     }
21950                 }
21951 
21952                 if (convert_to_pinned_p)
21953                 {
21954                     assert (last_npinned_plug_p != FALSE);
21955                     assert (last_pinned_plug_p == FALSE);
21956                     convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
21957                                             ps, artificial_pinned_size);
21958                     enque_pinned_plug (plug_start, FALSE, 0);
21959                     last_pinned_plug = plug_start;
21960                 }
21961                 else
21962                 {
21963                     if (!new_address)
21964                     {
21965                         //verify that we are at then end of the ephemeral segment
21966                         assert (generation_allocation_segment (consing_gen) ==
21967                                 ephemeral_heap_segment);
21968                         //verify that we are near the end
21969                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
21970                                 heap_segment_allocated (ephemeral_heap_segment));
21971                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
21972                                 (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
21973                     }
21974                     else
21975                     {
21976 #ifdef SIMPLE_DPRINTF
21977                         dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)",
21978                             (size_t)(node_gap_size (plug_start)),
21979                             plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
21980                                 (size_t)new_address + ps, ps,
21981                                 (is_plug_padded (plug_start) ? 1 : 0)));
21982 #endif //SIMPLE_DPRINTF
21983 
21984 #ifdef SHORT_PLUGS
21985                         if (is_plug_padded (plug_start))
21986                         {
21987                             dprintf (3, ("%Ix was padded", plug_start));
21988                             dd_padding_size (dd_active_old) += Align (min_obj_size);
21989                         }
21990 #endif //SHORT_PLUGS
21991                     }
21992                 }
21993             }
21994 
21995             if (pinned_plug_p)
21996             {
21997                 if (fire_pinned_plug_events_p)
21998                     FireEtwPinPlugAtGCTime(plug_start, plug_end,
21999                                            (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start)),
22000                                            GetClrInstanceId());
22001 
22002                 if (merge_with_last_pin_p)
22003                 {
22004                     merge_with_last_pinned_plug (last_pinned_plug, ps);
22005                 }
22006                 else
22007                 {
22008                     assert (last_pinned_plug == plug_start);
22009                     set_pinned_info (plug_start, ps, consing_gen);
22010                 }
22011 
22012                 new_address = plug_start;
22013 
22014                 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)",
22015                             (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
22016                             (size_t)plug_end, ps,
22017                             (merge_with_last_pin_p ? 1 : 0)));
22018 
22019                 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
22020                 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
22021                 dd_added_pinned_size (dd_active_old) += added_pinning_size;
22022                 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
22023 
22024                 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
22025                 {
22026                     last_gen1_pin_end = plug_end;
22027                 }
22028             }
22029 
22030 #ifdef _DEBUG
22031             // detect forward allocation in the same segment
22032             assert (!((new_address > plug_start) &&
22033                 (new_address < heap_segment_reserved (seg1))));
22034 #endif //_DEBUG
22035 
22036             if (!merge_with_last_pin_p)
22037             {
22038                 if (current_brick != brick_of (plug_start))
22039                 {
22040                     current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
22041                     sequence_number = 0;
22042                     tree = 0;
22043                 }
22044 
22045                 set_node_relocation_distance (plug_start, (new_address - plug_start));
22046                 if (last_node && (node_relocation_distance (last_node) ==
22047                                   (node_relocation_distance (plug_start) +
22048                                    (int)node_gap_size (plug_start))))
22049                 {
22050                     //dprintf(3,( " Lb"));
22051                     dprintf (3, ("%Ix Lb", plug_start));
22052                     set_node_left (plug_start);
22053                 }
22054                 if (0 == sequence_number)
22055                 {
22056                     dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
22057                     tree = plug_start;
22058                 }
22059 
22060                 verify_pins_with_post_plug_info("before insert node");
22061 
22062                 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
22063                 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
22064                 last_node = plug_start;
22065 
22066 #ifdef _DEBUG
22067                 // If we detect if the last plug is pinned plug right before us, we should save this gap info
22068                 if (!pinned_plug_p)
22069                 {
22070                     if (mark_stack_tos > 0)
22071                     {
22072                         mark& m = mark_stack_array[mark_stack_tos - 1];
22073                         if (m.has_post_plug_info())
22074                         {
22075                             uint8_t* post_plug_info_start = m.saved_post_plug_info_start;
22076                             size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
22077                             if ((uint8_t*)current_plug_gap_start == post_plug_info_start)
22078                             {
22079                                 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
22080                                     *current_plug_gap_start, *(current_plug_gap_start + 1),
22081                                     *(current_plug_gap_start + 2)));
22082                                 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
22083                             }
22084                         }
22085                     }
22086                 }
22087 #endif //_DEBUG
22088 
22089                 verify_pins_with_post_plug_info("after insert node");
22090             }
22091         }
22092 
22093         if (num_pinned_plugs_in_plug > 1)
22094         {
22095             dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
22096         }
22097 
22098         {
22099 #ifdef MARK_LIST
22100             if (use_mark_list)
22101             {
22102                while ((mark_list_next < mark_list_index) &&
22103                       (*mark_list_next <= x))
22104                {
22105                    mark_list_next++;
22106                }
22107                if ((mark_list_next < mark_list_index)
22108 #ifdef MULTIPLE_HEAPS
22109                    && (*mark_list_next < end) //for multiple segments
22110 #endif //MULTIPLE_HEAPS
22111                    )
22112                    x = *mark_list_next;
22113                else
22114                    x = end;
22115             }
22116             else
22117 #endif //MARK_LIST
22118             {
22119                 uint8_t* xl = x;
22120 #ifdef BACKGROUND_GC
22121                 if (current_c_gc_state == c_gc_state_marking)
22122                 {
22123                     assert (recursive_gc_sync::background_running_p());
22124                     while ((xl < end) && !marked (xl))
22125                     {
22126                         dprintf (4, ("-%Ix-", (size_t)xl));
22127                         assert ((size (xl) > 0));
22128                         background_object_marked (xl, TRUE);
22129                         xl = xl + Align (size (xl));
22130                         Prefetch (xl);
22131                     }
22132                 }
22133                 else
22134 #endif //BACKGROUND_GC
22135                 {
22136                     while ((xl < end) && !marked (xl))
22137                     {
22138                         dprintf (4, ("-%Ix-", (size_t)xl));
22139                         assert ((size (xl) > 0));
22140                         xl = xl + Align (size (xl));
22141                         Prefetch (xl);
22142                     }
22143                 }
22144                 assert (xl <= end);
22145                 x = xl;
22146             }
22147         }
22148     }
22149 
22150     while (!pinned_plug_que_empty_p())
22151     {
22152         if (settings.promotion)
22153         {
22154             uint8_t* pplug = pinned_plug (oldest_pin());
22155             if (in_range_for_segment (pplug, ephemeral_heap_segment))
22156             {
22157                 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
22158                 //allocate all of the generation gaps
22159                 while (active_new_gen_number > 0)
22160                 {
22161                     active_new_gen_number--;
22162 
22163                     if (active_new_gen_number == (max_generation - 1))
22164                     {
22165                         maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
22166                         if (!demote_gen1_p)
22167                             advance_pins_for_demotion (consing_gen);
22168                     }
22169 
22170                     generation* gen = generation_of (active_new_gen_number);
22171                     plan_generation_start (gen, consing_gen, 0);
22172 
22173                     if (demotion_low == MAX_PTR)
22174                     {
22175                         demotion_low = pplug;
22176                         dprintf (3, ("end plan: dlow->%Ix", demotion_low));
22177                     }
22178 
22179                     dprintf (2, ("(%d)gen%d plan start: %Ix",
22180                                   heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
22181                     assert (generation_plan_allocation_start (gen));
22182                 }
22183             }
22184         }
22185 
22186         if (pinned_plug_que_empty_p())
22187             break;
22188 
22189         size_t  entry = deque_pinned_plug();
22190         mark*  m = pinned_plug_of (entry);
22191         uint8_t*  plug = pinned_plug (m);
22192         size_t  len = pinned_len (m);
22193 
22194         // detect pinned block in different segment (later) than
22195         // allocation segment
22196         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
22197 
22198         while ((plug < generation_allocation_pointer (consing_gen)) ||
22199                (plug >= heap_segment_allocated (nseg)))
22200         {
22201             assert ((plug < heap_segment_mem (nseg)) ||
22202                     (plug > heap_segment_reserved (nseg)));
22203             //adjust the end of the segment to be the end of the plug
22204             assert (generation_allocation_pointer (consing_gen)>=
22205                     heap_segment_mem (nseg));
22206             assert (generation_allocation_pointer (consing_gen)<=
22207                     heap_segment_committed (nseg));
22208 
22209             heap_segment_plan_allocated (nseg) =
22210                 generation_allocation_pointer (consing_gen);
22211             //switch allocation segment
22212             nseg = heap_segment_next_rw (nseg);
22213             generation_allocation_segment (consing_gen) = nseg;
22214             //reset the allocation pointer and limits
22215             generation_allocation_pointer (consing_gen) =
22216                 heap_segment_mem (nseg);
22217         }
22218 
22219         set_new_pin_info (m, generation_allocation_pointer (consing_gen));
22220         dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
22221             (size_t)(brick_table[brick_of (plug)])));
22222 
22223         generation_allocation_pointer (consing_gen) = plug + len;
22224         generation_allocation_limit (consing_gen) =
22225             generation_allocation_pointer (consing_gen);
22226         //Add the size of the pinned plug to the right pinned allocations
22227         //find out which gen this pinned plug came from
22228         int frgn = object_gennum (plug);
22229         if ((frgn != (int)max_generation) && settings.promotion)
22230         {
22231             generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
22232         }
22233 
22234     }
22235 
22236     plan_generation_starts (consing_gen);
22237     print_free_and_plug ("AP");
22238 
22239     {
22240 #ifdef SIMPLE_DPRINTF
22241         for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22242         {
22243             generation* temp_gen = generation_of (gen_idx);
22244             dynamic_data* temp_dd = dynamic_data_of (gen_idx);
22245 
22246             int added_pinning_ratio = 0;
22247             int artificial_pinned_ratio = 0;
22248 
22249             if (dd_pinned_survived_size (temp_dd) != 0)
22250             {
22251                 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22252                 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22253             }
22254 
22255             size_t padding_size =
22256 #ifdef SHORT_PLUGS
22257                 dd_padding_size (temp_dd);
22258 #else
22259                 0;
22260 #endif //SHORT_PLUGS
22261             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",
22262                 gen_idx,
22263                 generation_allocation_start (temp_gen),
22264                 generation_plan_allocation_start (temp_gen),
22265                 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
22266                 generation_allocation_size (temp_gen),
22267                 generation_pinned_allocation_compact_size (temp_gen),
22268                 generation_pinned_allocation_sweep_size (temp_gen),
22269                 dd_survived_size (temp_dd),
22270                 dd_pinned_survived_size (temp_dd),
22271                 added_pinning_ratio,
22272                 artificial_pinned_ratio,
22273                 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
22274                 padding_size));
22275         }
22276 #endif //SIMPLE_DPRINTF
22277     }
22278 
22279     if (settings.condemned_generation == (max_generation - 1 ))
22280     {
22281         size_t plan_gen2_size = generation_plan_size (max_generation);
22282         size_t growth = plan_gen2_size - old_gen2_size;
22283 
22284         if (growth > 0)
22285         {
22286             dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, gen1 c alloc: %Id",
22287                 growth, generation_end_seg_allocated (generation_of (max_generation)),
22288                 generation_condemned_allocated (generation_of (max_generation - 1))));
22289         }
22290         else
22291         {
22292             dprintf (1, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id",
22293                 (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)),
22294                 generation_condemned_allocated (generation_of (max_generation - 1))));
22295         }
22296 
22297         generation* older_gen = generation_of (settings.condemned_generation + 1);
22298         size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
22299         size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
22300         size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated;
22301         size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated;
22302 
22303         dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
22304                     r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
22305                     r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen),
22306                     r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
22307 
22308         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",
22309             free_list_allocated, rejected_free_space, end_seg_allocated,
22310             condemned_allocated, generation_condemned_allocated (generation_of (settings.condemned_generation))));
22311 
22312         maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info);
22313         maxgen_size_info->free_list_allocated = free_list_allocated;
22314         maxgen_size_info->free_list_rejected = rejected_free_space;
22315         maxgen_size_info->end_seg_allocated = end_seg_allocated;
22316         maxgen_size_info->condemned_allocated = condemned_allocated;
22317         maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance;
22318         maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance;
22319 
22320 #ifdef FREE_USAGE_STATS
22321         int free_list_efficiency = 0;
22322         if ((free_list_allocated + rejected_free_space) != 0)
22323             free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100);
22324 
22325         int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100);
22326 
22327         dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%",
22328                     older_gen->gen_num,
22329                     free_list_efficiency, running_free_list_efficiency));
22330 
22331         dprintf (1, ("gen2 free list change"));
22332         for (int j = 0; j < NUM_GEN_POWER2; j++)
22333         {
22334             dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id",
22335                 heap_number,
22336                 settings.gc_index,
22337                 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j],
22338                 (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
22339                 (generation_of(max_generation - 1))->gen_plugs[j]));
22340         }
22341 #endif //FREE_USAGE_STATS
22342     }
22343 
22344     size_t fragmentation =
22345         generation_fragmentation (generation_of (condemned_gen_number),
22346                                   consing_gen,
22347                                   heap_segment_allocated (ephemeral_heap_segment));
22348 
22349     dprintf (2,("Fragmentation: %Id", fragmentation));
22350     dprintf (2,("---- End of Plan phase ----"));
22351 
22352 #ifdef TIME_GC
22353     finish = GetCycleCount32();
22354     plan_time = finish - start;
22355 #endif //TIME_GC
22356 
22357     // We may update write barrier code.  We assume here EE has been suspended if we are on a GC thread.
22358     assert(IsGCInProgress());
22359 
22360     BOOL should_expand = FALSE;
22361     BOOL should_compact= FALSE;
22362     ephemeral_promotion = FALSE;
22363 
22364 #ifdef BIT64
22365     if ((!settings.concurrent) &&
22366         ((condemned_gen_number < max_generation) &&
22367          ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
22368     {
22369         dprintf (2, ("gen0 reduction count is %d, condemning %d, mem load %d",
22370                      settings.gen0_reduction_count,
22371                      condemned_gen_number,
22372                      settings.entry_memory_load));
22373         should_compact = TRUE;
22374 
22375         get_gc_data_per_heap()->set_mechanism (gc_heap_compact,
22376             ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load));
22377 
22378         if ((condemned_gen_number >= (max_generation - 1)) &&
22379             dt_low_ephemeral_space_p (tuning_deciding_expansion))
22380         {
22381             dprintf (2, ("Not enough space for all ephemeral generations with compaction"));
22382             should_expand = TRUE;
22383         }
22384     }
22385     else
22386     {
22387 #endif // BIT64
22388         should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
22389 #ifdef BIT64
22390     }
22391 #endif // BIT64
22392 
22393 #ifdef FEATURE_LOH_COMPACTION
22394     loh_compacted_p = FALSE;
22395 #endif //FEATURE_LOH_COMPACTION
22396 
22397     if (condemned_gen_number == max_generation)
22398     {
22399 #ifdef FEATURE_LOH_COMPACTION
22400         if (settings.loh_compaction)
22401         {
22402             if (plan_loh())
22403             {
22404                 should_compact = TRUE;
22405                 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced);
22406                 loh_compacted_p = TRUE;
22407             }
22408         }
22409         else
22410         {
22411             if ((heap_number == 0) && (loh_pinned_queue))
22412             {
22413                 loh_pinned_queue_decay--;
22414 
22415                 if (!loh_pinned_queue_decay)
22416                 {
22417                     delete loh_pinned_queue;
22418                     loh_pinned_queue = 0;
22419                 }
22420             }
22421         }
22422 
22423         if (!loh_compacted_p)
22424 #endif //FEATURE_LOH_COMPACTION
22425         {
22426             GCToEEInterface::DiagWalkLOHSurvivors(__this);
22427             sweep_large_objects();
22428         }
22429     }
22430     else
22431     {
22432         settings.loh_compaction = FALSE;
22433     }
22434 
22435 #ifdef MULTIPLE_HEAPS
22436 
22437     new_heap_segment = NULL;
22438 
22439     if (should_compact && should_expand)
22440         gc_policy = policy_expand;
22441     else if (should_compact)
22442         gc_policy = policy_compact;
22443     else
22444         gc_policy = policy_sweep;
22445 
22446     //vote for result of should_compact
22447     dprintf (3, ("Joining for compaction decision"));
22448     gc_t_join.join(this, gc_join_decide_on_compaction);
22449     if (gc_t_join.joined())
22450     {
22451         //safe place to delete large heap segments
22452         if (condemned_gen_number == max_generation)
22453         {
22454             for (int i = 0; i < n_heaps; i++)
22455             {
22456                 g_heaps [i]->rearrange_large_heap_segments ();
22457             }
22458         }
22459 
22460         settings.demotion = FALSE;
22461         int pol_max = policy_sweep;
22462 #ifdef GC_CONFIG_DRIVEN
22463         BOOL is_compaction_mandatory = FALSE;
22464 #endif //GC_CONFIG_DRIVEN
22465 
22466         int i;
22467         for (i = 0; i < n_heaps; i++)
22468         {
22469             if (pol_max < g_heaps[i]->gc_policy)
22470                 pol_max = policy_compact;
22471             // set the demotion flag is any of the heap has demotion
22472             if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
22473             {
22474                 (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit);
22475                 settings.demotion = TRUE;
22476             }
22477 
22478 #ifdef GC_CONFIG_DRIVEN
22479             if (!is_compaction_mandatory)
22480             {
22481                 int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact);
22482                 if (compact_reason >= 0)
22483                 {
22484                     if (gc_heap_compact_reason_mandatory_p[compact_reason])
22485                         is_compaction_mandatory = TRUE;
22486                 }
22487             }
22488 #endif //GC_CONFIG_DRIVEN
22489         }
22490 
22491 #ifdef GC_CONFIG_DRIVEN
22492         if (!is_compaction_mandatory)
22493         {
22494             // If compaction is not mandatory we can feel free to change it to a sweeping GC.
22495             // Note that we may want to change this to only checking every so often instead of every single GC.
22496             if (should_do_sweeping_gc (pol_max >= policy_compact))
22497             {
22498                 pol_max = policy_sweep;
22499             }
22500             else
22501             {
22502                 if (pol_max == policy_sweep)
22503                     pol_max = policy_compact;
22504             }
22505         }
22506 #endif //GC_CONFIG_DRIVEN
22507 
22508         for (i = 0; i < n_heaps; i++)
22509         {
22510             if (pol_max > g_heaps[i]->gc_policy)
22511                 g_heaps[i]->gc_policy = pol_max;
22512             //get the segment while we are serialized
22513             if (g_heaps[i]->gc_policy == policy_expand)
22514             {
22515                 g_heaps[i]->new_heap_segment =
22516                      g_heaps[i]->soh_get_segment_to_expand();
22517                 if (!g_heaps[i]->new_heap_segment)
22518                 {
22519                     set_expand_in_full_gc (condemned_gen_number);
22520                     //we are out of memory, cancel the expansion
22521                     g_heaps[i]->gc_policy = policy_compact;
22522                 }
22523             }
22524         }
22525 
22526         BOOL is_full_compacting_gc = FALSE;
22527 
22528         if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
22529         {
22530             full_gc_counts[gc_type_compacting]++;
22531             is_full_compacting_gc = TRUE;
22532         }
22533 
22534         for (i = 0; i < n_heaps; i++)
22535         {
22536             //copy the card and brick tables
22537             if (g_gc_card_table!= g_heaps[i]->card_table)
22538             {
22539                 g_heaps[i]->copy_brick_card_table();
22540             }
22541 
22542             if (is_full_compacting_gc)
22543             {
22544                 g_heaps[i]->loh_alloc_since_cg = 0;
22545             }
22546         }
22547 
22548         //start all threads on the roots.
22549         dprintf(3, ("Starting all gc threads after compaction decision"));
22550         gc_t_join.restart();
22551     }
22552 
22553     //reset the local variable accordingly
22554     should_compact = (gc_policy >= policy_compact);
22555     should_expand  = (gc_policy >= policy_expand);
22556 
22557 #else //MULTIPLE_HEAPS
22558 
22559     //safe place to delete large heap segments
22560     if (condemned_gen_number == max_generation)
22561     {
22562         rearrange_large_heap_segments ();
22563     }
22564 
22565     settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
22566     if (settings.demotion)
22567         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
22568 
22569 #ifdef GC_CONFIG_DRIVEN
22570     BOOL is_compaction_mandatory = FALSE;
22571     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
22572     if (compact_reason >= 0)
22573         is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason];
22574 
22575     if (!is_compaction_mandatory)
22576     {
22577         if (should_do_sweeping_gc (should_compact))
22578             should_compact = FALSE;
22579         else
22580             should_compact = TRUE;
22581     }
22582 #endif //GC_CONFIG_DRIVEN
22583 
22584     if (should_compact && (condemned_gen_number == max_generation))
22585     {
22586         full_gc_counts[gc_type_compacting]++;
22587         loh_alloc_since_cg = 0;
22588     }
22589 #endif //MULTIPLE_HEAPS
22590 
22591     if (should_compact)
22592     {
22593         dprintf (2,( "**** Doing Compacting GC ****"));
22594 
22595         if (should_expand)
22596         {
22597 #ifndef MULTIPLE_HEAPS
22598             heap_segment* new_heap_segment = soh_get_segment_to_expand();
22599 #endif //!MULTIPLE_HEAPS
22600             if (new_heap_segment)
22601             {
22602                 consing_gen = expand_heap(condemned_gen_number,
22603                                           consing_gen,
22604                                           new_heap_segment);
22605             }
22606 
22607             // If we couldn't get a new segment, or we were able to
22608             // reserve one but no space to commit, we couldn't
22609             // expand heap.
22610             if (ephemeral_heap_segment != new_heap_segment)
22611             {
22612                 set_expand_in_full_gc (condemned_gen_number);
22613                 should_expand = FALSE;
22614             }
22615         }
22616         generation_allocation_limit (condemned_gen1) =
22617             generation_allocation_pointer (condemned_gen1);
22618         if ((condemned_gen_number < max_generation))
22619         {
22620             generation_allocator (older_gen)->commit_alloc_list_changes();
22621 
22622             // Fix the allocation area of the older generation
22623             fix_older_allocation_area (older_gen);
22624         }
22625         assert (generation_allocation_segment (consing_gen) ==
22626                 ephemeral_heap_segment);
22627 
22628         GCToEEInterface::DiagWalkSurvivors(__this);
22629 
22630         relocate_phase (condemned_gen_number, first_condemned_address);
22631         compact_phase (condemned_gen_number, first_condemned_address,
22632                        (!settings.demotion && settings.promotion));
22633         fix_generation_bounds (condemned_gen_number, consing_gen);
22634         assert (generation_allocation_limit (youngest_generation) ==
22635                 generation_allocation_pointer (youngest_generation));
22636         if (condemned_gen_number >= (max_generation -1))
22637         {
22638 #ifdef MULTIPLE_HEAPS
22639             // this needs be serialized just because we have one
22640             // segment_standby_list/seg_table for all heaps. We should make it at least
22641             // so that when hoarding is not on we don't need this join because
22642             // decommitting memory can take a long time.
22643             //must serialize on deleting segments
22644             gc_t_join.join(this, gc_join_rearrange_segs_compaction);
22645             if (gc_t_join.joined())
22646             {
22647                 for (int i = 0; i < n_heaps; i++)
22648                 {
22649                     g_heaps[i]->rearrange_heap_segments(TRUE);
22650                 }
22651                 gc_t_join.restart();
22652             }
22653 #else
22654             rearrange_heap_segments(TRUE);
22655 #endif //MULTIPLE_HEAPS
22656 
22657             if (should_expand)
22658             {
22659                 //fix the start_segment for the ephemeral generations
22660                 for (int i = 0; i < max_generation; i++)
22661                 {
22662                     generation* gen = generation_of (i);
22663                     generation_start_segment (gen) = ephemeral_heap_segment;
22664                     generation_allocation_segment (gen) = ephemeral_heap_segment;
22665                 }
22666             }
22667         }
22668 
22669         {
22670 #ifdef FEATURE_PREMORTEM_FINALIZATION
22671             finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
22672                                                        (!settings.demotion && settings.promotion));
22673 #endif // FEATURE_PREMORTEM_FINALIZATION
22674 
22675 #ifdef MULTIPLE_HEAPS
22676             dprintf(3, ("Joining after end of compaction"));
22677             gc_t_join.join(this, gc_join_adjust_handle_age_compact);
22678             if (gc_t_join.joined())
22679 #endif //MULTIPLE_HEAPS
22680             {
22681 #ifdef MULTIPLE_HEAPS
22682                 //join all threads to make sure they are synchronized
22683                 dprintf(3, ("Restarting after Promotion granted"));
22684                 gc_t_join.restart();
22685 #endif //MULTIPLE_HEAPS
22686             }
22687 
22688             ScanContext sc;
22689             sc.thread_number = heap_number;
22690             sc.promotion = FALSE;
22691             sc.concurrent = FALSE;
22692             // new generations bounds are set can call this guy
22693             if (settings.promotion && !settings.demotion)
22694             {
22695                 dprintf (2, ("Promoting EE roots for gen %d",
22696                              condemned_gen_number));
22697                 GCScan::GcPromotionsGranted(condemned_gen_number,
22698                                                 max_generation, &sc);
22699             }
22700             else if (settings.demotion)
22701             {
22702                 dprintf (2, ("Demoting EE roots for gen %d",
22703                              condemned_gen_number));
22704                 GCScan::GcDemote (condemned_gen_number, max_generation, &sc);
22705             }
22706         }
22707 
22708         {
22709             gen0_big_free_spaces = 0;
22710 
22711             reset_pinned_queue_bos();
22712             unsigned int  gen_number = min (max_generation, 1 + condemned_gen_number);
22713             generation*  gen = generation_of (gen_number);
22714             uint8_t*  low = generation_allocation_start (generation_of (gen_number-1));
22715             uint8_t*  high =  heap_segment_allocated (ephemeral_heap_segment);
22716 
22717             while (!pinned_plug_que_empty_p())
22718             {
22719                 mark*  m = pinned_plug_of (deque_pinned_plug());
22720                 size_t len = pinned_len (m);
22721                 uint8_t*  arr = (pinned_plug (m) - len);
22722                 dprintf(3,("free [%Ix %Ix[ pin",
22723                             (size_t)arr, (size_t)arr + len));
22724                 if (len != 0)
22725                 {
22726                     assert (len >= Align (min_obj_size));
22727                     make_unused_array (arr, len);
22728                     // fix fully contained bricks + first one
22729                     // if the array goes beyond the first brick
22730                     size_t start_brick = brick_of (arr);
22731                     size_t end_brick = brick_of (arr + len);
22732                     if (end_brick != start_brick)
22733                     {
22734                         dprintf (3,
22735                                     ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
22736                                     start_brick, end_brick, (size_t)arr));
22737                         set_brick (start_brick,
22738                                     arr - brick_address (start_brick));
22739                         size_t brick = start_brick+1;
22740                         while (brick < end_brick)
22741                         {
22742                             set_brick (brick, start_brick - brick);
22743                             brick++;
22744                         }
22745                     }
22746 
22747                     //when we take an old segment to make the new
22748                     //ephemeral segment. we can have a bunch of
22749                     //pinned plugs out of order going to the new ephemeral seg
22750                     //and then the next plugs go back to max_generation
22751                     if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
22752                         (heap_segment_reserved (ephemeral_heap_segment) > arr))
22753                     {
22754 
22755                         while ((low <= arr) && (high > arr))
22756                         {
22757                             gen_number--;
22758                             assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
22759                                     settings.demotion || !settings.promotion);
22760                             dprintf (3, ("new free list generation %d", gen_number));
22761 
22762                             gen = generation_of (gen_number);
22763                             if (gen_number >= 1)
22764                                 low = generation_allocation_start (generation_of (gen_number-1));
22765                             else
22766                                 low = high;
22767                         }
22768                     }
22769                     else
22770                     {
22771                         dprintf (3, ("new free list generation %d", max_generation));
22772                         gen_number = max_generation;
22773                         gen = generation_of (gen_number);
22774                     }
22775 
22776                     dprintf(3,("threading it into generation %d", gen_number));
22777                     thread_gap (arr, len, gen);
22778                     add_gen_free (gen_number, len);
22779                 }
22780             }
22781         }
22782 
22783 #ifdef _DEBUG
22784         for (int x = 0; x <= max_generation; x++)
22785         {
22786             assert (generation_allocation_start (generation_of (x)));
22787         }
22788 #endif //_DEBUG
22789 
22790         if (!settings.demotion && settings.promotion)
22791         {
22792             //clear card for generation 1. generation 0 is empty
22793             clear_card_for_addresses (
22794                 generation_allocation_start (generation_of (1)),
22795                 generation_allocation_start (generation_of (0)));
22796         }
22797         if (settings.promotion && !settings.demotion)
22798         {
22799             uint8_t* start = generation_allocation_start (youngest_generation);
22800             MAYBE_UNUSED_VAR(start);
22801             assert (heap_segment_allocated (ephemeral_heap_segment) ==
22802                     (start + Align (size (start))));
22803         }
22804     }
22805     else
22806     {
22807         //force promotion for sweep
22808         settings.promotion = TRUE;
22809         settings.compaction = FALSE;
22810 
22811         ScanContext sc;
22812         sc.thread_number = heap_number;
22813         sc.promotion = FALSE;
22814         sc.concurrent = FALSE;
22815 
22816         dprintf (2, ("**** Doing Mark and Sweep GC****"));
22817 
22818         if ((condemned_gen_number < max_generation))
22819         {
22820             generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
22821             generation_free_list_space (older_gen) = r_free_list_space;
22822             generation_free_obj_space (older_gen) = r_free_obj_space;
22823             generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
22824             generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
22825             generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
22826             generation_allocation_limit (older_gen) = r_allocation_limit;
22827             generation_allocation_pointer (older_gen) = r_allocation_pointer;
22828             generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
22829             generation_allocation_segment (older_gen) = r_allocation_segment;
22830         }
22831 
22832         if ((condemned_gen_number < max_generation))
22833         {
22834             // Fix the allocation area of the older generation
22835             fix_older_allocation_area (older_gen);
22836         }
22837 
22838         GCToEEInterface::DiagWalkSurvivors(__this);
22839 
22840         gen0_big_free_spaces = 0;
22841         make_free_lists (condemned_gen_number);
22842         recover_saved_pinned_info();
22843 
22844 #ifdef FEATURE_PREMORTEM_FINALIZATION
22845         finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
22846 #endif // FEATURE_PREMORTEM_FINALIZATION
22847 // MTHTS: leave single thread for HT processing on plan_phase
22848 #ifdef MULTIPLE_HEAPS
22849         dprintf(3, ("Joining after end of sweep"));
22850         gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
22851         if (gc_t_join.joined())
22852 #endif //MULTIPLE_HEAPS
22853         {
22854             GCScan::GcPromotionsGranted(condemned_gen_number,
22855                                             max_generation, &sc);
22856             if (condemned_gen_number >= (max_generation -1))
22857             {
22858 #ifdef MULTIPLE_HEAPS
22859                 for (int i = 0; i < n_heaps; i++)
22860                 {
22861                     g_heaps[i]->rearrange_heap_segments(FALSE);
22862                 }
22863 #else
22864                 rearrange_heap_segments(FALSE);
22865 #endif //MULTIPLE_HEAPS
22866             }
22867 
22868 #ifdef MULTIPLE_HEAPS
22869             //join all threads to make sure they are synchronized
22870             dprintf(3, ("Restarting after Promotion granted"));
22871             gc_t_join.restart();
22872 #endif //MULTIPLE_HEAPS
22873         }
22874 
22875 #ifdef _DEBUG
22876         for (int x = 0; x <= max_generation; x++)
22877         {
22878             assert (generation_allocation_start (generation_of (x)));
22879         }
22880 #endif //_DEBUG
22881 
22882         //clear card for generation 1. generation 0 is empty
22883         clear_card_for_addresses (
22884             generation_allocation_start (generation_of (1)),
22885             generation_allocation_start (generation_of (0)));
22886         assert ((heap_segment_allocated (ephemeral_heap_segment) ==
22887                  (generation_allocation_start (youngest_generation) +
22888                   Align (min_obj_size))));
22889     }
22890 
22891     //verify_partial();
22892 }
22893 #ifdef _PREFAST_
22894 #pragma warning(pop)
22895 #endif //_PREFAST_
22896 
22897 
22898 /*****************************
22899 Called after compact phase to fix all generation gaps
22900 ********************************/
22901 void gc_heap::fix_generation_bounds (int condemned_gen_number,
22902                                      generation* consing_gen)
22903 {
22904     UNREFERENCED_PARAMETER(consing_gen);
22905 
22906     assert (generation_allocation_segment (consing_gen) ==
22907             ephemeral_heap_segment);
22908 
22909     //assign the planned allocation start to the generation
22910     int gen_number = condemned_gen_number;
22911     int bottom_gen = 0;
22912 
22913     while (gen_number >= bottom_gen)
22914     {
22915         generation*  gen = generation_of (gen_number);
22916         dprintf(3,("Fixing generation pointers for %Ix", gen_number));
22917         if ((gen_number < max_generation) && ephemeral_promotion)
22918         {
22919             make_unused_array (saved_ephemeral_plan_start[gen_number],
22920                                saved_ephemeral_plan_start_size[gen_number]);
22921         }
22922         reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
22923         make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
22924         dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
22925         gen_number--;
22926     }
22927 #ifdef MULTIPLE_HEAPS
22928     if (ephemeral_promotion)
22929     {
22930         //we are creating a generation fault. set the cards.
22931         // and we are only doing this for multiple heaps because in the single heap scenario the
22932         // new ephemeral generations will be empty and there'll be no need to set cards for the
22933         // old ephemeral generations that got promoted into max_generation.
22934         ptrdiff_t delta = 0;
22935 #ifdef SEG_MAPPING_TABLE
22936         heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
22937 #else //SEG_MAPPING_TABLE
22938         heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
22939 #endif //SEG_MAPPING_TABLE
22940 
22941         assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
22942         size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
22943         size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
22944         while (card != end_card)
22945         {
22946             set_card (card);
22947             card++;
22948         }
22949     }
22950 #endif //MULTIPLE_HEAPS
22951     {
22952         alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
22953         //reset the allocated size
22954         uint8_t* start = generation_allocation_start (youngest_generation);
22955         MAYBE_UNUSED_VAR(start);
22956         if (settings.promotion && !settings.demotion)
22957         {
22958             assert ((start + Align (size (start))) ==
22959                     heap_segment_plan_allocated(ephemeral_heap_segment));
22960         }
22961 
22962         heap_segment_allocated(ephemeral_heap_segment)=
22963             heap_segment_plan_allocated(ephemeral_heap_segment);
22964     }
22965 }
22966 
22967 uint8_t* gc_heap::generation_limit (int gen_number)
22968 {
22969     if (settings.promotion)
22970     {
22971         if (gen_number <= 1)
22972             return heap_segment_reserved (ephemeral_heap_segment);
22973         else
22974             return generation_allocation_start (generation_of ((gen_number - 2)));
22975     }
22976     else
22977     {
22978         if (gen_number <= 0)
22979             return heap_segment_reserved (ephemeral_heap_segment);
22980         else
22981             return generation_allocation_start (generation_of ((gen_number - 1)));
22982     }
22983 }
22984 
22985 BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
22986 {
22987     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
22988     size_t size = Align (min_obj_size)*(condemned_gen_number+1);
22989     assert ((start + size) <=
22990             heap_segment_reserved (ephemeral_heap_segment));
22991     if ((start + size) >
22992         heap_segment_committed (ephemeral_heap_segment))
22993     {
22994         if (!grow_heap_segment (ephemeral_heap_segment, start + size))
22995         {
22996             return FALSE;
22997         }
22998     }
22999     return TRUE;
23000 }
23001 
23002 uint8_t* gc_heap::allocate_at_end (size_t size)
23003 {
23004     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23005     size = Align (size);
23006     uint8_t* result = start;
23007     // only called to allocate a min obj so can't overflow here.
23008     assert ((start + size) <=
23009             heap_segment_reserved (ephemeral_heap_segment));
23010     //ensure_gap_allocation took care of it
23011     assert ((start + size) <=
23012             heap_segment_committed (ephemeral_heap_segment));
23013     heap_segment_allocated (ephemeral_heap_segment) += size;
23014     return result;
23015 }
23016 
23017 
23018 void gc_heap::make_free_lists (int condemned_gen_number)
23019 {
23020 #ifdef TIME_GC
23021     unsigned start;
23022     unsigned finish;
23023     start = GetCycleCount32();
23024 #endif //TIME_GC
23025 
23026     //Promotion has to happen in sweep case.
23027     assert (settings.promotion);
23028 
23029     generation* condemned_gen = generation_of (condemned_gen_number);
23030     uint8_t* start_address = generation_allocation_start (condemned_gen);
23031 
23032     size_t  current_brick = brick_of (start_address);
23033     heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23034 
23035     PREFIX_ASSUME(current_heap_segment != NULL);
23036 
23037     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
23038     size_t  end_brick = brick_of (end_address-1);
23039     make_free_args args;
23040     args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
23041     args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
23042                               MAX_PTR :
23043                               (generation_limit (args.free_list_gen_number)));
23044     args.free_list_gen = generation_of (args.free_list_gen_number);
23045     args.highest_plug = 0;
23046 
23047     if ((start_address < end_address) ||
23048         (condemned_gen_number == max_generation))
23049     {
23050         while (1)
23051         {
23052             if ((current_brick > end_brick))
23053             {
23054                 if (args.current_gen_limit == MAX_PTR)
23055                 {
23056                     //We had an empty segment
23057                     //need to allocate the generation start
23058 
23059                     generation* gen = generation_of (max_generation);
23060 
23061                     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
23062 
23063                     PREFIX_ASSUME(start_seg != NULL);
23064 
23065                     uint8_t* gap = heap_segment_mem (start_seg);
23066 
23067                     generation_allocation_start (gen) = gap;
23068                     heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
23069                     make_unused_array (gap, Align (min_obj_size));
23070                     reset_allocation_pointers (gen, gap);
23071                     dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
23072                                  max_generation, (size_t)gap));
23073                     args.current_gen_limit = generation_limit (args.free_list_gen_number);
23074                 }
23075                 if (heap_segment_next_rw (current_heap_segment))
23076                 {
23077                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
23078                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
23079                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23080 
23081                     continue;
23082                 }
23083                 else
23084                 {
23085                     break;
23086                 }
23087             }
23088             {
23089                 int brick_entry =  brick_table [ current_brick ];
23090                 if ((brick_entry >= 0))
23091                 {
23092                     make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
23093                     dprintf(3,("Fixing brick entry %Ix to %Ix",
23094                                current_brick, (size_t)args.highest_plug));
23095                     set_brick (current_brick,
23096                                (args.highest_plug - brick_address (current_brick)));
23097                 }
23098                 else
23099                 {
23100                     if ((brick_entry > -32768))
23101                     {
23102 
23103 #ifdef _DEBUG
23104                         ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
23105                         if ((brick_entry != -32767) && (! ((offset == brick_entry))))
23106                         {
23107                             assert ((brick_entry == -1));
23108                         }
23109 #endif //_DEBUG
23110                         //init to -1 for faster find_first_object
23111                         set_brick (current_brick, -1);
23112                     }
23113                 }
23114             }
23115             current_brick++;
23116         }
23117     }
23118     {
23119         int bottom_gen = 0;
23120         args.free_list_gen_number--;
23121         while (args.free_list_gen_number >= bottom_gen)
23122         {
23123             uint8_t*  gap = 0;
23124             generation* gen2 = generation_of (args.free_list_gen_number);
23125             gap = allocate_at_end (Align(min_obj_size));
23126             generation_allocation_start (gen2) = gap;
23127             reset_allocation_pointers (gen2, gap);
23128             dprintf(3,("Fixing generation start of %d to: %Ix",
23129                        args.free_list_gen_number, (size_t)gap));
23130             PREFIX_ASSUME(gap != NULL);
23131             make_unused_array (gap, Align (min_obj_size));
23132 
23133             args.free_list_gen_number--;
23134         }
23135 
23136         //reset the allocated size
23137         uint8_t* start2 = generation_allocation_start (youngest_generation);
23138         alloc_allocated = start2 + Align (size (start2));
23139     }
23140 
23141 #ifdef TIME_GC
23142     finish = GetCycleCount32();
23143     sweep_time = finish - start;
23144 #endif //TIME_GC
23145 }
23146 
23147 void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args)
23148 {
23149     assert ((tree != NULL));
23150     {
23151         int  right_node = node_right_child (tree);
23152         int left_node = node_left_child (tree);
23153         args->highest_plug = 0;
23154         if (! (0 == tree))
23155         {
23156             if (! (0 == left_node))
23157             {
23158                 make_free_list_in_brick (tree + left_node, args);
23159 
23160             }
23161             {
23162                 uint8_t*  plug = tree;
23163                 size_t  gap_size = node_gap_size (tree);
23164                 uint8_t*  gap = (plug - gap_size);
23165                 dprintf (3,("Making free list %Ix len %d in %d",
23166                 //dprintf (3,("F: %Ix len %Ix in %d",
23167                         (size_t)gap, gap_size, args->free_list_gen_number));
23168                 args->highest_plug = tree;
23169 #ifdef SHORT_PLUGS
23170                 if (is_plug_padded (plug))
23171                 {
23172                     dprintf (3, ("%Ix padded", plug));
23173                     clear_plug_padded (plug);
23174                 }
23175 #endif //SHORT_PLUGS
23176             gen_crossing:
23177                 {
23178                     if ((args->current_gen_limit == MAX_PTR) ||
23179                         ((plug >= args->current_gen_limit) &&
23180                          ephemeral_pointer_p (plug)))
23181                     {
23182                         dprintf(3,(" Crossing Generation boundary at %Ix",
23183                                (size_t)args->current_gen_limit));
23184                         if (!(args->current_gen_limit == MAX_PTR))
23185                         {
23186                             args->free_list_gen_number--;
23187                             args->free_list_gen = generation_of (args->free_list_gen_number);
23188                         }
23189                         dprintf(3,( " Fixing generation start of %d to: %Ix",
23190                                 args->free_list_gen_number, (size_t)gap));
23191 
23192                         reset_allocation_pointers (args->free_list_gen, gap);
23193                         args->current_gen_limit = generation_limit (args->free_list_gen_number);
23194 
23195                         if ((gap_size >= (2*Align (min_obj_size))))
23196                         {
23197                             dprintf(3,(" Splitting the gap in two %Id left",
23198                                    gap_size));
23199                             make_unused_array (gap, Align(min_obj_size));
23200                             gap_size = (gap_size - Align(min_obj_size));
23201                             gap = (gap + Align(min_obj_size));
23202                         }
23203                         else
23204                         {
23205                             make_unused_array (gap, gap_size);
23206                             gap_size = 0;
23207                         }
23208                         goto gen_crossing;
23209                     }
23210                 }
23211 
23212                 thread_gap (gap, gap_size, args->free_list_gen);
23213                 add_gen_free (args->free_list_gen->gen_num, gap_size);
23214             }
23215             if (! (0 == right_node))
23216             {
23217                 make_free_list_in_brick (tree + right_node, args);
23218             }
23219         }
23220     }
23221 }
23222 
23223 void gc_heap::thread_gap (uint8_t* gap_start, size_t size, generation*  gen)
23224 {
23225     assert (generation_allocation_start (gen));
23226     if ((size > 0))
23227     {
23228         if ((gen->gen_num == 0) && (size > CLR_SIZE))
23229         {
23230             gen0_big_free_spaces += size;
23231         }
23232 
23233         assert ((heap_segment_rw (generation_start_segment (gen))!=
23234                  ephemeral_heap_segment) ||
23235                 (gap_start > generation_allocation_start (gen)));
23236         // The beginning of a segment gap is not aligned
23237         assert (size >= Align (min_obj_size));
23238         make_unused_array (gap_start, size,
23239                           (!settings.concurrent && (gen != youngest_generation)),
23240                           (gen->gen_num == max_generation));
23241         dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
23242 
23243         if ((size >= min_free_list))
23244         {
23245             generation_free_list_space (gen) += size;
23246             generation_allocator (gen)->thread_item (gap_start, size);
23247         }
23248         else
23249         {
23250             generation_free_obj_space (gen) += size;
23251         }
23252     }
23253 }
23254 
23255 void gc_heap::loh_thread_gap_front (uint8_t* gap_start, size_t size, generation*  gen)
23256 {
23257     assert (generation_allocation_start (gen));
23258     if (size >= min_free_list)
23259     {
23260         generation_free_list_space (gen) += size;
23261         generation_allocator (gen)->thread_item_front (gap_start, size);
23262     }
23263 }
23264 
23265 void gc_heap::make_unused_array (uint8_t* x, size_t size, BOOL clearp, BOOL resetp)
23266 {
23267     dprintf (3, ("Making unused array [%Ix, %Ix[",
23268         (size_t)x, (size_t)(x+size)));
23269     assert (size >= Align (min_obj_size));
23270 
23271 //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
23272 //    check_batch_mark_array_bits (x, x+size);
23273 //#endif //VERIFY_HEAP && BACKGROUND_GC
23274 
23275     if (resetp)
23276         reset_memory (x, size);
23277 
23278     ((CObjectHeader*)x)->SetFree(size);
23279 
23280 #ifdef BIT64
23281 
23282 #if BIGENDIAN
23283 #error "This won't work on big endian platforms"
23284 #endif
23285 
23286     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23287 
23288     if (size_as_object < size)
23289     {
23290         //
23291         // If the size is more than 4GB, we need to create multiple objects because of
23292         // the Array::m_NumComponents is uint32_t and the high 32 bits of unused array
23293         // size is ignored in regular object size computation.
23294         //
23295         uint8_t * tmp = x + size_as_object;
23296         size_t remaining_size = size - size_as_object;
23297 
23298         while (remaining_size > UINT32_MAX)
23299         {
23300             // Make sure that there will be at least Align(min_obj_size) left
23301             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE)
23302                 - Align (min_obj_size, get_alignment_constant (FALSE));
23303 
23304             ((CObjectHeader*)tmp)->SetFree(current_size);
23305 
23306             remaining_size -= current_size;
23307             tmp += current_size;
23308         }
23309 
23310         ((CObjectHeader*)tmp)->SetFree(remaining_size);
23311     }
23312 #endif
23313 
23314     if (clearp)
23315         clear_card_for_addresses (x, x + Align(size));
23316 }
23317 
23318 // Clear memory set by make_unused_array.
23319 void gc_heap::clear_unused_array (uint8_t* x, size_t size)
23320 {
23321     // Also clear the sync block
23322     *(((PTR_PTR)x)-1) = 0;
23323 
23324     ((CObjectHeader*)x)->UnsetFree();
23325 
23326 #ifdef BIT64
23327 
23328 #if BIGENDIAN
23329 #error "This won't work on big endian platforms"
23330 #endif
23331 
23332     // The memory could have been cleared in the meantime. We have to mirror the algorithm
23333     // from make_unused_array since we cannot depend on the object sizes in memory.
23334     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23335 
23336     if (size_as_object < size)
23337     {
23338         uint8_t * tmp = x + size_as_object;
23339         size_t remaining_size = size - size_as_object;
23340 
23341         while (remaining_size > UINT32_MAX)
23342         {
23343             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE)
23344                 - Align (min_obj_size, get_alignment_constant (FALSE));
23345 
23346             ((CObjectHeader*)tmp)->UnsetFree();
23347 
23348             remaining_size -= current_size;
23349             tmp += current_size;
23350         }
23351 
23352         ((CObjectHeader*)tmp)->UnsetFree();
23353     }
23354 #else
23355     UNREFERENCED_PARAMETER(size);
23356 #endif
23357 }
23358 
23359 inline
23360 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address)
23361 {
23362     uint8_t* candidate = 0;
23363     int cn;
23364     while (1)
23365     {
23366         if (tree < old_address)
23367         {
23368             if ((cn = node_right_child (tree)) != 0)
23369             {
23370                 assert (candidate < tree);
23371                 candidate = tree;
23372                 tree = tree + cn;
23373                 Prefetch (tree - 8);
23374                 continue;
23375             }
23376             else
23377                 break;
23378         }
23379         else if (tree > old_address)
23380         {
23381             if ((cn = node_left_child (tree)) != 0)
23382             {
23383                 tree = tree + cn;
23384                 Prefetch (tree - 8);
23385                 continue;
23386             }
23387             else
23388                 break;
23389         } else
23390             break;
23391     }
23392     if (tree <= old_address)
23393         return tree;
23394     else if (candidate)
23395         return candidate;
23396     else
23397         return tree;
23398 }
23399 
23400 #ifdef FEATURE_BASICFREEZE
23401 bool gc_heap::frozen_object_p (Object* obj)
23402 {
23403     heap_segment* pSegment = gc_heap::find_segment ((uint8_t*)obj, FALSE);
23404     _ASSERTE(pSegment);
23405 
23406     return heap_segment_read_only_p(pSegment);
23407 }
23408 #endif // FEATURE_BASICFREEZE
23409 
23410 #ifdef FEATURE_REDHAWK
23411 // TODO: this was added on RH, we have not done perf runs to see if this is the right
23412 // thing to do for other versions of the CLR.
23413 inline
23414 #endif // FEATURE_REDHAWK
23415 void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL)
23416 {
23417     uint8_t* old_address = *pold_address;
23418     if (!((old_address >= gc_low) && (old_address < gc_high)))
23419 #ifdef MULTIPLE_HEAPS
23420     {
23421         UNREFERENCED_PARAMETER(thread);
23422         if (old_address == 0)
23423             return;
23424         gc_heap* hp = heap_of (old_address);
23425         if ((hp == this) ||
23426             !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
23427             return;
23428     }
23429 #else //MULTIPLE_HEAPS
23430         return ;
23431 #endif //MULTIPLE_HEAPS
23432     // delta translates old_address into address_gc (old_address);
23433     size_t  brick = brick_of (old_address);
23434     int    brick_entry =  brick_table [ brick ];
23435     uint8_t*  new_address = old_address;
23436     if (! ((brick_entry == 0)))
23437     {
23438     retry:
23439         {
23440             while (brick_entry < 0)
23441             {
23442                 brick = (brick + brick_entry);
23443                 brick_entry =  brick_table [ brick ];
23444             }
23445             uint8_t* old_loc = old_address;
23446 
23447             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
23448                                       old_loc);
23449             if ((node <= old_loc))
23450                 new_address = (old_address + node_relocation_distance (node));
23451             else
23452             {
23453                 if (node_left_p (node))
23454                 {
23455                     dprintf(3,(" L: %Ix", (size_t)node));
23456                     new_address = (old_address +
23457                                    (node_relocation_distance (node) +
23458                                     node_gap_size (node)));
23459                 }
23460                 else
23461                 {
23462                     brick = brick - 1;
23463                     brick_entry =  brick_table [ brick ];
23464                     goto retry;
23465                 }
23466             }
23467         }
23468 
23469         *pold_address = new_address;
23470         return;
23471     }
23472 
23473 #ifdef FEATURE_LOH_COMPACTION
23474     if (loh_compacted_p
23475 #ifdef FEATURE_BASICFREEZE
23476         && !frozen_object_p((Object*)old_address)
23477 #endif // FEATURE_BASICFREEZE
23478         )
23479     {
23480         *pold_address = old_address + loh_node_relocation_distance (old_address);
23481     }
23482     else
23483 #endif //FEATURE_LOH_COMPACTION
23484     {
23485         *pold_address = new_address;
23486     }
23487 }
23488 
23489 inline void
23490 gc_heap::check_class_object_demotion (uint8_t* obj)
23491 {
23492 #ifdef COLLECTIBLE_CLASS
23493     if (is_collectible(obj))
23494     {
23495         check_class_object_demotion_internal (obj);
23496     }
23497 #else
23498     UNREFERENCED_PARAMETER(obj);
23499 #endif //COLLECTIBLE_CLASS
23500 }
23501 
23502 #ifdef COLLECTIBLE_CLASS
23503 NOINLINE void
23504 gc_heap::check_class_object_demotion_internal (uint8_t* obj)
23505 {
23506     if (settings.demotion)
23507     {
23508 #ifdef MULTIPLE_HEAPS
23509         // We set the card without checking the demotion range 'cause at this point
23510         // the handle that points to the loader allocator object may or may not have
23511         // been relocated by other GC threads.
23512         set_card (card_of (obj));
23513 #else
23514         THREAD_FROM_HEAP;
23515         uint8_t* class_obj = get_class_object (obj);
23516         dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
23517         uint8_t* temp_class_obj = class_obj;
23518         uint8_t** temp = &temp_class_obj;
23519         relocate_address (temp THREAD_NUMBER_ARG);
23520 
23521         check_demotion_helper (temp, obj);
23522 #endif //MULTIPLE_HEAPS
23523     }
23524 }
23525 
23526 #endif //COLLECTIBLE_CLASS
23527 
23528 inline void
23529 gc_heap::check_demotion_helper (uint8_t** pval, uint8_t* parent_obj)
23530 {
23531     // detect if we are demoting an object
23532     if ((*pval < demotion_high) &&
23533         (*pval >= demotion_low))
23534     {
23535         dprintf(3, ("setting card %Ix:%Ix",
23536                     card_of((uint8_t*)pval),
23537                     (size_t)pval));
23538 
23539         set_card (card_of (parent_obj));
23540     }
23541 #ifdef MULTIPLE_HEAPS
23542     else if (settings.demotion)
23543     {
23544         dprintf (4, ("Demotion active, computing heap_of object"));
23545         gc_heap* hp = heap_of (*pval);
23546         if ((*pval < hp->demotion_high) &&
23547             (*pval >= hp->demotion_low))
23548         {
23549             dprintf(3, ("setting card %Ix:%Ix",
23550                         card_of((uint8_t*)pval),
23551                         (size_t)pval));
23552 
23553             set_card (card_of (parent_obj));
23554         }
23555     }
23556 #endif //MULTIPLE_HEAPS
23557 }
23558 
23559 inline void
23560 gc_heap::reloc_survivor_helper (uint8_t** pval)
23561 {
23562     THREAD_FROM_HEAP;
23563     relocate_address (pval THREAD_NUMBER_ARG);
23564 
23565     check_demotion_helper (pval, (uint8_t*)pval);
23566 }
23567 
23568 inline void
23569 gc_heap::relocate_obj_helper (uint8_t* x, size_t s)
23570 {
23571     THREAD_FROM_HEAP;
23572     if (contain_pointers (x))
23573     {
23574         dprintf (3, ("$%Ix$", (size_t)x));
23575 
23576         go_through_object_nostart (method_table(x), x, s, pval,
23577                             {
23578                                 uint8_t* child = *pval;
23579                                 reloc_survivor_helper (pval);
23580                                 if (child)
23581                                 {
23582                                     dprintf (3, ("%Ix->%Ix->%Ix", (uint8_t*)pval, child, *pval));
23583                                 }
23584                             });
23585 
23586     }
23587     check_class_object_demotion (x);
23588 }
23589 
23590 inline
23591 void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc)
23592 {
23593     THREAD_FROM_HEAP;
23594 
23595     uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0);
23596     relocate_address (address_to_reloc THREAD_NUMBER_ARG);
23597     if (address_to_reloc)
23598     {
23599         dprintf (3, ("SR %Ix: %Ix->%Ix", (uint8_t*)address_to_reloc, old_val, *address_to_reloc));
23600     }
23601 
23602     //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval);
23603     uint8_t* relocated_addr = *address_to_reloc;
23604     if ((relocated_addr < demotion_high) &&
23605         (relocated_addr >= demotion_low))
23606     {
23607         dprintf (3, ("set card for location %Ix(%Ix)",
23608                     (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23609 
23610         set_card (card_of ((uint8_t*)address_to_set_card));
23611     }
23612 #ifdef MULTIPLE_HEAPS
23613     else if (settings.demotion)
23614     {
23615         gc_heap* hp = heap_of (relocated_addr);
23616         if ((relocated_addr < hp->demotion_high) &&
23617             (relocated_addr >= hp->demotion_low))
23618         {
23619             dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)",
23620                         relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23621 
23622             set_card (card_of ((uint8_t*)address_to_set_card));
23623         }
23624     }
23625 #endif //MULTIPLE_HEAPS
23626 }
23627 
23628 void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
23629 {
23630     THREAD_FROM_HEAP;
23631     uint8_t* plug = pinned_plug (pinned_plug_entry);
23632     uint8_t* pre_plug_start = plug - sizeof (plug_and_gap);
23633     // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
23634     // address. Consider this scenario:
23635     // gen1 start | 3-ptr sized NP | PP
23636     // 0          | 0x18           | 0x30
23637     // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
23638     // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
23639     // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree).
23640     pre_plug_start += sizeof (uint8_t*);
23641     uint8_t** old_address = &pre_plug_start;
23642 
23643     uint8_t* old_val = (old_address ? *old_address : 0);
23644     relocate_address (old_address THREAD_NUMBER_ARG);
23645     if (old_address)
23646     {
23647         dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix",
23648             (uint8_t*)old_address, old_val, *old_address, (pre_plug_start - sizeof (uint8_t*))));
23649     }
23650 
23651     pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (uint8_t*));
23652 }
23653 
23654 inline
23655 void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned)
23656 {
23657     THREAD_FROM_HEAP;
23658     uint8_t* plug = pinned_plug (pinned_plug_entry);
23659 
23660     if (!is_pinned)
23661     {
23662         //// Temporary - we just wanna make sure we are doing things right when padding is needed.
23663         //if ((x + s) < plug)
23664         //{
23665         //    dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix",
23666         //        x, (x + s), (plug- (x + s)), plug));
23667         //    GCToOSInterface::DebugBreak();
23668         //}
23669 
23670         relocate_pre_plug_info (pinned_plug_entry);
23671     }
23672 
23673     verify_pins_with_post_plug_info("after relocate_pre_plug_info");
23674 
23675     uint8_t* saved_plug_info_start = 0;
23676     uint8_t** saved_info_to_relocate = 0;
23677 
23678     if (is_pinned)
23679     {
23680         saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start());
23681         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23682     }
23683     else
23684     {
23685         saved_plug_info_start = (plug - sizeof (plug_and_gap));
23686         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
23687     }
23688 
23689     uint8_t** current_saved_info_to_relocate = 0;
23690     uint8_t* child = 0;
23691 
23692     dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
23693 
23694     if (contain_pointers (x))
23695     {
23696         dprintf (3,("$%Ix$", (size_t)x));
23697 
23698         go_through_object_nostart (method_table(x), x, s, pval,
23699         {
23700             dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (uint8_t*)pval, *pval));
23701 
23702             if ((uint8_t*)pval >= end)
23703             {
23704                 current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**);
23705                 child = *current_saved_info_to_relocate;
23706                 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
23707                 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
23708                     (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
23709             }
23710             else
23711             {
23712                 reloc_survivor_helper (pval);
23713             }
23714         });
23715     }
23716 
23717     check_class_object_demotion (x);
23718 }
23719 
23720 void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end)
23721 {
23722     uint8_t*  x = plug;
23723     while (x < plug_end)
23724     {
23725         size_t s = size (x);
23726         uint8_t* next_obj = x + Align (s);
23727         Prefetch (next_obj);
23728         relocate_obj_helper (x, s);
23729         assert (s > 0);
23730         x = next_obj;
23731     }
23732 }
23733 
23734 // if we expanded, right now we are not handling it as We are not saving the new reloc info.
23735 void gc_heap::verify_pins_with_post_plug_info (const char* msg)
23736 {
23737 #if defined  (_DEBUG) && defined (VERIFY_HEAP)
23738     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
23739     {
23740         if (!verify_pinned_queue_p)
23741             return;
23742 
23743         if (settings.heap_expansion)
23744             return;
23745 
23746         for (size_t i = 0; i < mark_stack_tos; i++)
23747         {
23748             mark& m = mark_stack_array[i];
23749 
23750             mark* pinned_plug_entry = pinned_plug_of(i);
23751 
23752             if (pinned_plug_entry->has_post_plug_info() &&
23753                 pinned_plug_entry->post_short_p() &&
23754                 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
23755             {
23756                 uint8_t* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
23757                 // object after pin
23758                 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d",
23759                     next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
23760                     (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
23761 
23762                 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
23763 
23764                 if (node_gap_size (next_obj) != *post_plug_debug)
23765                 {
23766                     dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix",
23767                         next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
23768                     FATAL_GC_ERROR();
23769                 }
23770                 post_plug_debug++;
23771                 // can't do node_relocation_distance here as it clears the left bit.
23772                 //if (node_relocation_distance (next_obj) != *post_plug_debug)
23773                 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
23774                 {
23775                     dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix",
23776                         next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
23777                     FATAL_GC_ERROR();
23778                 }
23779                 if (node_left_child (next_obj) > 0)
23780                 {
23781                     dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
23782                     FATAL_GC_ERROR();
23783                 }
23784             }
23785         }
23786 
23787         dprintf (3, ("%s verified", msg));
23788     }
23789 #else // _DEBUG && VERIFY_HEAP
23790     UNREFERENCED_PARAMETER(msg);
23791 #endif // _DEBUG && VERIFY_HEAP
23792 }
23793 
23794 #ifdef COLLECTIBLE_CLASS
23795 // We don't want to burn another ptr size space for pinned plugs to record this so just
23796 // set the card unconditionally for collectible objects if we are demoting.
23797 inline void
23798 gc_heap::unconditional_set_card_collectible (uint8_t* obj)
23799 {
23800     if (settings.demotion)
23801     {
23802         set_card (card_of (obj));
23803     }
23804 }
23805 #endif //COLLECTIBLE_CLASS
23806 
23807 void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry)
23808 {
23809     uint8_t*  x = plug;
23810     uint8_t* p_plug = pinned_plug (pinned_plug_entry);
23811     BOOL is_pinned = (plug == p_plug);
23812     BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
23813 
23814     plug_end += sizeof (gap_reloc_pair);
23815 
23816     //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
23817     dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
23818 
23819     verify_pins_with_post_plug_info("begin reloc short surv");
23820 
23821     while (x < plug_end)
23822     {
23823         if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
23824         {
23825             dprintf (3, ("last obj %Ix is short", x));
23826 
23827             if (is_pinned)
23828             {
23829 #ifdef COLLECTIBLE_CLASS
23830                 if (pinned_plug_entry->post_short_collectible_p())
23831                     unconditional_set_card_collectible (x);
23832 #endif //COLLECTIBLE_CLASS
23833 
23834                 // Relocate the saved references based on bits set.
23835                 uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start());
23836                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23837                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
23838                 {
23839                     if (pinned_plug_entry->post_short_bit_p (i))
23840                     {
23841                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
23842                     }
23843                 }
23844             }
23845             else
23846             {
23847 #ifdef COLLECTIBLE_CLASS
23848                 if (pinned_plug_entry->pre_short_collectible_p())
23849                     unconditional_set_card_collectible (x);
23850 #endif //COLLECTIBLE_CLASS
23851 
23852                 relocate_pre_plug_info (pinned_plug_entry);
23853 
23854                 // Relocate the saved references based on bits set.
23855                 uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap));
23856                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
23857                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
23858                 {
23859                     if (pinned_plug_entry->pre_short_bit_p (i))
23860                     {
23861                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
23862                     }
23863                 }
23864             }
23865 
23866             break;
23867         }
23868 
23869         size_t s = size (x);
23870         uint8_t* next_obj = x + Align (s);
23871         Prefetch (next_obj);
23872 
23873         if (next_obj >= plug_end)
23874         {
23875             dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix",
23876                 next_obj, plug, plug_end));
23877 
23878             verify_pins_with_post_plug_info("before reloc short obj");
23879 
23880             relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
23881         }
23882         else
23883         {
23884             relocate_obj_helper (x, s);
23885         }
23886 
23887         assert (s > 0);
23888         x = next_obj;
23889     }
23890 
23891     verify_pins_with_post_plug_info("end reloc short surv");
23892 }
23893 
23894 void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end,
23895                                           BOOL check_last_object_p,
23896                                           mark* pinned_plug_entry)
23897 {
23898     //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
23899     dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
23900 
23901     if (check_last_object_p)
23902     {
23903         relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
23904     }
23905     else
23906     {
23907         relocate_survivor_helper (plug, plug_end);
23908     }
23909 }
23910 
23911 void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args)
23912 {
23913     assert ((tree != NULL));
23914 
23915     dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
23916         tree, args->last_plug,
23917         (tree + node_left_child (tree)),
23918         (tree + node_right_child (tree)),
23919         node_gap_size (tree)));
23920 
23921     if (node_left_child (tree))
23922     {
23923         relocate_survivors_in_brick (tree + node_left_child (tree), args);
23924     }
23925     {
23926         uint8_t*  plug = tree;
23927         BOOL   has_post_plug_info_p = FALSE;
23928         BOOL   has_pre_plug_info_p = FALSE;
23929 
23930         if (tree == oldest_pinned_plug)
23931         {
23932             args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
23933                                                                &has_post_plug_info_p);
23934             assert (tree == pinned_plug (args->pinned_plug_entry));
23935 
23936             dprintf (3, ("tree is the oldest pin: %Ix", tree));
23937         }
23938         if (args->last_plug)
23939         {
23940             size_t  gap_size = node_gap_size (tree);
23941             uint8_t*  gap = (plug - gap_size);
23942             dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
23943             assert (gap_size >= Align (min_obj_size));
23944             uint8_t*  last_plug_end = gap;
23945 
23946             BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
23947 
23948             {
23949                 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
23950             }
23951         }
23952         else
23953         {
23954             assert (!has_pre_plug_info_p);
23955         }
23956 
23957         args->last_plug = plug;
23958         args->is_shortened = has_post_plug_info_p;
23959         if (has_post_plug_info_p)
23960         {
23961             dprintf (3, ("setting %Ix as shortened", plug));
23962         }
23963         dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
23964     }
23965     if (node_right_child (tree))
23966     {
23967         relocate_survivors_in_brick (tree + node_right_child (tree), args);
23968     }
23969 }
23970 
23971 inline
23972 void gc_heap::update_oldest_pinned_plug()
23973 {
23974     oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
23975 }
23976 
23977 void gc_heap::relocate_survivors (int condemned_gen_number,
23978                                   uint8_t* first_condemned_address)
23979 {
23980     generation* condemned_gen = generation_of (condemned_gen_number);
23981     uint8_t*  start_address = first_condemned_address;
23982     size_t  current_brick = brick_of (start_address);
23983     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23984 
23985     PREFIX_ASSUME(current_heap_segment != NULL);
23986 
23987     uint8_t*  end_address = 0;
23988 
23989     reset_pinned_queue_bos();
23990     update_oldest_pinned_plug();
23991 
23992     end_address = heap_segment_allocated (current_heap_segment);
23993 
23994     size_t  end_brick = brick_of (end_address - 1);
23995     relocate_args args;
23996     args.low = gc_low;
23997     args.high = gc_high;
23998     args.is_shortened = FALSE;
23999     args.pinned_plug_entry = 0;
24000     args.last_plug = 0;
24001     while (1)
24002     {
24003         if (current_brick > end_brick)
24004         {
24005             if (args.last_plug)
24006             {
24007                 {
24008                     assert (!(args.is_shortened));
24009                     relocate_survivors_in_plug (args.last_plug,
24010                                                 heap_segment_allocated (current_heap_segment),
24011                                                 args.is_shortened,
24012                                                 args.pinned_plug_entry);
24013                 }
24014 
24015                 args.last_plug = 0;
24016             }
24017 
24018             if (heap_segment_next_rw (current_heap_segment))
24019             {
24020                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24021                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24022                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24023                 continue;
24024             }
24025             else
24026             {
24027                 break;
24028             }
24029         }
24030         {
24031             int brick_entry =  brick_table [ current_brick ];
24032 
24033             if (brick_entry >= 0)
24034             {
24035                 relocate_survivors_in_brick (brick_address (current_brick) +
24036                                              brick_entry -1,
24037                                              &args);
24038             }
24039         }
24040         current_brick++;
24041     }
24042 }
24043 
24044 void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args)
24045 {
24046     if (check_last_object_p)
24047     {
24048         size += sizeof (gap_reloc_pair);
24049         mark* entry = args->pinned_plug_entry;
24050 
24051         if (args->is_shortened)
24052         {
24053             assert (entry->has_post_plug_info());
24054             entry->swap_post_plug_and_saved_for_profiler();
24055         }
24056         else
24057         {
24058             assert (entry->has_pre_plug_info());
24059             entry->swap_pre_plug_and_saved_for_profiler();
24060         }
24061     }
24062 
24063     ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
24064     STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
24065     ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
24066 
24067     (args->fn) (plug, (plug + size), reloc, args->profiling_context, settings.compaction, FALSE);
24068 
24069     if (check_last_object_p)
24070     {
24071         mark* entry = args->pinned_plug_entry;
24072 
24073         if (args->is_shortened)
24074         {
24075             entry->swap_post_plug_and_saved_for_profiler();
24076         }
24077         else
24078         {
24079             entry->swap_pre_plug_and_saved_for_profiler();
24080         }
24081     }
24082 }
24083 
24084 void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args)
24085 {
24086     assert ((tree != NULL));
24087     if (node_left_child (tree))
24088     {
24089         walk_relocation_in_brick (tree + node_left_child (tree), args);
24090     }
24091 
24092     uint8_t*  plug = tree;
24093     BOOL   has_pre_plug_info_p = FALSE;
24094     BOOL   has_post_plug_info_p = FALSE;
24095 
24096     if (tree == oldest_pinned_plug)
24097     {
24098         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24099                                                            &has_post_plug_info_p);
24100         assert (tree == pinned_plug (args->pinned_plug_entry));
24101     }
24102 
24103     if (args->last_plug != 0)
24104     {
24105         size_t gap_size = node_gap_size (tree);
24106         uint8_t*  gap = (plug - gap_size);
24107         uint8_t*  last_plug_end = gap;
24108         size_t last_plug_size = (last_plug_end - args->last_plug);
24109         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix",
24110             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24111 
24112         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24113         if (!check_last_object_p)
24114         {
24115             assert (last_plug_size >= Align (min_obj_size));
24116         }
24117 
24118         walk_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24119     }
24120     else
24121     {
24122         assert (!has_pre_plug_info_p);
24123     }
24124 
24125     dprintf (3, ("set args last plug to plug: %Ix", plug));
24126     args->last_plug = plug;
24127     args->is_shortened = has_post_plug_info_p;
24128 
24129     if (node_right_child (tree))
24130     {
24131         walk_relocation_in_brick (tree + node_right_child (tree), args);
24132     }
24133 }
24134 
24135 void gc_heap::walk_relocation (size_t profiling_context, record_surv_fn fn)
24136 {
24137     generation* condemned_gen = generation_of (settings.condemned_generation);
24138     uint8_t*  start_address = generation_allocation_start (condemned_gen);
24139     size_t  current_brick = brick_of (start_address);
24140     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24141 
24142     PREFIX_ASSUME(current_heap_segment != NULL);
24143 
24144     reset_pinned_queue_bos();
24145     update_oldest_pinned_plug();
24146     size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24147     walk_relocate_args args;
24148     args.is_shortened = FALSE;
24149     args.pinned_plug_entry = 0;
24150     args.last_plug = 0;
24151     args.profiling_context = profiling_context;
24152     args.fn = fn;
24153 
24154     while (1)
24155     {
24156         if (current_brick > end_brick)
24157         {
24158             if (args.last_plug)
24159             {
24160                 walk_plug (args.last_plug,
24161                            (heap_segment_allocated (current_heap_segment) - args.last_plug),
24162                            args.is_shortened,
24163                            &args);
24164                 args.last_plug = 0;
24165             }
24166             if (heap_segment_next_rw (current_heap_segment))
24167             {
24168                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24169                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24170                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24171                 continue;
24172             }
24173             else
24174             {
24175                 break;
24176             }
24177         }
24178         {
24179             int brick_entry =  brick_table [ current_brick ];
24180             if (brick_entry >= 0)
24181             {
24182                 walk_relocation_in_brick (brick_address (current_brick) +
24183                                           brick_entry - 1,
24184                                           &args);
24185             }
24186         }
24187         current_brick++;
24188     }
24189 }
24190 
24191 void gc_heap::walk_survivors (record_surv_fn fn, size_t context, walk_surv_type type)
24192 {
24193     if (type == walk_for_gc)
24194         walk_survivors_relocation (context, fn);
24195 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24196     else if (type == walk_for_bgc)
24197         walk_survivors_for_bgc (context, fn);
24198 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
24199     else if (type == walk_for_loh)
24200         walk_survivors_for_loh (context, fn);
24201     else
24202         assert (!"unknown type!");
24203 }
24204 
24205 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24206 void gc_heap::walk_survivors_for_bgc (size_t profiling_context, record_surv_fn fn)
24207 {
24208     // This should only be called for BGCs
24209     assert(settings.concurrent);
24210 
24211     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24212 
24213     BOOL small_object_segments = TRUE;
24214     int align_const = get_alignment_constant (small_object_segments);
24215 
24216     while (1)
24217     {
24218         if (seg == 0)
24219         {
24220             if (small_object_segments)
24221             {
24222                 //switch to large segment
24223                 small_object_segments = FALSE;
24224 
24225                 align_const = get_alignment_constant (small_object_segments);
24226                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24227 
24228                 PREFIX_ASSUME(seg != NULL);
24229 
24230                 continue;
24231             }
24232             else
24233                 break;
24234         }
24235 
24236         uint8_t* o = heap_segment_mem (seg);
24237         uint8_t* end = heap_segment_allocated (seg);
24238 
24239         while (o < end)
24240         {
24241             if (method_table(o) == g_pFreeObjectMethodTable)
24242             {
24243                 o += Align (size (o), align_const);
24244                 continue;
24245             }
24246 
24247             // It's survived. Make a fake plug, starting at o,
24248             // and send the event
24249 
24250             uint8_t* plug_start = o;
24251 
24252             while (method_table(o) != g_pFreeObjectMethodTable)
24253             {
24254                 o += Align (size (o), align_const);
24255                 if (o >= end)
24256                 {
24257                     break;
24258                 }
24259             }
24260 
24261             uint8_t* plug_end = o;
24262 
24263             fn (plug_start,
24264                 plug_end,
24265                 0,              // Reloc distance == 0 as this is non-compacting
24266                 profiling_context,
24267                 FALSE,          // Non-compacting
24268                 TRUE);          // BGC
24269         }
24270 
24271         seg = heap_segment_next (seg);
24272     }
24273 }
24274 #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24275 
24276 void gc_heap::relocate_phase (int condemned_gen_number,
24277                               uint8_t* first_condemned_address)
24278 {
24279     ScanContext sc;
24280     sc.thread_number = heap_number;
24281     sc.promotion = FALSE;
24282     sc.concurrent = FALSE;
24283 
24284 
24285 #ifdef TIME_GC
24286         unsigned start;
24287         unsigned finish;
24288         start = GetCycleCount32();
24289 #endif //TIME_GC
24290 
24291 //  %type%  category = quote (relocate);
24292     dprintf (2,("---- Relocate phase -----"));
24293 
24294 #ifdef MULTIPLE_HEAPS
24295     //join all threads to make sure they are synchronized
24296     dprintf(3, ("Joining after end of plan"));
24297     gc_t_join.join(this, gc_join_begin_relocate_phase);
24298     if (gc_t_join.joined())
24299 #endif //MULTIPLE_HEAPS
24300 
24301     {
24302 #ifdef MULTIPLE_HEAPS
24303 
24304         //join all threads to make sure they are synchronized
24305         dprintf(3, ("Restarting for relocation"));
24306         gc_t_join.restart();
24307 #endif //MULTIPLE_HEAPS
24308     }
24309 
24310     dprintf(3,("Relocating roots"));
24311     GCScan::GcScanRoots(GCHeap::Relocate,
24312                             condemned_gen_number, max_generation, &sc);
24313 
24314     verify_pins_with_post_plug_info("after reloc stack");
24315 
24316 #ifdef BACKGROUND_GC
24317     if (recursive_gc_sync::background_running_p())
24318     {
24319         scan_background_roots (GCHeap::Relocate, heap_number, &sc);
24320     }
24321 #endif //BACKGROUND_GC
24322 
24323     if (condemned_gen_number != max_generation)
24324     {
24325         dprintf(3,("Relocating cross generation pointers"));
24326         mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
24327         verify_pins_with_post_plug_info("after reloc cards");
24328     }
24329     if (condemned_gen_number != max_generation)
24330     {
24331         dprintf(3,("Relocating cross generation pointers for large objects"));
24332         mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
24333     }
24334     else
24335     {
24336 #ifdef FEATURE_LOH_COMPACTION
24337         if (loh_compacted_p)
24338         {
24339             assert (settings.condemned_generation == max_generation);
24340             relocate_in_loh_compact();
24341         }
24342         else
24343 #endif //FEATURE_LOH_COMPACTION
24344         {
24345             relocate_in_large_objects ();
24346         }
24347     }
24348     {
24349         dprintf(3,("Relocating survivors"));
24350         relocate_survivors (condemned_gen_number,
24351                             first_condemned_address);
24352     }
24353 
24354 #ifdef FEATURE_PREMORTEM_FINALIZATION
24355         dprintf(3,("Relocating finalization data"));
24356         finalize_queue->RelocateFinalizationData (condemned_gen_number,
24357                                                        __this);
24358 #endif // FEATURE_PREMORTEM_FINALIZATION
24359 
24360 
24361 // MTHTS
24362     {
24363         dprintf(3,("Relocating handle table"));
24364         GCScan::GcScanHandles(GCHeap::Relocate,
24365                                   condemned_gen_number, max_generation, &sc);
24366     }
24367 
24368 #ifdef MULTIPLE_HEAPS
24369     //join all threads to make sure they are synchronized
24370     dprintf(3, ("Joining after end of relocation"));
24371     gc_t_join.join(this, gc_join_relocate_phase_done);
24372 
24373 #endif //MULTIPLE_HEAPS
24374 
24375 #ifdef TIME_GC
24376         finish = GetCycleCount32();
24377         reloc_time = finish - start;
24378 #endif //TIME_GC
24379 
24380     dprintf(2,( "---- End of Relocate phase ----"));
24381 }
24382 
24383 // This compares to see if tree is the current pinned plug and returns info
24384 // for this pinned plug. Also advances the pinned queue if that's the case.
24385 //
24386 // We don't change the values of the plug info if tree is not the same as
24387 // the current pinned plug - the caller is responsible for setting the right
24388 // values to begin with.
24389 //
24390 // POPO TODO: We are keeping this temporarily as this is also used by realloc
24391 // where it passes FALSE to deque_p, change it to use the same optimization
24392 // as relocate. Not as essential since realloc is already a slow path.
24393 mark* gc_heap::get_next_pinned_entry (uint8_t* tree,
24394                                       BOOL* has_pre_plug_info_p,
24395                                       BOOL* has_post_plug_info_p,
24396                                       BOOL deque_p)
24397 {
24398     if (!pinned_plug_que_empty_p())
24399     {
24400         mark* oldest_entry = oldest_pin();
24401         uint8_t* oldest_plug = pinned_plug (oldest_entry);
24402         if (tree == oldest_plug)
24403         {
24404             *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24405             *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24406 
24407             if (deque_p)
24408             {
24409                 deque_pinned_plug();
24410             }
24411 
24412             dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d",
24413                 tree,
24414                 (*has_pre_plug_info_p ? 1 : 0),
24415                 (*has_post_plug_info_p ? 1 : 0)));
24416 
24417             return oldest_entry;
24418         }
24419     }
24420 
24421     return NULL;
24422 }
24423 
24424 // This also deques the oldest entry and update the oldest plug
24425 mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p,
24426                                         BOOL* has_post_plug_info_p)
24427 {
24428     mark* oldest_entry = oldest_pin();
24429     *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24430     *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24431 
24432     deque_pinned_plug();
24433     update_oldest_pinned_plug();
24434     return oldest_entry;
24435 }
24436 
24437 inline
24438 void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24439 {
24440     if (copy_cards_p)
24441         copy_cards_for_addresses (dest, src, len);
24442     else
24443         clear_card_for_addresses (dest, dest + len);
24444 }
24445 
24446 // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
24447 // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
24448 // we won't need to individually recover each overwritten part of plugs.
24449 inline
24450 void  gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24451 {
24452     if (dest != src)
24453     {
24454 #ifdef BACKGROUND_GC
24455         if (current_c_gc_state == c_gc_state_marking)
24456         {
24457             //TODO: should look to see whether we should consider changing this
24458             // to copy a consecutive region of the mark array instead.
24459             copy_mark_bits_for_addresses (dest, src, len);
24460         }
24461 #endif //BACKGROUND_GC
24462         //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24463         dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24464         memcopy (dest - plug_skew, src - plug_skew, (int)len);
24465 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24466         if (SoftwareWriteWatch::IsEnabledForGCHeap())
24467         {
24468             // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC
24469             // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the
24470             // object at (src + len), so it can be ignored anyway.
24471             SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew);
24472         }
24473 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24474         copy_cards_range (dest, src, len, copy_cards_p);
24475     }
24476 }
24477 
24478 void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args)
24479 {
24480     args->print();
24481     uint8_t* reloc_plug = plug + args->last_plug_relocation;
24482 
24483     if (check_last_object_p)
24484     {
24485         size += sizeof (gap_reloc_pair);
24486         mark* entry = args->pinned_plug_entry;
24487 
24488         if (args->is_shortened)
24489         {
24490             assert (entry->has_post_plug_info());
24491             entry->swap_post_plug_and_saved();
24492         }
24493         else
24494         {
24495             assert (entry->has_pre_plug_info());
24496             entry->swap_pre_plug_and_saved();
24497         }
24498     }
24499 
24500     int  old_brick_entry =  brick_table [brick_of (plug)];
24501 
24502     assert (node_relocation_distance (plug) == args->last_plug_relocation);
24503 
24504 #ifdef FEATURE_STRUCTALIGN
24505     ptrdiff_t alignpad = node_alignpad(plug);
24506     if (alignpad)
24507     {
24508         make_unused_array (reloc_plug - alignpad, alignpad);
24509         if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
24510         {
24511             // The alignment padding is straddling one or more bricks;
24512             // it has to be the last "object" of its first brick.
24513             fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
24514         }
24515     }
24516 #else // FEATURE_STRUCTALIGN
24517     size_t unused_arr_size = 0;
24518     BOOL  already_padded_p = FALSE;
24519 #ifdef SHORT_PLUGS
24520     if (is_plug_padded (plug))
24521     {
24522         already_padded_p = TRUE;
24523         clear_plug_padded (plug);
24524         unused_arr_size = Align (min_obj_size);
24525     }
24526 #endif //SHORT_PLUGS
24527     if (node_realigned (plug))
24528     {
24529         unused_arr_size += switch_alignment_size (already_padded_p);
24530     }
24531 
24532     if (unused_arr_size != 0)
24533     {
24534         make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
24535 
24536         if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
24537         {
24538             dprintf (3, ("fix B for padding: %Id: %Ix->%Ix",
24539                 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
24540             // The alignment padding is straddling one or more bricks;
24541             // it has to be the last "object" of its first brick.
24542             fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
24543         }
24544     }
24545 #endif // FEATURE_STRUCTALIGN
24546 
24547 #ifdef SHORT_PLUGS
24548     if (is_plug_padded (plug))
24549     {
24550         make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
24551 
24552         if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
24553         {
24554             // The alignment padding is straddling one or more bricks;
24555             // it has to be the last "object" of its first brick.
24556             fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
24557         }
24558     }
24559 #endif //SHORT_PLUGS
24560 
24561     gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
24562 
24563     if (args->check_gennum_p)
24564     {
24565         int src_gennum = args->src_gennum;
24566         if (src_gennum == -1)
24567         {
24568             src_gennum = object_gennum (plug);
24569         }
24570 
24571         int dest_gennum = object_gennum_plan (reloc_plug);
24572 
24573         if (src_gennum < dest_gennum)
24574         {
24575             generation_allocation_size (generation_of (dest_gennum)) += size;
24576         }
24577     }
24578 
24579     size_t current_reloc_brick = args->current_compacted_brick;
24580 
24581     if (brick_of (reloc_plug) != current_reloc_brick)
24582     {
24583         dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix",
24584             current_reloc_brick, brick_of (reloc_plug)));
24585 
24586         if (args->before_last_plug)
24587         {
24588             dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
24589                      current_reloc_brick,
24590                      args->before_last_plug,
24591                      (args->before_last_plug - brick_address (current_reloc_brick))));
24592 
24593             {
24594                 set_brick (current_reloc_brick,
24595                         args->before_last_plug - brick_address (current_reloc_brick));
24596             }
24597         }
24598         current_reloc_brick = brick_of (reloc_plug);
24599     }
24600     size_t end_brick = brick_of (reloc_plug + size-1);
24601     if (end_brick != current_reloc_brick)
24602     {
24603         // The plug is straddling one or more bricks
24604         // It has to be the last plug of its first brick
24605         dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
24606                  current_reloc_brick, (size_t)reloc_plug,
24607                  (reloc_plug - brick_address (current_reloc_brick))));
24608 
24609         {
24610             set_brick (current_reloc_brick,
24611                     reloc_plug - brick_address (current_reloc_brick));
24612         }
24613         // update all intervening brick
24614         size_t brick = current_reloc_brick + 1;
24615         dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
24616             brick, (end_brick - 1)));
24617         while (brick < end_brick)
24618         {
24619             set_brick (brick, -1);
24620             brick++;
24621         }
24622         // code last brick offset as a plug address
24623         args->before_last_plug = brick_address (end_brick) -1;
24624         current_reloc_brick = end_brick;
24625         dprintf (3, ("setting before last to %Ix, last brick to %Ix",
24626             args->before_last_plug, current_reloc_brick));
24627     }
24628     else
24629     {
24630         dprintf (3, ("still in the same brick: %Ix", end_brick));
24631         args->before_last_plug = reloc_plug;
24632     }
24633     args->current_compacted_brick = current_reloc_brick;
24634 
24635     if (check_last_object_p)
24636     {
24637         mark* entry = args->pinned_plug_entry;
24638 
24639         if (args->is_shortened)
24640         {
24641             entry->swap_post_plug_and_saved();
24642         }
24643         else
24644         {
24645             entry->swap_pre_plug_and_saved();
24646         }
24647     }
24648 }
24649 
24650 void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args)
24651 {
24652     assert (tree != NULL);
24653     int   left_node = node_left_child (tree);
24654     int   right_node = node_right_child (tree);
24655     ptrdiff_t relocation = node_relocation_distance (tree);
24656 
24657     args->print();
24658 
24659     if (left_node)
24660     {
24661         dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
24662         compact_in_brick ((tree + left_node), args);
24663     }
24664 
24665     uint8_t*  plug = tree;
24666     BOOL   has_pre_plug_info_p = FALSE;
24667     BOOL   has_post_plug_info_p = FALSE;
24668 
24669     if (tree == oldest_pinned_plug)
24670     {
24671         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24672                                                            &has_post_plug_info_p);
24673         assert (tree == pinned_plug (args->pinned_plug_entry));
24674     }
24675 
24676     if (args->last_plug != 0)
24677     {
24678         size_t gap_size = node_gap_size (tree);
24679         uint8_t*  gap = (plug - gap_size);
24680         uint8_t*  last_plug_end = gap;
24681         size_t last_plug_size = (last_plug_end - args->last_plug);
24682         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix",
24683             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24684 
24685         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24686         if (!check_last_object_p)
24687         {
24688             assert (last_plug_size >= Align (min_obj_size));
24689         }
24690 
24691         compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24692     }
24693     else
24694     {
24695         assert (!has_pre_plug_info_p);
24696     }
24697 
24698     dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
24699     args->last_plug = plug;
24700     args->last_plug_relocation = relocation;
24701     args->is_shortened = has_post_plug_info_p;
24702 
24703     if (right_node)
24704     {
24705         dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
24706         compact_in_brick ((tree + right_node), args);
24707     }
24708 }
24709 
24710 void gc_heap::recover_saved_pinned_info()
24711 {
24712     reset_pinned_queue_bos();
24713 
24714     while (!(pinned_plug_que_empty_p()))
24715     {
24716         mark* oldest_entry = oldest_pin();
24717         oldest_entry->recover_plug_info();
24718 #ifdef GC_CONFIG_DRIVEN
24719         if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info())
24720             record_interesting_data_point (idp_pre_and_post_pin);
24721         else if (oldest_entry->has_pre_plug_info())
24722             record_interesting_data_point (idp_pre_pin);
24723         else if (oldest_entry->has_post_plug_info())
24724             record_interesting_data_point (idp_post_pin);
24725 #endif //GC_CONFIG_DRIVEN
24726 
24727         deque_pinned_plug();
24728     }
24729 }
24730 
24731 void gc_heap::compact_phase (int condemned_gen_number,
24732                              uint8_t*  first_condemned_address,
24733                              BOOL clear_cards)
24734 {
24735 //  %type%  category = quote (compact);
24736 #ifdef TIME_GC
24737         unsigned start;
24738         unsigned finish;
24739         start = GetCycleCount32();
24740 #endif //TIME_GC
24741     generation*   condemned_gen = generation_of (condemned_gen_number);
24742     uint8_t*  start_address = first_condemned_address;
24743     size_t   current_brick = brick_of (start_address);
24744     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24745 
24746     PREFIX_ASSUME(current_heap_segment != NULL);
24747 
24748     reset_pinned_queue_bos();
24749     update_oldest_pinned_plug();
24750 
24751     BOOL reused_seg = expand_reused_seg_p();
24752     if (reused_seg)
24753     {
24754         for (int i = 1; i <= max_generation; i++)
24755         {
24756             generation_allocation_size (generation_of (i)) = 0;
24757         }
24758     }
24759 
24760     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
24761 
24762     size_t  end_brick = brick_of (end_address-1);
24763     compact_args args;
24764     args.last_plug = 0;
24765     args.before_last_plug = 0;
24766     args.current_compacted_brick = ~((size_t)1);
24767     args.is_shortened = FALSE;
24768     args.pinned_plug_entry = 0;
24769     args.copy_cards_p =  (condemned_gen_number >= 1) || !clear_cards;
24770     args.check_gennum_p = reused_seg;
24771     if (args.check_gennum_p)
24772     {
24773         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
24774     }
24775 
24776     dprintf (2,("---- Compact Phase: %Ix(%Ix)----",
24777         first_condemned_address, brick_of (first_condemned_address)));
24778 
24779 #ifdef MULTIPLE_HEAPS
24780     //restart
24781     if (gc_t_join.joined())
24782     {
24783 #endif //MULTIPLE_HEAPS
24784 
24785 #ifdef MULTIPLE_HEAPS
24786         dprintf(3, ("Restarting for compaction"));
24787         gc_t_join.restart();
24788     }
24789 #endif //MULTIPLE_HEAPS
24790 
24791     reset_pinned_queue_bos();
24792 
24793 #ifdef FEATURE_LOH_COMPACTION
24794     if (loh_compacted_p)
24795     {
24796         compact_loh();
24797     }
24798 #endif //FEATURE_LOH_COMPACTION
24799 
24800     if ((start_address < end_address) ||
24801         (condemned_gen_number == max_generation))
24802     {
24803         while (1)
24804         {
24805             if (current_brick > end_brick)
24806             {
24807                 if (args.last_plug != 0)
24808                 {
24809                     dprintf (3, ("compacting last plug: %Ix", args.last_plug))
24810                     compact_plug (args.last_plug,
24811                                   (heap_segment_allocated (current_heap_segment) - args.last_plug),
24812                                   args.is_shortened,
24813                                   &args);
24814                 }
24815 
24816                 if (heap_segment_next_rw (current_heap_segment))
24817                 {
24818                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
24819                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
24820                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24821                     args.last_plug = 0;
24822                     if (args.check_gennum_p)
24823                     {
24824                         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
24825                     }
24826                     continue;
24827                 }
24828                 else
24829                 {
24830                     if (args.before_last_plug !=0)
24831                     {
24832                         dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
24833                                     args.current_compacted_brick, (size_t)args.before_last_plug));
24834                         assert (args.current_compacted_brick != ~1u);
24835                         set_brick (args.current_compacted_brick,
24836                                    args.before_last_plug - brick_address (args.current_compacted_brick));
24837                     }
24838                     break;
24839                 }
24840             }
24841             {
24842                 int  brick_entry =  brick_table [ current_brick ];
24843                 dprintf (3, ("B: %Ix(%Ix)->%Ix",
24844                     current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
24845 
24846                 if (brick_entry >= 0)
24847                 {
24848                     compact_in_brick ((brick_address (current_brick) + brick_entry -1),
24849                                       &args);
24850 
24851                 }
24852             }
24853             current_brick++;
24854         }
24855     }
24856 
24857     recover_saved_pinned_info();
24858 
24859 #ifdef TIME_GC
24860     finish = GetCycleCount32();
24861     compact_time = finish - start;
24862 #endif //TIME_GC
24863 
24864     concurrent_print_time_delta ("compact end");
24865 
24866     dprintf(2,("---- End of Compact phase ----"));
24867 }
24868 
24869 #ifdef MULTIPLE_HEAPS
24870 
24871 #ifdef _MSC_VER
24872 #pragma warning(push)
24873 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
24874 #endif //_MSC_VER
24875 void gc_heap::gc_thread_stub (void* arg)
24876 {
24877     ClrFlsSetThreadType (ThreadType_GC);
24878     STRESS_LOG_RESERVE_MEM (GC_STRESSLOG_MULTIPLY);
24879 
24880 #ifndef FEATURE_REDHAWK
24881     // We commit the thread's entire stack to ensure we're robust in low memory conditions.
24882     BOOL fSuccess = Thread::CommitThreadStack(NULL);
24883 
24884     if (!fSuccess)
24885     {
24886 #ifdef BACKGROUND_GC
24887         // For background GC we revert to doing a blocking GC.
24888         return;
24889 #else
24890         STRESS_LOG0(LF_GC, LL_ALWAYS, "Thread::CommitThreadStack failed.");
24891         _ASSERTE(!"Thread::CommitThreadStack failed.");
24892         EEPOLICY_HANDLE_FATAL_ERROR(COR_E_STACKOVERFLOW);
24893 #endif //BACKGROUND_GC
24894     }
24895 #endif // FEATURE_REDHAWK
24896 
24897     gc_heap* heap = (gc_heap*)arg;
24898     _alloca (256*heap->heap_number);
24899     heap->gc_thread_function();
24900 }
24901 #ifdef _MSC_VER
24902 #pragma warning(pop)
24903 #endif //_MSC_VER
24904 
24905 #endif //MULTIPLE_HEAPS
24906 
24907 #ifdef BACKGROUND_GC
24908 
24909 #ifdef _MSC_VER
24910 #pragma warning(push)
24911 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
24912 #endif //_MSC_VER
24913 uint32_t __stdcall gc_heap::bgc_thread_stub (void* arg)
24914 {
24915     gc_heap* heap = (gc_heap*)arg;
24916     return heap->bgc_thread_function();
24917 }
24918 #ifdef _MSC_VER
24919 #pragma warning(pop)
24920 #endif //_MSC_VER
24921 
24922 #endif //BACKGROUND_GC
24923 
24924 /*------------------ Background GC ----------------------------*/
24925 
24926 #ifdef BACKGROUND_GC
24927 
24928 void gc_heap::background_drain_mark_list (int thread)
24929 {
24930     UNREFERENCED_PARAMETER(thread);
24931 
24932     size_t saved_c_mark_list_index = c_mark_list_index;
24933 
24934     if (saved_c_mark_list_index)
24935     {
24936         concurrent_print_time_delta ("SML");
24937     }
24938     while (c_mark_list_index != 0)
24939     {
24940         size_t current_index = c_mark_list_index - 1;
24941         uint8_t* o = c_mark_list [current_index];
24942         background_mark_object (o THREAD_NUMBER_ARG);
24943         c_mark_list_index--;
24944     }
24945     if (saved_c_mark_list_index)
24946     {
24947 
24948         concurrent_print_time_delta ("EML");
24949     }
24950 
24951     fire_drain_mark_list_event (saved_c_mark_list_index);
24952 }
24953 
24954 
24955 // The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
24956 #ifdef MULTIPLE_HEAPS
24957 // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
24958 // them. So we can use the same static variables.
24959 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
24960 {
24961     // Whenever we call this method there may have been preceding object promotions. So set
24962     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
24963     // based on the how the scanning proceeded).
24964     s_fUnscannedPromotions = TRUE;
24965 
24966     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
24967     // the state of this thread's portion of the dependent handle table. That's because promotions on other
24968     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
24969     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
24970     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
24971     // as all the others or they'll get out of step).
24972     while (true)
24973     {
24974         // The various worker threads are all currently racing in this code. We need to work out if at least
24975         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
24976         // dependent handle table when both of the following conditions apply:
24977         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
24978         //     object happens to correspond to a primary in one of our handles we might potentially have to
24979         //     promote the associated secondary).
24980         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
24981         //
24982         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
24983         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
24984         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
24985         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
24986         // follows below. Note that we can't read this outside of the join since on any iteration apart from
24987         // the first threads will be racing between reading this value and completing their previous
24988         // iteration's table scan.
24989         //
24990         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
24991         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
24992         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
24993         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
24994         // we're safely joined.
24995         if (GCScan::GcDhUnpromotedHandlesExist(sc))
24996             s_fUnpromotedHandles = TRUE;
24997 
24998         // Synchronize all the threads so we can read our state variables safely. The following shared
24999         // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
25000         // single thread inside the join.
25001         bgc_t_join.join(this, gc_join_scan_dependent_handles);
25002         if (bgc_t_join.joined())
25003         {
25004             // We're synchronized so it's safe to read our shared state variables. We update another shared
25005             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
25006             // the loop. We scan if there has been at least one object promotion since last time and at least
25007             // one thread has a dependent handle table with a potential handle promotion possible.
25008             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
25009 
25010             // Reset our shared state variables (ready to be set again on this scan or with a good initial
25011             // value for the next call if we're terminating the loop).
25012             s_fUnscannedPromotions = FALSE;
25013             s_fUnpromotedHandles = FALSE;
25014 
25015             if (!s_fScanRequired)
25016             {
25017                 uint8_t* all_heaps_max = 0;
25018                 uint8_t* all_heaps_min = MAX_PTR;
25019                 int i;
25020                 for (i = 0; i < n_heaps; i++)
25021                 {
25022                     if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25023                         all_heaps_max = g_heaps[i]->background_max_overflow_address;
25024                     if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25025                         all_heaps_min = g_heaps[i]->background_min_overflow_address;
25026                 }
25027                 for (i = 0; i < n_heaps; i++)
25028                 {
25029                     g_heaps[i]->background_max_overflow_address = all_heaps_max;
25030                     g_heaps[i]->background_min_overflow_address = all_heaps_min;
25031                 }
25032             }
25033 
25034             // Restart all the workers.
25035             dprintf(2, ("Starting all gc thread mark stack overflow processing"));
25036             bgc_t_join.restart();
25037         }
25038 
25039         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25040         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
25041         // global flag indicating that at least one object promotion may have occurred (the usual comment
25042         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
25043         // exit the method since we unconditionally set this variable on method entry anyway).
25044         if (background_process_mark_overflow (sc->concurrent))
25045             s_fUnscannedPromotions = TRUE;
25046 
25047         // If we decided that no scan was required we can terminate the loop now.
25048         if (!s_fScanRequired)
25049             break;
25050 
25051         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
25052         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
25053         // could miss noting the promotion of some primary objects).
25054         bgc_t_join.join(this, gc_join_rescan_dependent_handles);
25055         if (bgc_t_join.joined())
25056         {
25057             // Restart all the workers.
25058             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
25059             bgc_t_join.restart();
25060         }
25061 
25062         // If the portion of the dependent handle table managed by this worker has handles that could still be
25063         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
25064         // could require a rescan of handles on this or other workers.
25065         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25066             if (GCScan::GcDhReScan(sc))
25067                 s_fUnscannedPromotions = TRUE;
25068     }
25069 }
25070 #else
25071 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25072 {
25073     // Whenever we call this method there may have been preceding object promotions. So set
25074     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25075     // based on the how the scanning proceeded).
25076     bool fUnscannedPromotions = true;
25077 
25078     // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
25079     // scan without performing any new promotions.
25080     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
25081     {
25082         // On each iteration of the loop start with the assumption that no further objects have been promoted.
25083         fUnscannedPromotions = false;
25084 
25085         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25086         // being visible. If there was an overflow (background_process_mark_overflow returned true) then
25087         // additional objects now appear to be promoted and we should set the flag.
25088         if (background_process_mark_overflow (sc->concurrent))
25089             fUnscannedPromotions = true;
25090 
25091         // Perform the scan and set the flag if any promotions resulted.
25092         if (GCScan::GcDhReScan (sc))
25093             fUnscannedPromotions = true;
25094     }
25095 
25096     // Perform a last processing of any overflowed mark stack.
25097     background_process_mark_overflow (sc->concurrent);
25098 }
25099 #endif //MULTIPLE_HEAPS
25100 
25101 void gc_heap::recover_bgc_settings()
25102 {
25103     if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
25104     {
25105         dprintf (2, ("restoring bgc settings"));
25106         settings = saved_bgc_settings;
25107         GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
25108     }
25109 }
25110 
25111 void gc_heap::allow_fgc()
25112 {
25113     assert (bgc_thread == GetThread());
25114 
25115     if (GCToEEInterface::IsPreemptiveGCDisabled(bgc_thread) && GCToEEInterface::CatchAtSafePoint(bgc_thread))
25116     {
25117         GCToEEInterface::EnablePreemptiveGC(bgc_thread);
25118         GCToEEInterface::DisablePreemptiveGC(bgc_thread);
25119     }
25120 }
25121 
25122 BOOL gc_heap::should_commit_mark_array()
25123 {
25124     return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
25125 }
25126 
25127 void gc_heap::clear_commit_flag()
25128 {
25129     generation* gen = generation_of (max_generation);
25130     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25131     while (1)
25132     {
25133         if (seg == 0)
25134         {
25135             if (gen != large_object_generation)
25136             {
25137                 gen = large_object_generation;
25138                 seg = heap_segment_in_range (generation_start_segment (gen));
25139             }
25140             else
25141             {
25142                 break;
25143             }
25144         }
25145 
25146         if (seg->flags & heap_segment_flags_ma_committed)
25147         {
25148             seg->flags &= ~heap_segment_flags_ma_committed;
25149         }
25150 
25151         if (seg->flags & heap_segment_flags_ma_pcommitted)
25152         {
25153             seg->flags &= ~heap_segment_flags_ma_pcommitted;
25154         }
25155 
25156         seg = heap_segment_next (seg);
25157     }
25158 }
25159 
25160 void gc_heap::clear_commit_flag_global()
25161 {
25162 #ifdef MULTIPLE_HEAPS
25163     for (int i = 0; i < n_heaps; i++)
25164     {
25165         g_heaps[i]->clear_commit_flag();
25166     }
25167 #else
25168     clear_commit_flag();
25169 #endif //MULTIPLE_HEAPS
25170 }
25171 
25172 void gc_heap::verify_mark_array_cleared (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25173 {
25174 #ifdef _DEBUG
25175     size_t  markw = mark_word_of (begin);
25176     size_t  markw_end = mark_word_of (end);
25177 
25178     while (markw < markw_end)
25179     {
25180         if (mark_array_addr[markw])
25181         {
25182             dprintf  (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
25183                             markw, mark_array_addr[markw], mark_word_address (markw)));
25184             FATAL_GC_ERROR();
25185         }
25186         markw++;
25187     }
25188 #else // _DEBUG
25189     UNREFERENCED_PARAMETER(begin);
25190     UNREFERENCED_PARAMETER(end);
25191     UNREFERENCED_PARAMETER(mark_array_addr);
25192 #endif //_DEBUG
25193 }
25194 
25195 void gc_heap::verify_mark_array_cleared (heap_segment* seg, uint32_t* mark_array_addr)
25196 {
25197     verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
25198 }
25199 
25200 BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp,
25201                                          heap_segment* seg,
25202                                          uint32_t* new_card_table,
25203                                          uint8_t* new_lowest_address)
25204 {
25205     UNREFERENCED_PARAMETER(hp); // compiler bug? -- this *is*, indeed, referenced
25206 
25207     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25208     uint8_t* end = heap_segment_reserved (seg);
25209 
25210     uint8_t* lowest = hp->background_saved_lowest_address;
25211     uint8_t* highest = hp->background_saved_highest_address;
25212 
25213     uint8_t* commit_start = NULL;
25214     uint8_t* commit_end = NULL;
25215     size_t commit_flag = 0;
25216 
25217     if ((highest >= start) &&
25218         (lowest <= end))
25219     {
25220         if ((start >= lowest) && (end <= highest))
25221         {
25222             dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25223                                     start, end, lowest, highest));
25224             commit_flag = heap_segment_flags_ma_committed;
25225         }
25226         else
25227         {
25228             dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25229                                     start, end, lowest, highest));
25230             commit_flag = heap_segment_flags_ma_pcommitted;
25231         }
25232 
25233         commit_start = max (lowest, start);
25234         commit_end = min (highest, end);
25235 
25236         if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
25237         {
25238             return FALSE;
25239         }
25240 
25241         if (new_card_table == 0)
25242         {
25243             new_card_table = g_gc_card_table;
25244         }
25245 
25246         if (hp->card_table != new_card_table)
25247         {
25248             if (new_lowest_address == 0)
25249             {
25250                 new_lowest_address = g_gc_lowest_address;
25251             }
25252 
25253             uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))];
25254             uint32_t* ma = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
25255 
25256             dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix",
25257                                     hp->card_table, new_card_table,
25258                                     hp->mark_array, ma));
25259 
25260             if (!commit_mark_array_by_range (commit_start, commit_end, ma))
25261             {
25262                 return FALSE;
25263             }
25264         }
25265 
25266         seg->flags |= commit_flag;
25267     }
25268 
25269     return TRUE;
25270 }
25271 
25272 BOOL gc_heap::commit_mark_array_by_range (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25273 {
25274     size_t beg_word = mark_word_of (begin);
25275     size_t end_word = mark_word_of (align_on_mark_word (end));
25276     uint8_t* commit_start = align_lower_page ((uint8_t*)&mark_array_addr[beg_word]);
25277     uint8_t* commit_end = align_on_page ((uint8_t*)&mark_array_addr[end_word]);
25278     size_t size = (size_t)(commit_end - commit_start);
25279 
25280 #ifdef SIMPLE_DPRINTF
25281     dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
25282                             begin, end,
25283                             beg_word, end_word,
25284                             (end_word - beg_word) * sizeof (uint32_t),
25285                             &mark_array_addr[beg_word],
25286                             &mark_array_addr[end_word],
25287                             (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
25288                             commit_start, commit_end,
25289                             size));
25290 #endif //SIMPLE_DPRINTF
25291 
25292     if (GCToOSInterface::VirtualCommit (commit_start, size))
25293     {
25294         // We can only verify the mark array is cleared from begin to end, the first and the last
25295         // page aren't necessarily all cleared 'cause they could be used by other segments or
25296         // card bundle.
25297         verify_mark_array_cleared (begin, end, mark_array_addr);
25298         return TRUE;
25299     }
25300     else
25301     {
25302         dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (uint32_t)));
25303         return FALSE;
25304     }
25305 }
25306 
25307 BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, uint32_t* new_mark_array_addr)
25308 {
25309     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25310     uint8_t* end = heap_segment_reserved (seg);
25311 
25312 #ifdef MULTIPLE_HEAPS
25313     uint8_t* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
25314     uint8_t* highest = heap_segment_heap (seg)->background_saved_highest_address;
25315 #else
25316     uint8_t* lowest = background_saved_lowest_address;
25317     uint8_t* highest = background_saved_highest_address;
25318 #endif //MULTIPLE_HEAPS
25319 
25320     if ((highest >= start) &&
25321         (lowest <= end))
25322     {
25323         start = max (lowest, start);
25324         end = min (highest, end);
25325         if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
25326         {
25327             return FALSE;
25328         }
25329     }
25330 
25331     return TRUE;
25332 }
25333 
25334 BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, uint32_t* mark_array_addr)
25335 {
25336     dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix",
25337         seg,
25338         heap_segment_reserved (seg),
25339         mark_array_addr));
25340     uint8_t* start = (heap_segment_read_only_p (seg) ? heap_segment_mem (seg) : (uint8_t*)seg);
25341 
25342     return commit_mark_array_by_range (start, heap_segment_reserved (seg), mark_array_addr);
25343 }
25344 
25345 BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr)
25346 {
25347     UNREFERENCED_PARAMETER(mark_array_addr);
25348 
25349     dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix",
25350                             lowest_address, highest_address, mark_array));
25351 
25352     generation* gen = generation_of (max_generation);
25353     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25354     while (1)
25355     {
25356         if (seg == 0)
25357         {
25358             if (gen != large_object_generation)
25359             {
25360                 gen = large_object_generation;
25361                 seg = heap_segment_in_range (generation_start_segment (gen));
25362             }
25363             else
25364             {
25365                 break;
25366             }
25367         }
25368 
25369         dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
25370 
25371         if (!(seg->flags & heap_segment_flags_ma_committed))
25372         {
25373             // For ro segments they could always be only partially in range so we'd
25374             // be calling this at the beginning of every BGC. We are not making this
25375             // more efficient right now - ro segments are currently only used by redhawk.
25376             if (heap_segment_read_only_p (seg))
25377             {
25378                 if ((heap_segment_mem (seg) >= lowest_address) &&
25379                     (heap_segment_reserved (seg) <= highest_address))
25380                 {
25381                     if (commit_mark_array_by_seg (seg, mark_array))
25382                     {
25383                         seg->flags |= heap_segment_flags_ma_committed;
25384                     }
25385                     else
25386                     {
25387                         return FALSE;
25388                     }
25389                 }
25390                 else
25391                 {
25392                     uint8_t* start = max (lowest_address, heap_segment_mem (seg));
25393                     uint8_t* end = min (highest_address, heap_segment_reserved (seg));
25394                     if (commit_mark_array_by_range (start, end, mark_array))
25395                     {
25396                         seg->flags |= heap_segment_flags_ma_pcommitted;
25397                     }
25398                     else
25399                     {
25400                         return FALSE;
25401                     }
25402                 }
25403             }
25404             else
25405             {
25406                 // For normal segments they are by design completely in range so just
25407                 // commit the whole mark array for each seg.
25408                 if (commit_mark_array_by_seg (seg, mark_array))
25409                 {
25410                     if (seg->flags & heap_segment_flags_ma_pcommitted)
25411                     {
25412                         seg->flags &= ~heap_segment_flags_ma_pcommitted;
25413                     }
25414                     seg->flags |= heap_segment_flags_ma_committed;
25415                 }
25416                 else
25417                 {
25418                     return FALSE;
25419                 }
25420             }
25421         }
25422 
25423         seg = heap_segment_next (seg);
25424     }
25425 
25426     return TRUE;
25427 }
25428 
25429 // This function doesn't check the commit flag since it's for a new array -
25430 // the mark_array flag for these segments will remain the same.
25431 BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr)
25432 {
25433     dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
25434     generation* gen = generation_of (max_generation);
25435     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25436     while (1)
25437     {
25438         if (seg == 0)
25439         {
25440             if (gen != large_object_generation)
25441             {
25442                 gen = large_object_generation;
25443                 seg = heap_segment_in_range (generation_start_segment (gen));
25444             }
25445             else
25446             {
25447                 break;
25448             }
25449         }
25450 
25451         if (!commit_mark_array_with_check (seg, new_mark_array_addr))
25452         {
25453             return FALSE;
25454         }
25455 
25456         seg = heap_segment_next (seg);
25457     }
25458 
25459 #ifdef MULTIPLE_HEAPS
25460     if (new_heap_segment)
25461     {
25462         if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
25463         {
25464             return FALSE;
25465         }
25466     }
25467 #endif //MULTIPLE_HEAPS
25468 
25469     return TRUE;
25470 }
25471 
25472 BOOL gc_heap::commit_new_mark_array_global (uint32_t* new_mark_array)
25473 {
25474 #ifdef MULTIPLE_HEAPS
25475     for (int i = 0; i < n_heaps; i++)
25476     {
25477         if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
25478         {
25479             return FALSE;
25480         }
25481     }
25482 #else
25483     if (!commit_new_mark_array (new_mark_array))
25484     {
25485         return FALSE;
25486     }
25487 #endif //MULTIPLE_HEAPS
25488 
25489     return TRUE;
25490 }
25491 
25492 void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
25493 {
25494     // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
25495     // been set to NULL.
25496     if (mark_array == NULL)
25497     {
25498         return;
25499     }
25500 
25501     dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
25502 
25503     size_t flags = seg->flags;
25504 
25505     if ((flags & heap_segment_flags_ma_committed) ||
25506         (flags & heap_segment_flags_ma_pcommitted))
25507     {
25508         uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25509         uint8_t* end = heap_segment_reserved (seg);
25510 
25511         if (flags & heap_segment_flags_ma_pcommitted)
25512         {
25513             start = max (lowest_address, start);
25514             end = min (highest_address, end);
25515         }
25516 
25517         size_t beg_word = mark_word_of (start);
25518         size_t end_word = mark_word_of (align_on_mark_word (end));
25519         uint8_t* decommit_start = align_on_page ((uint8_t*)&mark_array[beg_word]);
25520         uint8_t* decommit_end = align_lower_page ((uint8_t*)&mark_array[end_word]);
25521         size_t size = (size_t)(decommit_end - decommit_start);
25522 
25523 #ifdef SIMPLE_DPRINTF
25524         dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
25525                                 seg,
25526                                 beg_word, end_word,
25527                                 (end_word - beg_word) * sizeof (uint32_t),
25528                                 &mark_array[beg_word],
25529                                 &mark_array[end_word],
25530                                 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
25531                                 decommit_start, decommit_end,
25532                                 size));
25533 #endif //SIMPLE_DPRINTF
25534 
25535         if (decommit_start < decommit_end)
25536         {
25537             if (!GCToOSInterface::VirtualDecommit (decommit_start, size))
25538             {
25539                 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualDecommit on %Ix for %Id bytes failed",
25540                                         decommit_start, size));
25541                 assert (!"decommit failed");
25542             }
25543         }
25544 
25545         dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
25546     }
25547 }
25548 
25549 void gc_heap::background_mark_phase ()
25550 {
25551     verify_mark_array_cleared();
25552 
25553     ScanContext sc;
25554     sc.thread_number = heap_number;
25555     sc.promotion = TRUE;
25556     sc.concurrent = FALSE;
25557 
25558     THREAD_FROM_HEAP;
25559     Thread* current_thread = GetThread();
25560     BOOL cooperative_mode = TRUE;
25561 #ifndef MULTIPLE_HEAPS
25562     const int thread = heap_number;
25563 #endif //!MULTIPLE_HEAPS
25564 
25565     dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
25566 
25567     assert (settings.concurrent);
25568 
25569 #ifdef TIME_GC
25570     unsigned start;
25571     unsigned finish;
25572     start = GetCycleCount32();
25573 #endif //TIME_GC
25574 
25575 #ifdef FFIND_OBJECT
25576     if (gen0_must_clear_bricks > 0)
25577         gen0_must_clear_bricks--;
25578 #endif //FFIND_OBJECT
25579 
25580     background_soh_alloc_count = 0;
25581     background_loh_alloc_count = 0;
25582     bgc_overflow_count = 0;
25583 
25584     bpromoted_bytes (heap_number) = 0;
25585     static uint32_t num_sizedrefs = 0;
25586 
25587     background_min_overflow_address = MAX_PTR;
25588     background_max_overflow_address = 0;
25589     background_min_soh_overflow_address = MAX_PTR;
25590     background_max_soh_overflow_address = 0;
25591     processed_soh_overflow_p = FALSE;
25592 
25593     {
25594         //set up the mark lists from g_mark_list
25595         assert (g_mark_list);
25596         mark_list = g_mark_list;
25597         //dont use the mark list for full gc
25598         //because multiple segments are more complex to handle and the list
25599         //is likely to overflow
25600         mark_list_end = &mark_list [0];
25601         mark_list_index = &mark_list [0];
25602 
25603         c_mark_list_index = 0;
25604 
25605         shigh = (uint8_t*) 0;
25606         slow  = MAX_PTR;
25607 
25608         generation*   gen = generation_of (max_generation);
25609 
25610         dprintf(3,("BGC: stack marking"));
25611         sc.concurrent = TRUE;
25612 
25613         GCScan::GcScanRoots(background_promote_callback,
25614                                 max_generation, max_generation,
25615                                 &sc);
25616     }
25617 
25618     {
25619         dprintf(3,("BGC: finalization marking"));
25620         finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
25621     }
25622 
25623     size_t total_loh_size = generation_size (max_generation + 1);
25624     bgc_begin_loh_size = total_loh_size;
25625     bgc_alloc_spin_loh = 0;
25626     bgc_loh_size_increased = 0;
25627     bgc_loh_allocated_in_free = 0;
25628     size_t total_soh_size = generation_sizes (generation_of (max_generation));
25629 
25630     dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25631 
25632     {
25633         //concurrent_print_time_delta ("copying stack roots");
25634         concurrent_print_time_delta ("CS");
25635 
25636         fire_bgc_event (BGC1stNonConEnd);
25637 
25638         expanded_in_fgc = FALSE;
25639         saved_overflow_ephemeral_seg = 0;
25640         current_bgc_state = bgc_reset_ww;
25641 
25642         // we don't need a join here - just whichever thread that gets here
25643         // first can change the states and call restart_vm.
25644         // this is not true - we can't let the EE run when we are scanning stack.
25645         // since we now allow reset ww to run concurrently and have a join for it,
25646         // we can do restart ee on the 1st thread that got here. Make sure we handle the
25647         // sizedref handles correctly.
25648 #ifdef MULTIPLE_HEAPS
25649         bgc_t_join.join(this, gc_join_restart_ee);
25650         if (bgc_t_join.joined())
25651 #endif //MULTIPLE_HEAPS
25652         {
25653 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25654             // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset
25655             // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while
25656             // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below.
25657 #ifdef WRITE_WATCH
25658             concurrent_print_time_delta ("CRWW begin");
25659 
25660 #ifdef MULTIPLE_HEAPS
25661             for (int i = 0; i < n_heaps; i++)
25662             {
25663                 g_heaps[i]->reset_write_watch (FALSE);
25664             }
25665 #else
25666             reset_write_watch (FALSE);
25667 #endif //MULTIPLE_HEAPS
25668 
25669             concurrent_print_time_delta ("CRWW");
25670 #endif //WRITE_WATCH
25671 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25672 
25673             num_sizedrefs = SystemDomain::System()->GetTotalNumSizedRefHandles();
25674 
25675             // this c_write is not really necessary because restart_vm
25676             // has an instruction that will flush the cpu cache (interlocked
25677             // or whatever) but we don't want to rely on that.
25678             dprintf (BGC_LOG, ("setting cm_in_progress"));
25679             c_write (cm_in_progress, TRUE);
25680 
25681             //restart all thread, doing the marking from the array
25682             assert (dont_restart_ee_p);
25683             dont_restart_ee_p = FALSE;
25684 
25685             restart_vm();
25686             GCToOSInterface::YieldThread (0);
25687 #ifdef MULTIPLE_HEAPS
25688             dprintf(3, ("Starting all gc threads for gc"));
25689             bgc_t_join.restart();
25690 #endif //MULTIPLE_HEAPS
25691         }
25692 
25693 #ifdef MULTIPLE_HEAPS
25694         bgc_t_join.join(this, gc_join_after_reset);
25695         if (bgc_t_join.joined())
25696 #endif //MULTIPLE_HEAPS
25697         {
25698             disable_preemptive (current_thread, TRUE);
25699 
25700 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25701             // When software write watch is enabled, resetting write watch is done while the runtime is suspended above. The
25702             // post-reset call to revisit_written_pages is only necessary for concurrent reset_write_watch, to discard dirtied
25703             // pages during the concurrent reset.
25704 
25705 #ifdef WRITE_WATCH
25706             concurrent_print_time_delta ("CRWW begin");
25707 
25708 #ifdef MULTIPLE_HEAPS
25709             for (int i = 0; i < n_heaps; i++)
25710             {
25711                 g_heaps[i]->reset_write_watch (TRUE);
25712             }
25713 #else
25714             reset_write_watch (TRUE);
25715 #endif //MULTIPLE_HEAPS
25716 
25717             concurrent_print_time_delta ("CRWW");
25718 #endif //WRITE_WATCH
25719 
25720 #ifdef MULTIPLE_HEAPS
25721             for (int i = 0; i < n_heaps; i++)
25722             {
25723                 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
25724             }
25725 #else
25726             revisit_written_pages (TRUE, TRUE);
25727 #endif //MULTIPLE_HEAPS
25728 
25729             concurrent_print_time_delta ("CRW");
25730 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25731 
25732 #ifdef MULTIPLE_HEAPS
25733             for (int i = 0; i < n_heaps; i++)
25734             {
25735                 g_heaps[i]->current_bgc_state = bgc_mark_handles;
25736             }
25737 #else
25738             current_bgc_state = bgc_mark_handles;
25739 #endif //MULTIPLE_HEAPS
25740 
25741             current_c_gc_state = c_gc_state_marking;
25742 
25743             enable_preemptive (current_thread);
25744 
25745 #ifdef MULTIPLE_HEAPS
25746             dprintf(3, ("Joining BGC threads after resetting writewatch"));
25747             bgc_t_join.restart();
25748 #endif //MULTIPLE_HEAPS
25749         }
25750 
25751         disable_preemptive (current_thread, TRUE);
25752 
25753         if (num_sizedrefs > 0)
25754         {
25755             GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
25756 
25757             enable_preemptive (current_thread);
25758 
25759 #ifdef MULTIPLE_HEAPS
25760             bgc_t_join.join(this, gc_join_scan_sizedref_done);
25761             if (bgc_t_join.joined())
25762             {
25763                 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
25764                 bgc_t_join.restart();
25765             }
25766 #endif //MULTIPLE_HEAPS
25767 
25768             disable_preemptive (current_thread, TRUE);
25769         }
25770 
25771         dprintf (3,("BGC: handle table marking"));
25772         GCScan::GcScanHandles(background_promote,
25773                                   max_generation, max_generation,
25774                                   &sc);
25775         //concurrent_print_time_delta ("concurrent marking handle table");
25776         concurrent_print_time_delta ("CRH");
25777 
25778         current_bgc_state = bgc_mark_stack;
25779         dprintf (2,("concurrent draining mark list"));
25780         background_drain_mark_list (thread);
25781         //concurrent_print_time_delta ("concurrent marking stack roots");
25782         concurrent_print_time_delta ("CRS");
25783 
25784         dprintf (2,("concurrent revisiting dirtied pages"));
25785         revisit_written_pages (TRUE);
25786         revisit_written_pages (TRUE);
25787         //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
25788         concurrent_print_time_delta ("CRre");
25789 
25790         enable_preemptive (current_thread);
25791 
25792 #ifdef MULTIPLE_HEAPS
25793         bgc_t_join.join(this, gc_join_concurrent_overflow);
25794         if (bgc_t_join.joined())
25795         {
25796             uint8_t* all_heaps_max = 0;
25797             uint8_t* all_heaps_min = MAX_PTR;
25798             int i;
25799             for (i = 0; i < n_heaps; i++)
25800             {
25801                 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix",
25802                     i,
25803                     g_heaps[i]->background_max_overflow_address,
25804                     g_heaps[i]->background_min_overflow_address));
25805                 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25806                     all_heaps_max = g_heaps[i]->background_max_overflow_address;
25807                 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25808                     all_heaps_min = g_heaps[i]->background_min_overflow_address;
25809             }
25810             for (i = 0; i < n_heaps; i++)
25811             {
25812                 g_heaps[i]->background_max_overflow_address = all_heaps_max;
25813                 g_heaps[i]->background_min_overflow_address = all_heaps_min;
25814             }
25815             dprintf(3, ("Starting all bgc threads after updating the overflow info"));
25816             bgc_t_join.restart();
25817         }
25818 #endif //MULTIPLE_HEAPS
25819 
25820         disable_preemptive (current_thread, TRUE);
25821 
25822         dprintf (2, ("before CRov count: %d", bgc_overflow_count));
25823         bgc_overflow_count = 0;
25824         background_process_mark_overflow (TRUE);
25825         dprintf (2, ("after CRov count: %d", bgc_overflow_count));
25826         bgc_overflow_count = 0;
25827         //concurrent_print_time_delta ("concurrent processing mark overflow");
25828         concurrent_print_time_delta ("CRov");
25829 
25830         // Stop all threads, crawl all stacks and revisit changed pages.
25831         fire_bgc_event (BGC1stConEnd);
25832 
25833         dprintf (2, ("Stopping the EE"));
25834 
25835         enable_preemptive (current_thread);
25836 
25837 #ifdef MULTIPLE_HEAPS
25838         bgc_t_join.join(this, gc_join_suspend_ee);
25839         if (bgc_t_join.joined())
25840         {
25841             bgc_threads_sync_event.Reset();
25842 
25843             dprintf(3, ("Joining BGC threads for non concurrent final marking"));
25844             bgc_t_join.restart();
25845         }
25846 #endif //MULTIPLE_HEAPS
25847 
25848         if (heap_number == 0)
25849         {
25850             enter_spin_lock (&gc_lock);
25851 
25852             bgc_suspend_EE ();
25853             //suspend_EE ();
25854             bgc_threads_sync_event.Set();
25855         }
25856         else
25857         {
25858             bgc_threads_sync_event.Wait(INFINITE, FALSE);
25859             dprintf (2, ("bgc_threads_sync_event is signalled"));
25860         }
25861 
25862         assert (settings.concurrent);
25863         assert (settings.condemned_generation == max_generation);
25864 
25865         dprintf (2, ("clearing cm_in_progress"));
25866         c_write (cm_in_progress, FALSE);
25867 
25868         bgc_alloc_lock->check();
25869 
25870         current_bgc_state = bgc_final_marking;
25871 
25872         //concurrent_print_time_delta ("concurrent marking ended");
25873         concurrent_print_time_delta ("CR");
25874 
25875         fire_bgc_event (BGC2ndNonConBegin);
25876 
25877         mark_absorb_new_alloc();
25878 
25879         // We need a join here 'cause find_object would complain if the gen0
25880         // bricks of another heap haven't been fixed up. So we need to make sure
25881         // that every heap's gen0 bricks are fixed up before we proceed.
25882 #ifdef MULTIPLE_HEAPS
25883         bgc_t_join.join(this, gc_join_after_absorb);
25884         if (bgc_t_join.joined())
25885         {
25886             dprintf(3, ("Joining BGC threads after absorb"));
25887             bgc_t_join.restart();
25888         }
25889 #endif //MULTIPLE_HEAPS
25890 
25891         // give VM a chance to do work
25892         GCToEEInterface::GcBeforeBGCSweepWork();
25893 
25894         //reset the flag, indicating that the EE no longer expect concurrent
25895         //marking
25896         sc.concurrent = FALSE;
25897 
25898         total_loh_size = generation_size (max_generation + 1);
25899         total_soh_size = generation_sizes (generation_of (max_generation));
25900 
25901         dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25902 
25903         dprintf (2, ("nonconcurrent marking stack roots"));
25904         GCScan::GcScanRoots(background_promote,
25905                                 max_generation, max_generation,
25906                                 &sc);
25907         //concurrent_print_time_delta ("nonconcurrent marking stack roots");
25908         concurrent_print_time_delta ("NRS");
25909 
25910 //        finalize_queue->EnterFinalizeLock();
25911         finalize_queue->GcScanRoots(background_promote, heap_number, 0);
25912 //        finalize_queue->LeaveFinalizeLock();
25913 
25914         dprintf (2, ("nonconcurrent marking handle table"));
25915         GCScan::GcScanHandles(background_promote,
25916                                   max_generation, max_generation,
25917                                   &sc);
25918         //concurrent_print_time_delta ("nonconcurrent marking handle table");
25919         concurrent_print_time_delta ("NRH");
25920 
25921         dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
25922         revisit_written_pages (FALSE);
25923         //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
25924         concurrent_print_time_delta ("NRre LOH");
25925 
25926 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25927 #ifdef MULTIPLE_HEAPS
25928         bgc_t_join.join(this, gc_join_disable_software_write_watch);
25929         if (bgc_t_join.joined())
25930 #endif // MULTIPLE_HEAPS
25931         {
25932             // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to
25933             // avoid further perf penalty after the runtime is restarted
25934             SoftwareWriteWatch::DisableForGCHeap();
25935 
25936 #ifdef MULTIPLE_HEAPS
25937             dprintf(3, ("Restarting BGC threads after disabling software write watch"));
25938             bgc_t_join.restart();
25939 #endif // MULTIPLE_HEAPS
25940         }
25941 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25942 
25943         dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
25944         bgc_overflow_count = 0;
25945 
25946         // Dependent handles need to be scanned with a special algorithm (see the header comment on
25947         // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
25948         // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
25949         // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
25950         // The call to background_scan_dependent_handles is what will cycle through more iterations if
25951         // required and will also perform processing of any mark stack overflow once the dependent handle
25952         // table has been fully promoted.
25953         dprintf (2, ("1st dependent handle scan and process mark overflow"));
25954         GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
25955         background_scan_dependent_handles (&sc);
25956         //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
25957         concurrent_print_time_delta ("NR 1st Hov");
25958 
25959         dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
25960         bgc_overflow_count = 0;
25961 
25962 #ifdef MULTIPLE_HEAPS
25963         bgc_t_join.join(this, gc_join_null_dead_short_weak);
25964         if (bgc_t_join.joined())
25965 #endif //MULTIPLE_HEAPS
25966         {
25967             GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
25968 
25969 #ifdef MULTIPLE_HEAPS
25970             dprintf(3, ("Joining BGC threads for short weak handle scan"));
25971             bgc_t_join.restart();
25972 #endif //MULTIPLE_HEAPS
25973         }
25974 
25975         // null out the target of short weakref that were not promoted.
25976         GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
25977 
25978         //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
25979         concurrent_print_time_delta ("NR GcShortWeakPtrScan");
25980     }
25981 
25982     {
25983 #ifdef MULTIPLE_HEAPS
25984         bgc_t_join.join(this, gc_join_scan_finalization);
25985         if (bgc_t_join.joined())
25986         {
25987             dprintf(3, ("Joining BGC threads for finalization"));
25988             bgc_t_join.restart();
25989         }
25990 #endif //MULTIPLE_HEAPS
25991 
25992         //Handle finalization.
25993         dprintf(3,("Marking finalization data"));
25994         //concurrent_print_time_delta ("bgc joined to mark finalization");
25995         concurrent_print_time_delta ("NRj");
25996 
25997 //        finalize_queue->EnterFinalizeLock();
25998         finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
25999 //        finalize_queue->LeaveFinalizeLock();
26000 
26001         concurrent_print_time_delta ("NRF");
26002     }
26003 
26004     dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
26005     bgc_overflow_count = 0;
26006 
26007     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
26008     // for finalization. As before background_scan_dependent_handles will also process any mark stack
26009     // overflow.
26010     dprintf (2, ("2nd dependent handle scan and process mark overflow"));
26011     background_scan_dependent_handles (&sc);
26012     //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
26013     concurrent_print_time_delta ("NR 2nd Hov");
26014 
26015 #ifdef MULTIPLE_HEAPS
26016     bgc_t_join.join(this, gc_join_null_dead_long_weak);
26017     if (bgc_t_join.joined())
26018     {
26019         dprintf(2, ("Joining BGC threads for weak pointer deletion"));
26020         bgc_t_join.restart();
26021     }
26022 #endif //MULTIPLE_HEAPS
26023 
26024     // null out the target of long weakref that were not promoted.
26025     GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
26026     concurrent_print_time_delta ("NR GcWeakPtrScan");
26027 
26028 #ifdef MULTIPLE_HEAPS
26029     bgc_t_join.join(this, gc_join_null_dead_syncblk);
26030     if (bgc_t_join.joined())
26031 #endif //MULTIPLE_HEAPS
26032     {
26033         dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
26034         // scan for deleted entries in the syncblk cache
26035         GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
26036         concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
26037 #ifdef MULTIPLE_HEAPS
26038         dprintf(2, ("Starting BGC threads for end of background mark phase"));
26039         bgc_t_join.restart();
26040 #endif //MULTIPLE_HEAPS
26041     }
26042 
26043     gen0_bricks_cleared = FALSE;
26044 
26045     dprintf (2, ("end of bgc mark: loh: %d, soh: %d",
26046                  generation_size (max_generation + 1),
26047                  generation_sizes (generation_of (max_generation))));
26048 
26049     for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
26050     {
26051         generation* gen = generation_of (gen_idx);
26052         dynamic_data* dd = dynamic_data_of (gen_idx);
26053         dd_begin_data_size (dd) = generation_size (gen_idx) -
26054                                    (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
26055                                    Align (size (generation_allocation_start (gen)));
26056         dd_survived_size (dd) = 0;
26057         dd_pinned_survived_size (dd) = 0;
26058         dd_artificial_pinned_survived_size (dd) = 0;
26059         dd_added_pinned_size (dd) = 0;
26060     }
26061 
26062     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26063     PREFIX_ASSUME(seg != NULL);
26064 
26065     while (seg)
26066     {
26067         seg->flags &= ~heap_segment_flags_swept;
26068 
26069         if (heap_segment_allocated (seg) == heap_segment_mem (seg))
26070         {
26071             // This can't happen...
26072             FATAL_GC_ERROR();
26073         }
26074 
26075         if (seg == ephemeral_heap_segment)
26076         {
26077             heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
26078         }
26079         else
26080         {
26081             heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
26082         }
26083 
26084         dprintf (2, ("seg %Ix background allocated is %Ix",
26085                       heap_segment_mem (seg),
26086                       heap_segment_background_allocated (seg)));
26087         seg = heap_segment_next_rw (seg);
26088     }
26089 
26090     // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
26091     // we can't let the user code consume the left over parts in these alloc contexts.
26092     repair_allocation_contexts (FALSE);
26093 
26094 #ifdef TIME_GC
26095         finish = GetCycleCount32();
26096         mark_time = finish - start;
26097 #endif //TIME_GC
26098 
26099     dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d",
26100         generation_free_list_space (generation_of (max_generation)),
26101         generation_free_obj_space (generation_of (max_generation))));
26102 
26103     dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
26104 }
26105 
26106 void
26107 gc_heap::suspend_EE ()
26108 {
26109     dprintf (2, ("suspend_EE"));
26110 #ifdef MULTIPLE_HEAPS
26111     gc_heap* hp = gc_heap::g_heaps[0];
26112     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26113 #else
26114     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26115 #endif //MULTIPLE_HEAPS
26116 }
26117 
26118 #ifdef MULTIPLE_HEAPS
26119 void
26120 gc_heap::bgc_suspend_EE ()
26121 {
26122     for (int i = 0; i < n_heaps; i++)
26123     {
26124         gc_heap::g_heaps[i]->reset_gc_done();
26125     }
26126     gc_started = TRUE;
26127     dprintf (2, ("bgc_suspend_EE"));
26128     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26129 
26130     gc_started = FALSE;
26131     for (int i = 0; i < n_heaps; i++)
26132     {
26133         gc_heap::g_heaps[i]->set_gc_done();
26134     }
26135 }
26136 #else
26137 void
26138 gc_heap::bgc_suspend_EE ()
26139 {
26140     reset_gc_done();
26141     gc_started = TRUE;
26142     dprintf (2, ("bgc_suspend_EE"));
26143     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26144     gc_started = FALSE;
26145     set_gc_done();
26146 }
26147 #endif //MULTIPLE_HEAPS
26148 
26149 void
26150 gc_heap::restart_EE ()
26151 {
26152     dprintf (2, ("restart_EE"));
26153 #ifdef MULTIPLE_HEAPS
26154     GCToEEInterface::RestartEE(FALSE);
26155 #else
26156     GCToEEInterface::RestartEE(FALSE);
26157 #endif //MULTIPLE_HEAPS
26158 }
26159 
26160 inline uint8_t* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
26161 {
26162     if (concurrent_p)
26163     {
26164         uint8_t* end = ((seg == ephemeral_heap_segment) ?
26165                      generation_allocation_start (generation_of (max_generation-1)) :
26166                      heap_segment_allocated (seg));
26167         return align_lower_page (end);
26168     }
26169     else
26170     {
26171         return heap_segment_allocated (seg);
26172     }
26173 }
26174 
26175 void gc_heap::revisit_written_page (uint8_t* page,
26176                                     uint8_t* end,
26177                                     BOOL concurrent_p,
26178                                     heap_segment* seg,
26179                                     uint8_t*& last_page,
26180                                     uint8_t*& last_object,
26181                                     BOOL large_objects_p,
26182                                     size_t& num_marked_objects)
26183 {
26184     UNREFERENCED_PARAMETER(seg);
26185 
26186     uint8_t*   start_address = page;
26187     uint8_t*   o             = 0;
26188     int align_const = get_alignment_constant (!large_objects_p);
26189     uint8_t* high_address = end;
26190     uint8_t* current_lowest_address = background_saved_lowest_address;
26191     uint8_t* current_highest_address = background_saved_highest_address;
26192     BOOL no_more_loop_p = FALSE;
26193 
26194     THREAD_FROM_HEAP;
26195 #ifndef MULTIPLE_HEAPS
26196     const int thread = heap_number;
26197 #endif //!MULTIPLE_HEAPS
26198 
26199     if (large_objects_p)
26200     {
26201         o = last_object;
26202     }
26203     else
26204     {
26205         if (((last_page + OS_PAGE_SIZE) == page)
26206             || (start_address <= last_object))
26207         {
26208             o = last_object;
26209         }
26210         else
26211         {
26212             o = find_first_object (start_address, last_object);
26213             // We can visit the same object again, but on a different page.
26214             assert (o >= last_object);
26215         }
26216     }
26217 
26218     dprintf (3,("page %Ix start: %Ix, %Ix[ ",
26219                (size_t)page, (size_t)o,
26220                (size_t)(min (high_address, page + OS_PAGE_SIZE))));
26221 
26222     while (o < (min (high_address, page + OS_PAGE_SIZE)))
26223     {
26224         size_t s;
26225 
26226         if (concurrent_p && large_objects_p)
26227         {
26228             bgc_alloc_lock->bgc_mark_set (o);
26229 
26230             if (((CObjectHeader*)o)->IsFree())
26231             {
26232                 s = unused_array_size (o);
26233             }
26234             else
26235             {
26236                 s = size (o);
26237             }
26238         }
26239         else
26240         {
26241             s = size (o);
26242         }
26243 
26244         dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
26245 
26246         assert (Align (s) >= Align (min_obj_size));
26247 
26248         uint8_t* next_o =  o + Align (s, align_const);
26249 
26250         if (next_o >= start_address)
26251         {
26252 #ifdef MULTIPLE_HEAPS
26253             if (concurrent_p)
26254             {
26255                 // We set last_object here for SVR BGC here because SVR BGC has more than
26256                 // one GC thread. When we have more than one GC thread we would run into this
26257                 // situation if we skipped unmarked objects:
26258                 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it
26259                 // for revisit.
26260                 // bgc thread 2 marks X and all its current children.
26261                 // user thread comes along and dirties more (and later) pages in X.
26262                 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
26263                 // on them because it had already skipped X. We need to detect that this object is now
26264                 // marked and mark the children on the dirtied pages.
26265                 // In the future if we have less BGC threads than we have heaps we should add
26266                 // the check to the number of BGC threads.
26267                 last_object = o;
26268             }
26269 #endif //MULTIPLE_HEAPS
26270 
26271             if (contain_pointers (o) &&
26272                 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
26273                 background_marked (o)))
26274             {
26275                 dprintf (3, ("going through %Ix", (size_t)o));
26276                 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
26277                                     if ((uint8_t*)poo >= min (high_address, page + OS_PAGE_SIZE))
26278                                     {
26279                                         no_more_loop_p = TRUE;
26280                                         goto end_limit;
26281                                     }
26282                                     uint8_t* oo = *poo;
26283 
26284                                     num_marked_objects++;
26285                                     background_mark_object (oo THREAD_NUMBER_ARG);
26286                                 );
26287             }
26288             else if (
26289                 concurrent_p &&
26290 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // see comment below
26291                 large_objects_p &&
26292 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26293                 ((CObjectHeader*)o)->IsFree() &&
26294                 (next_o > min (high_address, page + OS_PAGE_SIZE)))
26295             {
26296                 // We need to not skip the object here because of this corner scenario:
26297                 // A large object was being allocated during BGC mark so we first made it
26298                 // into a free object, then cleared its memory. In this loop we would detect
26299                 // that it's a free object which normally we would skip. But by the next time
26300                 // we call GetWriteWatch we could still be on this object and the object had
26301                 // been made into a valid object and some of its memory was changed. We need
26302                 // to be sure to process those written pages so we can't skip the object just
26303                 // yet.
26304                 //
26305                 // Similarly, when using software write watch, don't advance last_object when
26306                 // the current object is a free object that spans beyond the current page or
26307                 // high_address. Software write watch acquires gc_lock before the concurrent
26308                 // GetWriteWatch() call during revisit_written_pages(). A foreground GC may
26309                 // happen at that point and allocate from this free region, so when
26310                 // revisit_written_pages() continues, it cannot skip now-valid objects in this
26311                 // region.
26312                 no_more_loop_p = TRUE;
26313                 goto end_limit;
26314             }
26315         }
26316 end_limit:
26317         if (concurrent_p && large_objects_p)
26318         {
26319             bgc_alloc_lock->bgc_mark_done ();
26320         }
26321         if (no_more_loop_p)
26322         {
26323             break;
26324         }
26325         o = next_o;
26326     }
26327 
26328 #ifdef MULTIPLE_HEAPS
26329     if (concurrent_p)
26330     {
26331         assert (last_object < (min (high_address, page + OS_PAGE_SIZE)));
26332     }
26333     else
26334 #endif //MULTIPLE_HEAPS
26335     {
26336         last_object = o;
26337     }
26338 
26339     dprintf (3,("Last object: %Ix", (size_t)last_object));
26340     last_page = align_lower_page (o);
26341 }
26342 
26343 // When reset_only_p is TRUE, we should only reset pages that are in range
26344 // because we need to consider the segments or part of segments that were
26345 // allocated out of range all live.
26346 void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
26347 {
26348 #ifdef WRITE_WATCH
26349     if (concurrent_p && !reset_only_p)
26350     {
26351         current_bgc_state = bgc_revisit_soh;
26352     }
26353 
26354     size_t total_dirtied_pages = 0;
26355     size_t total_marked_objects = 0;
26356 
26357     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26358 
26359     PREFIX_ASSUME(seg != NULL);
26360 
26361     bool reset_watch_state = !!concurrent_p;
26362     bool is_runtime_suspended = !concurrent_p;
26363     BOOL small_object_segments = TRUE;
26364     int align_const = get_alignment_constant (small_object_segments);
26365 
26366     while (1)
26367     {
26368         if (seg == 0)
26369         {
26370             if (small_object_segments)
26371             {
26372                 //switch to large segment
26373                 if (concurrent_p && !reset_only_p)
26374                 {
26375                     current_bgc_state = bgc_revisit_loh;
26376                 }
26377 
26378                 if (!reset_only_p)
26379                 {
26380                     dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26381                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26382                     concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
26383                     total_dirtied_pages = 0;
26384                     total_marked_objects = 0;
26385                 }
26386 
26387                 small_object_segments = FALSE;
26388                 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
26389 
26390                 dprintf (3, ("now revisiting large object segments"));
26391                 align_const = get_alignment_constant (small_object_segments);
26392                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
26393 
26394                 PREFIX_ASSUME(seg != NULL);
26395 
26396                 continue;
26397             }
26398             else
26399             {
26400                 if (reset_only_p)
26401                 {
26402                     dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
26403                 }
26404                 else
26405                 {
26406                     dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26407                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26408                 }
26409                 break;
26410             }
26411         }
26412         uint8_t* base_address = (uint8_t*)heap_segment_mem (seg);
26413         //we need to truncate to the base of the page because
26414         //some newly allocated could exist beyond heap_segment_allocated
26415         //and if we reset the last page write watch status,
26416         // they wouldn't be guaranteed to be visited -> gc hole.
26417         uintptr_t bcount = array_size;
26418         uint8_t* last_page = 0;
26419         uint8_t* last_object = heap_segment_mem (seg);
26420         uint8_t* high_address = 0;
26421 
26422         BOOL skip_seg_p = FALSE;
26423 
26424         if (reset_only_p)
26425         {
26426             if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
26427                 (heap_segment_reserved (seg) <= background_saved_highest_address))
26428             {
26429                 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number,
26430                     heap_segment_mem (seg), heap_segment_reserved (seg)));
26431                 skip_seg_p = TRUE;
26432             }
26433         }
26434 
26435         if (!skip_seg_p)
26436         {
26437             dprintf (3, ("looking at seg %Ix", (size_t)last_object));
26438 
26439             if (reset_only_p)
26440             {
26441                 base_address = max (base_address, background_saved_lowest_address);
26442                 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
26443             }
26444 
26445             dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address,
26446                 heap_segment_mem (seg), heap_segment_reserved (seg)));
26447 
26448 
26449             while (1)
26450             {
26451                 if (reset_only_p)
26452                 {
26453                     high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
26454                     high_address = min (high_address, background_saved_highest_address);
26455                 }
26456                 else
26457                 {
26458                     high_address = high_page (seg, concurrent_p);
26459                 }
26460 
26461                 if ((base_address < high_address) &&
26462                     (bcount >= array_size))
26463                 {
26464                     ptrdiff_t region_size = high_address - base_address;
26465                     dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
26466 
26467 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26468                     // When the runtime is not suspended, it's possible for the table to be resized concurrently with the scan
26469                     // for dirty pages below. Prevent that by synchronizing with grow_brick_card_tables(). When the runtime is
26470                     // suspended, it's ok to scan for dirty pages concurrently from multiple background GC threads for disjoint
26471                     // memory regions.
26472                     if (!is_runtime_suspended)
26473                     {
26474                         enter_spin_lock(&gc_lock);
26475                     }
26476 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26477 
26478                     get_write_watch_for_gc_heap (reset_watch_state, base_address, region_size,
26479                                                  (void**)background_written_addresses,
26480                                                  &bcount, is_runtime_suspended);
26481 
26482 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26483                     if (!is_runtime_suspended)
26484                     {
26485                         leave_spin_lock(&gc_lock);
26486                     }
26487 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26488 
26489                     if (bcount != 0)
26490                     {
26491                         total_dirtied_pages += bcount;
26492 
26493                         dprintf (3, ("Found %d pages [%Ix, %Ix[",
26494                                         bcount, (size_t)base_address, (size_t)high_address));
26495                     }
26496 
26497                     if (!reset_only_p)
26498                     {
26499                         for (unsigned i = 0; i < bcount; i++)
26500                         {
26501     #ifdef NO_WRITE_BARRIER
26502                             card_table [card_word (card_of (background_written_addresses [i]))] = ~0u;
26503                             dprintf (3,("Set Cards [%p:%p, %p:%p[",
26504                                         card_of (background_written_addresses [i]), g_addresses [i],
26505                                         card_of (background_written_addresses [i]+OS_PAGE_SIZE), background_written_addresses [i]+OS_PAGE_SIZE));
26506     #endif //NO_WRITE_BARRIER
26507                             uint8_t* page = (uint8_t*)background_written_addresses[i];
26508                             dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i,
26509                                 (size_t)page, (size_t)high_address));
26510                             if (page < high_address)
26511                             {
26512                                 //search for marked objects in the page
26513                                 revisit_written_page (page, high_address, concurrent_p,
26514                                                     seg, last_page, last_object,
26515                                                     !small_object_segments,
26516                                                     total_marked_objects);
26517                             }
26518                             else
26519                             {
26520                                 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
26521                                 assert (!"page shouldn't have exceeded limit");
26522                             }
26523                         }
26524                     }
26525 
26526                     if (bcount >= array_size){
26527                         base_address = background_written_addresses [array_size-1] + OS_PAGE_SIZE;
26528                         bcount = array_size;
26529                     }
26530                 }
26531                 else
26532                 {
26533                     break;
26534                 }
26535             }
26536         }
26537 
26538         seg = heap_segment_next_rw (seg);
26539     }
26540 
26541 #endif //WRITE_WATCH
26542 }
26543 
26544 void gc_heap::background_grow_c_mark_list()
26545 {
26546     assert (c_mark_list_index >= c_mark_list_length);
26547     BOOL should_drain_p = FALSE;
26548     THREAD_FROM_HEAP;
26549 #ifndef MULTIPLE_HEAPS
26550     const int thread = heap_number;
26551 #endif //!MULTIPLE_HEAPS
26552 
26553     dprintf (2, ("stack copy buffer overflow"));
26554     uint8_t** new_c_mark_list = 0;
26555     {
26556         FAULT_NOT_FATAL();
26557         if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (uint8_t*))))
26558         {
26559             should_drain_p = TRUE;
26560         }
26561         else
26562         {
26563             new_c_mark_list = new (nothrow) uint8_t*[c_mark_list_length*2];
26564             if (new_c_mark_list == 0)
26565             {
26566                 should_drain_p = TRUE;
26567             }
26568         }
26569     }
26570     if (should_drain_p)
26571 
26572     {
26573         dprintf (2, ("No more memory for the stacks copy, draining.."));
26574         //drain the list by marking its elements
26575         background_drain_mark_list (thread);
26576     }
26577     else
26578     {
26579         assert (new_c_mark_list);
26580         memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(uint8_t*));
26581         c_mark_list_length = c_mark_list_length*2;
26582         delete c_mark_list;
26583         c_mark_list = new_c_mark_list;
26584     }
26585 }
26586 
26587 void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
26588                                   uint32_t flags)
26589 {
26590     UNREFERENCED_PARAMETER(sc);
26591     //in order to save space on the array, mark the object,
26592     //knowing that it will be visited later
26593     assert (settings.concurrent);
26594 
26595     THREAD_NUMBER_FROM_CONTEXT;
26596 #ifndef MULTIPLE_HEAPS
26597     const int thread = 0;
26598 #endif //!MULTIPLE_HEAPS
26599 
26600     uint8_t* o = (uint8_t*)*ppObject;
26601 
26602     if (o == 0)
26603         return;
26604 
26605     HEAP_FROM_THREAD;
26606 
26607     gc_heap* hp = gc_heap::heap_of (o);
26608 
26609     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
26610     {
26611         return;
26612     }
26613 
26614 #ifdef INTERIOR_POINTERS
26615     if (flags & GC_CALL_INTERIOR)
26616     {
26617         o = hp->find_object (o, hp->background_saved_lowest_address);
26618         if (o == 0)
26619             return;
26620     }
26621 #endif //INTERIOR_POINTERS
26622 
26623 #ifdef FEATURE_CONSERVATIVE_GC
26624     // For conservative GC, a value on stack may point to middle of a free object.
26625     // In this case, we don't need to promote the pointer.
26626     if (g_pConfig->GetGCConservative() && ((CObjectHeader*)o)->IsFree())
26627     {
26628         return;
26629     }
26630 #endif //FEATURE_CONSERVATIVE_GC
26631 
26632 #ifdef _DEBUG
26633     ((CObjectHeader*)o)->Validate();
26634 #endif //_DEBUG
26635 
26636     dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
26637     if (o && (size (o) > LARGE_OBJECT_SIZE))
26638     {
26639         dprintf (3, ("Brc %Ix", (size_t)o));
26640     }
26641 
26642     if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
26643     {
26644         hpt->background_grow_c_mark_list();
26645     }
26646     dprintf (3, ("pushing %08x into mark_list", (size_t)o));
26647     hpt->c_mark_list [hpt->c_mark_list_index++] = o;
26648 
26649     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Background Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
26650 }
26651 
26652 void gc_heap::mark_absorb_new_alloc()
26653 {
26654     fix_allocation_contexts (FALSE);
26655 
26656     gen0_bricks_cleared = FALSE;
26657 
26658     clear_gen0_bricks();
26659 }
26660 
26661 BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
26662 {
26663     BOOL success = FALSE;
26664     BOOL thread_created = FALSE;
26665     dprintf (2, ("Preparing gc thread"));
26666 
26667     gh->bgc_threads_timeout_cs.Enter();
26668     if (!(gh->bgc_thread_running))
26669     {
26670         dprintf (2, ("GC thread not runnning"));
26671         if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
26672         {
26673             success = TRUE;
26674             thread_created = TRUE;
26675         }
26676     }
26677     else
26678     {
26679         dprintf (3, ("GC thread already running"));
26680         success = TRUE;
26681     }
26682     gh->bgc_threads_timeout_cs.Leave();
26683 
26684     if(thread_created)
26685         FireEtwGCCreateConcurrentThread_V1(GetClrInstanceId());
26686 
26687     return success;
26688 }
26689 
26690 BOOL gc_heap::create_bgc_thread(gc_heap* gh)
26691 {
26692     assert (background_gc_done_event.IsValid());
26693 
26694     //dprintf (2, ("Creating BGC thread"));
26695 
26696     gh->bgc_thread = GCToEEInterface::CreateBackgroundThread(gh->bgc_thread_stub, gh);
26697     gh->bgc_thread_running = (gh->bgc_thread != NULL);
26698 
26699     return gh->bgc_thread_running;
26700 }
26701 
26702 BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
26703 {
26704     BOOL ret = FALSE;
26705     dprintf (3, ("Creating concurrent GC thread for the first time"));
26706     if (!background_gc_done_event.CreateManualEventNoThrow(TRUE))
26707     {
26708         goto cleanup;
26709     }
26710     if (!bgc_threads_sync_event.CreateManualEventNoThrow(FALSE))
26711     {
26712         goto cleanup;
26713     }
26714     if (!ee_proceed_event.CreateAutoEventNoThrow(FALSE))
26715     {
26716         goto cleanup;
26717     }
26718     if (!bgc_start_event.CreateManualEventNoThrow(FALSE))
26719     {
26720         goto cleanup;
26721     }
26722 
26723 #ifdef MULTIPLE_HEAPS
26724     bgc_t_join.init (number_of_heaps, join_flavor_bgc);
26725 #else
26726     UNREFERENCED_PARAMETER(number_of_heaps);
26727 #endif //MULTIPLE_HEAPS
26728 
26729     ret = TRUE;
26730 
26731 cleanup:
26732 
26733     if (!ret)
26734     {
26735         if (background_gc_done_event.IsValid())
26736         {
26737             background_gc_done_event.CloseEvent();
26738         }
26739         if (bgc_threads_sync_event.IsValid())
26740         {
26741             bgc_threads_sync_event.CloseEvent();
26742         }
26743         if (ee_proceed_event.IsValid())
26744         {
26745             ee_proceed_event.CloseEvent();
26746         }
26747         if (bgc_start_event.IsValid())
26748         {
26749             bgc_start_event.CloseEvent();
26750         }
26751     }
26752 
26753     return ret;
26754 }
26755 
26756 BOOL gc_heap::create_bgc_thread_support()
26757 {
26758     BOOL ret = FALSE;
26759     uint8_t** parr;
26760 
26761     if (!gc_lh_block_event.CreateManualEventNoThrow(FALSE))
26762     {
26763         goto cleanup;
26764     }
26765 
26766     //needs to have room for enough smallest objects fitting on a page
26767     parr = new (nothrow) (uint8_t* [1 + page_size / MIN_OBJECT_SIZE]);
26768     if (!parr)
26769     {
26770         goto cleanup;
26771     }
26772 
26773     make_c_mark_list (parr);
26774 
26775     ret = TRUE;
26776 
26777 cleanup:
26778 
26779     if (!ret)
26780     {
26781         if (gc_lh_block_event.IsValid())
26782         {
26783             gc_lh_block_event.CloseEvent();
26784         }
26785     }
26786 
26787     return ret;
26788 }
26789 
26790 int gc_heap::check_for_ephemeral_alloc()
26791 {
26792     int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
26793 
26794     if (gen == -1)
26795     {
26796 #ifdef MULTIPLE_HEAPS
26797         for (int heap_index = 0; heap_index < n_heaps; heap_index++)
26798 #endif //MULTIPLE_HEAPS
26799         {
26800             for (int i = 0; i <= (max_generation - 1); i++)
26801             {
26802 #ifdef MULTIPLE_HEAPS
26803                 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
26804 #else
26805                 if (get_new_allocation (i) <= 0)
26806 #endif //MULTIPLE_HEAPS
26807                 {
26808                     gen = max (gen, i);
26809                 }
26810                 else
26811                     break;
26812             }
26813         }
26814     }
26815 
26816     return gen;
26817 }
26818 
26819 // Wait for gc to finish sequential part
26820 void gc_heap::wait_to_proceed()
26821 {
26822     assert (background_gc_done_event.IsValid());
26823     assert (bgc_start_event.IsValid());
26824 
26825     user_thread_wait(&ee_proceed_event, FALSE);
26826 }
26827 
26828 // Start a new concurrent gc
26829 void gc_heap::start_c_gc()
26830 {
26831     assert (background_gc_done_event.IsValid());
26832     assert (bgc_start_event.IsValid());
26833 
26834 //Need to make sure that the gc thread is in the right place.
26835     background_gc_done_event.Wait(INFINITE, FALSE);
26836     background_gc_done_event.Reset();
26837     bgc_start_event.Set();
26838 }
26839 
26840 void gc_heap::do_background_gc()
26841 {
26842     dprintf (2, ("starting a BGC"));
26843 #ifdef MULTIPLE_HEAPS
26844     for (int i = 0; i < n_heaps; i++)
26845     {
26846         g_heaps[i]->init_background_gc();
26847     }
26848 #else
26849     init_background_gc();
26850 #endif //MULTIPLE_HEAPS
26851     //start the background gc
26852     start_c_gc ();
26853 
26854     //wait until we get restarted by the BGC.
26855     wait_to_proceed();
26856 }
26857 
26858 void gc_heap::kill_gc_thread()
26859 {
26860     //assert (settings.concurrent == FALSE);
26861 
26862     // We are doing a two-stage shutdown now.
26863     // In the first stage, we do minimum work, and call ExitProcess at the end.
26864     // In the secodn stage, we have the Loader lock and only one thread is
26865     // alive.  Hence we do not need to kill gc thread.
26866     background_gc_done_event.CloseEvent();
26867     gc_lh_block_event.CloseEvent();
26868     bgc_start_event.CloseEvent();
26869     bgc_threads_timeout_cs.Destroy();
26870     bgc_thread = 0;
26871     recursive_gc_sync::shutdown();
26872 }
26873 
26874 uint32_t gc_heap::bgc_thread_function()
26875 {
26876     assert (background_gc_done_event.IsValid());
26877     assert (bgc_start_event.IsValid());
26878 
26879     dprintf (3, ("gc_thread thread starting..."));
26880 
26881     BOOL do_exit = FALSE;
26882 
26883     Thread* current_thread = GetThread();
26884     BOOL cooperative_mode = TRUE;
26885     bgc_thread_id.SetToCurrentThread();
26886     dprintf (1, ("bgc_thread_id is set to %x", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()));
26887     while (1)
26888     {
26889         // Wait for work to do...
26890         dprintf (3, ("bgc thread: waiting..."));
26891 
26892         cooperative_mode = enable_preemptive (current_thread);
26893         //current_thread->m_fPreemptiveGCDisabled = 0;
26894 
26895         uint32_t result = bgc_start_event.Wait(
26896 #ifdef _DEBUG
26897 #ifdef MULTIPLE_HEAPS
26898                                              INFINITE,
26899 #else
26900                                              2000,
26901 #endif //MULTIPLE_HEAPS
26902 #else //_DEBUG
26903 #ifdef MULTIPLE_HEAPS
26904                                              INFINITE,
26905 #else
26906                                              20000,
26907 #endif //MULTIPLE_HEAPS
26908 #endif //_DEBUG
26909             FALSE);
26910         dprintf (2, ("gc thread: finished waiting"));
26911 
26912         // not calling disable_preemptive here 'cause we
26913         // can't wait for GC complete here - RestartEE will be called
26914         // when we've done the init work.
26915 
26916         if (result == WAIT_TIMEOUT)
26917         {
26918             // Should join the bgc threads and terminate all of them
26919             // at once.
26920             dprintf (1, ("GC thread timeout"));
26921             bgc_threads_timeout_cs.Enter();
26922             if (!keep_bgc_threads_p)
26923             {
26924                 dprintf (2, ("GC thread exiting"));
26925                 bgc_thread_running = FALSE;
26926                 bgc_thread = 0;
26927                 bgc_thread_id.Clear();
26928                 do_exit = TRUE;
26929             }
26930             bgc_threads_timeout_cs.Leave();
26931             if (do_exit)
26932                 break;
26933             else
26934             {
26935                 dprintf (3, ("GC thread needed, not exiting"));
26936                 continue;
26937             }
26938         }
26939         // if we signal the thread with no concurrent work to do -> exit
26940         if (!settings.concurrent)
26941         {
26942             dprintf (3, ("no concurrent GC needed, exiting"));
26943             break;
26944         }
26945 #ifdef TRACE_GC
26946         //trace_gc = TRUE;
26947 #endif //TRACE_GC
26948         recursive_gc_sync::begin_background();
26949         dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d",
26950             generation_free_list_space (generation_of (max_generation)),
26951             generation_free_obj_space (generation_of (max_generation)),
26952             dd_fragmentation (dynamic_data_of (max_generation))));
26953 
26954         gc1();
26955 
26956         current_bgc_state = bgc_not_in_process;
26957 
26958 #ifdef TRACE_GC
26959         //trace_gc = FALSE;
26960 #endif //TRACE_GC
26961 
26962         enable_preemptive (current_thread);
26963 #ifdef MULTIPLE_HEAPS
26964         bgc_t_join.join(this, gc_join_done);
26965         if (bgc_t_join.joined())
26966 #endif //MULTIPLE_HEAPS
26967         {
26968             enter_spin_lock (&gc_lock);
26969             dprintf (SPINLOCK_LOG, ("bgc Egc"));
26970 
26971             bgc_start_event.Reset();
26972             do_post_gc();
26973 #ifdef MULTIPLE_HEAPS
26974             for (int gen = max_generation; gen <= (max_generation + 1); gen++)
26975             {
26976                 size_t desired_per_heap = 0;
26977                 size_t total_desired = 0;
26978                 gc_heap* hp = 0;
26979                 dynamic_data* dd;
26980                 for (int i = 0; i < n_heaps; i++)
26981                 {
26982                     hp = g_heaps[i];
26983                     dd = hp->dynamic_data_of (gen);
26984                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
26985                     if (temp_total_desired < total_desired)
26986                     {
26987                         // we overflowed.
26988                         total_desired = (size_t)MAX_PTR;
26989                         break;
26990                     }
26991                     total_desired = temp_total_desired;
26992                 }
26993 
26994                 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
26995 
26996                 for (int i = 0; i < n_heaps; i++)
26997                 {
26998                     hp = gc_heap::g_heaps[i];
26999                     dd = hp->dynamic_data_of (gen);
27000                     dd_desired_allocation (dd) = desired_per_heap;
27001                     dd_gc_new_allocation (dd) = desired_per_heap;
27002                     dd_new_allocation (dd) = desired_per_heap;
27003                 }
27004             }
27005 #endif //MULTIPLE_HEAPS
27006 #ifdef MULTIPLE_HEAPS
27007             fire_pevents();
27008 #endif //MULTIPLE_HEAPS
27009 
27010             c_write (settings.concurrent, FALSE);
27011             recursive_gc_sync::end_background();
27012             keep_bgc_threads_p = FALSE;
27013             background_gc_done_event.Set();
27014 
27015             dprintf (SPINLOCK_LOG, ("bgc Lgc"));
27016             leave_spin_lock (&gc_lock);
27017 #ifdef MULTIPLE_HEAPS
27018             dprintf(1, ("End of BGC - starting all BGC threads"));
27019             bgc_t_join.restart();
27020 #endif //MULTIPLE_HEAPS
27021         }
27022         // We can't disable preempt here because there might've been a GC already
27023         // started and decided to do a BGC and waiting for a BGC thread to restart
27024         // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
27025         // to restart the VM so we deadlock.
27026         //gc_heap::disable_preemptive (current_thread, TRUE);
27027     }
27028 
27029     FireEtwGCTerminateConcurrentThread_V1(GetClrInstanceId());
27030 
27031     dprintf (3, ("bgc_thread thread exiting"));
27032     return 0;
27033 }
27034 
27035 #endif //BACKGROUND_GC
27036 
27037 //Clear the cards [start_card, end_card[
27038 void gc_heap::clear_cards (size_t start_card, size_t end_card)
27039 {
27040     if (start_card < end_card)
27041     {
27042         size_t start_word = card_word (start_card);
27043         size_t end_word = card_word (end_card);
27044         if (start_word < end_word)
27045         {
27046             unsigned bits = card_bit (start_card);
27047             card_table [start_word] &= lowbits (~0, bits);
27048             for (size_t i = start_word+1; i < end_word; i++)
27049                 card_table [i] = 0;
27050             bits = card_bit (end_card);
27051             // Don't write beyond end_card (and possibly uncommitted card table space).
27052             if (bits != 0)
27053             {
27054                 card_table [end_word] &= highbits (~0, bits);
27055             }
27056         }
27057         else
27058         {
27059             card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
27060                                         highbits (~0, card_bit (end_card)));
27061         }
27062 #ifdef VERYSLOWDEBUG
27063         size_t  card = start_card;
27064         while (card < end_card)
27065         {
27066             assert (! (card_set_p (card)));
27067             card++;
27068         }
27069 #endif //VERYSLOWDEBUG
27070         dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
27071                   start_card, (size_t)card_address (start_card),
27072                   end_card, (size_t)card_address (end_card)));
27073     }
27074 }
27075 
27076 void gc_heap::clear_card_for_addresses (uint8_t* start_address, uint8_t* end_address)
27077 {
27078     size_t   start_card = card_of (align_on_card (start_address));
27079     size_t   end_card = card_of (align_lower_card (end_address));
27080     clear_cards (start_card, end_card);
27081 }
27082 
27083 // copy [srccard, ...[ to [dst_card, end_card[
27084 // This will set the same bit twice. Can be optimized.
27085 inline
27086 void gc_heap::copy_cards (size_t dst_card, size_t src_card,
27087                  size_t end_card, BOOL nextp)
27088 {
27089     // If the range is empty, this function is a no-op - with the subtlety that
27090     // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
27091     // outside the committed region.  To avoid the access, leave early.
27092     if (!(dst_card < end_card))
27093         return;
27094 
27095     unsigned int srcbit = card_bit (src_card);
27096     unsigned int dstbit = card_bit (dst_card);
27097     size_t srcwrd = card_word (src_card);
27098     size_t dstwrd = card_word (dst_card);
27099     unsigned int srctmp = card_table[srcwrd];
27100     unsigned int dsttmp = card_table[dstwrd];
27101     for (size_t card = dst_card; card < end_card; card++)
27102     {
27103         if (srctmp & (1 << srcbit))
27104             dsttmp |= 1 << dstbit;
27105         else
27106             dsttmp &= ~(1 << dstbit);
27107         if (!(++srcbit % 32))
27108         {
27109             srctmp = card_table[++srcwrd];
27110             srcbit = 0;
27111         }
27112         if (nextp)
27113         {
27114             if (srctmp & (1 << srcbit))
27115                 dsttmp |= 1 << dstbit;
27116         }
27117         if (!(++dstbit % 32))
27118         {
27119             card_table[dstwrd] = dsttmp;
27120             dstwrd++;
27121             dsttmp = card_table[dstwrd];
27122             dstbit = 0;
27123         }
27124     }
27125     card_table[dstwrd] = dsttmp;
27126 }
27127 
27128 void gc_heap::copy_cards_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27129 {
27130     ptrdiff_t relocation_distance = src - dest;
27131     size_t start_dest_card = card_of (align_on_card (dest));
27132     size_t end_dest_card = card_of (dest + len - 1);
27133     size_t dest_card = start_dest_card;
27134     size_t src_card = card_of (card_address (dest_card)+relocation_distance);
27135     dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
27136                  src_card, (size_t)src, dest_card, (size_t)dest));
27137     dprintf (3,(" %Ix->%Ix:%Ix[",
27138               (size_t)src+len, end_dest_card, (size_t)dest+len));
27139 
27140     dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
27141         dest, src, len, relocation_distance, (align_on_card (dest))));
27142 
27143     dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
27144         start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
27145 
27146     //First card has two boundaries
27147     if (start_dest_card != card_of (dest))
27148     {
27149         if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
27150             card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
27151         {
27152             dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
27153                     (card_address (start_dest_card) + relocation_distance),
27154                     card_of (card_address (start_dest_card) + relocation_distance),
27155                     (src + len - 1),
27156                     card_of (src + len - 1)));
27157 
27158             dprintf (3, ("setting card: %Ix", card_of (dest)));
27159             set_card (card_of (dest));
27160         }
27161     }
27162 
27163     if (card_set_p (card_of (src)))
27164         set_card (card_of (dest));
27165 
27166 
27167     copy_cards (dest_card, src_card, end_dest_card,
27168                 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
27169 
27170     //Last card has two boundaries.
27171     if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
27172         card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
27173     {
27174         dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
27175                 (card_address (end_dest_card) + relocation_distance),
27176                 card_of (card_address (end_dest_card) + relocation_distance),
27177                 src,
27178                 card_of (src)));
27179 
27180         dprintf (3, ("setting card: %Ix", end_dest_card));
27181         set_card (end_dest_card);
27182     }
27183 
27184     if (card_set_p (card_of (src + len - 1)))
27185         set_card (end_dest_card);
27186 }
27187 
27188 #ifdef BACKGROUND_GC
27189 // this does not need the Interlocked version of mark_array_set_marked.
27190 void gc_heap::copy_mark_bits_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27191 {
27192     dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
27193                  (size_t)src, (size_t)dest,
27194                  (size_t)src+len, (size_t)dest+len));
27195 
27196     uint8_t* src_o = src;
27197     uint8_t* dest_o;
27198     uint8_t* src_end = src + len;
27199     int align_const = get_alignment_constant (TRUE);
27200     ptrdiff_t reloc = dest - src;
27201 
27202     while (src_o < src_end)
27203     {
27204         uint8_t*  next_o = src_o + Align (size (src_o), align_const);
27205 
27206         if (background_object_marked (src_o, TRUE))
27207         {
27208             dest_o = src_o + reloc;
27209 
27210             //if (background_object_marked (dest_o, FALSE))
27211             //{
27212             //    dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
27213             //    FATAL_GC_ERROR();
27214             //}
27215 
27216             background_mark (dest_o,
27217                              background_saved_lowest_address,
27218                              background_saved_highest_address);
27219             dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
27220         }
27221 
27222         src_o = next_o;
27223     }
27224 }
27225 #endif //BACKGROUND_GC
27226 
27227 void gc_heap::fix_brick_to_highest (uint8_t* o, uint8_t* next_o)
27228 {
27229     size_t new_current_brick = brick_of (o);
27230     set_brick (new_current_brick,
27231                (o - brick_address (new_current_brick)));
27232     size_t b = 1 + new_current_brick;
27233     size_t limit = brick_of (next_o);
27234     //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
27235     dprintf(3,("b:%Ix->%Ix-%Ix",
27236                new_current_brick, (size_t)o, (size_t)next_o));
27237     while (b < limit)
27238     {
27239         set_brick (b,(new_current_brick - b));
27240         b++;
27241     }
27242 }
27243 
27244 // start can not be >= heap_segment_allocated for the segment.
27245 uint8_t* gc_heap::find_first_object (uint8_t* start, uint8_t* first_object)
27246 {
27247     size_t brick = brick_of (start);
27248     uint8_t* o = 0;
27249     //last_object == null -> no search shortcut needed
27250     if ((brick == brick_of (first_object) || (start <= first_object)))
27251     {
27252         o = first_object;
27253     }
27254     else
27255     {
27256         ptrdiff_t  min_brick = (ptrdiff_t)brick_of (first_object);
27257         ptrdiff_t  prev_brick = (ptrdiff_t)brick - 1;
27258         int         brick_entry = 0;
27259         while (1)
27260         {
27261             if (prev_brick < min_brick)
27262             {
27263                 break;
27264             }
27265             if ((brick_entry =  brick_table [ prev_brick ]) >= 0)
27266             {
27267                 break;
27268             }
27269             assert (! ((brick_entry == 0)));
27270             prev_brick = (brick_entry + prev_brick);
27271 
27272         }
27273         o = ((prev_brick < min_brick) ? first_object :
27274                       brick_address (prev_brick) + brick_entry - 1);
27275         assert (o <= start);
27276     }
27277 
27278     assert (Align (size (o)) >= Align (min_obj_size));
27279     uint8_t*  next_o = o + Align (size (o));
27280     size_t curr_cl = (size_t)next_o / brick_size;
27281     size_t min_cl = (size_t)first_object / brick_size;
27282 
27283     //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
27284 #ifdef TRACE_GC
27285     unsigned int n_o = 1;
27286 #endif //TRACE_GC
27287 
27288     uint8_t* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27289 
27290     while (next_o <= start)
27291     {
27292         do
27293         {
27294 #ifdef TRACE_GC
27295             n_o++;
27296 #endif //TRACE_GC
27297             o = next_o;
27298             assert (Align (size (o)) >= Align (min_obj_size));
27299             next_o = o + Align (size (o));
27300             Prefetch (next_o);
27301         }while (next_o < next_b);
27302 
27303         if (((size_t)next_o / brick_size) != curr_cl)
27304         {
27305             if (curr_cl >= min_cl)
27306             {
27307                 fix_brick_to_highest (o, next_o);
27308             }
27309             curr_cl = (size_t) next_o / brick_size;
27310         }
27311         next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27312     }
27313 
27314     size_t bo = brick_of (o);
27315     //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix",
27316     dprintf (3, ("%Id o, [%Ix-[%Ix",
27317         n_o, bo, brick));
27318     if (bo < brick)
27319     {
27320         set_brick (bo, (o - brick_address(bo)));
27321         size_t b = 1 + bo;
27322         int x = -1;
27323         while (b < brick)
27324         {
27325             set_brick (b,x--);
27326             b++;
27327         }
27328     }
27329 
27330     return o;
27331 }
27332 
27333 #ifdef CARD_BUNDLE
27334 BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
27335 {
27336     dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
27337                  dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
27338 
27339     if (card_bundles_enabled())
27340     {
27341         size_t cardb = cardw_card_bundle (cardw);
27342         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
27343         while (1)
27344         {
27345             //find a non null bundle
27346             while ((cardb < end_cardb) &&
27347                    (card_bundle_set_p (cardb)==0))
27348             {
27349                 cardb++;
27350             }
27351             if (cardb == end_cardb)
27352                 return FALSE;
27353             //find a non empty card word
27354 
27355             uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
27356             uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
27357             while ((card_word < card_word_end) &&
27358                    !(*card_word))
27359             {
27360                 card_word++;
27361             }
27362             if (card_word != card_word_end)
27363             {
27364                 cardw = (card_word - &card_table [0]);
27365                 return TRUE;
27366             }
27367             else if ((cardw <= card_bundle_cardw (cardb)) &&
27368                      (card_word == &card_table [card_bundle_cardw (cardb+1)]))
27369             {
27370                 // a whole bundle was explored and is empty
27371                 dprintf  (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
27372                         dd_collection_count (dynamic_data_of (0)),
27373                         cardb, card_bundle_cardw (cardb),
27374                         card_bundle_cardw (cardb+1)));
27375                 card_bundle_clear (cardb);
27376             }
27377             cardb++;
27378         }
27379     }
27380     else
27381     {
27382         uint32_t* card_word = &card_table[cardw];
27383         uint32_t* card_word_end = &card_table [cardw_end];
27384 
27385         while (card_word < card_word_end)
27386         {
27387             if ((*card_word) != 0)
27388             {
27389                 cardw = (card_word - &card_table [0]);
27390                 return TRUE;
27391             }
27392             card_word++;
27393         }
27394         return FALSE;
27395 
27396     }
27397 
27398 }
27399 
27400 #endif //CARD_BUNDLE
27401 
27402 BOOL gc_heap::find_card (uint32_t* card_table, size_t& card,
27403                 size_t card_word_end, size_t& end_card)
27404 {
27405     uint32_t* last_card_word;
27406     uint32_t y;
27407     uint32_t z;
27408     // Find the first card which is set
27409 
27410     last_card_word = &card_table [card_word (card)];
27411     z = card_bit (card);
27412     y = (*last_card_word) >> z;
27413     if (!y)
27414     {
27415         z = 0;
27416 #ifdef CARD_BUNDLE
27417         size_t lcw = card_word(card)+1;
27418         if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
27419             return FALSE;
27420         else
27421         {
27422             last_card_word = &card_table [lcw];
27423             y = *last_card_word;
27424         }
27425 
27426 #else //CARD_BUNDLE
27427         do
27428         {
27429             ++last_card_word;
27430         }
27431 
27432         while ((last_card_word < &card_table [card_word_end]) &&
27433                !(*last_card_word));
27434         if (last_card_word < &card_table [card_word_end])
27435             y = *last_card_word;
27436         else
27437             return FALSE;
27438 #endif //CARD_BUNDLE
27439     }
27440 
27441 
27442     // Look for the lowest bit set
27443     if (y)
27444     {
27445         while (!(y & 1))
27446         {
27447             z++;
27448             y = y / 2;
27449         }
27450     }
27451     card = (last_card_word - &card_table [0])* card_word_width + z;
27452     do
27453     {
27454         z++;
27455         y = y / 2;
27456         if ((z == card_word_width) &&
27457             (last_card_word < &card_table [card_word_end]))
27458         {
27459 
27460             do
27461             {
27462                 y = *(++last_card_word);
27463             }while ((last_card_word < &card_table [card_word_end]) &&
27464 #ifdef _MSC_VER
27465                      (y == (1 << card_word_width)-1)
27466 #else
27467                      // if left shift count >= width of type,
27468                      // gcc reports error.
27469                      (y == ~0u)
27470 #endif // _MSC_VER
27471                 );
27472             z = 0;
27473         }
27474     } while (y & 1);
27475 
27476     end_card = (last_card_word - &card_table [0])* card_word_width + z;
27477     //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
27478     dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
27479     return TRUE;
27480 }
27481 
27482 
27483     //because of heap expansion, computing end is complicated.
27484 uint8_t* compute_next_end (heap_segment* seg, uint8_t* low)
27485 {
27486     if ((low >=  heap_segment_mem (seg)) &&
27487         (low < heap_segment_allocated (seg)))
27488         return low;
27489     else
27490         return heap_segment_allocated (seg);
27491 }
27492 
27493 uint8_t*
27494 gc_heap::compute_next_boundary (uint8_t* low, int gen_number,
27495                                 BOOL relocating)
27496 {
27497     UNREFERENCED_PARAMETER(low);
27498 
27499     //when relocating, the fault line is the plan start of the younger
27500     //generation because the generation is promoted.
27501     if (relocating && (gen_number == (settings.condemned_generation + 1)))
27502     {
27503         generation* gen = generation_of (gen_number - 1);
27504         uint8_t* gen_alloc = generation_plan_allocation_start (gen);
27505         assert (gen_alloc);
27506         return gen_alloc;
27507     }
27508     else
27509     {
27510         assert (gen_number > settings.condemned_generation);
27511         return generation_allocation_start (generation_of (gen_number - 1 ));
27512     }
27513 
27514 }
27515 
27516 inline void
27517 gc_heap::keep_card_live (uint8_t* o, size_t& n_gen,
27518                          size_t& cg_pointers_found)
27519 {
27520     THREAD_FROM_HEAP;
27521     if ((gc_low <= o) && (gc_high > o))
27522     {
27523         n_gen++;
27524     }
27525 #ifdef MULTIPLE_HEAPS
27526     else if (o)
27527     {
27528         gc_heap* hp = heap_of (o);
27529         if (hp != this)
27530         {
27531             if ((hp->gc_low <= o) &&
27532                 (hp->gc_high > o))
27533             {
27534                 n_gen++;
27535             }
27536         }
27537     }
27538 #endif //MULTIPLE_HEAPS
27539     cg_pointers_found ++;
27540     dprintf (4, ("keep card live for %Ix", o));
27541 }
27542 
27543 inline void
27544 gc_heap::mark_through_cards_helper (uint8_t** poo, size_t& n_gen,
27545                                     size_t& cg_pointers_found,
27546                                     card_fn fn, uint8_t* nhigh,
27547                                     uint8_t* next_boundary)
27548 {
27549     THREAD_FROM_HEAP;
27550     if ((gc_low <= *poo) && (gc_high > *poo))
27551     {
27552         n_gen++;
27553         call_fn(fn) (poo THREAD_NUMBER_ARG);
27554     }
27555 #ifdef MULTIPLE_HEAPS
27556     else if (*poo)
27557     {
27558         gc_heap* hp = heap_of_gc (*poo);
27559         if (hp != this)
27560         {
27561             if ((hp->gc_low <= *poo) &&
27562                 (hp->gc_high > *poo))
27563             {
27564                 n_gen++;
27565                 call_fn(fn) (poo THREAD_NUMBER_ARG);
27566             }
27567             if ((fn == &gc_heap::relocate_address) ||
27568                 ((hp->ephemeral_low <= *poo) &&
27569                  (hp->ephemeral_high > *poo)))
27570             {
27571                 cg_pointers_found++;
27572             }
27573         }
27574     }
27575 #endif //MULTIPLE_HEAPS
27576     if ((next_boundary <= *poo) && (nhigh > *poo))
27577     {
27578         cg_pointers_found ++;
27579         dprintf (4, ("cg pointer %Ix found, %Id so far",
27580                      (size_t)*poo, cg_pointers_found ));
27581 
27582     }
27583 }
27584 
27585 BOOL gc_heap::card_transition (uint8_t* po, uint8_t* end, size_t card_word_end,
27586                                size_t& cg_pointers_found,
27587                                size_t& n_eph, size_t& n_card_set,
27588                                size_t& card, size_t& end_card,
27589                                BOOL& foundp, uint8_t*& start_address,
27590                                uint8_t*& limit, size_t& n_cards_cleared)
27591 {
27592     dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
27593     dprintf (3, ("ct: %Id cg", cg_pointers_found));
27594     BOOL passed_end_card_p = FALSE;
27595     foundp = FALSE;
27596 
27597     if (cg_pointers_found == 0)
27598     {
27599         //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
27600         dprintf(3,(" CC [%Ix, %Ix[ ",
27601                 (size_t)card_address(card), (size_t)po));
27602         clear_cards (card, card_of(po));
27603         n_card_set -= (card_of (po) - card);
27604         n_cards_cleared += (card_of (po) - card);
27605 
27606     }
27607     n_eph +=cg_pointers_found;
27608     cg_pointers_found = 0;
27609     card = card_of (po);
27610     if (card >= end_card)
27611     {
27612         passed_end_card_p = TRUE;
27613         dprintf (3, ("card %Ix exceeding end_card %Ix",
27614                     (size_t)card, (size_t)end_card));
27615         foundp = find_card (card_table, card, card_word_end, end_card);
27616         if (foundp)
27617         {
27618             n_card_set+= end_card - card;
27619             start_address = card_address (card);
27620             dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
27621                         (size_t)card, (size_t)start_address,
27622                         (size_t)card_address (end_card)));
27623         }
27624         limit = min (end, card_address (end_card));
27625 
27626         assert (!((limit == card_address (end_card))&&
27627                 card_set_p (end_card)));
27628     }
27629 
27630     return passed_end_card_p;
27631 }
27632 
27633 void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
27634 {
27635 #ifdef BACKGROUND_GC
27636     dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
27637                  current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
27638     heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
27639     PREFIX_ASSUME(soh_seg  != NULL);
27640     while (soh_seg )
27641     {
27642         dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix",
27643             soh_seg,
27644             heap_segment_background_allocated (soh_seg),
27645             heap_segment_allocated (soh_seg)));
27646         soh_seg = heap_segment_next_rw (soh_seg);
27647     }
27648 #endif //BACKGROUND_GC
27649 
27650     uint8_t* low = gc_low;
27651     uint8_t* high = gc_high;
27652     size_t  end_card          = 0;
27653     generation*   oldest_gen        = generation_of (max_generation);
27654     int           curr_gen_number   = max_generation;
27655     uint8_t*         gen_boundary      = generation_allocation_start
27656         (generation_of (curr_gen_number - 1));
27657     uint8_t*         next_boundary     = (compute_next_boundary
27658                                        (gc_low, curr_gen_number, relocating));
27659     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
27660 
27661     PREFIX_ASSUME(seg != NULL);
27662 
27663     uint8_t*         beg               = generation_allocation_start (oldest_gen);
27664     uint8_t*         end               = compute_next_end (seg, low);
27665     uint8_t*         last_object       = beg;
27666 
27667     size_t  cg_pointers_found = 0;
27668 
27669     size_t  card_word_end = (card_of (align_on_card_word (end)) /
27670                              card_word_width);
27671 
27672     size_t        n_eph             = 0;
27673     size_t        n_gen             = 0;
27674     size_t        n_card_set        = 0;
27675     uint8_t*         nhigh             = (relocating ?
27676                                        heap_segment_plan_allocated (ephemeral_heap_segment) : high);
27677 
27678     BOOL          foundp            = FALSE;
27679     uint8_t*         start_address     = 0;
27680     uint8_t*         limit             = 0;
27681     size_t        card              = card_of (beg);
27682 #ifdef BACKGROUND_GC
27683     BOOL consider_bgc_mark_p        = FALSE;
27684     BOOL check_current_sweep_p      = FALSE;
27685     BOOL check_saved_sweep_p        = FALSE;
27686     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
27687 #endif //BACKGROUND_GC
27688 
27689     dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
27690     size_t total_cards_cleared = 0;
27691 
27692     while (1)
27693     {
27694         if (card_of(last_object) > card)
27695         {
27696             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
27697             if (cg_pointers_found == 0)
27698             {
27699                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
27700                 clear_cards (card, card_of(last_object));
27701                 n_card_set -= (card_of (last_object) - card);
27702                 total_cards_cleared += (card_of (last_object) - card);
27703             }
27704             n_eph +=cg_pointers_found;
27705             cg_pointers_found = 0;
27706             card = card_of (last_object);
27707         }
27708         if (card >= end_card)
27709         {
27710             foundp = find_card (card_table, card, card_word_end, end_card);
27711             if (foundp)
27712             {
27713                 n_card_set+= end_card - card;
27714                 start_address = max (beg, card_address (card));
27715             }
27716             limit = min (end, card_address (end_card));
27717         }
27718         if ((!foundp) || (last_object >= end) || (card_address (card) >= end))
27719         {
27720             if ((foundp) && (cg_pointers_found == 0))
27721             {
27722                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
27723                            (size_t)end));
27724                 clear_cards (card, card_of (end));
27725                 n_card_set -= (card_of (end) - card);
27726                 total_cards_cleared += (card_of (end) - card);
27727             }
27728             n_eph +=cg_pointers_found;
27729             cg_pointers_found = 0;
27730             if ((seg = heap_segment_next_in_range (seg)) != 0)
27731             {
27732 #ifdef BACKGROUND_GC
27733                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
27734 #endif //BACKGROUND_GC
27735                 beg = heap_segment_mem (seg);
27736                 end = compute_next_end (seg, low);
27737                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
27738                 card = card_of (beg);
27739                 last_object = beg;
27740                 end_card = 0;
27741                 continue;
27742             }
27743             else
27744             {
27745                 break;
27746             }
27747         }
27748 
27749         assert (card_set_p (card));
27750         {
27751             uint8_t*   o             = last_object;
27752 
27753             o = find_first_object (start_address, last_object);
27754                 //Never visit an object twice.
27755                 assert (o >= last_object);
27756 
27757                 //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
27758                 dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
27759                        card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
27760 
27761             while (o < limit)
27762             {
27763                 assert (Align (size (o)) >= Align (min_obj_size));
27764                 size_t s = size (o);
27765 
27766                 uint8_t* next_o =  o + Align (s);
27767                 Prefetch (next_o);
27768 
27769                 if ((o >= gen_boundary) &&
27770                     (seg == ephemeral_heap_segment))
27771                 {
27772                     dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
27773                     curr_gen_number--;
27774                     assert ((curr_gen_number > 0));
27775                     gen_boundary = generation_allocation_start
27776                         (generation_of (curr_gen_number - 1));
27777                     next_boundary = (compute_next_boundary
27778                                      (low, curr_gen_number, relocating));
27779                 }
27780 
27781                 dprintf (4, ("|%Ix|", (size_t)o));
27782 
27783                 if (next_o < start_address)
27784                 {
27785                     goto end_object;
27786                 }
27787 
27788 #ifdef BACKGROUND_GC
27789                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
27790                 {
27791                     goto end_object;
27792                 }
27793 #endif //BACKGROUND_GC
27794 
27795 #ifdef COLLECTIBLE_CLASS
27796                 if (is_collectible(o))
27797                 {
27798                     BOOL passed_end_card_p = FALSE;
27799 
27800                     if (card_of (o) > card)
27801                     {
27802                         passed_end_card_p = card_transition (o, end, card_word_end,
27803                             cg_pointers_found,
27804                             n_eph, n_card_set,
27805                             card, end_card,
27806                             foundp, start_address,
27807                             limit, total_cards_cleared);
27808                     }
27809 
27810                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
27811                     {
27812                         // card is valid and it covers the head of the object
27813                         if (fn == &gc_heap::relocate_address)
27814                         {
27815                             keep_card_live (o, n_gen, cg_pointers_found);
27816                         }
27817                         else
27818                         {
27819                             uint8_t* class_obj = get_class_object (o);
27820                             mark_through_cards_helper (&class_obj, n_gen,
27821                                                     cg_pointers_found, fn,
27822                                                     nhigh, next_boundary);
27823                         }
27824                     }
27825 
27826                     if (passed_end_card_p)
27827                     {
27828                         if (foundp && (card_address (card) < next_o))
27829                         {
27830                             goto go_through_refs;
27831                         }
27832                         else if (foundp && (start_address < limit))
27833                         {
27834                             next_o = find_first_object (start_address, o);
27835                             goto end_object;
27836                         }
27837                         else
27838                             goto end_limit;
27839                     }
27840                 }
27841 
27842 go_through_refs:
27843 #endif //COLLECTIBLE_CLASS
27844 
27845                 if (contain_pointers (o))
27846                 {
27847                     dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
27848 
27849                     {
27850                         dprintf (4, ("normal object path"));
27851                         go_through_object
27852                             (method_table(o), o, s, poo,
27853                              start_address, use_start, (o + s),
27854                              {
27855                                  dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
27856                                  if (card_of ((uint8_t*)poo) > card)
27857                                  {
27858                                     BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
27859                                             card_word_end,
27860                                             cg_pointers_found,
27861                                             n_eph, n_card_set,
27862                                             card, end_card,
27863                                             foundp, start_address,
27864                                             limit, total_cards_cleared);
27865 
27866                                      if (passed_end_card_p)
27867                                      {
27868                                         if (foundp && (card_address (card) < next_o))
27869                                         {
27870                                              //new_start();
27871                                              {
27872                                                  if (ppstop <= (uint8_t**)start_address)
27873                                                      {break;}
27874                                                  else if (poo < (uint8_t**)start_address)
27875                                                      {poo = (uint8_t**)start_address;}
27876                                              }
27877                                         }
27878                                         else if (foundp && (start_address < limit))
27879                                         {
27880                                             next_o = find_first_object (start_address, o);
27881                                             goto end_object;
27882                                         }
27883                                          else
27884                                             goto end_limit;
27885                                      }
27886                                  }
27887 
27888                                  mark_through_cards_helper (poo, n_gen,
27889                                                             cg_pointers_found, fn,
27890                                                             nhigh, next_boundary);
27891                              }
27892                             );
27893                     }
27894                 }
27895 
27896             end_object:
27897                 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
27898                 {
27899                     if (brick_table [brick_of (o)] <0)
27900                         fix_brick_to_highest (o, next_o);
27901                 }
27902                 o = next_o;
27903             }
27904         end_limit:
27905             last_object = o;
27906         }
27907     }
27908     // compute the efficiency ratio of the card table
27909     if (!relocating)
27910     {
27911         generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
27912         dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d",
27913             n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
27914     }
27915     else
27916     {
27917         dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d",
27918             n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
27919     }
27920 }
27921 
27922 #ifdef SEG_REUSE_STATS
27923 size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
27924 {
27925     size_t total_items = 0;
27926     *total_size = 0;
27927     for (int i = 0; i < count; i++)
27928     {
27929         total_items += ordered_indices[i];
27930         *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
27931         dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
27932     }
27933     dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
27934     return total_items;
27935 }
27936 #endif // SEG_REUSE_STATS
27937 
27938 void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug)
27939 {
27940     // detect pinned plugs
27941     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
27942     {
27943         deque_pinned_plug();
27944         update_oldest_pinned_plug();
27945         dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
27946     }
27947     else
27948     {
27949         size_t plug_size = last_plug_size + Align(min_obj_size);
27950         BOOL is_padded = FALSE;
27951 
27952 #ifdef SHORT_PLUGS
27953         plug_size += Align (min_obj_size);
27954         is_padded = TRUE;
27955 #endif //SHORT_PLUGS
27956 
27957 #ifdef RESPECT_LARGE_ALIGNMENT
27958         plug_size += switch_alignment_size (is_padded);
27959 #endif //RESPECT_LARGE_ALIGNMENT
27960 
27961         total_ephemeral_plugs += plug_size;
27962         size_t plug_size_power2 = round_up_power2 (plug_size);
27963         ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
27964         dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array",
27965             heap_number,
27966             last_plug,
27967             plug_size,
27968             (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
27969     }
27970 }
27971 
27972 void gc_heap::count_plugs_in_brick (uint8_t* tree, uint8_t*& last_plug)
27973 {
27974     assert ((tree != NULL));
27975     if (node_left_child (tree))
27976     {
27977         count_plugs_in_brick (tree + node_left_child (tree), last_plug);
27978     }
27979 
27980     if (last_plug != 0)
27981     {
27982         uint8_t*  plug = tree;
27983         size_t gap_size = node_gap_size (plug);
27984         uint8_t*   gap = (plug - gap_size);
27985         uint8_t*  last_plug_end = gap;
27986         size_t  last_plug_size = (last_plug_end - last_plug);
27987         dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
27988             tree, last_plug, gap_size, gap, last_plug_size));
27989 
27990         if (tree == oldest_pinned_plug)
27991         {
27992             dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
27993                 tree, last_plug, last_plug_size));
27994             mark* m = oldest_pin();
27995             if (m->has_pre_plug_info())
27996             {
27997                 last_plug_size += sizeof (gap_reloc_pair);
27998                 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
27999             }
28000         }
28001         // Can't assert here - if it's a pinned plug it can be less.
28002         //assert (last_plug_size >= Align (min_obj_size));
28003 
28004         count_plug (last_plug_size, last_plug);
28005     }
28006 
28007     last_plug = tree;
28008 
28009     if (node_right_child (tree))
28010     {
28011         count_plugs_in_brick (tree + node_right_child (tree), last_plug);
28012     }
28013 }
28014 
28015 void gc_heap::build_ordered_plug_indices ()
28016 {
28017     memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
28018     memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
28019 
28020     uint8_t*  start_address = generation_limit (max_generation);
28021     uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment);
28022     size_t  current_brick = brick_of (start_address);
28023     size_t  end_brick = brick_of (end_address - 1);
28024     uint8_t* last_plug = 0;
28025 
28026     //Look for the right pinned plug to start from.
28027     reset_pinned_queue_bos();
28028     while (!pinned_plug_que_empty_p())
28029     {
28030         mark* m = oldest_pin();
28031         if ((m->first >= start_address) && (m->first < end_address))
28032         {
28033             dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
28034 
28035             break;
28036         }
28037         else
28038             deque_pinned_plug();
28039     }
28040 
28041     update_oldest_pinned_plug();
28042 
28043     while (current_brick <= end_brick)
28044     {
28045         int brick_entry =  brick_table [ current_brick ];
28046         if (brick_entry >= 0)
28047         {
28048             count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
28049         }
28050 
28051         current_brick++;
28052     }
28053 
28054     if (last_plug !=0)
28055     {
28056         count_plug (end_address - last_plug, last_plug);
28057     }
28058 
28059     // we need to make sure that after fitting all the existing plugs, we
28060     // have big enough free space left to guarantee that the next allocation
28061     // will succeed.
28062     size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
28063     total_ephemeral_plugs += extra_size;
28064     dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
28065     ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
28066 
28067     memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
28068 
28069 #ifdef SEG_REUSE_STATS
28070     dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
28071     size_t total_plug_power2 = 0;
28072     dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
28073     dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))",
28074                 total_ephemeral_plugs,
28075                 total_plug_power2,
28076                 (total_ephemeral_plugs ?
28077                     (total_plug_power2 * 100 / total_ephemeral_plugs) :
28078                     0)));
28079     dprintf (SEG_REUSE_LOG_0, ("-------------------"));
28080 #endif // SEG_REUSE_STATS
28081 }
28082 
28083 void gc_heap::init_ordered_free_space_indices ()
28084 {
28085     memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
28086     memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
28087 }
28088 
28089 void gc_heap::trim_free_spaces_indices ()
28090 {
28091     trimmed_free_space_index = -1;
28092     size_t max_count = max_free_space_items - 1;
28093     size_t count = 0;
28094     int i = 0;
28095     for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
28096     {
28097         count += ordered_free_space_indices[i];
28098 
28099         if (count >= max_count)
28100         {
28101             break;
28102         }
28103     }
28104 
28105     ptrdiff_t extra_free_space_items = count - max_count;
28106 
28107     if (extra_free_space_items > 0)
28108     {
28109         ordered_free_space_indices[i] -= extra_free_space_items;
28110         free_space_items = max_count;
28111         trimmed_free_space_index = i;
28112     }
28113     else
28114     {
28115         free_space_items = count;
28116     }
28117 
28118     if (i == -1)
28119     {
28120         i = 0;
28121     }
28122 
28123     free_space_buckets = MAX_NUM_BUCKETS - i;
28124 
28125     for (--i; i >= 0; i--)
28126     {
28127         ordered_free_space_indices[i] = 0;
28128     }
28129 
28130     memcpy (saved_ordered_free_space_indices,
28131             ordered_free_space_indices,
28132             sizeof(ordered_free_space_indices));
28133 }
28134 
28135 // We fit as many plugs as we can and update the number of plugs left and the number
28136 // of free spaces left.
28137 BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
28138 {
28139     assert (small_index <= big_index);
28140     assert (big_index < MAX_NUM_BUCKETS);
28141 
28142     size_t small_blocks = ordered_blocks[small_index];
28143 
28144     if (small_blocks == 0)
28145     {
28146         return TRUE;
28147     }
28148 
28149     size_t big_spaces = ordered_spaces[big_index];
28150 
28151     if (big_spaces == 0)
28152     {
28153         return FALSE;
28154     }
28155 
28156     dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces",
28157         heap_number,
28158         small_blocks, (small_index + MIN_INDEX_POWER2),
28159         big_spaces, (big_index + MIN_INDEX_POWER2)));
28160 
28161     size_t big_to_small = big_spaces << (big_index - small_index);
28162 
28163     ptrdiff_t extra_small_spaces = big_to_small - small_blocks;
28164     dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks",
28165         heap_number,
28166         big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
28167     BOOL can_fit = (extra_small_spaces >= 0);
28168 
28169     if (can_fit)
28170     {
28171         dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks",
28172             heap_number,
28173             extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
28174     }
28175 
28176     int i = 0;
28177 
28178     dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
28179     ordered_spaces[big_index] = 0;
28180     if (extra_small_spaces > 0)
28181     {
28182         dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
28183         ordered_blocks[small_index] = 0;
28184         for (i = small_index; i < big_index; i++)
28185         {
28186             if (extra_small_spaces & 1)
28187             {
28188                 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d",
28189                     heap_number,
28190                     (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
28191                 ordered_spaces[i] += 1;
28192             }
28193             extra_small_spaces >>= 1;
28194         }
28195 
28196         dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d",
28197             heap_number,
28198             (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
28199         ordered_spaces[i] += extra_small_spaces;
28200     }
28201     else
28202     {
28203         dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d",
28204             heap_number,
28205             (small_index + MIN_INDEX_POWER2),
28206             ordered_blocks[small_index],
28207             (ordered_blocks[small_index] - big_to_small)));
28208         ordered_blocks[small_index] -= big_to_small;
28209     }
28210 
28211 #ifdef SEG_REUSE_STATS
28212     size_t temp;
28213     dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
28214     dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
28215 
28216     dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
28217     dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
28218 #endif //SEG_REUSE_STATS
28219 
28220     return can_fit;
28221 }
28222 
28223 // space_index gets updated to the biggest available space index.
28224 BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
28225 {
28226     assert (*space_index >= block_index);
28227 
28228     while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
28229     {
28230         (*space_index)--;
28231         if (*space_index < block_index)
28232         {
28233             return FALSE;
28234         }
28235     }
28236 
28237     return TRUE;
28238 }
28239 
28240 BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
28241 {
28242 #ifdef FEATURE_STRUCTALIGN
28243     // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
28244     return FALSE;
28245 #endif // FEATURE_STRUCTALIGN
28246     int space_index = count - 1;
28247     for (int block_index = (count - 1); block_index >= 0; block_index--)
28248     {
28249         if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
28250         {
28251             return FALSE;
28252         }
28253     }
28254 
28255     return TRUE;
28256 }
28257 
28258 void gc_heap::build_ordered_free_spaces (heap_segment* seg)
28259 {
28260     assert (bestfit_seg);
28261 
28262     //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2,
28263     //                    ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets),
28264     //                    free_space_buckets,
28265     //                    free_space_items);
28266 
28267     bestfit_seg->add_buckets (MIN_INDEX_POWER2,
28268                         ordered_free_space_indices,
28269                         MAX_NUM_BUCKETS,
28270                         free_space_items);
28271 
28272     assert (settings.condemned_generation == max_generation);
28273 
28274     uint8_t* first_address = heap_segment_mem (seg);
28275     uint8_t* end_address   = heap_segment_reserved (seg);
28276     //look through the pinned plugs for relevant ones.
28277     //Look for the right pinned plug to start from.
28278     reset_pinned_queue_bos();
28279     mark* m = 0;
28280     // See comment in can_expand_into_p why we need (max_generation + 1).
28281     size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
28282     BOOL has_fit_gen_starts = FALSE;
28283 
28284     while (!pinned_plug_que_empty_p())
28285     {
28286         m = oldest_pin();
28287         if ((pinned_plug (m) >= first_address) &&
28288             (pinned_plug (m) < end_address) &&
28289             (pinned_len (m) >= eph_gen_starts))
28290         {
28291 
28292             assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
28293             break;
28294         }
28295         else
28296         {
28297             deque_pinned_plug();
28298         }
28299     }
28300 
28301     if (!pinned_plug_que_empty_p())
28302     {
28303         bestfit_seg->add ((void*)m, TRUE, TRUE);
28304         deque_pinned_plug();
28305         m = oldest_pin();
28306         has_fit_gen_starts = TRUE;
28307     }
28308 
28309     while (!pinned_plug_que_empty_p() &&
28310             ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28311     {
28312         bestfit_seg->add ((void*)m, TRUE, FALSE);
28313         deque_pinned_plug();
28314         m = oldest_pin();
28315     }
28316 
28317     if (commit_end_of_seg)
28318     {
28319         if (!has_fit_gen_starts)
28320         {
28321             assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
28322         }
28323         bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
28324     }
28325 
28326 #ifdef _DEBUG
28327     bestfit_seg->check();
28328 #endif //_DEBUG
28329 }
28330 
28331 BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
28332 {
28333     if (!end_of_segment_p)
28334     {
28335         trim_free_spaces_indices ();
28336     }
28337 
28338     BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices,
28339                                              ordered_free_space_indices,
28340                                              MAX_NUM_BUCKETS);
28341 
28342     return can_bestfit;
28343 }
28344 
28345 BOOL gc_heap::best_fit (size_t free_space,
28346                         size_t largest_free_space,
28347                         size_t additional_space,
28348                         BOOL* use_additional_space)
28349 {
28350     dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
28351 
28352     assert (!additional_space || (additional_space && use_additional_space));
28353     if (use_additional_space)
28354     {
28355         *use_additional_space = FALSE;
28356     }
28357 
28358     if (ordered_plug_indices_init == FALSE)
28359     {
28360         total_ephemeral_plugs = 0;
28361         build_ordered_plug_indices();
28362         ordered_plug_indices_init = TRUE;
28363     }
28364     else
28365     {
28366         memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
28367     }
28368 
28369     if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
28370     {
28371         dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
28372         size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
28373         BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
28374         if (!can_fit_empty_eph)
28375         {
28376             can_fit_empty_eph = (additional_space >= empty_eph);
28377 
28378             if (can_fit_empty_eph)
28379             {
28380                 *use_additional_space = TRUE;
28381             }
28382         }
28383 
28384         return can_fit_empty_eph;
28385     }
28386 
28387     if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
28388     {
28389         dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
28390         return FALSE;
28391     }
28392 
28393     if ((free_space + additional_space) == 0)
28394     {
28395         dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
28396         return FALSE;
28397     }
28398 
28399 #ifdef SEG_REUSE_STATS
28400     dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
28401     size_t total_free_space_power2 = 0;
28402     size_t total_free_space_items =
28403         dump_buckets (ordered_free_space_indices,
28404                       MAX_NUM_BUCKETS,
28405                       &total_free_space_power2);
28406     dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
28407 
28408     dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
28409                 total_ephemeral_plugs,
28410                 free_space,
28411                 total_free_space_power2,
28412                 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
28413                 additional_space));
28414 
28415     size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
28416     memcpy (saved_all_free_space_indices,
28417             ordered_free_space_indices,
28418             sizeof(saved_all_free_space_indices));
28419 
28420 #endif // SEG_REUSE_STATS
28421 
28422     if (total_ephemeral_plugs > (free_space + additional_space))
28423     {
28424         return FALSE;
28425     }
28426 
28427     use_bestfit = try_best_fit(FALSE);
28428 
28429     if (!use_bestfit && additional_space)
28430     {
28431         int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
28432 
28433         if (relative_free_space_index != -1)
28434         {
28435             int relative_plug_index = 0;
28436             size_t plugs_to_fit = 0;
28437 
28438             for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
28439             {
28440                 plugs_to_fit = ordered_plug_indices[relative_plug_index];
28441                 if (plugs_to_fit != 0)
28442                 {
28443                     break;
28444                 }
28445             }
28446 
28447             if ((relative_plug_index > relative_free_space_index) ||
28448                 ((relative_plug_index == relative_free_space_index) &&
28449                 (plugs_to_fit > 1)))
28450             {
28451 #ifdef SEG_REUSE_STATS
28452                 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
28453                             (relative_free_space_index + MIN_INDEX_POWER2),
28454                             plugs_to_fit,
28455                             (relative_plug_index + MIN_INDEX_POWER2)));
28456 #endif // SEG_REUSE_STATS
28457                 goto adjust;
28458             }
28459 
28460             dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
28461             ordered_free_space_indices[relative_free_space_index]++;
28462             use_bestfit = try_best_fit(TRUE);
28463             if (use_bestfit)
28464             {
28465                 free_space_items++;
28466                 // Since we might've trimmed away some of the free spaces we had, we should see
28467                 // if we really need to use end of seg space - if it's the same or smaller than
28468                 // the largest space we trimmed we can just add that one back instead of
28469                 // using end of seg.
28470                 if (relative_free_space_index > trimmed_free_space_index)
28471                 {
28472                     *use_additional_space = TRUE;
28473                 }
28474                 else
28475                 {
28476                     // If the addition space is <= than the last trimmed space, we
28477                     // should just use that last trimmed space instead.
28478                     saved_ordered_free_space_indices[trimmed_free_space_index]++;
28479                 }
28480             }
28481         }
28482     }
28483 
28484 adjust:
28485 
28486     if (!use_bestfit)
28487     {
28488         dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
28489 
28490 #ifdef SEG_REUSE_STATS
28491         size_t saved_max = max_free_space_items;
28492         BOOL temp_bestfit = FALSE;
28493 
28494         dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
28495         dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
28496 
28497         // TODO: need to take the end of segment into consideration.
28498         while (max_free_space_items <= total_free_space_items)
28499         {
28500             max_free_space_items += max_free_space_items / 2;
28501             dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
28502             memcpy (ordered_free_space_indices,
28503                     saved_all_free_space_indices,
28504                     sizeof(ordered_free_space_indices));
28505             if (try_best_fit(FALSE))
28506             {
28507                 temp_bestfit = TRUE;
28508                 break;
28509             }
28510         }
28511 
28512         if (temp_bestfit)
28513         {
28514             dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
28515         }
28516         else
28517         {
28518             dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
28519         }
28520 
28521         dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
28522         max_free_space_items = saved_max;
28523 #endif // SEG_REUSE_STATS
28524         if (free_space_items)
28525         {
28526             max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
28527             max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
28528         }
28529         else
28530         {
28531             max_free_space_items = MAX_NUM_FREE_SPACES;
28532         }
28533     }
28534 
28535     dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
28536     dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
28537 
28538     return use_bestfit;
28539 }
28540 
28541 BOOL gc_heap::process_free_space (heap_segment* seg,
28542                          size_t free_space,
28543                          size_t min_free_size,
28544                          size_t min_cont_size,
28545                          size_t* total_free_space,
28546                          size_t* largest_free_space)
28547 {
28548     *total_free_space += free_space;
28549     *largest_free_space = max (*largest_free_space, free_space);
28550 
28551 #ifdef SIMPLE_DPRINTF
28552     dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix",
28553                 free_space, *total_free_space, *largest_free_space));
28554 #endif //SIMPLE_DPRINTF
28555 
28556     if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
28557     {
28558 #ifdef SIMPLE_DPRINTF
28559         dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit",
28560             settings.condemned_generation,
28561             *total_free_space, min_free_size, *largest_free_space, min_cont_size,
28562             (size_t)seg));
28563 #else
28564         UNREFERENCED_PARAMETER(seg);
28565 #endif //SIMPLE_DPRINTF
28566         return TRUE;
28567     }
28568 
28569     int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
28570     if (free_space_index != -1)
28571     {
28572         ordered_free_space_indices[free_space_index]++;
28573     }
28574     return FALSE;
28575 }
28576 
28577 BOOL gc_heap::expand_reused_seg_p()
28578 {
28579     BOOL reused_seg = FALSE;
28580     int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
28581     if ((heap_expand_mechanism == expand_reuse_bestfit) ||
28582         (heap_expand_mechanism == expand_reuse_normal))
28583     {
28584         reused_seg = TRUE;
28585     }
28586 
28587     return reused_seg;
28588 }
28589 
28590 BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
28591                                  allocator* gen_allocator)
28592 {
28593     min_cont_size += END_SPACE_AFTER_GC;
28594     use_bestfit = FALSE;
28595     commit_end_of_seg = FALSE;
28596     bestfit_first_pin = 0;
28597     uint8_t* first_address = heap_segment_mem (seg);
28598     uint8_t* end_address   = heap_segment_reserved (seg);
28599     size_t end_extra_space = end_space_after_gc();
28600 
28601     if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
28602     {
28603         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
28604                                    first_address, end_address, end_extra_space));
28605         return FALSE;
28606     }
28607 
28608     end_address -= end_extra_space;
28609 
28610     dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix",
28611         settings.condemned_generation, min_free_size, min_cont_size));
28612     size_t eph_gen_starts = eph_gen_starts_size;
28613 
28614     if (settings.condemned_generation == max_generation)
28615     {
28616         size_t free_space = 0;
28617         size_t largest_free_space = free_space;
28618         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
28619         //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from.
28620         //We are going to allocate the generation starts in the 1st free space,
28621         //so start from the first free space that's big enough for gen starts and a min object size.
28622         // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it -
28623         // we could use it by allocating the last generation start a bit bigger but
28624         // the complexity isn't worth the effort (those plugs are from gen2
28625         // already anyway).
28626         reset_pinned_queue_bos();
28627         mark* m = 0;
28628         BOOL has_fit_gen_starts = FALSE;
28629 
28630         init_ordered_free_space_indices ();
28631         while (!pinned_plug_que_empty_p())
28632         {
28633             m = oldest_pin();
28634             if ((pinned_plug (m) >= first_address) &&
28635                 (pinned_plug (m) < end_address) &&
28636                 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
28637             {
28638                 break;
28639             }
28640             else
28641             {
28642                 deque_pinned_plug();
28643             }
28644         }
28645 
28646         if (!pinned_plug_que_empty_p())
28647         {
28648             bestfit_first_pin = pinned_plug (m) - pinned_len (m);
28649 
28650             if (process_free_space (seg,
28651                                     pinned_len (m) - eph_gen_starts,
28652                                     min_free_size, min_cont_size,
28653                                     &free_space, &largest_free_space))
28654             {
28655                 return TRUE;
28656             }
28657 
28658             deque_pinned_plug();
28659             m = oldest_pin();
28660             has_fit_gen_starts = TRUE;
28661         }
28662 
28663         dprintf (3, ("first pin is %Ix", pinned_plug (m)));
28664 
28665         //tally up free space
28666         while (!pinned_plug_que_empty_p() &&
28667                ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28668         {
28669             dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
28670             if (process_free_space (seg,
28671                                     pinned_len (m),
28672                                     min_free_size, min_cont_size,
28673                                     &free_space, &largest_free_space))
28674             {
28675                 return TRUE;
28676             }
28677 
28678             deque_pinned_plug();
28679             m = oldest_pin();
28680         }
28681 
28682         //try to find space at the end of the segment.
28683         size_t end_space = (end_address - heap_segment_plan_allocated (seg));
28684         size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0);
28685         dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
28686         if (end_space >= additional_space)
28687         {
28688             BOOL can_fit = TRUE;
28689             commit_end_of_seg = TRUE;
28690 
28691             if (largest_free_space < min_cont_size)
28692             {
28693                 if (end_space >= min_cont_size)
28694                 {
28695                     additional_space = max (min_cont_size, additional_space);
28696                     dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph",
28697                         seg));
28698                 }
28699                 else
28700                 {
28701                     if (settings.concurrent)
28702                     {
28703                         can_fit = FALSE;
28704                         commit_end_of_seg = FALSE;
28705                     }
28706                     else
28707                     {
28708                         size_t additional_space_bestfit = additional_space;
28709                         if (!has_fit_gen_starts)
28710                         {
28711                             if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
28712                             {
28713                                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
28714                                         additional_space_bestfit));
28715                                 return FALSE;
28716                             }
28717 
28718                             bestfit_first_pin = heap_segment_plan_allocated (seg);
28719                             additional_space_bestfit -= eph_gen_starts;
28720                         }
28721 
28722                         can_fit = best_fit (free_space,
28723                                             largest_free_space,
28724                                             additional_space_bestfit,
28725                                             &commit_end_of_seg);
28726 
28727                         if (can_fit)
28728                         {
28729                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg",
28730                                 seg, (commit_end_of_seg ? "with" : "without")));
28731                         }
28732                         else
28733                         {
28734                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
28735                         }
28736                     }
28737                 }
28738             }
28739             else
28740             {
28741                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
28742             }
28743 
28744             assert (additional_space <= end_space);
28745             if (commit_end_of_seg)
28746             {
28747                 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
28748                 {
28749                     dprintf (2, ("Couldn't commit end of segment?!"));
28750                     use_bestfit = FALSE;
28751 
28752                     return FALSE;
28753                 }
28754 
28755                 if (use_bestfit)
28756                 {
28757                     // We increase the index here because growing heap segment could create a discrepency with
28758                     // the additional space we used (could be bigger).
28759                     size_t free_space_end_of_seg =
28760                         heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
28761                     int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
28762                     saved_ordered_free_space_indices[relative_free_space_index]++;
28763                 }
28764             }
28765 
28766             if (use_bestfit)
28767             {
28768                 memcpy (ordered_free_space_indices,
28769                         saved_ordered_free_space_indices,
28770                         sizeof(ordered_free_space_indices));
28771                 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
28772                 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
28773                 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
28774             }
28775 
28776             return can_fit;
28777         }
28778 
28779         dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
28780         return FALSE;
28781     }
28782     else
28783     {
28784         assert (settings.condemned_generation == (max_generation-1));
28785         size_t free_space = (end_address - heap_segment_plan_allocated (seg));
28786         size_t largest_free_space = free_space;
28787         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
28788         //find the first free list in range of the current segment
28789         size_t sz_list = gen_allocator->first_bucket_size();
28790         unsigned int a_l_idx = 0;
28791         uint8_t* free_list = 0;
28792         for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
28793         {
28794             if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
28795             {
28796                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
28797                 while (free_list)
28798                 {
28799                     if ((free_list >= first_address) &&
28800                         (free_list < end_address) &&
28801                         (unused_array_size (free_list) >= eph_gen_starts))
28802                     {
28803                         goto next;
28804                     }
28805                     else
28806                     {
28807                         free_list = free_list_slot (free_list);
28808                     }
28809                 }
28810             }
28811         }
28812 next:
28813         if (free_list)
28814         {
28815             init_ordered_free_space_indices ();
28816             if (process_free_space (seg,
28817                                     unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size),
28818                                     min_free_size, min_cont_size,
28819                                     &free_space, &largest_free_space))
28820             {
28821                 return TRUE;
28822             }
28823 
28824             free_list = free_list_slot (free_list);
28825         }
28826         else
28827         {
28828             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
28829             return FALSE;
28830         }
28831 
28832        //tally up free space
28833 
28834         while (1)
28835         {
28836             while (free_list)
28837             {
28838                 if ((free_list >= first_address) && (free_list < end_address) &&
28839                     process_free_space (seg,
28840                                         unused_array_size (free_list),
28841                                         min_free_size, min_cont_size,
28842                                         &free_space, &largest_free_space))
28843                 {
28844                     return TRUE;
28845                 }
28846 
28847                 free_list = free_list_slot (free_list);
28848             }
28849             a_l_idx++;
28850             if (a_l_idx < gen_allocator->number_of_buckets())
28851             {
28852                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
28853             }
28854             else
28855                 break;
28856         }
28857 
28858         dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
28859         return FALSE;
28860 
28861         /*
28862         BOOL can_fit = best_fit (free_space, 0, NULL);
28863         if (can_fit)
28864         {
28865             dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
28866         }
28867         else
28868         {
28869             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
28870         }
28871 
28872         return can_fit;
28873         */
28874     }
28875 }
28876 
28877 void gc_heap::realloc_plug (size_t last_plug_size, uint8_t*& last_plug,
28878                             generation* gen, uint8_t* start_address,
28879                             unsigned int& active_new_gen_number,
28880                             uint8_t*& last_pinned_gap, BOOL& leftp,
28881                             BOOL shortened_p
28882 #ifdef SHORT_PLUGS
28883                             , mark* pinned_plug_entry
28884 #endif //SHORT_PLUGS
28885                             )
28886 {
28887     // detect generation boundaries
28888     // make sure that active_new_gen_number is not the youngest generation.
28889     // because the generation_limit wouldn't return the right thing in this case.
28890     if (!use_bestfit)
28891     {
28892         if ((active_new_gen_number > 1) &&
28893             (last_plug >= generation_limit (active_new_gen_number)))
28894         {
28895             assert (last_plug >= start_address);
28896             active_new_gen_number--;
28897             realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
28898             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
28899             leftp = FALSE;
28900         }
28901     }
28902 
28903     // detect pinned plugs
28904     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
28905     {
28906         size_t  entry = deque_pinned_plug();
28907         mark*  m = pinned_plug_of (entry);
28908 
28909         size_t saved_pinned_len = pinned_len(m);
28910         pinned_len(m) = last_plug - last_pinned_gap;
28911         //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
28912 
28913         if (m->has_post_plug_info())
28914         {
28915             last_plug_size += sizeof (gap_reloc_pair);
28916             dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
28917         }
28918 
28919         last_pinned_gap = last_plug + last_plug_size;
28920         dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
28921             pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
28922         leftp = FALSE;
28923 
28924         //we are creating a generation fault. set the cards.
28925         {
28926             size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
28927             size_t card = card_of (last_plug);
28928             while (card != end_card)
28929             {
28930                 set_card (card);
28931                 card++;
28932             }
28933         }
28934     }
28935     else if (last_plug >= start_address)
28936     {
28937 #ifdef FEATURE_STRUCTALIGN
28938         int requiredAlignment;
28939         ptrdiff_t pad;
28940         node_aligninfo (last_plug, requiredAlignment, pad);
28941 
28942         // from how we previously aligned the plug's destination address,
28943         // compute the actual alignment offset.
28944         uint8_t* reloc_plug = last_plug + node_relocation_distance (last_plug);
28945         ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
28946         if (!alignmentOffset)
28947         {
28948             // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
28949             alignmentOffset = requiredAlignment;
28950         }
28951 
28952         //clear the alignment info because we are reallocating
28953         clear_node_aligninfo (last_plug);
28954 #else // FEATURE_STRUCTALIGN
28955         //clear the realignment flag because we are reallocating
28956         clear_node_realigned (last_plug);
28957 #endif // FEATURE_STRUCTALIGN
28958         BOOL adjacentp = FALSE;
28959         BOOL set_padding_on_saved_p = FALSE;
28960 
28961         if (shortened_p)
28962         {
28963             last_plug_size += sizeof (gap_reloc_pair);
28964 
28965 #ifdef SHORT_PLUGS
28966             assert (pinned_plug_entry != NULL);
28967             if (last_plug_size <= sizeof (plug_and_gap))
28968             {
28969                 set_padding_on_saved_p = TRUE;
28970             }
28971 #endif //SHORT_PLUGS
28972 
28973             dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
28974         }
28975 
28976 #ifdef SHORT_PLUGS
28977         clear_padding_in_expand (last_plug, set_padding_on_saved_p, pinned_plug_entry);
28978 #endif //SHORT_PLUGS
28979 
28980         uint8_t* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug,
28981 #ifdef SHORT_PLUGS
28982                                      set_padding_on_saved_p,
28983                                      pinned_plug_entry,
28984 #endif //SHORT_PLUGS
28985                                      TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
28986 
28987         dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
28988         assert (new_address);
28989         set_node_relocation_distance (last_plug, new_address - last_plug);
28990 #ifdef FEATURE_STRUCTALIGN
28991         if (leftp && node_alignpad (last_plug) == 0)
28992 #else // FEATURE_STRUCTALIGN
28993         if (leftp && !node_realigned (last_plug))
28994 #endif // FEATURE_STRUCTALIGN
28995         {
28996             // TODO - temporarily disable L optimization because of a bug in it.
28997             //set_node_left (last_plug);
28998         }
28999         dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
29000         leftp = adjacentp;
29001     }
29002 }
29003 
29004 void gc_heap::realloc_in_brick (uint8_t* tree, uint8_t*& last_plug,
29005                                 uint8_t* start_address,
29006                                 generation* gen,
29007                                 unsigned int& active_new_gen_number,
29008                                 uint8_t*& last_pinned_gap, BOOL& leftp)
29009 {
29010     assert (tree != NULL);
29011     int   left_node = node_left_child (tree);
29012     int   right_node = node_right_child (tree);
29013 
29014     dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d",
29015         tree, last_pinned_gap, last_plug, left_node, right_node));
29016 
29017     if (left_node)
29018     {
29019         dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
29020         realloc_in_brick ((tree + left_node), last_plug, start_address,
29021                           gen, active_new_gen_number, last_pinned_gap,
29022                           leftp);
29023     }
29024 
29025     if (last_plug != 0)
29026     {
29027         uint8_t*  plug = tree;
29028 
29029         BOOL has_pre_plug_info_p = FALSE;
29030         BOOL has_post_plug_info_p = FALSE;
29031         mark* pinned_plug_entry = get_next_pinned_entry (tree,
29032                                                          &has_pre_plug_info_p,
29033                                                          &has_post_plug_info_p,
29034                                                          FALSE);
29035 
29036         // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
29037         // The pinned plugs are handled in realloc_plug.
29038         size_t gap_size = node_gap_size (plug);
29039         uint8_t*   gap = (plug - gap_size);
29040         uint8_t*  last_plug_end = gap;
29041         size_t  last_plug_size = (last_plug_end - last_plug);
29042         // Cannot assert this - a plug could be less than that due to the shortened ones.
29043         //assert (last_plug_size >= Align (min_obj_size));
29044         dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
29045             plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
29046         realloc_plug (last_plug_size, last_plug, gen, start_address,
29047                       active_new_gen_number, last_pinned_gap,
29048                       leftp, has_pre_plug_info_p
29049 #ifdef SHORT_PLUGS
29050                       , pinned_plug_entry
29051 #endif //SHORT_PLUGS
29052                       );
29053     }
29054 
29055     last_plug = tree;
29056 
29057     if (right_node)
29058     {
29059         dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
29060         realloc_in_brick ((tree + right_node), last_plug, start_address,
29061                           gen, active_new_gen_number, last_pinned_gap,
29062                           leftp);
29063     }
29064 }
29065 
29066 void
29067 gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
29068                         uint8_t* start_address, uint8_t* end_address,
29069                         unsigned active_new_gen_number)
29070 {
29071     dprintf (3, ("--- Reallocing ---"));
29072 
29073     if (use_bestfit)
29074     {
29075         //make sure that every generation has a planned allocation start
29076         int  gen_number = max_generation - 1;
29077         while (gen_number >= 0)
29078         {
29079             generation* gen = generation_of (gen_number);
29080             if (0 == generation_plan_allocation_start (gen))
29081             {
29082                 generation_plan_allocation_start (gen) =
29083                     bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
29084                 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
29085                 assert (generation_plan_allocation_start (gen));
29086             }
29087             gen_number--;
29088         }
29089     }
29090 
29091     uint8_t* first_address = start_address;
29092     //Look for the right pinned plug to start from.
29093     reset_pinned_queue_bos();
29094     uint8_t* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
29095     while (!pinned_plug_que_empty_p())
29096     {
29097         mark* m = oldest_pin();
29098         if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
29099         {
29100             if (pinned_plug (m) < first_address)
29101             {
29102                 first_address = pinned_plug (m);
29103             }
29104             break;
29105         }
29106         else
29107             deque_pinned_plug();
29108     }
29109 
29110     size_t  current_brick = brick_of (first_address);
29111     size_t  end_brick = brick_of (end_address-1);
29112     uint8_t*  last_plug = 0;
29113 
29114     uint8_t* last_pinned_gap = heap_segment_plan_allocated (seg);
29115     BOOL leftp = FALSE;
29116 
29117     dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
29118         start_address, first_address, pinned_plug (oldest_pin())));
29119 
29120     while (current_brick <= end_brick)
29121     {
29122         int   brick_entry =  brick_table [ current_brick ];
29123         if (brick_entry >= 0)
29124         {
29125             realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
29126                               last_plug, start_address, consing_gen,
29127                               active_new_gen_number, last_pinned_gap,
29128                               leftp);
29129         }
29130         current_brick++;
29131     }
29132 
29133     if (last_plug != 0)
29134     {
29135         realloc_plug (end_address - last_plug, last_plug, consing_gen,
29136                       start_address,
29137                       active_new_gen_number, last_pinned_gap,
29138                       leftp, FALSE
29139 #ifdef SHORT_PLUGS
29140                       , NULL
29141 #endif //SHORT_PLUGS
29142                       );
29143     }
29144 
29145     //Fix the old segment allocated size
29146     assert (last_pinned_gap >= heap_segment_mem (seg));
29147     assert (last_pinned_gap <= heap_segment_committed (seg));
29148     heap_segment_plan_allocated (seg) = last_pinned_gap;
29149 }
29150 
29151 void gc_heap::verify_no_pins (uint8_t* start, uint8_t* end)
29152 {
29153 #ifdef VERIFY_HEAP
29154     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
29155     {
29156         BOOL contains_pinned_plugs = FALSE;
29157         size_t mi = 0;
29158         mark* m = 0;
29159         while (mi != mark_stack_tos)
29160         {
29161             m = pinned_plug_of (mi);
29162             if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
29163             {
29164                 contains_pinned_plugs = TRUE;
29165                 break;
29166             }
29167             else
29168                 mi++;
29169         }
29170 
29171         if (contains_pinned_plugs)
29172         {
29173             FATAL_GC_ERROR();
29174         }
29175     }
29176 #endif //VERIFY_HEAP
29177 }
29178 
29179 void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
29180 {
29181     if (!should_expand_in_full_gc)
29182     {
29183         if ((condemned_gen_number != max_generation) &&
29184             (settings.pause_mode != pause_low_latency) &&
29185             (settings.pause_mode != pause_sustained_low_latency))
29186         {
29187             should_expand_in_full_gc = TRUE;
29188         }
29189     }
29190 }
29191 
29192 void gc_heap::save_ephemeral_generation_starts()
29193 {
29194     for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
29195     {
29196         saved_ephemeral_plan_start[ephemeral_generation] =
29197             generation_plan_allocation_start (generation_of (ephemeral_generation));
29198         saved_ephemeral_plan_start_size[ephemeral_generation] =
29199             generation_plan_allocation_start_size (generation_of (ephemeral_generation));
29200     }
29201 }
29202 
29203 generation* gc_heap::expand_heap (int condemned_generation,
29204                                   generation* consing_gen,
29205                                   heap_segment* new_heap_segment)
29206 {
29207     UNREFERENCED_PARAMETER(condemned_generation);
29208     assert (condemned_generation >= (max_generation -1));
29209     unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
29210     uint8_t*  start_address = generation_limit (max_generation);
29211     uint8_t*  end_address = heap_segment_allocated (ephemeral_heap_segment);
29212     BOOL should_promote_ephemeral = FALSE;
29213     ptrdiff_t eph_size = total_ephemeral_size;
29214 #ifdef BACKGROUND_GC
29215     dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
29216 #endif //BACKGROUND_GC
29217     settings.heap_expansion = TRUE;
29218 
29219 #ifdef BACKGROUND_GC
29220     if (cm_in_progress)
29221     {
29222         if (!expanded_in_fgc)
29223         {
29224             expanded_in_fgc = TRUE;
29225         }
29226     }
29227 #endif //BACKGROUND_GC
29228 
29229     //reset the elevation state for next time.
29230     dprintf (2, ("Elevation: elevation = el_none"));
29231     if (settings.should_lock_elevation && !expand_reused_seg_p())
29232         settings.should_lock_elevation = FALSE;
29233 
29234     heap_segment* new_seg = new_heap_segment;
29235 
29236     if (!new_seg)
29237         return consing_gen;
29238 
29239     //copy the card and brick tables
29240     if (g_gc_card_table!= card_table)
29241         copy_brick_card_table();
29242 
29243     BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
29244     dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
29245 
29246     assert (generation_plan_allocation_start (generation_of (max_generation-1)));
29247     assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
29248             heap_segment_mem (ephemeral_heap_segment));
29249     assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
29250             heap_segment_committed (ephemeral_heap_segment));
29251 
29252     assert (generation_plan_allocation_start (youngest_generation));
29253     assert (generation_plan_allocation_start (youngest_generation) <
29254             heap_segment_plan_allocated (ephemeral_heap_segment));
29255 
29256     if (settings.pause_mode == pause_no_gc)
29257     {
29258         // We don't reuse for no gc, so the size used on the new eph seg is eph_size.
29259         if ((size_t)(heap_segment_reserved (new_seg) - heap_segment_mem (new_seg)) < (eph_size + soh_allocation_no_gc))
29260             should_promote_ephemeral = TRUE;
29261     }
29262     else
29263     {
29264         if (!use_bestfit)
29265         {
29266             should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
29267         }
29268     }
29269 
29270     if (should_promote_ephemeral)
29271     {
29272         ephemeral_promotion = TRUE;
29273         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_new_seg_ep);
29274         dprintf (2, ("promoting ephemeral"));
29275         save_ephemeral_generation_starts();
29276     }
29277     else
29278     {
29279         // commit the new ephemeral segment all at once if it is a new one.
29280         if ((eph_size > 0) && new_segment_p)
29281         {
29282 #ifdef FEATURE_STRUCTALIGN
29283             // The destination may require a larger alignment padding than the source.
29284             // Assume the worst possible alignment padding.
29285             eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
29286 #endif // FEATURE_STRUCTALIGN
29287 #ifdef RESPECT_LARGE_ALIGNMENT
29288             //Since the generation start can be larger than min_obj_size
29289             //The alignment could be switched.
29290             eph_size += switch_alignment_size(FALSE);
29291 #endif //RESPECT_LARGE_ALIGNMENT
29292             //Since the generation start can be larger than min_obj_size
29293             //Compare the alignment of the first object in gen1
29294             if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
29295             {
29296                 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
29297                 return consing_gen;
29298             }
29299             heap_segment_used (new_seg) = heap_segment_committed (new_seg);
29300         }
29301 
29302         //Fix the end of the old ephemeral heap segment
29303         heap_segment_plan_allocated (ephemeral_heap_segment) =
29304             generation_plan_allocation_start (generation_of (max_generation-1));
29305 
29306         dprintf (3, ("Old ephemeral allocated set to %Ix",
29307                     (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
29308     }
29309 
29310     if (new_segment_p)
29311     {
29312         // TODO - Is this really necessary? We should think about it.
29313         //initialize the first brick
29314         size_t first_brick = brick_of (heap_segment_mem (new_seg));
29315         set_brick (first_brick,
29316                 heap_segment_mem (new_seg) - brick_address (first_brick));
29317     }
29318 
29319     //From this point on, we cannot run out of memory
29320 
29321     //reset the allocation of the consing generation back to the end of the
29322     //old ephemeral segment
29323     generation_allocation_limit (consing_gen) =
29324         heap_segment_plan_allocated (ephemeral_heap_segment);
29325     generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
29326     generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
29327 
29328     //clear the generation gap for all of the ephemeral generations
29329     {
29330         int generation_num = max_generation-1;
29331         while (generation_num >= 0)
29332         {
29333             generation* gen = generation_of (generation_num);
29334             generation_plan_allocation_start (gen) = 0;
29335             generation_num--;
29336         }
29337     }
29338 
29339     heap_segment* old_seg = ephemeral_heap_segment;
29340     ephemeral_heap_segment = new_seg;
29341 
29342     //Note: the ephemeral segment shouldn't be threaded onto the segment chain
29343     //because the relocation and compact phases shouldn't see it
29344 
29345     // set the generation members used by allocate_in_expanded_heap
29346     // and switch to ephemeral generation
29347     consing_gen = ensure_ephemeral_heap_segment (consing_gen);
29348 
29349     if (!should_promote_ephemeral)
29350     {
29351         realloc_plugs (consing_gen, old_seg, start_address, end_address,
29352                     active_new_gen_number);
29353     }
29354 
29355     if (!use_bestfit)
29356     {
29357         repair_allocation_in_expanded_heap (consing_gen);
29358     }
29359 
29360     // assert that the generation gap for all of the ephemeral generations were allocated.
29361 #ifdef _DEBUG
29362     {
29363         int generation_num = max_generation-1;
29364         while (generation_num >= 0)
29365         {
29366             generation* gen = generation_of (generation_num);
29367             assert (generation_plan_allocation_start (gen));
29368             generation_num--;
29369         }
29370     }
29371 #endif // _DEBUG
29372 
29373     if (!new_segment_p)
29374     {
29375         dprintf (2, ("Demoting ephemeral segment"));
29376         //demote the entire segment.
29377         settings.demotion = TRUE;
29378         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
29379         demotion_low = heap_segment_mem (ephemeral_heap_segment);
29380         demotion_high = heap_segment_reserved (ephemeral_heap_segment);
29381     }
29382     else
29383     {
29384         demotion_low = MAX_PTR;
29385         demotion_high = 0;
29386 #ifndef MULTIPLE_HEAPS
29387         settings.demotion = FALSE;
29388         get_gc_data_per_heap()->clear_mechanism_bit (gc_demotion_bit);
29389 #endif //!MULTIPLE_HEAPS
29390     }
29391     ptrdiff_t eph_size1 = total_ephemeral_size;
29392     MAYBE_UNUSED_VAR(eph_size1);
29393 
29394     if (!should_promote_ephemeral && new_segment_p)
29395     {
29396         assert (eph_size1 <= eph_size);
29397     }
29398 
29399     if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
29400     {
29401         // This is to catch when we accidently delete a segment that has pins.
29402         verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
29403     }
29404 
29405     verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
29406 
29407     dprintf(2,("---- End of Heap Expansion ----"));
29408     return consing_gen;
29409 }
29410 
29411 bool gc_heap::init_dynamic_data()
29412 {
29413     qpf = GCToOSInterface::QueryPerformanceFrequency();
29414 
29415     uint32_t now = (uint32_t)GetHighPrecisionTimeStamp();
29416 
29417     //clear some fields
29418     for (int i = 0; i < max_generation+1; i++)
29419     {
29420         dynamic_data* dd = dynamic_data_of (i);
29421         dd->gc_clock = 0;
29422         dd->time_clock = now;
29423     }
29424 
29425 #ifdef GC_CONFIG_DRIVEN
29426     if (heap_number == 0)
29427         time_init = now;
29428 #endif //GC_CONFIG_DRIVEN
29429 
29430     // get the registry setting for generation 0 size
29431     size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
29432 
29433     dprintf (2, ("gen 0 size: %Id", gen0size));
29434 
29435     dynamic_data* dd = dynamic_data_of (0);
29436     dd->current_size = 0;
29437     dd->promoted_size = 0;
29438     dd->collection_count = 0;
29439 //  dd->limit = 3.0f;
29440 #ifdef MULTIPLE_HEAPS
29441     dd->limit = 20.0f;     // be more aggressive on server gc
29442     dd->max_limit = 40.0f;
29443 #else
29444     dd->limit = 9.0f;
29445 //  dd->max_limit = 15.0f; //10.0f;
29446     dd->max_limit = 20.0f;
29447 #endif //MULTIPLE_HEAPS
29448     dd->min_gc_size = Align(gen0size / 8 * 5);
29449     dd->min_size = dd->min_gc_size;
29450     //dd->max_size = Align (gen0size);
29451 
29452 #ifdef BACKGROUND_GC
29453     //gc_can_use_concurrent is not necessarily 0 for server builds
29454     bool can_use_concurrent = gc_can_use_concurrent;
29455 #else // !BACKGROUND_GC
29456     bool can_use_concurrent = false;
29457 #endif // BACKGROUND_GC
29458 
29459 #ifdef MULTIPLE_HEAPS
29460     dd->max_size = max (6*1024*1024, min ( Align(get_valid_segment_size()/2), 200*1024*1024));
29461 #else //MULTIPLE_HEAPS
29462     dd->max_size = (can_use_concurrent ?
29463                     6*1024*1024 :
29464                     max (6*1024*1024,  min ( Align(get_valid_segment_size()/2), 200*1024*1024)));
29465 #endif //MULTIPLE_HEAPS
29466     dd->new_allocation = dd->min_gc_size;
29467     dd->gc_new_allocation = dd->new_allocation;
29468     dd->desired_allocation = dd->new_allocation;
29469     dd->default_new_allocation = dd->min_gc_size;
29470     dd->fragmentation = 0;
29471     dd->fragmentation_limit = 40000;
29472     dd->fragmentation_burden_limit = 0.5f;
29473 
29474     dd =  dynamic_data_of (1);
29475     dd->current_size = 0;
29476     dd->promoted_size = 0;
29477     dd->collection_count = 0;
29478     dd->limit = 2.0f;
29479 //  dd->max_limit = 15.0f;
29480     dd->max_limit = 7.0f;
29481     dd->min_gc_size = 9*32*1024;
29482     dd->min_size = dd->min_gc_size;
29483 //  dd->max_size = 2397152;
29484 #ifdef MULTIPLE_HEAPS
29485     dd->max_size = max (6*1024*1024, Align(get_valid_segment_size()/2));
29486 #else //MULTIPLE_HEAPS
29487     dd->max_size = (can_use_concurrent ?
29488                     6*1024*1024 :
29489                     max (6*1024*1024, Align(get_valid_segment_size()/2)));
29490 #endif //MULTIPLE_HEAPS
29491     dd->new_allocation = dd->min_gc_size;
29492     dd->gc_new_allocation = dd->new_allocation;
29493     dd->desired_allocation = dd->new_allocation;
29494     dd->default_new_allocation = dd->min_gc_size;
29495     dd->fragmentation = 0;
29496     dd->fragmentation_limit = 80000;
29497     dd->fragmentation_burden_limit = 0.5f;
29498 
29499     dd =  dynamic_data_of (2);
29500     dd->current_size = 0;
29501     dd->promoted_size = 0;
29502     dd->collection_count = 0;
29503     dd->limit = 1.2f;
29504     dd->max_limit = 1.8f;
29505     dd->min_gc_size = 256*1024;
29506     dd->min_size = dd->min_gc_size;
29507     dd->max_size = SSIZE_T_MAX;
29508     dd->new_allocation = dd->min_gc_size;
29509     dd->gc_new_allocation = dd->new_allocation;
29510     dd->desired_allocation = dd->new_allocation;
29511     dd->default_new_allocation = dd->min_gc_size;
29512     dd->fragmentation = 0;
29513     dd->fragmentation_limit = 200000;
29514     dd->fragmentation_burden_limit = 0.25f;
29515 
29516     //dynamic data for large objects
29517     dd =  dynamic_data_of (3);
29518     dd->current_size = 0;
29519     dd->promoted_size = 0;
29520     dd->collection_count = 0;
29521     dd->limit = 1.25f;
29522     dd->max_limit = 4.5f;
29523     dd->min_gc_size = 3*1024*1024;
29524     dd->min_size = dd->min_gc_size;
29525     dd->max_size = SSIZE_T_MAX;
29526     dd->new_allocation = dd->min_gc_size;
29527     dd->gc_new_allocation = dd->new_allocation;
29528     dd->desired_allocation = dd->new_allocation;
29529     dd->default_new_allocation = dd->min_gc_size;
29530     dd->fragmentation = 0;
29531     dd->fragmentation_limit = 0;
29532     dd->fragmentation_burden_limit = 0.0f;
29533 
29534     return true;
29535 }
29536 
29537 float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
29538 {
29539     if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
29540         return ((limit - limit*cst) / (1.0f - (cst * limit)));
29541     else
29542         return max_limit;
29543 }
29544 
29545 
29546 //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may
29547 //not be correct (collection happened too soon). Correct with a linear estimation based on the previous
29548 //value of the budget
29549 static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation,
29550                                        size_t previous_desired_allocation, size_t collection_count)
29551 {
29552     if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
29553     {
29554         dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0)));
29555         new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
29556     }
29557 #if 0
29558     size_t smoothing = 3; // exponential smoothing factor
29559     if (smoothing  > collection_count)
29560         smoothing  = collection_count;
29561     new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
29562 #else
29563     UNREFERENCED_PARAMETER(collection_count);
29564 #endif //0
29565     return new_allocation;
29566 }
29567 
29568 size_t gc_heap::desired_new_allocation (dynamic_data* dd,
29569                                         size_t out, int gen_number,
29570                                         int pass)
29571 {
29572     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29573 
29574     if (dd_begin_data_size (dd) == 0)
29575     {
29576         size_t new_allocation = dd_default_new_allocation (dd);
29577         current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;
29578         return new_allocation;
29579     }
29580     else
29581     {
29582         float     cst;
29583         size_t    previous_desired_allocation = dd_desired_allocation (dd);
29584         size_t    current_size = dd_current_size (dd);
29585         float     max_limit = dd_max_limit (dd);
29586         float     limit = dd_limit (dd);
29587         size_t    min_gc_size = dd_min_gc_size (dd);
29588         float     f = 0;
29589         size_t    max_size = dd_max_size (dd);
29590         size_t    new_allocation = 0;
29591         float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
29592         if (gen_number >= max_generation)
29593         {
29594             size_t    new_size = 0;
29595 
29596             cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
29597 
29598             f = surv_to_growth (cst, limit, max_limit);
29599             size_t max_growth_size = (size_t)(max_size / f);
29600             if (current_size >= max_growth_size)
29601             {
29602                 new_size = max_size;
29603             }
29604             else
29605             {
29606                 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
29607             }
29608 
29609             assert ((new_size >= current_size) || (new_size == max_size));
29610 
29611             if (gen_number == max_generation)
29612             {
29613                 new_allocation  =  max((new_size - current_size), min_gc_size);
29614 
29615                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29616                                                           dd_desired_allocation (dd), dd_collection_count (dd));
29617 
29618                 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
29619                 {
29620                     //reducing allocation in case of fragmentation
29621                     size_t new_allocation1 = max (min_gc_size,
29622                                                   // CAN OVERFLOW
29623                                                   (size_t)((float)new_allocation * current_size /
29624                                                            ((float)current_size + 2*dd_fragmentation (dd))));
29625                     dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
29626                                  new_allocation, new_allocation1));
29627                     new_allocation = new_allocation1;
29628                 }
29629             }
29630             else //large object heap
29631             {
29632                 uint64_t available_physical = 0;
29633                 get_memory_info (NULL, &available_physical);
29634                 if (available_physical > 1024*1024)
29635                     available_physical -= 1024*1024;
29636 
29637                 uint64_t available_free = available_physical + (uint64_t)generation_free_list_space (generation_of (gen_number));
29638                 if (available_free > (uint64_t)MAX_PTR)
29639                 {
29640                     available_free = (uint64_t)MAX_PTR;
29641                 }
29642 
29643                 //try to avoid OOM during large object allocation
29644                 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))),
29645                                           (size_t)available_free),
29646                                       max ((current_size/4), min_gc_size));
29647 
29648                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29649                                                           dd_desired_allocation (dd), dd_collection_count (dd));
29650 
29651             }
29652         }
29653         else
29654         {
29655             size_t survivors = out;
29656             cst = float (survivors) / float (dd_begin_data_size (dd));
29657             f = surv_to_growth (cst, limit, max_limit);
29658             new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
29659 
29660             new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29661                                                       dd_desired_allocation (dd), dd_collection_count (dd));
29662 
29663             if (gen_number == 0)
29664             {
29665                 if (pass == 0)
29666                 {
29667 
29668                     //printf ("%f, %Id\n", cst, new_allocation);
29669                     size_t free_space = generation_free_list_space (generation_of (gen_number));
29670                     // DTREVIEW - is min_gc_size really a good choice?
29671                     // on 64-bit this will almost always be true.
29672                     dprintf (GTC_LOG, ("frag: %Id, min: %Id", free_space, min_gc_size));
29673                     if (free_space > min_gc_size)
29674                     {
29675                         settings.gen0_reduction_count = 2;
29676                     }
29677                     else
29678                     {
29679                         if (settings.gen0_reduction_count > 0)
29680                             settings.gen0_reduction_count--;
29681                     }
29682                 }
29683                 if (settings.gen0_reduction_count > 0)
29684                 {
29685                     dprintf (2, ("Reducing new allocation based on fragmentation"));
29686                     new_allocation = min (new_allocation,
29687                                           max (min_gc_size, (max_size/3)));
29688                 }
29689             }
29690         }
29691 
29692         size_t new_allocation_ret =
29693             Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
29694         int gen_data_index = gen_number;
29695         gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
29696         gen_data->new_allocation = new_allocation_ret;
29697 
29698         dd_surv (dd) = cst;
29699 
29700 #ifdef SIMPLE_DPRINTF
29701         dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
29702                      heap_number, gen_number, out, current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)),
29703                      (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
29704 #else
29705         dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
29706         dprintf (1,("current: %Id alloc: %Id ", current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd))));
29707         dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
29708                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
29709 #endif //SIMPLE_DPRINTF
29710 
29711         return new_allocation_ret;
29712     }
29713 }
29714 
29715 //returns the planned size of a generation (including free list element)
29716 size_t gc_heap::generation_plan_size (int gen_number)
29717 {
29718     if (0 == gen_number)
29719         return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
29720                     generation_plan_allocation_start (generation_of (gen_number))),
29721                    (int)Align (min_obj_size));
29722     else
29723     {
29724         generation* gen = generation_of (gen_number);
29725         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
29726             return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
29727                     generation_plan_allocation_start (generation_of (gen_number)));
29728         else
29729         {
29730             size_t gensize = 0;
29731             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
29732 
29733             PREFIX_ASSUME(seg != NULL);
29734 
29735             while (seg && (seg != ephemeral_heap_segment))
29736             {
29737                 gensize += heap_segment_plan_allocated (seg) -
29738                            heap_segment_mem (seg);
29739                 seg = heap_segment_next_rw (seg);
29740             }
29741             if (seg)
29742             {
29743                 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
29744                             heap_segment_mem (ephemeral_heap_segment));
29745             }
29746             return gensize;
29747         }
29748     }
29749 
29750 }
29751 
29752 //returns the size of a generation (including free list element)
29753 size_t gc_heap::generation_size (int gen_number)
29754 {
29755     if (0 == gen_number)
29756         return max((heap_segment_allocated (ephemeral_heap_segment) -
29757                     generation_allocation_start (generation_of (gen_number))),
29758                    (int)Align (min_obj_size));
29759     else
29760     {
29761         generation* gen = generation_of (gen_number);
29762         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
29763             return (generation_allocation_start (generation_of (gen_number - 1)) -
29764                     generation_allocation_start (generation_of (gen_number)));
29765         else
29766         {
29767             size_t gensize = 0;
29768             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
29769 
29770             PREFIX_ASSUME(seg != NULL);
29771 
29772             while (seg && (seg != ephemeral_heap_segment))
29773             {
29774                 gensize += heap_segment_allocated (seg) -
29775                            heap_segment_mem (seg);
29776                 seg = heap_segment_next_rw (seg);
29777             }
29778             if (seg)
29779             {
29780                 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
29781                             heap_segment_mem (ephemeral_heap_segment));
29782             }
29783 
29784             return gensize;
29785         }
29786     }
29787 
29788 }
29789 
29790 size_t  gc_heap::compute_in (int gen_number)
29791 {
29792     assert (gen_number != 0);
29793     dynamic_data* dd = dynamic_data_of (gen_number);
29794 
29795     size_t in = generation_allocation_size (generation_of (gen_number));
29796 
29797     if (gen_number == max_generation && ephemeral_promotion)
29798     {
29799         in = 0;
29800         for (int i = 0; i <= max_generation; i++)
29801         {
29802             dynamic_data* dd = dynamic_data_of (i);
29803             in += dd_survived_size (dd);
29804             if (i != max_generation)
29805             {
29806                 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
29807             }
29808         }
29809     }
29810 
29811     dd_gc_new_allocation (dd) -= in;
29812     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
29813 
29814     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29815     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
29816     gen_data->in = in;
29817 
29818     generation_allocation_size (generation_of (gen_number)) = 0;
29819     return in;
29820 }
29821 
29822 void  gc_heap::compute_promoted_allocation (int gen_number)
29823 {
29824     compute_in (gen_number);
29825 }
29826 
29827 #ifdef BIT64
29828 inline
29829 size_t gc_heap::trim_youngest_desired (uint32_t memory_load,
29830                                        size_t total_new_allocation,
29831                                        size_t total_min_allocation)
29832 {
29833     if (memory_load < MAX_ALLOWED_MEM_LOAD)
29834     {
29835         // If the total of memory load and gen0 budget exceeds
29836         // our max memory load limit, trim the gen0 budget so the total
29837         // is the max memory load limit.
29838         size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
29839         return min (total_new_allocation, remain_memory_load);
29840     }
29841     else
29842     {
29843         return max (mem_one_percent, total_min_allocation);
29844     }
29845 }
29846 
29847 size_t gc_heap::joined_youngest_desired (size_t new_allocation)
29848 {
29849     dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
29850 
29851     size_t final_new_allocation = new_allocation;
29852     if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
29853     {
29854         uint32_t num_heaps = 1;
29855 
29856 #ifdef MULTIPLE_HEAPS
29857         num_heaps = gc_heap::n_heaps;
29858 #endif //MULTIPLE_HEAPS
29859 
29860         size_t total_new_allocation = new_allocation * num_heaps;
29861         size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
29862 
29863         if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
29864             (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
29865         {
29866             uint32_t memory_load = 0;
29867             get_memory_info (&memory_load);
29868             dprintf (2, ("Current emory load: %d", memory_load));
29869 
29870             size_t final_total =
29871                 trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation);
29872             size_t max_new_allocation =
29873 #ifdef MULTIPLE_HEAPS
29874                                          dd_max_size (g_heaps[0]->dynamic_data_of (0));
29875 #else //MULTIPLE_HEAPS
29876                                          dd_max_size (dynamic_data_of (0));
29877 #endif //MULTIPLE_HEAPS
29878 
29879             final_new_allocation  = min (Align ((final_total / num_heaps), get_alignment_constant (TRUE)), max_new_allocation);
29880         }
29881     }
29882 
29883     if (final_new_allocation < new_allocation)
29884     {
29885         settings.gen0_reduction_count = 2;
29886     }
29887 
29888     return final_new_allocation;
29889 }
29890 #endif // BIT64
29891 
29892 inline
29893 gc_history_per_heap* gc_heap::get_gc_data_per_heap()
29894 {
29895 #ifdef BACKGROUND_GC
29896     return (settings.concurrent ? &bgc_data_per_heap : &gc_data_per_heap);
29897 #else
29898     return &gc_data_per_heap;
29899 #endif //BACKGROUND_GC
29900 }
29901 
29902 void gc_heap::compute_new_dynamic_data (int gen_number)
29903 {
29904     PREFIX_ASSUME(gen_number >= 0);
29905     PREFIX_ASSUME(gen_number <= max_generation);
29906 
29907     dynamic_data* dd = dynamic_data_of (gen_number);
29908     generation*   gen = generation_of (gen_number);
29909     size_t        in = (gen_number==0) ? 0 : compute_in (gen_number);
29910 
29911     size_t total_gen_size = generation_size (gen_number);
29912     //keep track of fragmentation
29913     dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
29914     dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
29915 
29916     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29917 
29918     size_t out = dd_survived_size (dd);
29919 
29920     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
29921     gen_data->size_after = total_gen_size;
29922     gen_data->free_list_space_after = generation_free_list_space (gen);
29923     gen_data->free_obj_space_after = generation_free_obj_space (gen);
29924 
29925     if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
29926     {
29927         // When we are in the low latency mode, we can still be
29928         // condemning more than gen1's 'cause of induced GCs.
29929         dd_desired_allocation (dd) = low_latency_alloc;
29930     }
29931     else
29932     {
29933         if (gen_number == 0)
29934         {
29935             //compensate for dead finalizable objects promotion.
29936             //they shoudn't be counted for growth.
29937             size_t final_promoted = 0;
29938             final_promoted = min (promoted_bytes (heap_number), out);
29939             // Prefast: this is clear from above but prefast needs to be told explicitly
29940             PREFIX_ASSUME(final_promoted <= out);
29941 
29942             dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
29943             dd_freach_previous_promotion (dd) = final_promoted;
29944             size_t lower_bound = desired_new_allocation  (dd, out-final_promoted, gen_number, 0);
29945 
29946             if (settings.condemned_generation == 0)
29947             {
29948                 //there is no noise.
29949                 dd_desired_allocation (dd) = lower_bound;
29950             }
29951             else
29952             {
29953                 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
29954 
29955                 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
29956                 //assert ( lower_bound <= higher_bound);
29957 
29958                 //discount the noise. Change the desired allocation
29959                 //only if the previous value is outside of the range.
29960                 if (dd_desired_allocation (dd) < lower_bound)
29961                 {
29962                     dd_desired_allocation (dd) = lower_bound;
29963                 }
29964                 else if (dd_desired_allocation (dd) > higher_bound)
29965                 {
29966                     dd_desired_allocation (dd) = higher_bound;
29967                 }
29968 #if defined (BIT64) && !defined (MULTIPLE_HEAPS)
29969                 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
29970 #endif // BIT64 && !MULTIPLE_HEAPS
29971                 trim_youngest_desired_low_memory();
29972                 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
29973             }
29974         }
29975         else
29976         {
29977             dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
29978         }
29979     }
29980 
29981     gen_data->pinned_surv = dd_pinned_survived_size (dd);
29982     gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd);
29983 
29984     dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
29985     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
29986 
29987     //update counter
29988     dd_promoted_size (dd) = out;
29989     if (gen_number == max_generation)
29990     {
29991         dd = dynamic_data_of (max_generation+1);
29992         total_gen_size = generation_size (max_generation + 1);
29993         dd_fragmentation (dd) = generation_free_list_space (large_object_generation) +
29994                                 generation_free_obj_space (large_object_generation);
29995         dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
29996         dd_survived_size (dd) = dd_current_size (dd);
29997         in = 0;
29998         out = dd_current_size (dd);
29999         dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
30000         dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
30001                                            get_alignment_constant (FALSE));
30002         dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30003 
30004         gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
30005         gen_data->size_after = total_gen_size;
30006         gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
30007         gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
30008         gen_data->npinned_surv = out;
30009 #ifdef BACKGROUND_GC
30010         end_loh_size = total_gen_size;
30011 #endif //BACKGROUND_GC
30012         //update counter
30013         dd_promoted_size (dd) = out;
30014     }
30015 }
30016 
30017 void gc_heap::trim_youngest_desired_low_memory()
30018 {
30019     if (g_low_memory_status)
30020     {
30021         size_t committed_mem = 0;
30022         heap_segment* seg = generation_start_segment (generation_of (max_generation));
30023         while (seg)
30024         {
30025             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30026             seg = heap_segment_next (seg);
30027         }
30028         seg = generation_start_segment (generation_of (max_generation + 1));
30029         while (seg)
30030         {
30031             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30032             seg = heap_segment_next (seg);
30033         }
30034 
30035         dynamic_data* dd = dynamic_data_of (0);
30036         size_t current = dd_desired_allocation (dd);
30037         size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_gc_size (dd));
30038 
30039         dd_desired_allocation (dd) = min (current, candidate);
30040     }
30041 }
30042 
30043 void gc_heap::decommit_ephemeral_segment_pages()
30044 {
30045     if (settings.concurrent)
30046     {
30047         return;
30048     }
30049 
30050     size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30051     dynamic_data* dd = dynamic_data_of (0);
30052 
30053 #ifndef MULTIPLE_HEAPS
30054     size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
30055     size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
30056     size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
30057 
30058     if (dd_desired_allocation (dd) > gc_gen0_desired_high)
30059     {
30060         gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space;
30061     }
30062 
30063     if (ephemeral_elapsed >= decommit_timeout)
30064     {
30065         slack_space = min (slack_space, gc_gen0_desired_high);
30066 
30067         gc_last_ephemeral_decommit_time = dd_time_clock(dd);
30068         gc_gen0_desired_high = 0;
30069     }
30070 #endif //!MULTIPLE_HEAPS
30071 
30072     if (settings.condemned_generation >= (max_generation-1))
30073     {
30074         size_t new_slack_space =
30075 #ifdef BIT64
30076                     max(min(min(get_valid_segment_size()/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
30077 #else
30078 #ifdef FEATURE_CORECLR
30079                     dd_desired_allocation (dd);
30080 #else
30081                     dd_max_size (dd);
30082 #endif //FEATURE_CORECLR
30083 #endif // BIT64
30084 
30085         slack_space = min (slack_space, new_slack_space);
30086     }
30087 
30088     decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);
30089 
30090     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30091     current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30092 }
30093 
30094 size_t gc_heap::new_allocation_limit (size_t size, size_t free_size, int gen_number)
30095 {
30096     dynamic_data* dd        = dynamic_data_of (gen_number);
30097     ptrdiff_t           new_alloc = dd_new_allocation (dd);
30098     assert (new_alloc == (ptrdiff_t)Align (new_alloc,
30099                                            get_alignment_constant (!(gen_number == (max_generation+1)))));
30100     size_t        limit     = min (max (new_alloc, (ptrdiff_t)size), (ptrdiff_t)free_size);
30101     assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
30102     dd_new_allocation (dd) = (new_alloc - limit );
30103     return limit;
30104 }
30105 
30106 //This is meant to be called by decide_on_compacting.
30107 
30108 size_t gc_heap::generation_fragmentation (generation* gen,
30109                                           generation* consing_gen,
30110                                           uint8_t* end)
30111 {
30112     size_t frag;
30113     uint8_t* alloc = generation_allocation_pointer (consing_gen);
30114     // If the allocation pointer has reached the ephemeral segment
30115     // fine, otherwise the whole ephemeral segment is considered
30116     // fragmentation
30117     if (in_range_for_segment (alloc, ephemeral_heap_segment))
30118         {
30119             if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
30120                 frag = end - alloc;
30121             else
30122             {
30123                 // case when no survivors, allocated set to beginning
30124                 frag = 0;
30125             }
30126             dprintf (3, ("ephemeral frag: %Id", frag));
30127         }
30128     else
30129         frag = (heap_segment_allocated (ephemeral_heap_segment) -
30130                 heap_segment_mem (ephemeral_heap_segment));
30131     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30132 
30133     PREFIX_ASSUME(seg != NULL);
30134 
30135     while (seg != ephemeral_heap_segment)
30136     {
30137         frag += (heap_segment_allocated (seg) -
30138                  heap_segment_plan_allocated (seg));
30139         dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
30140                      (heap_segment_allocated (seg) -
30141                       heap_segment_plan_allocated (seg))));
30142 
30143         seg = heap_segment_next_rw (seg);
30144         assert (seg);
30145     }
30146     dprintf (3, ("frag: %Id discounting pinned plugs", frag));
30147     //add the length of the dequeued plug free space
30148     size_t bos = 0;
30149     while (bos < mark_stack_bos)
30150     {
30151         frag += (pinned_len (pinned_plug_of (bos)));
30152         bos++;
30153     }
30154 
30155     return frag;
30156 }
30157 
30158 // for SOH this returns the total sizes of the generation and its
30159 // younger generation(s).
30160 // for LOH this returns just LOH size.
30161 size_t gc_heap::generation_sizes (generation* gen)
30162 {
30163     size_t result = 0;
30164     if (generation_start_segment (gen ) == ephemeral_heap_segment)
30165         result = (heap_segment_allocated (ephemeral_heap_segment) -
30166                   generation_allocation_start (gen));
30167     else
30168     {
30169         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
30170 
30171         PREFIX_ASSUME(seg != NULL);
30172 
30173         while (seg)
30174         {
30175             result += (heap_segment_allocated (seg) -
30176                        heap_segment_mem (seg));
30177             seg = heap_segment_next_in_range (seg);
30178         }
30179     }
30180 
30181     return result;
30182 }
30183 
30184 BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
30185                                     size_t fragmentation,
30186                                     BOOL& should_expand)
30187 {
30188     BOOL should_compact = FALSE;
30189     should_expand = FALSE;
30190     generation*   gen = generation_of (condemned_gen_number);
30191     dynamic_data* dd = dynamic_data_of (condemned_gen_number);
30192     size_t gen_sizes     = generation_sizes(gen);
30193     float  fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
30194                                     (float (fragmentation) / gen_sizes) );
30195 
30196     dprintf (GTC_LOG, ("fragmentation: %Id (%d%%)", fragmentation, (int)(fragmentation_burden * 100.0)));
30197 
30198 #ifdef STRESS_HEAP
30199     // for pure GC stress runs we need compaction, for GC stress "mix"
30200     // we need to ensure a better mix of compacting and sweeping collections
30201     if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
30202         && !g_pConfig->IsGCStressMix())
30203         should_compact = TRUE;
30204 
30205 #ifdef GC_STATS
30206     // in GC stress "mix" mode, for stress induced collections make sure we
30207     // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
30208     // against the GC's determination, as it may lead to premature OOMs.
30209     if (g_pConfig->IsGCStressMix() && settings.stress_induced)
30210     {
30211         int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
30212         int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
30213         if (compactions < sweeps / 10)
30214         {
30215             should_compact = TRUE;
30216         }
30217     }
30218 #endif // GC_STATS
30219 #endif //STRESS_HEAP
30220 
30221     if (g_pConfig->GetGCForceCompact())
30222         should_compact = TRUE;
30223 
30224     if ((condemned_gen_number == max_generation) && last_gc_before_oom)
30225     {
30226         should_compact = TRUE;
30227         last_gc_before_oom = FALSE;
30228         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc);
30229     }
30230 
30231     if (settings.reason == reason_induced_compacting)
30232     {
30233         dprintf (2, ("induced compacting GC"));
30234         should_compact = TRUE;
30235         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
30236     }
30237 
30238     dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
30239                 fragmentation, (int) (100*fragmentation_burden)));
30240 
30241     if (!should_compact)
30242     {
30243         if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
30244         {
30245             dprintf(GTC_LOG, ("compacting due to low ephemeral"));
30246             should_compact = TRUE;
30247             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral);
30248         }
30249     }
30250 
30251     if (should_compact)
30252     {
30253         if ((condemned_gen_number >= (max_generation - 1)))
30254         {
30255             if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
30256             {
30257                 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
30258                 should_expand = TRUE;
30259             }
30260         }
30261     }
30262 
30263 #ifdef BIT64
30264     BOOL high_memory = FALSE;
30265 #endif // BIT64
30266 
30267     if (!should_compact)
30268     {
30269         // We are not putting this in dt_high_frag_p because it's not exactly
30270         // high fragmentation - it's just enough planned fragmentation for us to
30271         // want to compact. Also the "fragmentation" we are talking about here
30272         // is different from anywhere else.
30273         BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
30274                                 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
30275 
30276         if (frag_exceeded)
30277         {
30278 #ifdef BACKGROUND_GC
30279             // do not force compaction if this was a stress-induced GC
30280             IN_STRESS_HEAP(if (!settings.stress_induced))
30281             {
30282 #endif // BACKGROUND_GC
30283             assert (settings.concurrent == FALSE);
30284             should_compact = TRUE;
30285             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag);
30286 #ifdef BACKGROUND_GC
30287             }
30288 #endif // BACKGROUND_GC
30289         }
30290 
30291 #ifdef BIT64
30292         // check for high memory situation
30293         if(!should_compact)
30294         {
30295             uint32_t num_heaps = 1;
30296 #ifdef MULTIPLE_HEAPS
30297             num_heaps = gc_heap::n_heaps;
30298 #endif // MULTIPLE_HEAPS
30299 
30300             ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
30301             if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th))
30302             {
30303                 if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps)))
30304                 {
30305                     dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
30306                     should_compact = TRUE;
30307                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag);
30308                 }
30309                 high_memory = TRUE;
30310             }
30311             else if(settings.entry_memory_load >= v_high_memory_load_th)
30312             {
30313                 if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps)))
30314                 {
30315                     dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
30316                     should_compact = TRUE;
30317                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag);
30318                 }
30319                 high_memory = TRUE;
30320             }
30321         }
30322 #endif // BIT64
30323     }
30324 
30325     // The purpose of calling ensure_gap_allocation here is to make sure
30326     // that we actually are able to commit the memory to allocate generation
30327     // starts.
30328     if ((should_compact == FALSE) &&
30329         (ensure_gap_allocation (condemned_gen_number) == FALSE))
30330     {
30331         should_compact = TRUE;
30332         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps);
30333     }
30334 
30335     if (settings.condemned_generation == max_generation)
30336     {
30337         //check the progress
30338         if (
30339 #ifdef BIT64
30340             (high_memory && !should_compact) ||
30341 #endif // BIT64
30342             (generation_plan_allocation_start (generation_of (max_generation - 1)) >=
30343                 generation_allocation_start (generation_of (max_generation - 1))))
30344         {
30345             dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked",
30346                      generation_size (max_generation),
30347                      generation_plan_size (max_generation)));
30348             //no progress -> lock
30349             settings.should_lock_elevation = TRUE;
30350         }
30351     }
30352 
30353     if (settings.pause_mode == pause_no_gc)
30354     {
30355         should_compact = TRUE;
30356         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment))
30357             < soh_allocation_no_gc)
30358         {
30359             should_expand = TRUE;
30360         }
30361     }
30362 
30363     dprintf (2, ("will %s", (should_compact ? "compact" : "sweep")));
30364     return should_compact;
30365 }
30366 
30367 size_t align_lower_good_size_allocation (size_t size)
30368 {
30369     return (size/64)*64;
30370 }
30371 
30372 size_t gc_heap::approximate_new_allocation()
30373 {
30374     dynamic_data* dd0 = dynamic_data_of (0);
30375     return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
30376 }
30377 
30378 // After we did a GC we expect to have at least this
30379 // much space at the end of the segment to satisfy
30380 // a reasonable amount of allocation requests.
30381 size_t gc_heap::end_space_after_gc()
30382 {
30383     return max ((dd_min_gc_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
30384 }
30385 
30386 BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
30387 {
30388     uint8_t* start = 0;
30389 
30390     if ((tp == tuning_deciding_condemned_gen) ||
30391         (tp == tuning_deciding_compaction))
30392     {
30393         start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
30394         if (settings.concurrent)
30395         {
30396             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)",
30397                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30398         }
30399         else
30400         {
30401             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)",
30402                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
30403         }
30404     }
30405     else if (tp == tuning_deciding_expansion)
30406     {
30407         start = heap_segment_plan_allocated (ephemeral_heap_segment);
30408         dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan",
30409             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
30410     }
30411     else
30412     {
30413         assert (tp == tuning_deciding_full_gc);
30414         dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)",
30415             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30416         start = alloc_allocated;
30417     }
30418 
30419     if (start == 0) // empty ephemeral generations
30420     {
30421         assert (tp == tuning_deciding_expansion);
30422         // if there are no survivors in the ephemeral segment,
30423         // this should be the beginning of ephemeral segment.
30424         start = generation_allocation_pointer (generation_of (max_generation));
30425         assert (start == heap_segment_mem (ephemeral_heap_segment));
30426     }
30427 
30428     if (tp == tuning_deciding_expansion)
30429     {
30430         assert (settings.condemned_generation >= (max_generation-1));
30431         size_t gen0size = approximate_new_allocation();
30432         size_t eph_size = gen0size;
30433 
30434         for (int j = 1; j <= max_generation-1; j++)
30435         {
30436             eph_size += 2*dd_min_size (dynamic_data_of(j));
30437         }
30438 
30439         // We must find room for one large object and enough room for gen0size
30440         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
30441         {
30442             dprintf (3, ("Enough room before end of segment"));
30443             return TRUE;
30444         }
30445         else
30446         {
30447             size_t room = align_lower_good_size_allocation
30448                 (heap_segment_reserved (ephemeral_heap_segment) - start);
30449             size_t end_seg = room;
30450 
30451             //look at the plug free space
30452             size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
30453             bool large_chunk_found = FALSE;
30454             size_t bos = 0;
30455             uint8_t* gen0start = generation_plan_allocation_start (youngest_generation);
30456             dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
30457             if (gen0start == 0)
30458                 return FALSE;
30459             dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
30460                          room, gen0size));
30461             while ((bos < mark_stack_bos) &&
30462                    !((room >= gen0size) && large_chunk_found))
30463             {
30464                 uint8_t* plug = pinned_plug (pinned_plug_of (bos));
30465                 if (in_range_for_segment (plug, ephemeral_heap_segment))
30466                 {
30467                     if (plug >= gen0start)
30468                     {
30469                         size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
30470                         room += chunk;
30471                         if (!large_chunk_found)
30472                         {
30473                             large_chunk_found = (chunk >= largest_alloc);
30474                         }
30475                         dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
30476                                      room, large_chunk_found));
30477                     }
30478                 }
30479                 bos++;
30480             }
30481 
30482             if (room >= gen0size)
30483             {
30484                 if (large_chunk_found)
30485                 {
30486                     dprintf (3, ("Enough room"));
30487                     return TRUE;
30488                 }
30489                 else
30490                 {
30491                     // now we need to find largest_alloc at the end of the segment.
30492                     if (end_seg >= end_space_after_gc())
30493                     {
30494                         dprintf (3, ("Enough room (may need end of seg)"));
30495                         return TRUE;
30496                     }
30497                 }
30498             }
30499 
30500             dprintf (3, ("Not enough room"));
30501                 return FALSE;
30502         }
30503     }
30504     else
30505     {
30506         size_t end_space = 0;
30507         dynamic_data* dd = dynamic_data_of (0);
30508         if ((tp == tuning_deciding_condemned_gen) ||
30509             (tp == tuning_deciding_full_gc))
30510         {
30511             end_space = 2*dd_min_size (dd);
30512         }
30513         else
30514         {
30515             assert (tp == tuning_deciding_compaction);
30516             end_space = approximate_new_allocation();
30517         }
30518 
30519         if (!((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space))
30520         {
30521             dprintf (GTC_LOG, ("ephemeral_gen_fit_p: does not fit without compaction"));
30522         }
30523         return ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space);
30524     }
30525 }
30526 
30527 CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_bytes)
30528 {
30529     //create a new alloc context because gen3context is shared.
30530     alloc_context acontext;
30531     acontext.alloc_ptr = 0;
30532     acontext.alloc_limit = 0;
30533     acontext.alloc_bytes = 0;
30534 #ifdef MULTIPLE_HEAPS
30535     acontext.set_alloc_heap(vm_heap);
30536 #endif //MULTIPLE_HEAPS
30537 
30538 #ifdef MARK_ARRAY
30539     uint8_t* current_lowest_address = lowest_address;
30540     uint8_t* current_highest_address = highest_address;
30541 #ifdef BACKGROUND_GC
30542     if (recursive_gc_sync::background_running_p())
30543     {
30544         current_lowest_address = background_saved_lowest_address;
30545         current_highest_address = background_saved_highest_address;
30546     }
30547 #endif //BACKGROUND_GC
30548 #endif // MARK_ARRAY
30549 
30550     #if BIT64
30551     size_t maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
30552     #else
30553     size_t maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
30554     #endif
30555 
30556     if (jsize >= maxObjectSize)
30557     {
30558         if (g_pConfig->IsGCBreakOnOOMEnabled())
30559         {
30560             GCToOSInterface::DebugBreak();
30561         }
30562         return NULL;
30563     }
30564 
30565     size_t size = AlignQword (jsize);
30566     int align_const = get_alignment_constant (FALSE);
30567 #ifdef FEATURE_LOH_COMPACTION
30568     size_t pad = Align (loh_padding_obj_size, align_const);
30569 #else
30570     size_t pad = 0;
30571 #endif //FEATURE_LOH_COMPACTION
30572 
30573     assert (size >= Align (min_obj_size, align_const));
30574 #ifdef _MSC_VER
30575 #pragma inline_depth(0)
30576 #endif //_MSC_VER
30577     if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
30578     {
30579         return 0;
30580     }
30581 
30582 #ifdef _MSC_VER
30583 #pragma inline_depth(20)
30584 #endif //_MSC_VER
30585 
30586 #ifdef FEATURE_LOH_COMPACTION
30587     // The GC allocator made a free object already in this alloc context and
30588     // adjusted the alloc_ptr accordingly.
30589 #endif //FEATURE_LOH_COMPACTION
30590 
30591     uint8_t*  result = acontext.alloc_ptr;
30592 
30593     assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
30594 
30595     CObjectHeader* obj = (CObjectHeader*)result;
30596 
30597 #ifdef MARK_ARRAY
30598     if (recursive_gc_sync::background_running_p())
30599     {
30600         if ((result < current_highest_address) && (result >= current_lowest_address))
30601         {
30602             dprintf (3, ("Clearing mark bit at address %Ix",
30603                      (size_t)(&mark_array [mark_word_of (result)])));
30604 
30605             mark_array_clear_marked (result);
30606         }
30607 #ifdef BACKGROUND_GC
30608         //the object has to cover one full mark uint32_t
30609         assert (size > mark_word_size);
30610         if (current_c_gc_state == c_gc_state_marking)
30611         {
30612             dprintf (3, ("Concurrent allocation of a large object %Ix",
30613                         (size_t)obj));
30614             //mark the new block specially so we know it is a new object
30615             if ((result < current_highest_address) && (result >= current_lowest_address))
30616             {
30617                 dprintf (3, ("Setting mark bit at address %Ix",
30618                             (size_t)(&mark_array [mark_word_of (result)])));
30619 
30620                 mark_array_set_marked (result);
30621             }
30622         }
30623 #endif //BACKGROUND_GC
30624     }
30625 #endif //MARK_ARRAY
30626 
30627     assert (obj != 0);
30628     assert ((size_t)obj == Align ((size_t)obj, align_const));
30629 
30630     alloc_bytes += acontext.alloc_bytes;
30631     return obj;
30632 }
30633 
30634 void reset_memory (uint8_t* o, size_t sizeo)
30635 {
30636 #ifndef FEATURE_PAL
30637     if (sizeo > 128 * 1024)
30638     {
30639         // We cannot reset the memory for the useful part of a free object.
30640         size_t size_to_skip = min_free_list - plug_skew;
30641 
30642         size_t page_start = align_on_page ((size_t)(o + size_to_skip));
30643         size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
30644         // Note we need to compensate for an OS bug here. This bug would cause the MEM_RESET to fail
30645         // on write watched memory.
30646         if (reset_mm_p)
30647         {
30648             reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, true /* unlock */);
30649         }
30650     }
30651 #endif //!FEATURE_PAL
30652 }
30653 
30654 void gc_heap::reset_large_object (uint8_t* o)
30655 {
30656     // If it's a large object, allow the O/S to discard the backing store for these pages.
30657     reset_memory (o, size(o));
30658 }
30659 
30660 BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp)
30661 {
30662     BOOL m = FALSE;
30663     // It shouldn't be necessary to do these comparisons because this is only used for blocking
30664     // GCs and LOH segments cannot be out of range.
30665     if ((o >= lowest_address) && (o < highest_address))
30666     {
30667         if (marked (o))
30668         {
30669             if (clearp)
30670             {
30671                 clear_marked (o);
30672                 if (pinned (o))
30673                     clear_pinned(o);
30674             }
30675             m = TRUE;
30676         }
30677         else
30678             m = FALSE;
30679     }
30680     else
30681         m = TRUE;
30682     return m;
30683 }
30684 
30685 void gc_heap::walk_survivors_relocation (size_t profiling_context, record_surv_fn fn)
30686 {
30687     // Now walk the portion of memory that is actually being relocated.
30688     walk_relocation (profiling_context, fn);
30689 
30690 #ifdef FEATURE_LOH_COMPACTION
30691     if (loh_compacted_p)
30692     {
30693         walk_relocation_for_loh (profiling_context, fn);
30694     }
30695 #endif //FEATURE_LOH_COMPACTION
30696 }
30697 
30698 void gc_heap::walk_survivors_for_loh (size_t profiling_context, record_surv_fn fn)
30699 {
30700     generation* gen        = large_object_generation;
30701     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));;
30702 
30703     PREFIX_ASSUME(seg != NULL);
30704 
30705     uint8_t* o                = generation_allocation_start (gen);
30706     uint8_t* plug_end         = o;
30707     uint8_t* plug_start       = o;
30708 
30709     while (1)
30710     {
30711         if (o >= heap_segment_allocated (seg))
30712         {
30713             seg = heap_segment_next (seg);
30714             if (seg == 0)
30715                 break;
30716             else
30717                 o = heap_segment_mem (seg);
30718         }
30719         if (large_object_marked(o, FALSE))
30720         {
30721             plug_start = o;
30722 
30723             BOOL m = TRUE;
30724             while (m)
30725             {
30726                 o = o + AlignQword (size (o));
30727                 if (o >= heap_segment_allocated (seg))
30728                 {
30729                     break;
30730                 }
30731                 m = large_object_marked (o, FALSE);
30732             }
30733 
30734             plug_end = o;
30735 
30736             fn (plug_start, plug_end, 0, profiling_context, FALSE, FALSE);
30737         }
30738         else
30739         {
30740             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
30741             {
30742                 o = o + AlignQword (size (o));
30743             }
30744         }
30745     }
30746 }
30747 
30748 #ifdef BACKGROUND_GC
30749 
30750 BOOL gc_heap::background_object_marked (uint8_t* o, BOOL clearp)
30751 {
30752     BOOL m = FALSE;
30753     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
30754     {
30755         if (mark_array_marked (o))
30756         {
30757             if (clearp)
30758             {
30759                 mark_array_clear_marked (o);
30760                 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
30761                 dprintf (3, ("CM: %Ix", o));
30762             }
30763             m = TRUE;
30764         }
30765         else
30766             m = FALSE;
30767     }
30768     else
30769         m = TRUE;
30770 
30771     dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
30772     return m;
30773 }
30774 
30775 uint8_t* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
30776 {
30777     return
30778         (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
30779 }
30780 
30781 void gc_heap::set_mem_verify (uint8_t* start, uint8_t* end, uint8_t b)
30782 {
30783 #ifdef VERIFY_HEAP
30784     if (end > start)
30785     {
30786         if ((g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC) &&
30787            !(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_NO_MEM_FILL))
30788         {
30789             dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
30790             memset (start, b, (end - start));
30791         }
30792     }
30793 #endif //VERIFY_HEAP
30794 }
30795 
30796 void gc_heap::generation_delete_heap_segment (generation* gen,
30797                                               heap_segment* seg,
30798                                               heap_segment* prev_seg,
30799                                               heap_segment* next_seg)
30800 {
30801     dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
30802     if (gen == large_object_generation)
30803     {
30804         heap_segment_next (prev_seg) = next_seg;
30805 
30806         dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
30807 
30808         heap_segment_next (seg) = freeable_large_heap_segment;
30809         freeable_large_heap_segment = seg;
30810     }
30811     else
30812     {
30813         if (seg == ephemeral_heap_segment)
30814         {
30815             FATAL_GC_ERROR();
30816         }
30817 
30818         heap_segment_next (next_seg) = prev_seg;
30819 
30820         dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
30821         heap_segment_next (seg) = freeable_small_heap_segment;
30822         freeable_small_heap_segment = seg;
30823     }
30824 
30825     decommit_heap_segment (seg);
30826     seg->flags |= heap_segment_flags_decommitted;
30827 
30828     set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
30829 }
30830 
30831 void gc_heap::process_background_segment_end (heap_segment* seg,
30832                                           generation* gen,
30833                                           uint8_t* last_plug_end,
30834                                           heap_segment* start_seg,
30835                                           BOOL* delete_p)
30836 {
30837     *delete_p = FALSE;
30838     uint8_t* allocated = heap_segment_allocated (seg);
30839     uint8_t* background_allocated = heap_segment_background_allocated (seg);
30840 
30841     dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)",
30842                 (size_t)heap_segment_mem (seg), background_allocated, allocated));
30843 
30844 
30845     if (allocated != background_allocated)
30846     {
30847         if (gen == large_object_generation)
30848         {
30849             FATAL_GC_ERROR();
30850         }
30851 
30852         dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[",
30853                     (size_t)last_plug_end, background_allocated));
30854         thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
30855 
30856         fix_brick_to_highest (last_plug_end, background_allocated);
30857 
30858         // When we allowed fgc's during going through gaps, we could have erased the brick
30859         // that corresponds to bgc_allocated 'cause we had to update the brick there,
30860         // recover it here.
30861         fix_brick_to_highest (background_allocated, background_allocated);
30862     }
30863     else
30864     {
30865         // by default, if allocated == background_allocated, it can't
30866         // be the ephemeral segment.
30867         if (seg == ephemeral_heap_segment)
30868         {
30869             FATAL_GC_ERROR();
30870         }
30871 
30872         if (allocated == heap_segment_mem (seg))
30873         {
30874             // this can happen with LOH segments when multiple threads
30875             // allocate new segments and not all of them were needed to
30876             // satisfy allocation requests.
30877             assert (gen == large_object_generation);
30878         }
30879 
30880         if (last_plug_end == heap_segment_mem (seg))
30881         {
30882             dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
30883                         (size_t)allocated, (*delete_p ? "should" : "should not")));
30884 
30885             if (seg != start_seg)
30886             {
30887                 *delete_p = TRUE;
30888             }
30889         }
30890         else
30891         {
30892             dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
30893             heap_segment_allocated (seg) = last_plug_end;
30894             set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
30895 
30896             decommit_heap_segment_pages (seg, 0);
30897         }
30898     }
30899 
30900     dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
30901     bgc_verify_mark_array_cleared (seg);
30902 }
30903 
30904 void gc_heap::process_n_background_segments (heap_segment* seg,
30905                                              heap_segment* prev_seg,
30906                                              generation* gen)
30907 {
30908     assert (gen != large_object_generation);
30909 
30910     while (seg)
30911     {
30912         dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
30913         heap_segment* next_seg = heap_segment_next (seg);
30914 
30915         if (heap_segment_read_only_p (seg))
30916         {
30917             prev_seg = seg;
30918         }
30919         else
30920         {
30921             if (heap_segment_allocated (seg) == heap_segment_mem (seg))
30922             {
30923                 // This can happen - if we have a LOH segment where nothing survived
30924                 // or a SOH segment allocated by a gen1 GC when BGC was going where
30925                 // nothing survived last time we did a gen1 GC.
30926                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
30927             }
30928             else
30929             {
30930                 prev_seg = seg;
30931             }
30932         }
30933 
30934         verify_soh_segment_list();
30935         seg = next_seg;
30936     }
30937 }
30938 
30939 inline
30940 BOOL gc_heap::fgc_should_consider_object (uint8_t* o,
30941                                           heap_segment* seg,
30942                                           BOOL consider_bgc_mark_p,
30943                                           BOOL check_current_sweep_p,
30944                                           BOOL check_saved_sweep_p)
30945 {
30946     // the logic for this function must be kept in sync with the analogous function
30947     // in ToolBox\SOS\Strike\gc.cpp
30948 
30949     // TRUE means we don't need to check the bgc mark bit
30950     // FALSE means we do.
30951     BOOL no_bgc_mark_p = FALSE;
30952 
30953     if (consider_bgc_mark_p)
30954     {
30955         if (check_current_sweep_p && (o < current_sweep_pos))
30956         {
30957             dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
30958             no_bgc_mark_p = TRUE;
30959         }
30960 
30961         if (!no_bgc_mark_p)
30962         {
30963             if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
30964             {
30965                 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
30966                 no_bgc_mark_p = TRUE;
30967             }
30968 
30969             if (!check_saved_sweep_p)
30970             {
30971                 uint8_t* background_allocated = heap_segment_background_allocated (seg);
30972                 // if this was the saved ephemeral segment, check_saved_sweep_p
30973                 // would've been true.
30974                 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
30975                 // background_allocated could be 0 for the new segments acquired during bgc
30976                 // sweep and we still want no_bgc_mark_p to be true.
30977                 if (o >= background_allocated)
30978                 {
30979                     dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
30980                     no_bgc_mark_p = TRUE;
30981                 }
30982             }
30983         }
30984     }
30985     else
30986     {
30987         no_bgc_mark_p = TRUE;
30988     }
30989 
30990     dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
30991     return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
30992 }
30993 
30994 // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
30995 // if it's TRUE, check_current_sweep_p tells you if you should consider the
30996 // current sweep position or not.
30997 void gc_heap::should_check_bgc_mark (heap_segment* seg,
30998                                      BOOL* consider_bgc_mark_p,
30999                                      BOOL* check_current_sweep_p,
31000                                      BOOL* check_saved_sweep_p)
31001 {
31002     // the logic for this function must be kept in sync with the analogous function
31003     // in ToolBox\SOS\Strike\gc.cpp
31004     *consider_bgc_mark_p = FALSE;
31005     *check_current_sweep_p = FALSE;
31006     *check_saved_sweep_p = FALSE;
31007 
31008     if (current_c_gc_state == c_gc_state_planning)
31009     {
31010         // We are doing the current_sweep_pos comparison here because we have yet to
31011         // turn on the swept flag for the segment but in_range_for_segment will return
31012         // FALSE if the address is the same as reserved.
31013         if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
31014         {
31015             dprintf (3, ("seg %Ix is already swept by bgc", seg));
31016         }
31017         else
31018         {
31019             *consider_bgc_mark_p = TRUE;
31020 
31021             dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
31022 
31023             if (seg == saved_sweep_ephemeral_seg)
31024             {
31025                 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
31026                 *check_saved_sweep_p = TRUE;
31027             }
31028 
31029             if (in_range_for_segment (current_sweep_pos, seg))
31030             {
31031                 dprintf (3, ("current sweep pos is %Ix and within seg %Ix",
31032                               current_sweep_pos, seg));
31033                 *check_current_sweep_p = TRUE;
31034             }
31035         }
31036     }
31037 }
31038 
31039 void gc_heap::background_ephemeral_sweep()
31040 {
31041     dprintf (3, ("bgc ephemeral sweep"));
31042 
31043     int align_const = get_alignment_constant (TRUE);
31044 
31045     saved_sweep_ephemeral_seg = ephemeral_heap_segment;
31046     saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
31047 
31048     // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
31049     // we thread onto a list first then publish it when we are done.
31050     allocator youngest_free_list;
31051     size_t youngest_free_list_space = 0;
31052     size_t youngest_free_obj_space = 0;
31053 
31054     youngest_free_list.clear();
31055 
31056     for (int i = 0; i <= (max_generation - 1); i++)
31057     {
31058         generation* gen_to_reset = generation_of (i);
31059         assert (generation_free_list_space (gen_to_reset) == 0);
31060         // Can only assert free_list_space is 0, not free_obj_space as the allocator could have added
31061         // something there.
31062     }
31063 
31064     for (int i = (max_generation - 1); i >= 0; i--)
31065     {
31066         generation* current_gen = generation_of (i);
31067         uint8_t* o = generation_allocation_start (current_gen);
31068         //Skip the generation gap object
31069         o = o + Align(size (o), align_const);
31070         uint8_t* end = ((i > 0) ?
31071                      generation_allocation_start (generation_of (i - 1)) :
31072                      heap_segment_allocated (ephemeral_heap_segment));
31073 
31074         uint8_t* plug_end = o;
31075         uint8_t* plug_start = o;
31076         BOOL marked_p = FALSE;
31077 
31078         while (o < end)
31079         {
31080             marked_p = background_object_marked (o, TRUE);
31081             if (marked_p)
31082             {
31083                 plug_start = o;
31084                 size_t plug_size = plug_start - plug_end;
31085 
31086                 if (i >= 1)
31087                 {
31088                     thread_gap (plug_end, plug_size, current_gen);
31089                 }
31090                 else
31091                 {
31092                     if (plug_size > 0)
31093                     {
31094                         make_unused_array (plug_end, plug_size);
31095                         if (plug_size >= min_free_list)
31096                         {
31097                             youngest_free_list_space += plug_size;
31098                             youngest_free_list.thread_item (plug_end, plug_size);
31099                         }
31100                         else
31101                         {
31102                             youngest_free_obj_space += plug_size;
31103                         }
31104                     }
31105                 }
31106 
31107                 fix_brick_to_highest (plug_end, plug_start);
31108                 fix_brick_to_highest (plug_start, plug_start);
31109 
31110                 BOOL m = TRUE;
31111                 while (m)
31112                 {
31113                     o = o + Align (size (o), align_const);
31114                     if (o >= end)
31115                     {
31116                         break;
31117                     }
31118 
31119                     m = background_object_marked (o, TRUE);
31120                 }
31121                 plug_end = o;
31122                 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31123             }
31124             else
31125             {
31126                 while ((o < end) && !background_object_marked (o, FALSE))
31127                 {
31128                     o = o + Align (size (o), align_const);
31129                 }
31130             }
31131         }
31132 
31133         if (plug_end != end)
31134         {
31135             if (i >= 1)
31136             {
31137                 thread_gap (plug_end, end - plug_end, current_gen);
31138             }
31139             else
31140             {
31141                 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
31142                 // the following line is temporary.
31143                 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
31144                 make_unused_array (plug_end, (end - plug_end));
31145             }
31146 
31147             fix_brick_to_highest (plug_end, end);
31148         }
31149 
31150         dd_fragmentation (dynamic_data_of (i)) =
31151             generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
31152     }
31153 
31154     generation* youngest_gen = generation_of (0);
31155     generation_free_list_space (youngest_gen) = youngest_free_list_space;
31156     generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
31157     dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
31158     generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
31159 }
31160 
31161 void gc_heap::background_sweep()
31162 {
31163     Thread* current_thread  = GetThread();
31164     generation* gen         = generation_of (max_generation);
31165     dynamic_data* dd        = dynamic_data_of (max_generation);
31166     // For SOH segments we go backwards.
31167     heap_segment* start_seg = ephemeral_heap_segment;
31168     PREFIX_ASSUME(start_seg != NULL);
31169     heap_segment* fseg      = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31170     heap_segment* seg       = start_seg;
31171     uint8_t* o                 = heap_segment_mem (seg);
31172 
31173     heap_segment* prev_seg = heap_segment_next (seg);
31174     int align_const        = get_alignment_constant (TRUE);
31175     if (seg == fseg)
31176     {
31177         assert (o == generation_allocation_start (generation_of (max_generation)));
31178         o = o + Align(size (o), align_const);
31179     }
31180 
31181     uint8_t* plug_end      = o;
31182     uint8_t* plug_start    = o;
31183     next_sweep_obj         = o;
31184     current_sweep_pos      = o;
31185 
31186     //uint8_t* end              = background_next_end (seg, (gen == large_object_generation));
31187     uint8_t* end              = heap_segment_background_allocated (seg);
31188     BOOL delete_p          = FALSE;
31189 
31190     //concurrent_print_time_delta ("finished with mark and start with sweep");
31191     concurrent_print_time_delta ("Sw");
31192     dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31193 
31194     //block concurrent allocation for large objects
31195     dprintf (3, ("lh state: planning"));
31196     if (gc_lh_block_event.IsValid())
31197     {
31198         gc_lh_block_event.Reset();
31199     }
31200 
31201     for (int i = 0; i <= (max_generation + 1); i++)
31202     {
31203         generation* gen_to_reset = generation_of (i);
31204         generation_allocator (gen_to_reset)->clear();
31205         generation_free_list_space (gen_to_reset) = 0;
31206         generation_free_obj_space (gen_to_reset) = 0;
31207         generation_free_list_allocated (gen_to_reset) = 0;
31208         generation_end_seg_allocated (gen_to_reset) = 0;
31209         generation_condemned_allocated (gen_to_reset) = 0;
31210         //reset the allocation so foreground gc can allocate into older generation
31211         generation_allocation_pointer (gen_to_reset)= 0;
31212         generation_allocation_limit (gen_to_reset) = 0;
31213         generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
31214     }
31215 
31216     fire_bgc_event (BGC2ndNonConEnd);
31217 
31218     current_bgc_state = bgc_sweep_soh;
31219     verify_soh_segment_list();
31220 
31221 #ifdef FEATURE_BASICFREEZE
31222     if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
31223         ro_segments_in_range)
31224     {
31225         sweep_ro_segments (generation_start_segment (gen));
31226     }
31227 #endif // FEATURE_BASICFREEZE
31228 
31229     //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
31230     if (current_c_gc_state != c_gc_state_planning)
31231     {
31232         current_c_gc_state = c_gc_state_planning;
31233     }
31234 
31235     concurrent_print_time_delta ("Swe");
31236 
31237     heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
31238     PREFIX_ASSUME(loh_seg  != NULL);
31239     while (loh_seg )
31240     {
31241         loh_seg->flags &= ~heap_segment_flags_swept;
31242         heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
31243         loh_seg = heap_segment_next_rw (loh_seg);
31244     }
31245 
31246 #ifdef MULTIPLE_HEAPS
31247     bgc_t_join.join(this, gc_join_restart_ee);
31248     if (bgc_t_join.joined())
31249 #endif //MULTIPLE_HEAPS
31250     {
31251 #ifdef MULTIPLE_HEAPS
31252         dprintf(2, ("Starting BGC threads for resuming EE"));
31253         bgc_t_join.restart();
31254 #endif //MULTIPLE_HEAPS
31255     }
31256 
31257     if (heap_number == 0)
31258     {
31259         restart_EE ();
31260     }
31261 
31262     fire_bgc_event (BGC2ndConBegin);
31263 
31264     background_ephemeral_sweep();
31265 
31266 #ifdef MULTIPLE_HEAPS
31267     bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
31268     if (bgc_t_join.joined())
31269 #endif //MULTIPLE_HEAPS
31270     {
31271 #ifdef FEATURE_EVENT_TRACE
31272         bgc_heap_walk_for_etw_p = ETW::GCLog::ShouldTrackMovementForEtw();
31273 #endif //FEATURE_EVENT_TRACE
31274 
31275         leave_spin_lock (&gc_lock);
31276 
31277 #ifdef MULTIPLE_HEAPS
31278         dprintf(2, ("Starting BGC threads for BGC sweeping"));
31279         bgc_t_join.restart();
31280 #endif //MULTIPLE_HEAPS
31281     }
31282 
31283     disable_preemptive (current_thread, TRUE);
31284 
31285     dprintf (2, ("bgs: sweeping gen2 objects"));
31286     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31287                     (size_t)heap_segment_mem (seg),
31288                     (size_t)heap_segment_allocated (seg),
31289                     (size_t)heap_segment_background_allocated (seg)));
31290 
31291     int num_objs = 256;
31292     int current_num_objs = 0;
31293     heap_segment* next_seg = 0;
31294 
31295     while (1)
31296     {
31297         if (o >= end)
31298         {
31299             if (gen == large_object_generation)
31300             {
31301                 next_seg = heap_segment_next (seg);
31302             }
31303             else
31304             {
31305                 next_seg = heap_segment_prev (fseg, seg);
31306             }
31307 
31308             delete_p = FALSE;
31309 
31310             if (!heap_segment_read_only_p (seg))
31311             {
31312                 if (gen == large_object_generation)
31313                 {
31314                     // we can treat all LOH segments as in the bgc domain
31315                     // regardless of whether we saw in bgc mark or not
31316                     // because we don't allow LOH allocations during bgc
31317                     // sweep anyway - the LOH segments can't change.
31318                     process_background_segment_end (seg, gen, plug_end,
31319                                                     start_seg, &delete_p);
31320                 }
31321                 else
31322                 {
31323                     assert (heap_segment_background_allocated (seg) != 0);
31324                     process_background_segment_end (seg, gen, plug_end,
31325                                                     start_seg, &delete_p);
31326 
31327                     assert (next_seg || !delete_p);
31328                 }
31329             }
31330 
31331             if (delete_p)
31332             {
31333                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
31334             }
31335             else
31336             {
31337                 prev_seg = seg;
31338                 dprintf (2, ("seg %Ix has been swept", seg));
31339                 seg->flags |= heap_segment_flags_swept;
31340             }
31341 
31342             verify_soh_segment_list();
31343 
31344             seg = next_seg;
31345 
31346             dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
31347 
31348             if (seg == 0)
31349             {
31350                 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31351 
31352                 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31353 
31354                 if (gen != large_object_generation)
31355                 {
31356                     dprintf (2, ("bgs: sweeping gen3 objects"));
31357                     current_bgc_state = bgc_sweep_loh;
31358                     gen = generation_of (max_generation+1);
31359                     start_seg = heap_segment_rw (generation_start_segment (gen));
31360 
31361                     PREFIX_ASSUME(start_seg != NULL);
31362 
31363                     seg = start_seg;
31364                     prev_seg = 0;
31365                     o = generation_allocation_start (gen);
31366                     assert (method_table (o) == g_pFreeObjectMethodTable);
31367                     align_const = get_alignment_constant (FALSE);
31368                     o = o + Align(size (o), align_const);
31369                     plug_end = o;
31370                     end = heap_segment_allocated (seg);
31371                     dprintf (2, ("sweeping gen3 objects"));
31372                     generation_free_obj_space (gen) = 0;
31373                     generation_allocator (gen)->clear();
31374                     generation_free_list_space (gen) = 0;
31375 
31376                     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31377                                     (size_t)heap_segment_mem (seg),
31378                                     (size_t)heap_segment_allocated (seg),
31379                                     (size_t)heap_segment_background_allocated (seg)));
31380                 }
31381                 else
31382                     break;
31383             }
31384             else
31385             {
31386                 o = heap_segment_mem (seg);
31387                 if (seg == fseg)
31388                 {
31389                     assert (gen != large_object_generation);
31390                     assert (o == generation_allocation_start (generation_of (max_generation)));
31391                     align_const = get_alignment_constant (TRUE);
31392                     o = o + Align(size (o), align_const);
31393                 }
31394 
31395                 plug_end = o;
31396                 current_sweep_pos = o;
31397                 next_sweep_obj = o;
31398 
31399                 allow_fgc();
31400                 end = background_next_end (seg, (gen == large_object_generation));
31401                 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31402                                 (size_t)heap_segment_mem (seg),
31403                                 (size_t)heap_segment_allocated (seg),
31404                                 (size_t)heap_segment_background_allocated (seg)));
31405             }
31406         }
31407 
31408         if ((o < end) && background_object_marked (o, TRUE))
31409         {
31410             plug_start = o;
31411             if (gen == large_object_generation)
31412             {
31413                 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
31414             }
31415 
31416             thread_gap (plug_end, plug_start-plug_end, gen);
31417             if (gen != large_object_generation)
31418             {
31419                 add_gen_free (max_generation, plug_start-plug_end);
31420                 fix_brick_to_highest (plug_end, plug_start);
31421                 // we need to fix the brick for the next plug here 'cause an FGC can
31422                 // happen and can't read a stale brick.
31423                 fix_brick_to_highest (plug_start, plug_start);
31424             }
31425 
31426             BOOL m = TRUE;
31427 
31428             while (m)
31429             {
31430                 next_sweep_obj = o + Align(size (o), align_const);
31431                 current_num_objs++;
31432                 if (current_num_objs >= num_objs)
31433                 {
31434                     current_sweep_pos = next_sweep_obj;
31435 
31436                     allow_fgc();
31437                     current_num_objs = 0;
31438                 }
31439 
31440                 o = next_sweep_obj;
31441                 if (o >= end)
31442                 {
31443                     break;
31444                 }
31445 
31446                 m = background_object_marked (o, TRUE);
31447             }
31448             plug_end = o;
31449             if (gen != large_object_generation)
31450             {
31451                 add_gen_plug (max_generation, plug_end-plug_start);
31452                 dd_survived_size (dd) += (plug_end - plug_start);
31453             }
31454             dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31455         }
31456         else
31457         {
31458             while ((o < end) && !background_object_marked (o, FALSE))
31459             {
31460                 next_sweep_obj = o + Align(size (o), align_const);;
31461                 current_num_objs++;
31462                 if (current_num_objs >= num_objs)
31463                 {
31464                     current_sweep_pos = plug_end;
31465                     dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
31466                     allow_fgc();
31467                     current_num_objs = 0;
31468                 }
31469 
31470                 o = next_sweep_obj;
31471             }
31472         }
31473     }
31474 
31475     size_t total_loh_size = generation_size (max_generation + 1);
31476     size_t total_soh_size = generation_sizes (generation_of (max_generation));
31477 
31478     dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
31479 
31480     dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id",
31481         generation_free_list_space (generation_of (max_generation)),
31482         generation_free_obj_space (generation_of (max_generation))));
31483     dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id",
31484         heap_number,
31485         generation_free_list_space (generation_of (max_generation + 1)),
31486         generation_free_obj_space (generation_of (max_generation + 1))));
31487 
31488     fire_bgc_event (BGC2ndConEnd);
31489     concurrent_print_time_delta ("background sweep");
31490 
31491     heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31492     PREFIX_ASSUME(reset_seg != NULL);
31493 
31494     while (reset_seg)
31495     {
31496         heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
31497         heap_segment_background_allocated (reset_seg) = 0;
31498         reset_seg = heap_segment_next_rw (reset_seg);
31499     }
31500 
31501     // We calculate dynamic data here because if we wait till we signal the lh event,
31502     // the allocation thread can change the fragmentation and we may read an intermediate
31503     // value (which can be greater than the generation size). Plus by that time it won't
31504     // be accurate.
31505     compute_new_dynamic_data (max_generation);
31506 
31507     enable_preemptive (current_thread);
31508 
31509 #ifdef MULTIPLE_HEAPS
31510     bgc_t_join.join(this, gc_join_set_state_free);
31511     if (bgc_t_join.joined())
31512 #endif //MULTIPLE_HEAPS
31513     {
31514         // TODO: We are using this join just to set the state. Should
31515         // look into eliminating it - check to make sure things that use
31516         // this state can live with per heap state like should_check_bgc_mark.
31517         current_c_gc_state = c_gc_state_free;
31518 
31519 #ifdef MULTIPLE_HEAPS
31520         dprintf(2, ("Starting BGC threads after background sweep phase"));
31521         bgc_t_join.restart();
31522 #endif //MULTIPLE_HEAPS
31523     }
31524 
31525     disable_preemptive (current_thread, TRUE);
31526 
31527     if (gc_lh_block_event.IsValid())
31528     {
31529         gc_lh_block_event.Set();
31530     }
31531 
31532     //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31533     dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
31534 }
31535 #endif //BACKGROUND_GC
31536 
31537 void gc_heap::sweep_large_objects ()
31538 {
31539     //this min value is for the sake of the dynamic tuning.
31540     //so we know that we are not starting even if we have no
31541     //survivors.
31542     generation* gen        = large_object_generation;
31543     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
31544 
31545     PREFIX_ASSUME(start_seg != NULL);
31546 
31547     heap_segment* seg      = start_seg;
31548     heap_segment* prev_seg = 0;
31549     uint8_t* o             = generation_allocation_start (gen);
31550     int align_const        = get_alignment_constant (FALSE);
31551 
31552     //Skip the generation gap object
31553     o = o + Align(size (o), align_const);
31554 
31555     uint8_t* plug_end         = o;
31556     uint8_t* plug_start       = o;
31557 
31558     generation_allocator (gen)->clear();
31559     generation_free_list_space (gen) = 0;
31560     generation_free_obj_space (gen) = 0;
31561 
31562 
31563     dprintf (3, ("sweeping large objects"));
31564     dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix",
31565                  (size_t)seg,
31566                  (size_t)heap_segment_mem (seg),
31567                  (size_t)heap_segment_allocated (seg),
31568                  o));
31569 
31570     while (1)
31571     {
31572         if (o >= heap_segment_allocated (seg))
31573         {
31574             heap_segment* next_seg = heap_segment_next (seg);
31575             //delete the empty segment if not the only one
31576             if ((plug_end == heap_segment_mem (seg)) &&
31577                 (seg != start_seg) && !heap_segment_read_only_p (seg))
31578             {
31579                 //prepare for deletion
31580                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
31581                 assert (prev_seg);
31582                 heap_segment_next (prev_seg) = next_seg;
31583                 heap_segment_next (seg) = freeable_large_heap_segment;
31584                 freeable_large_heap_segment = seg;
31585             }
31586             else
31587             {
31588                 if (!heap_segment_read_only_p (seg))
31589                 {
31590                     dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
31591                     heap_segment_allocated (seg) = plug_end;
31592                     decommit_heap_segment_pages (seg, 0);
31593                 }
31594                 prev_seg = seg;
31595             }
31596             seg = next_seg;
31597             if (seg == 0)
31598                 break;
31599             else
31600             {
31601                 o = heap_segment_mem (seg);
31602                 plug_end = o;
31603                 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
31604                              (size_t)heap_segment_mem (seg),
31605                              (size_t)heap_segment_allocated (seg)));
31606             }
31607         }
31608         if (large_object_marked(o, TRUE))
31609         {
31610             plug_start = o;
31611             //everything between plug_end and plug_start is free
31612             thread_gap (plug_end, plug_start-plug_end, gen);
31613 
31614             BOOL m = TRUE;
31615             while (m)
31616             {
31617                 o = o + AlignQword (size (o));
31618                 if (o >= heap_segment_allocated (seg))
31619                 {
31620                     break;
31621                 }
31622                 m = large_object_marked (o, TRUE);
31623             }
31624             plug_end = o;
31625             dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31626         }
31627         else
31628         {
31629             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
31630             {
31631                 o = o + AlignQword (size (o));
31632             }
31633         }
31634     }
31635 
31636     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31637 
31638     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31639 }
31640 
31641 void gc_heap::relocate_in_large_objects ()
31642 {
31643     relocate_args args;
31644     args.low = gc_low;
31645     args.high = gc_high;
31646     args.last_plug = 0;
31647 
31648     generation* gen = large_object_generation;
31649 
31650     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
31651 
31652     PREFIX_ASSUME(seg != NULL);
31653 
31654     uint8_t* o = generation_allocation_start (gen);
31655 
31656     while (1)
31657     {
31658         if (o >= heap_segment_allocated (seg))
31659         {
31660             seg = heap_segment_next_rw (seg);
31661             if (seg == 0)
31662                 break;
31663             else
31664             {
31665                 o = heap_segment_mem (seg);
31666             }
31667         }
31668         while (o < heap_segment_allocated (seg))
31669         {
31670             check_class_object_demotion (o);
31671             if (contain_pointers (o))
31672             {
31673                 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
31674                 go_through_object_nostart (method_table (o), o, size(o), pval,
31675                         {
31676                             reloc_survivor_helper (pval);
31677                         });
31678             }
31679             o = o + AlignQword (size (o));
31680         }
31681     }
31682 }
31683 
31684 void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
31685                                                     BOOL relocating)
31686 {
31687     uint8_t*      low               = gc_low;
31688     size_t        end_card          = 0;
31689     generation*   oldest_gen        = generation_of (max_generation+1);
31690     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
31691 
31692     PREFIX_ASSUME(seg != NULL);
31693 
31694     uint8_t*      beg               = generation_allocation_start (oldest_gen);
31695     uint8_t*      end               = heap_segment_allocated (seg);
31696 
31697     size_t  cg_pointers_found = 0;
31698 
31699     size_t  card_word_end = (card_of (align_on_card_word (end)) /
31700                              card_word_width);
31701 
31702     size_t      n_eph             = 0;
31703     size_t      n_gen             = 0;
31704     size_t      n_card_set        = 0;
31705     uint8_t*    next_boundary = (relocating ?
31706                               generation_plan_allocation_start (generation_of (max_generation -1)) :
31707                               ephemeral_low);
31708 
31709     uint8_t*    nhigh         = (relocating ?
31710                               heap_segment_plan_allocated (ephemeral_heap_segment) :
31711                               ephemeral_high);
31712 
31713     BOOL          foundp            = FALSE;
31714     uint8_t*      start_address     = 0;
31715     uint8_t*      limit             = 0;
31716     size_t        card              = card_of (beg);
31717     uint8_t*      o                 = beg;
31718 #ifdef BACKGROUND_GC
31719     BOOL consider_bgc_mark_p        = FALSE;
31720     BOOL check_current_sweep_p      = FALSE;
31721     BOOL check_saved_sweep_p        = FALSE;
31722     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31723 #endif //BACKGROUND_GC
31724 
31725     size_t total_cards_cleared = 0;
31726 
31727     //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
31728     dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
31729     while (1)
31730     {
31731         if ((o < end) && (card_of(o) > card))
31732         {
31733             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
31734             if (cg_pointers_found == 0)
31735             {
31736                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
31737                 clear_cards (card, card_of((uint8_t*)o));
31738                 total_cards_cleared += (card_of((uint8_t*)o) - card);
31739             }
31740             n_eph +=cg_pointers_found;
31741             cg_pointers_found = 0;
31742             card = card_of ((uint8_t*)o);
31743         }
31744         if ((o < end) &&(card >= end_card))
31745         {
31746             foundp = find_card (card_table, card, card_word_end, end_card);
31747             if (foundp)
31748             {
31749                 n_card_set+= end_card - card;
31750                 start_address = max (beg, card_address (card));
31751             }
31752             limit = min (end, card_address (end_card));
31753         }
31754         if ((!foundp) || (o >= end) || (card_address (card) >= end))
31755         {
31756             if ((foundp) && (cg_pointers_found == 0))
31757             {
31758                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
31759                            (size_t)card_address(card+1)));
31760                 clear_cards (card, card+1);
31761                 total_cards_cleared += 1;
31762             }
31763             n_eph +=cg_pointers_found;
31764             cg_pointers_found = 0;
31765             if ((seg = heap_segment_next_rw (seg)) != 0)
31766             {
31767 #ifdef BACKGROUND_GC
31768                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
31769 #endif //BACKGROUND_GC
31770                 beg = heap_segment_mem (seg);
31771                 end = compute_next_end (seg, low);
31772                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
31773                 card = card_of (beg);
31774                 o  = beg;
31775                 end_card = 0;
31776                 continue;
31777             }
31778             else
31779             {
31780                 break;
31781             }
31782         }
31783 
31784         assert (card_set_p (card));
31785         {
31786             dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
31787                        card, (size_t)o, (size_t)limit));
31788 
31789             assert (Align (size (o)) >= Align (min_obj_size));
31790             size_t s = size (o);
31791             uint8_t* next_o =  o + AlignQword (s);
31792             Prefetch (next_o);
31793 
31794             while (o < limit)
31795             {
31796                 s = size (o);
31797                 assert (Align (s) >= Align (min_obj_size));
31798                 next_o =  o + AlignQword (s);
31799                 Prefetch (next_o);
31800 
31801                 dprintf (4, ("|%Ix|", (size_t)o));
31802                 if (next_o < start_address)
31803                 {
31804                     goto end_object;
31805                 }
31806 
31807 #ifdef BACKGROUND_GC
31808                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
31809                 {
31810                     goto end_object;
31811                 }
31812 #endif //BACKGROUND_GC
31813 
31814 #ifdef COLLECTIBLE_CLASS
31815                 if (is_collectible(o))
31816                 {
31817                     BOOL passed_end_card_p = FALSE;
31818 
31819                     if (card_of (o) > card)
31820                     {
31821                         passed_end_card_p = card_transition (o, end, card_word_end,
31822                             cg_pointers_found,
31823                             n_eph, n_card_set,
31824                             card, end_card,
31825                             foundp, start_address,
31826                             limit, total_cards_cleared);
31827                     }
31828 
31829                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
31830                     {
31831                         // card is valid and it covers the head of the object
31832                         if (fn == &gc_heap::relocate_address)
31833                         {
31834                             keep_card_live (o, n_gen, cg_pointers_found);
31835                         }
31836                         else
31837                         {
31838                             uint8_t* class_obj = get_class_object (o);
31839                             mark_through_cards_helper (&class_obj, n_gen,
31840                                                     cg_pointers_found, fn,
31841                                                     nhigh, next_boundary);
31842                         }
31843                     }
31844 
31845                     if (passed_end_card_p)
31846                     {
31847                         if (foundp && (card_address (card) < next_o))
31848                         {
31849                             goto go_through_refs;
31850                         }
31851                         else
31852                         {
31853                             goto end_object;
31854                         }
31855                     }
31856                 }
31857 
31858 go_through_refs:
31859 #endif //COLLECTIBLE_CLASS
31860 
31861                 if (contain_pointers (o))
31862                 {
31863                     dprintf(3,("Going through %Ix", (size_t)o));
31864 
31865                     go_through_object (method_table(o), o, s, poo,
31866                                        start_address, use_start, (o + s),
31867                        {
31868                            if (card_of ((uint8_t*)poo) > card)
31869                            {
31870                                 BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
31871                                         card_word_end,
31872                                         cg_pointers_found,
31873                                         n_eph, n_card_set,
31874                                         card, end_card,
31875                                         foundp, start_address,
31876                                         limit, total_cards_cleared);
31877 
31878                                 if (passed_end_card_p)
31879                                 {
31880                                     if (foundp && (card_address (card) < next_o))
31881                                     {
31882                                         //new_start();
31883                                         {
31884                                             if (ppstop <= (uint8_t**)start_address)
31885                                             {break;}
31886                                             else if (poo < (uint8_t**)start_address)
31887                                             {poo = (uint8_t**)start_address;}
31888                                         }
31889                                     }
31890                                     else
31891                                     {
31892                                         goto end_object;
31893                                     }
31894                                 }
31895                             }
31896 
31897                            mark_through_cards_helper (poo, n_gen,
31898                                                       cg_pointers_found, fn,
31899                                                       nhigh, next_boundary);
31900                        }
31901                         );
31902                 }
31903 
31904             end_object:
31905                 o = next_o;
31906             }
31907 
31908         }
31909     }
31910 
31911     // compute the efficiency ratio of the card table
31912     if (!relocating)
31913     {
31914         generation_skip_ratio = min (((n_eph > 800) ?
31915                                       (int)(((float)n_gen / (float)n_eph) * 100) : 100),
31916                                      generation_skip_ratio);
31917 
31918         dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d",
31919              n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
31920     }
31921     else
31922     {
31923         dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d",
31924              n_eph, n_gen, n_card_set, generation_skip_ratio));
31925     }
31926 }
31927 
31928 void gc_heap::descr_segment (heap_segment* seg )
31929 {
31930 #ifdef TRACE_GC
31931     uint8_t*  x = heap_segment_mem (seg);
31932     while (x < heap_segment_allocated (seg))
31933     {
31934         dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
31935         x = x + Align(size (x));
31936     }
31937 #else // TRACE_GC
31938     UNREFERENCED_PARAMETER(seg);
31939 #endif // TRACE_GC
31940 }
31941 
31942 void gc_heap::descr_card_table ()
31943 {
31944 #ifdef TRACE_GC
31945     if (trace_gc && (print_level >= 4))
31946     {
31947         ptrdiff_t  min = -1;
31948         dprintf(3,("Card Table set at: "));
31949         for (size_t i = card_of (lowest_address); i < card_of (highest_address); i++)
31950         {
31951             if (card_set_p (i))
31952             {
31953                 if (min == -1)
31954                 {
31955                     min = i;
31956                 }
31957             }
31958             else
31959             {
31960                 if (! ((min == -1)))
31961                 {
31962                     dprintf (3,("[%Ix %Ix[, ",
31963                             (size_t)card_address (min), (size_t)card_address (i)));
31964                     min = -1;
31965                 }
31966             }
31967         }
31968     }
31969 #endif //TRACE_GC
31970 }
31971 
31972 void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
31973 {
31974 #ifdef MULTIPLE_HEAPS
31975     int n_heaps = g_theGCHeap->GetNumberOfHeaps ();
31976     for (int i = 0; i < n_heaps; i++)
31977     {
31978         gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
31979 #else //MULTIPLE_HEAPS
31980     {
31981         gc_heap* hp = NULL;
31982 #ifdef _PREFAST_
31983         // prefix complains about us dereferencing hp in wks build even though we only access static members
31984         // this way. not sure how to shut it up except for this ugly workaround:
31985         PREFIX_ASSUME(hp != NULL);
31986 #endif // _PREFAST_
31987 #endif //MULTIPLE_HEAPS
31988 
31989         int curr_gen_number0 = max_generation+1;
31990         while (curr_gen_number0 >= 0)
31991         {
31992             generation* gen = hp->generation_of (curr_gen_number0);
31993             heap_segment* seg = generation_start_segment (gen);
31994             while (seg && (seg != hp->ephemeral_heap_segment))
31995             {
31996                 assert (curr_gen_number0 > 0);
31997 
31998                 // report bounds from heap_segment_mem (seg) to
31999                 // heap_segment_allocated (seg);
32000                 // for generation # curr_gen_number0
32001                 // for heap # heap_no
32002 
32003                 fn(context, curr_gen_number0, heap_segment_mem (seg),
32004                                               heap_segment_allocated (seg),
32005                                               curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
32006 
32007                 seg = heap_segment_next (seg);
32008             }
32009             if (seg)
32010             {
32011                 assert (seg == hp->ephemeral_heap_segment);
32012                 assert (curr_gen_number0 <= max_generation);
32013                 //
32014                 if (curr_gen_number0 == max_generation)
32015                 {
32016                     if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
32017                     {
32018                         // report bounds from heap_segment_mem (seg) to
32019                         // generation_allocation_start (generation_of (max_generation-1))
32020                         // for heap # heap_number
32021 
32022                         fn(context, curr_gen_number0, heap_segment_mem (seg),
32023                                                       generation_allocation_start (hp->generation_of (max_generation-1)),
32024                                                       generation_allocation_start (hp->generation_of (max_generation-1)) );
32025                     }
32026                 }
32027                 else if (curr_gen_number0 != 0)
32028                 {
32029                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32030                     // to generation_allocation_start (generation_of (curr_gen_number0-1))
32031                     // for heap # heap_number
32032 
32033                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32034                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
32035                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
32036                 }
32037                 else
32038                 {
32039                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32040                     // to heap_segment_allocated (ephemeral_heap_segment);
32041                     // for heap # heap_number
32042 
32043                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32044                                                   heap_segment_allocated (hp->ephemeral_heap_segment),
32045                                                   heap_segment_reserved (hp->ephemeral_heap_segment) );
32046                 }
32047             }
32048             curr_gen_number0--;
32049         }
32050     }
32051 }
32052 
32053 #ifdef TRACE_GC
32054 // Note that when logging is on it can take a long time to go through the free items.
32055 void gc_heap::print_free_list (int gen, heap_segment* seg)
32056 {
32057     UNREFERENCED_PARAMETER(gen);
32058     UNREFERENCED_PARAMETER(seg);
32059 /*
32060     if (settings.concurrent == FALSE)
32061     {
32062         uint8_t* seg_start = heap_segment_mem (seg);
32063         uint8_t* seg_end = heap_segment_allocated (seg);
32064 
32065         dprintf (3, ("Free list in seg %Ix:", seg_start));
32066 
32067         size_t total_free_item = 0;
32068 
32069         allocator* gen_allocator = generation_allocator (generation_of (gen));
32070         for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
32071         {
32072             uint8_t* fo = gen_allocator->alloc_list_head_of (b);
32073             while (fo)
32074             {
32075                 if (fo >= seg_start && fo < seg_end)
32076                 {
32077                     total_free_item++;
32078 
32079                     size_t free_item_len = size(fo);
32080 
32081                     dprintf (3, ("[%Ix, %Ix[:%Id",
32082                                  (size_t)fo,
32083                                  (size_t)(fo + free_item_len),
32084                                  free_item_len));
32085                 }
32086 
32087                 fo = free_list_slot (fo);
32088             }
32089         }
32090 
32091         dprintf (3, ("total %Id free items", total_free_item));
32092     }
32093 */
32094 }
32095 #endif //TRACE_GC
32096 
32097 void gc_heap::descr_generations (BOOL begin_gc_p)
32098 {
32099     UNREFERENCED_PARAMETER(begin_gc_p);
32100 #ifdef STRESS_LOG
32101     if (StressLog::StressLogOn(LF_GC, LL_INFO10))
32102     {
32103         gc_heap* hp = 0;
32104 #ifdef MULTIPLE_HEAPS
32105         hp= this;
32106 #endif //MULTIPLE_HEAPS
32107 
32108         STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
32109         for (int n = max_generation; n >= 0; --n)
32110         {
32111             STRESS_LOG4(LF_GC, LL_INFO10, "    Generation %d [%p, %p] cur = %p\n",
32112                     n,
32113                     generation_allocation_start(generation_of(n)),
32114                     generation_allocation_limit(generation_of(n)),
32115                     generation_allocation_pointer(generation_of(n)));
32116 
32117             heap_segment* seg = generation_start_segment(generation_of(n));
32118             while (seg)
32119             {
32120                 STRESS_LOG4(LF_GC, LL_INFO10, "        Segment mem %p alloc = %p used %p committed %p\n",
32121                         heap_segment_mem(seg),
32122                         heap_segment_allocated(seg),
32123                         heap_segment_used(seg),
32124                         heap_segment_committed(seg));
32125                 seg = heap_segment_next(seg);
32126             }
32127         }
32128     }
32129 #endif  // STRESS_LOG
32130 
32131 #ifdef TRACE_GC
32132     dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
32133              (size_t) lowest_address, (size_t) highest_address));
32134 #ifdef BACKGROUND_GC
32135     dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
32136              (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
32137 #endif //BACKGROUND_GC
32138 
32139     if (heap_number == 0)
32140     {
32141         dprintf (1, ("total heap size: %Id, commit size: %Id", get_total_heap_size(), get_total_committed_size()));
32142     }
32143 
32144     int curr_gen_number = max_generation+1;
32145     while (curr_gen_number >= 0)
32146     {
32147         size_t total_gen_size = generation_size (curr_gen_number);
32148 #ifdef SIMPLE_DPRINTF
32149         dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
32150                       (begin_gc_p ? "BEG" : "END"),
32151                       settings.condemned_generation,
32152                       curr_gen_number,
32153                       total_gen_size,
32154                       dd_fragmentation (dynamic_data_of (curr_gen_number)),
32155                       generation_free_list_space (generation_of (curr_gen_number)),
32156                       generation_free_obj_space (generation_of (curr_gen_number)),
32157                       (total_gen_size ?
32158                         (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
32159                         0),
32160                       (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
32161                       (settings.heap_expansion ? "(EX)" : " "),
32162                       (settings.promotion ? "Promotion" : "NoPromotion")));
32163 #else
32164         dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
32165                       curr_gen_number,
32166                       size (generation_allocation_start (generation_of (curr_gen_number))),
32167                       total_gen_size,
32168                       dd_fragmentation (dynamic_data_of (curr_gen_number))));
32169 #endif //SIMPLE_DPRINTF
32170 
32171         generation* gen = generation_of (curr_gen_number);
32172         heap_segment* seg = generation_start_segment (gen);
32173         while (seg && (seg != ephemeral_heap_segment))
32174         {
32175             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
32176                         curr_gen_number,
32177                         (size_t)heap_segment_mem (seg),
32178                         (size_t)heap_segment_allocated (seg),
32179                         (size_t)heap_segment_committed (seg),
32180                         (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
32181                         (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
32182             print_free_list (curr_gen_number, seg);
32183             seg = heap_segment_next (seg);
32184         }
32185         if (seg && (seg != generation_start_segment (gen)))
32186         {
32187             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32188                          curr_gen_number,
32189                          (size_t)heap_segment_mem (seg),
32190                          (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
32191             print_free_list (curr_gen_number, seg);
32192 
32193         }
32194         else if (seg)
32195         {
32196             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32197                          curr_gen_number,
32198                          (size_t)generation_allocation_start (generation_of (curr_gen_number)),
32199                          (size_t)(((curr_gen_number == 0)) ?
32200                                   (heap_segment_allocated
32201                                    (generation_start_segment
32202                                     (generation_of (curr_gen_number)))) :
32203                                   (generation_allocation_start
32204                                    (generation_of (curr_gen_number - 1))))
32205                          ));
32206             print_free_list (curr_gen_number, seg);
32207         }
32208         curr_gen_number--;
32209     }
32210 
32211 #endif //TRACE_GC
32212 }
32213 
32214 #undef TRACE_GC
32215 
32216 //#define TRACE_GC
32217 
32218 //-----------------------------------------------------------------------------
32219 //
32220 //                                  VM Specific support
32221 //
32222 //-----------------------------------------------------------------------------
32223 
32224 
32225 #ifdef TRACE_GC
32226 
32227  unsigned int PromotedObjectCount  = 0;
32228  unsigned int CreatedObjectCount       = 0;
32229  unsigned int AllocDuration            = 0;
32230  unsigned int AllocCount               = 0;
32231  unsigned int AllocBigCount            = 0;
32232  unsigned int AllocSmallCount      = 0;
32233  unsigned int AllocStart             = 0;
32234 #endif //TRACE_GC
32235 
32236 //Static member variables.
32237 VOLATILE(BOOL)    GCHeap::GcInProgress            = FALSE;
32238 //GCTODO
32239 //CMCSafeLock*      GCHeap::fGcLock;
32240 CLREvent            *GCHeap::WaitForGCEvent         = NULL;
32241 //GCTODO
32242 #ifdef TRACE_GC
32243 unsigned int       GCHeap::GcDuration;
32244 #endif //TRACE_GC
32245 unsigned            GCHeap::GcCondemnedGeneration   = 0;
32246 size_t              GCHeap::totalSurvivedSize       = 0;
32247 #ifdef FEATURE_PREMORTEM_FINALIZATION
32248 CFinalize*          GCHeap::m_Finalize              = 0;
32249 BOOL                GCHeap::GcCollectClasses        = FALSE;
32250 VOLATILE(int32_t)      GCHeap::m_GCFLock               = 0;
32251 
32252 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32253 #ifdef STRESS_HEAP
32254 #ifdef BACKGROUND_GC
32255 int                 GCHeap::gc_stress_fgcs_in_bgc   = 0;
32256 #endif // BACKGROUND_GC
32257 #ifndef MULTIPLE_HEAPS
32258 OBJECTHANDLE        GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
32259 int                 GCHeap::m_CurStressObj          = 0;
32260 #endif // !MULTIPLE_HEAPS
32261 #endif // STRESS_HEAP
32262 #endif // FEATURE_REDHAWK
32263 
32264 #endif //FEATURE_PREMORTEM_FINALIZATION
32265 inline
32266 static void spin_lock ()
32267 {
32268     enter_spin_lock_noinstru (&m_GCLock);
32269 }
32270 
32271 inline
32272 void EnterAllocLock()
32273 {
32274 #if defined(_TARGET_X86_)
32275     __asm {
32276         inc dword ptr m_GCLock
32277         jz gotit
32278         call spin_lock
32279             gotit:
32280     }
32281 #else //_TARGET_X86_
32282     spin_lock();
32283 #endif //_TARGET_X86_
32284 }
32285 
32286 inline
32287 void LeaveAllocLock()
32288 {
32289     // Trick this out
32290     leave_spin_lock_noinstru (&m_GCLock);
32291 }
32292 
32293 class AllocLockHolder
32294 {
32295 public:
32296     AllocLockHolder()
32297     {
32298         EnterAllocLock();
32299     }
32300 
32301     ~AllocLockHolder()
32302     {
32303         LeaveAllocLock();
32304     }
32305 };
32306 
32307 // An explanation of locking for finalization:
32308 //
32309 // Multiple threads allocate objects.  During the allocation, they are serialized by
32310 // the AllocLock above.  But they release that lock before they register the object
32311 // for finalization.  That's because there is much contention for the alloc lock, but
32312 // finalization is presumed to be a rare case.
32313 //
32314 // So registering an object for finalization must be protected by the FinalizeLock.
32315 //
32316 // There is another logical queue that involves finalization.  When objects registered
32317 // for finalization become unreachable, they are moved from the "registered" queue to
32318 // the "unreachable" queue.  Note that this only happens inside a GC, so no other
32319 // threads can be manipulating either queue at that time.  Once the GC is over and
32320 // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
32321 // queue and call their finalizers.  This dequeue operation is also protected with
32322 // the finalize lock.
32323 //
32324 // At first, this seems unnecessary.  Only one thread is ever enqueuing or dequeuing
32325 // on the unreachable queue (either the GC thread during a GC or the finalizer thread
32326 // when a GC is not in progress).  The reason we share a lock with threads enqueuing
32327 // on the "registered" queue is that the "registered" and "unreachable" queues are
32328 // interrelated.
32329 //
32330 // They are actually two regions of a longer list, which can only grow at one end.
32331 // So to enqueue an object to the "registered" list, you actually rotate an unreachable
32332 // object at the boundary between the logical queues, out to the other end of the
32333 // unreachable queue -- where all growing takes place.  Then you move the boundary
32334 // pointer so that the gap we created at the boundary is now on the "registered"
32335 // side rather than the "unreachable" side.  Now the object can be placed into the
32336 // "registered" side at that point.  This is much more efficient than doing moves
32337 // of arbitrarily long regions, but it causes the two queues to require a shared lock.
32338 //
32339 // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock.  Instead, it relies
32340 // on the fact that the lock will only be taken for a brief period and that it will
32341 // never provoke or allow a GC while the lock is held.  This is critical.  If the
32342 // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
32343 // allow a GC), then the Alloc client would have to GC protect a finalizable object
32344 // to protect against that eventuality.  That is too slow!
32345 
32346 
32347 
32348 BOOL IsValidObject99(uint8_t *pObject)
32349 {
32350 #ifdef VERIFY_HEAP
32351     if (!((CObjectHeader*)pObject)->IsFree())
32352         ((CObjectHeader *) pObject)->Validate();
32353 #endif //VERIFY_HEAP
32354     return(TRUE);
32355 }
32356 
32357 #ifdef BACKGROUND_GC
32358 BOOL gc_heap::bgc_mark_array_range (heap_segment* seg,
32359                                     BOOL whole_seg_p,
32360                                     uint8_t** range_beg,
32361                                     uint8_t** range_end)
32362 {
32363     uint8_t* seg_start = heap_segment_mem (seg);
32364     uint8_t* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
32365 
32366     if ((seg_start < background_saved_highest_address) &&
32367         (seg_end > background_saved_lowest_address))
32368     {
32369         *range_beg = max (seg_start, background_saved_lowest_address);
32370         *range_end = min (seg_end, background_saved_highest_address);
32371         return TRUE;
32372     }
32373     else
32374     {
32375         return FALSE;
32376     }
32377 }
32378 
32379 void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
32380 {
32381 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32382     if (recursive_gc_sync::background_running_p() && g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
32383     {
32384         uint8_t* range_beg = 0;
32385         uint8_t* range_end = 0;
32386 
32387         if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
32388         {
32389             size_t  markw = mark_word_of (range_beg);
32390             size_t  markw_end = mark_word_of (range_end);
32391             while (markw < markw_end)
32392             {
32393                 if (mark_array [markw])
32394                 {
32395                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
32396                                     markw, mark_array [markw], mark_word_address (markw)));
32397                     FATAL_GC_ERROR();
32398                 }
32399                 markw++;
32400             }
32401             uint8_t* p = mark_word_address (markw_end);
32402             while (p < range_end)
32403             {
32404                 assert (!(mark_array_marked (p)));
32405                 p++;
32406             }
32407         }
32408     }
32409 #endif //VERIFY_HEAP && MARK_ARRAY
32410 }
32411 
32412 void gc_heap::verify_mark_bits_cleared (uint8_t* obj, size_t s)
32413 {
32414 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32415     size_t start_mark_bit = mark_bit_of (obj) + 1;
32416     size_t end_mark_bit = mark_bit_of (obj + s);
32417     unsigned int startbit = mark_bit_bit (start_mark_bit);
32418     unsigned int endbit = mark_bit_bit (end_mark_bit);
32419     size_t startwrd = mark_bit_word (start_mark_bit);
32420     size_t endwrd = mark_bit_word (end_mark_bit);
32421     unsigned int result = 0;
32422 
32423     unsigned int firstwrd = ~(lowbits (~0, startbit));
32424     unsigned int lastwrd = ~(highbits (~0, endbit));
32425 
32426     if (startwrd == endwrd)
32427     {
32428         unsigned int wrd = firstwrd & lastwrd;
32429         result = mark_array[startwrd] & wrd;
32430         if (result)
32431         {
32432             FATAL_GC_ERROR();
32433         }
32434         return;
32435     }
32436 
32437     // verify the first mark word is cleared.
32438     if (startbit)
32439     {
32440         result = mark_array[startwrd] & firstwrd;
32441         if (result)
32442         {
32443             FATAL_GC_ERROR();
32444         }
32445         startwrd++;
32446     }
32447 
32448     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
32449     {
32450         result = mark_array[wrdtmp];
32451         if (result)
32452         {
32453             FATAL_GC_ERROR();
32454         }
32455     }
32456 
32457     // set the last mark word.
32458     if (endbit)
32459     {
32460         result = mark_array[endwrd] & lastwrd;
32461         if (result)
32462         {
32463             FATAL_GC_ERROR();
32464         }
32465     }
32466 #endif //VERIFY_HEAP && MARK_ARRAY
32467 }
32468 
32469 void gc_heap::clear_all_mark_array()
32470 {
32471 #ifdef MARK_ARRAY
32472     //size_t num_dwords_written = 0;
32473     //size_t begin_time = GetHighPrecisionTimeStamp();
32474 
32475     generation* gen = generation_of (max_generation);
32476     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32477 
32478     while (1)
32479     {
32480         if (seg == 0)
32481         {
32482             if (gen != large_object_generation)
32483             {
32484                 gen = generation_of (max_generation+1);
32485                 seg = heap_segment_rw (generation_start_segment (gen));
32486             }
32487             else
32488             {
32489                 break;
32490             }
32491         }
32492 
32493         uint8_t* range_beg = 0;
32494         uint8_t* range_end = 0;
32495 
32496         if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
32497         {
32498             size_t markw = mark_word_of (range_beg);
32499             size_t markw_end = mark_word_of (range_end);
32500             size_t size_total = (markw_end - markw) * sizeof (uint32_t);
32501             //num_dwords_written = markw_end - markw;
32502             size_t size = 0;
32503             size_t size_left = 0;
32504 
32505             assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
32506 
32507             if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
32508             {
32509                 size = (size_total & ~(sizeof(PTR_PTR) - 1));
32510                 size_left = size_total - size;
32511                 assert ((size_left & (sizeof (uint32_t) - 1)) == 0);
32512             }
32513             else
32514             {
32515                 size = size_total;
32516             }
32517 
32518             memclr ((uint8_t*)&mark_array[markw], size);
32519 
32520             if (size_left != 0)
32521             {
32522                 uint32_t* markw_to_clear = &mark_array[markw + size / sizeof (uint32_t)];
32523                 for (size_t i = 0; i < (size_left / sizeof (uint32_t)); i++)
32524                 {
32525                     *markw_to_clear = 0;
32526                     markw_to_clear++;
32527                 }
32528             }
32529         }
32530 
32531         seg = heap_segment_next_rw (seg);
32532     }
32533 
32534     //size_t end_time = GetHighPrecisionTimeStamp() - begin_time;
32535 
32536     //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(uint32_t));
32537 
32538 #endif //MARK_ARRAY
32539 }
32540 
32541 #endif //BACKGROUND_GC
32542 
32543 void gc_heap::verify_mark_array_cleared (heap_segment* seg)
32544 {
32545 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32546     assert (card_table == g_gc_card_table);
32547     size_t  markw = mark_word_of (heap_segment_mem (seg));
32548     size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
32549 
32550     while (markw < markw_end)
32551     {
32552         if (mark_array [markw])
32553         {
32554             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
32555                             markw, mark_array [markw], mark_word_address (markw)));
32556             FATAL_GC_ERROR();
32557         }
32558         markw++;
32559     }
32560 #endif //VERIFY_HEAP && MARK_ARRAY
32561 }
32562 
32563 void gc_heap::verify_mark_array_cleared ()
32564 {
32565 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32566     if (recursive_gc_sync::background_running_p() && g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
32567     {
32568         generation* gen = generation_of (max_generation);
32569         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32570 
32571         while (1)
32572         {
32573             if (seg == 0)
32574             {
32575                 if (gen != large_object_generation)
32576                 {
32577                     gen = generation_of (max_generation+1);
32578                     seg = heap_segment_rw (generation_start_segment (gen));
32579                 }
32580                 else
32581                 {
32582                     break;
32583                 }
32584             }
32585 
32586             bgc_verify_mark_array_cleared (seg);
32587             seg = heap_segment_next_rw (seg);
32588         }
32589     }
32590 #endif //VERIFY_HEAP && MARK_ARRAY
32591 }
32592 
32593 void gc_heap::verify_seg_end_mark_array_cleared()
32594 {
32595 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32596     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
32597     {
32598         generation* gen = generation_of (max_generation);
32599         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32600 
32601         while (1)
32602         {
32603             if (seg == 0)
32604             {
32605                 if (gen != large_object_generation)
32606                 {
32607                     gen = generation_of (max_generation+1);
32608                     seg = heap_segment_rw (generation_start_segment (gen));
32609                 }
32610                 else
32611                 {
32612                     break;
32613                 }
32614             }
32615 
32616             // We already cleared all mark array bits for ephemeral generations
32617             // at the beginning of bgc sweep
32618             uint8_t* from = ((seg == ephemeral_heap_segment) ?
32619                           generation_allocation_start (generation_of (max_generation - 1)) :
32620                           heap_segment_allocated (seg));
32621             size_t  markw = mark_word_of (align_on_mark_word (from));
32622             size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
32623 
32624             while (from < mark_word_address (markw))
32625             {
32626                 if (is_mark_bit_set (from))
32627                 {
32628                     dprintf (3, ("mark bit for %Ix was not cleared", from));
32629                     FATAL_GC_ERROR();
32630                 }
32631 
32632                 from += mark_bit_pitch;
32633             }
32634 
32635             while (markw < markw_end)
32636             {
32637                 if (mark_array [markw])
32638                 {
32639                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
32640                                     markw, mark_array [markw], mark_word_address (markw)));
32641                     FATAL_GC_ERROR();
32642                 }
32643                 markw++;
32644             }
32645             seg = heap_segment_next_rw (seg);
32646         }
32647     }
32648 #endif //VERIFY_HEAP && MARK_ARRAY
32649 }
32650 
32651 // This function is called to make sure we don't mess up the segment list
32652 // in SOH. It's called by:
32653 // 1) begin and end of ephemeral GCs
32654 // 2) during bgc sweep when we switch segments.
32655 void gc_heap::verify_soh_segment_list()
32656 {
32657 #ifdef VERIFY_HEAP
32658     if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_GC)
32659     {
32660         generation* gen = generation_of (max_generation);
32661         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32662         heap_segment* last_seg = 0;
32663         while (seg)
32664         {
32665             last_seg = seg;
32666             seg = heap_segment_next_rw (seg);
32667         }
32668         if (last_seg != ephemeral_heap_segment)
32669         {
32670             FATAL_GC_ERROR();
32671         }
32672     }
32673 #endif //VERIFY_HEAP
32674 }
32675 
32676 // This function can be called at any foreground GCs or blocking GCs. For background GCs,
32677 // it can be called at the end of the final marking; and at any point during background
32678 // sweep.
32679 // NOTE - to be able to call this function during background sweep, we need to temporarily
32680 // NOT clear the mark array bits as we go.
32681 void gc_heap::verify_partial ()
32682 {
32683 #ifdef BACKGROUND_GC
32684     //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
32685     //generation* gen = large_object_generation;
32686     generation* gen = generation_of (max_generation);
32687     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32688     int align_const = get_alignment_constant (gen != large_object_generation);
32689 
32690     uint8_t* o = 0;
32691     uint8_t* end = 0;
32692     size_t s = 0;
32693 
32694     // Different ways to fail.
32695     BOOL mark_missed_p = FALSE;
32696     BOOL bad_ref_p = FALSE;
32697     BOOL free_ref_p = FALSE;
32698 
32699     while (1)
32700     {
32701         if (seg == 0)
32702         {
32703             if (gen != large_object_generation)
32704             {
32705                 //switch to LOH
32706                 gen = large_object_generation;
32707                 align_const = get_alignment_constant (gen != large_object_generation);
32708                 seg = heap_segment_rw (generation_start_segment (gen));
32709                 continue;
32710             }
32711             else
32712             {
32713                 break;
32714             }
32715         }
32716 
32717         o = heap_segment_mem (seg);
32718         end  = heap_segment_allocated (seg);
32719         //printf ("validating [%Ix-[%Ix\n", o, end);
32720         while (o < end)
32721         {
32722             s = size (o);
32723 
32724             BOOL marked_p = background_object_marked (o, FALSE);
32725 
32726             if (marked_p)
32727             {
32728                 go_through_object_cl (method_table (o), o, s, oo,
32729                     {
32730                         if (*oo)
32731                         {
32732                             //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
32733                             MethodTable *pMT = method_table (*oo);
32734 
32735                             if (pMT == g_pFreeObjectMethodTable)
32736                             {
32737                                 free_ref_p = TRUE;
32738                                 FATAL_GC_ERROR();
32739                             }
32740 
32741                             if (!pMT->SanityCheck())
32742                             {
32743                                 bad_ref_p = TRUE;
32744                                 dprintf (3, ("Bad member of %Ix %Ix",
32745                                             (size_t)oo, (size_t)*oo));
32746                                 FATAL_GC_ERROR();
32747                             }
32748 
32749                             if (current_bgc_state == bgc_final_marking)
32750                             {
32751                                 if (marked_p && !background_object_marked (*oo, FALSE))
32752                                 {
32753                                     mark_missed_p = TRUE;
32754                                     FATAL_GC_ERROR();
32755                                 }
32756                             }
32757                         }
32758                     }
32759                                     );
32760             }
32761 
32762             o = o + Align(s, align_const);
32763         }
32764         seg = heap_segment_next_rw (seg);
32765     }
32766 
32767     //printf ("didn't find any large object large enough...\n");
32768     //printf ("finished verifying loh\n");
32769 #endif //BACKGROUND_GC
32770 }
32771 
32772 #ifdef VERIFY_HEAP
32773 
32774 void
32775 gc_heap::verify_free_lists ()
32776 {
32777     for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
32778     {
32779         dprintf (3, ("Verifying free list for gen:%d", gen_num));
32780         allocator* gen_alloc = generation_allocator (generation_of (gen_num));
32781         size_t sz = gen_alloc->first_bucket_size();
32782         bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
32783 
32784         for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
32785         {
32786             uint8_t* free_list = gen_alloc->alloc_list_head_of (a_l_number);
32787             uint8_t* prev = 0;
32788             while (free_list)
32789             {
32790                 if (!((CObjectHeader*)free_list)->IsFree())
32791                 {
32792                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
32793                                  (size_t)free_list));
32794                     FATAL_GC_ERROR();
32795                 }
32796                 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
32797                     || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
32798                 {
32799                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
32800                                  (size_t)free_list));
32801                     FATAL_GC_ERROR();
32802                 }
32803                 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
32804                 {
32805                     dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
32806                                  (size_t)free_list));
32807                     FATAL_GC_ERROR();
32808                 }
32809                 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
32810                 {
32811                     dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
32812                                  (size_t)free_list));
32813                     FATAL_GC_ERROR();
32814                 }
32815 
32816                 prev = free_list;
32817                 free_list = free_list_slot (free_list);
32818             }
32819             //verify the sanity of the tail
32820             uint8_t* tail = gen_alloc->alloc_list_tail_of (a_l_number);
32821             if (!((tail == 0) || (tail == prev)))
32822             {
32823                 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
32824                 FATAL_GC_ERROR();
32825             }
32826             if (tail == 0)
32827             {
32828                 uint8_t* head = gen_alloc->alloc_list_head_of (a_l_number);
32829                 if ((head != 0) && (free_list_slot (head) != 0))
32830                 {
32831                     dprintf (3, ("Verifying Heap: tail of free list is not correct"));
32832                     FATAL_GC_ERROR();
32833                 }
32834             }
32835 
32836             sz *=2;
32837         }
32838     }
32839 }
32840 
32841 void
32842 gc_heap::verify_heap (BOOL begin_gc_p)
32843 {
32844     int             heap_verify_level = g_pConfig->GetHeapVerifyLevel();
32845     size_t          last_valid_brick = 0;
32846     BOOL            bCurrentBrickInvalid = FALSE;
32847     BOOL            large_brick_p = TRUE;
32848     size_t          curr_brick = 0;
32849     size_t          prev_brick = (size_t)-1;
32850     int             curr_gen_num = max_generation+1;
32851     heap_segment*   seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
32852 
32853     PREFIX_ASSUME(seg != NULL);
32854 
32855     uint8_t*        curr_object = heap_segment_mem (seg);
32856     uint8_t*        prev_object = 0;
32857     uint8_t*        begin_youngest = generation_allocation_start(generation_of(0));
32858     uint8_t*        end_youngest = heap_segment_allocated (ephemeral_heap_segment);
32859     uint8_t*        next_boundary = generation_allocation_start (generation_of (max_generation - 1));
32860     int             align_const = get_alignment_constant (FALSE);
32861     size_t          total_objects_verified = 0;
32862     size_t          total_objects_verified_deep = 0;
32863 
32864 #ifdef BACKGROUND_GC
32865     BOOL consider_bgc_mark_p    = FALSE;
32866     BOOL check_current_sweep_p  = FALSE;
32867     BOOL check_saved_sweep_p    = FALSE;
32868     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32869 #endif //BACKGROUND_GC
32870 
32871 #ifdef MULTIPLE_HEAPS
32872     t_join* current_join = &gc_t_join;
32873 #ifdef BACKGROUND_GC
32874     if (settings.concurrent && (bgc_thread_id.IsCurrentThread()))
32875     {
32876         // We always call verify_heap on entry of GC on the SVR GC threads.
32877         current_join = &bgc_t_join;
32878     }
32879 #endif //BACKGROUND_GC
32880 #endif //MULTIPLE_HEAPS
32881 
32882     UNREFERENCED_PARAMETER(begin_gc_p);
32883 #ifdef BACKGROUND_GC
32884     dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin",
32885         (begin_gc_p ? "BEG" : "END"),
32886         VolatileLoad(&settings.gc_index),
32887         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
32888 #else
32889     dprintf (2,("[%s]GC#%d: Verifying heap - begin",
32890                 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
32891 #endif //BACKGROUND_GC
32892 
32893 #ifndef MULTIPLE_HEAPS
32894     if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
32895         (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
32896     {
32897         FATAL_GC_ERROR();
32898     }
32899 #endif //MULTIPLE_HEAPS
32900 
32901 #ifdef BACKGROUND_GC
32902     //don't touch the memory because the program is allocating from it.
32903     if (!settings.concurrent)
32904 #endif //BACKGROUND_GC
32905     {
32906         if (!(heap_verify_level & EEConfig::HEAPVERIFY_NO_MEM_FILL))
32907         {
32908             //uninit the unused portions of segments.
32909             generation* gen1 = large_object_generation;
32910             heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
32911             PREFIX_ASSUME(seg1 != NULL);
32912 
32913             while (1)
32914             {
32915                 if (seg1)
32916                 {
32917                     uint8_t* clear_start = heap_segment_allocated (seg1) - plug_skew;
32918                     if (heap_segment_used (seg1) > clear_start)
32919                     {
32920                         dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa",
32921                                     heap_segment_mem (seg1),
32922                                     clear_start ,
32923                                     heap_segment_used (seg1)));
32924                         memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
32925                             (heap_segment_used (seg1) - clear_start));
32926                     }
32927                     seg1 = heap_segment_next_rw (seg1);
32928                 }
32929                 else
32930                 {
32931                     if (gen1 == large_object_generation)
32932                     {
32933                         gen1 = generation_of (max_generation);
32934                         seg1 = heap_segment_rw (generation_start_segment (gen1));
32935                         PREFIX_ASSUME(seg1 != NULL);
32936                     }
32937                     else
32938                     {
32939                         break;
32940                     }
32941                 }
32942             }
32943         }
32944     }
32945 
32946 #ifdef MULTIPLE_HEAPS
32947     current_join->join(this, gc_join_verify_copy_table);
32948     if (current_join->joined())
32949     {
32950         // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
32951         for (int i = 0; i < n_heaps; i++)
32952         {
32953             //copy the card and brick tables
32954             if (g_gc_card_table != g_heaps[i]->card_table)
32955             {
32956                 g_heaps[i]->copy_brick_card_table();
32957             }
32958         }
32959 
32960         current_join->restart();
32961     }
32962 #else
32963         if (g_gc_card_table != card_table)
32964             copy_brick_card_table();
32965 #endif //MULTIPLE_HEAPS
32966 
32967     //verify that the generation structures makes sense
32968     {
32969         generation* gen = generation_of (max_generation);
32970 
32971         assert (generation_allocation_start (gen) ==
32972                 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
32973         int gen_num = max_generation-1;
32974         generation* prev_gen = gen;
32975         while (gen_num >= 0)
32976         {
32977             gen = generation_of (gen_num);
32978             assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
32979             assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
32980             assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
32981 
32982             if (generation_start_segment (prev_gen ) ==
32983                 generation_start_segment (gen))
32984             {
32985                 assert (generation_allocation_start (prev_gen) <
32986                         generation_allocation_start (gen));
32987             }
32988             prev_gen = gen;
32989             gen_num--;
32990         }
32991     }
32992 
32993     while (1)
32994     {
32995         // Handle segment transitions
32996         if (curr_object >= heap_segment_allocated (seg))
32997         {
32998             if (curr_object > heap_segment_allocated(seg))
32999             {
33000                 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
33001                         (size_t)curr_object, (size_t)seg));
33002                 FATAL_GC_ERROR();
33003             }
33004             seg = heap_segment_next_in_range (seg);
33005             if (seg)
33006             {
33007 #ifdef BACKGROUND_GC
33008                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33009 #endif //BACKGROUND_GC
33010                 curr_object = heap_segment_mem(seg);
33011                 prev_object = 0;
33012                 continue;
33013             }
33014             else
33015             {
33016                 if (curr_gen_num == (max_generation+1))
33017                 {
33018                     curr_gen_num--;
33019                     seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
33020 
33021                     PREFIX_ASSUME(seg != NULL);
33022 
33023 #ifdef BACKGROUND_GC
33024                     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33025 #endif //BACKGROUND_GC
33026                     curr_object = heap_segment_mem (seg);
33027                     prev_object = 0;
33028                     large_brick_p = FALSE;
33029                     align_const = get_alignment_constant (TRUE);
33030                 }
33031                 else
33032                     break;  // Done Verifying Heap -- no more segments
33033             }
33034         }
33035 
33036         // Are we at the end of the youngest_generation?
33037         if (seg == ephemeral_heap_segment)
33038         {
33039             if (curr_object >= end_youngest)
33040             {
33041                 // prev_object length is too long if we hit this int3
33042                 if (curr_object > end_youngest)
33043                 {
33044                     dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
33045                             (size_t)curr_object, (size_t)end_youngest));
33046                     FATAL_GC_ERROR();
33047                 }
33048                 break;
33049             }
33050 
33051             if ((curr_object >= next_boundary) && (curr_gen_num > 0))
33052             {
33053                 curr_gen_num--;
33054                 if (curr_gen_num > 0)
33055                 {
33056                     next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
33057                 }
33058             }
33059         }
33060 
33061          //if (is_mark_set (curr_object))
33062          //{
33063          //        printf ("curr_object: %Ix is marked!",(size_t)curr_object);
33064          //        FATAL_GC_ERROR();
33065          //}
33066 
33067         size_t s = size (curr_object);
33068         dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
33069         if (s == 0)
33070         {
33071             dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
33072             FATAL_GC_ERROR();
33073         }
33074 
33075         // If object is not in the youngest generation, then lets
33076         // verify that the brick table is correct....
33077         if (((seg != ephemeral_heap_segment) ||
33078              (brick_of(curr_object) < brick_of(begin_youngest))))
33079         {
33080             curr_brick = brick_of(curr_object);
33081 
33082             // Brick Table Verification...
33083             //
33084             // On brick transition
33085             //     if brick is negative
33086             //          verify that brick indirects to previous valid brick
33087             //     else
33088             //          set current brick invalid flag to be flipped if we
33089             //          encounter an object at the correct place
33090             //
33091             if (curr_brick != prev_brick)
33092             {
33093                 // If the last brick we were examining had positive
33094                 // entry but we never found the matching object, then
33095                 // we have a problem
33096                 // If prev_brick was the last one of the segment
33097                 // it's ok for it to be invalid because it is never looked at
33098                 if (bCurrentBrickInvalid &&
33099                     (curr_brick != brick_of (heap_segment_mem (seg))) &&
33100                     !heap_segment_read_only_p (seg))
33101                 {
33102                     dprintf (3, ("curr brick %Ix invalid", curr_brick));
33103                     FATAL_GC_ERROR();
33104                 }
33105 
33106                 if (large_brick_p)
33107                 {
33108                     //large objects verify the table only if they are in
33109                     //range.
33110                     if ((heap_segment_reserved (seg) <= highest_address) &&
33111                         (heap_segment_mem (seg) >= lowest_address) &&
33112                         brick_table [curr_brick] != 0)
33113                     {
33114                         dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
33115                                 curr_brick, (size_t)curr_object));
33116                         FATAL_GC_ERROR();
33117                     }
33118                     else
33119                     {
33120                         bCurrentBrickInvalid = FALSE;
33121                     }
33122                 }
33123                 else
33124                 {
33125                     // If the current brick contains a negative value make sure
33126                     // that the indirection terminates at the last  valid brick
33127                     if (brick_table [curr_brick] <= 0)
33128                     {
33129                         if (brick_table [curr_brick] == 0)
33130                         {
33131                             dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
33132                                     curr_brick, (size_t)curr_object));
33133                             FATAL_GC_ERROR();
33134                         }
33135                         ptrdiff_t i = curr_brick;
33136                         while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
33137                                (brick_table[i] < 0))
33138                         {
33139                             i = i + brick_table[i];
33140                         }
33141                         if (i <  ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
33142                         {
33143                             dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
33144                                     i, brick_of (heap_segment_mem (seg)),
33145                                     curr_brick));
33146                             FATAL_GC_ERROR();
33147                         }
33148                         // if (i != last_valid_brick)
33149                         //  FATAL_GC_ERROR();
33150                         bCurrentBrickInvalid = FALSE;
33151                     }
33152                     else if (!heap_segment_read_only_p (seg))
33153                     {
33154                         bCurrentBrickInvalid = TRUE;
33155                     }
33156                 }
33157             }
33158 
33159             if (bCurrentBrickInvalid)
33160             {
33161                 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
33162                 {
33163                     bCurrentBrickInvalid = FALSE;
33164                     last_valid_brick = curr_brick;
33165                 }
33166             }
33167         }
33168 
33169         if (*((uint8_t**)curr_object) != (uint8_t *) g_pFreeObjectMethodTable)
33170         {
33171 #ifdef FEATURE_LOH_COMPACTION
33172             if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
33173             {
33174                 assert (method_table (prev_object) == g_pFreeObjectMethodTable);
33175             }
33176 #endif //FEATURE_LOH_COMPACTION
33177 
33178             total_objects_verified++;
33179 
33180             BOOL can_verify_deep = TRUE;
33181 #ifdef BACKGROUND_GC
33182             can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
33183 #endif //BACKGROUND_GC
33184 
33185             BOOL deep_verify_obj = can_verify_deep;
33186             if ((heap_verify_level & EEConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
33187                 deep_verify_obj = FALSE;
33188 
33189             ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
33190 
33191             if (can_verify_deep)
33192             {
33193                 if (curr_gen_num > 0)
33194                 {
33195                     BOOL need_card_p = FALSE;
33196                     if (contain_pointers_or_collectible (curr_object))
33197                     {
33198                         dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
33199                         size_t crd = card_of (curr_object);
33200                         BOOL found_card_p = card_set_p (crd);
33201 
33202 #ifdef COLLECTIBLE_CLASS
33203                         if (is_collectible(curr_object))
33204                         {
33205                             uint8_t* class_obj = get_class_object (curr_object);
33206                             if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
33207                             {
33208                                 if (!found_card_p)
33209                                 {
33210                                     dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
33211                                                 card_of (curr_object), (size_t)curr_object, class_obj));
33212 
33213                                     FATAL_GC_ERROR();
33214                                 }
33215                             }
33216                         }
33217 #endif //COLLECTIBLE_CLASS
33218 
33219                         if (contain_pointers(curr_object))
33220                         {
33221                             go_through_object_nostart
33222                                 (method_table(curr_object), curr_object, s, oo,
33223                                 {
33224                                     if ((crd != card_of ((uint8_t*)oo)) && !found_card_p)
33225                                     {
33226                                         crd = card_of ((uint8_t*)oo);
33227                                         found_card_p = card_set_p (crd);
33228                                         need_card_p = FALSE;
33229                                     }
33230                                     if ((*oo < ephemeral_high) && (*oo >= next_boundary))
33231                                     {
33232                                         need_card_p = TRUE;
33233                                     }
33234 
33235                                 if (need_card_p && !found_card_p)
33236                                 {
33237 
33238                                         dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33239                                                     card_of (curr_object), (size_t)curr_object,
33240                                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33241                                         FATAL_GC_ERROR();
33242                                     }
33243                                 }
33244                                     );
33245                         }
33246                         if (need_card_p && !found_card_p)
33247                         {
33248                             dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33249                                     card_of (curr_object), (size_t)curr_object,
33250                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33251                             FATAL_GC_ERROR();
33252                         }
33253                     }
33254                 }
33255                 total_objects_verified_deep++;
33256             }
33257         }
33258 
33259         prev_object = curr_object;
33260         prev_brick = curr_brick;
33261         curr_object = curr_object + Align(s, align_const);
33262         if (curr_object < prev_object)
33263         {
33264             dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
33265             FATAL_GC_ERROR();
33266         }
33267     }
33268 
33269 #ifdef BACKGROUND_GC
33270     dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id",
33271                  (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
33272                  (begin_gc_p ? "BEG" : "END"),
33273                  ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
33274                  total_objects_verified, total_objects_verified_deep));
33275     if (current_c_gc_state != c_gc_state_planning)
33276     {
33277         assert (total_objects_verified == total_objects_verified_deep);
33278     }
33279 #endif //BACKGROUND_GC
33280 
33281     verify_free_lists();
33282 
33283 #ifdef FEATURE_PREMORTEM_FINALIZATION
33284     finalize_queue->CheckFinalizerObjects();
33285 #endif // FEATURE_PREMORTEM_FINALIZATION
33286 
33287     {
33288         // to be consistent with handle table APIs pass a ScanContext*
33289         // to provide the heap number.  the SC isn't complete though so
33290         // limit its scope to handle table verification.
33291         ScanContext sc;
33292         sc.thread_number = heap_number;
33293         GCScan::VerifyHandleTable(max_generation, max_generation, &sc);
33294     }
33295 
33296 #ifdef MULTIPLE_HEAPS
33297     current_join->join(this, gc_join_verify_objects_done);
33298     if (current_join->joined())
33299 #endif //MULTIPLE_HEAPS
33300     {
33301         SyncBlockCache::GetSyncBlockCache()->VerifySyncTableEntry();
33302 #ifdef MULTIPLE_HEAPS
33303         current_join->restart();
33304 #endif //MULTIPLE_HEAPS
33305     }
33306 
33307 #ifdef BACKGROUND_GC
33308     if (!settings.concurrent)
33309     {
33310         if (current_c_gc_state == c_gc_state_planning)
33311         {
33312             // temporarily commenting this out 'cause an FGC
33313             // could be triggered before we sweep ephemeral.
33314             //verify_seg_end_mark_array_cleared();
33315         }
33316     }
33317 
33318     if (settings.concurrent)
33319     {
33320         verify_mark_array_cleared();
33321     }
33322     dprintf (2,("GC%d(%s): Verifying heap - end",
33323         VolatileLoad(&settings.gc_index),
33324         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
33325 #else
33326     dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
33327 #endif //BACKGROUND_GC
33328 }
33329 
33330 #endif  //VERIFY_HEAP
33331 
33332 
33333 void GCHeap::ValidateObjectMember (Object* obj)
33334 {
33335 #ifdef VERIFY_HEAP
33336     size_t s = size (obj);
33337     uint8_t* o = (uint8_t*)obj;
33338 
33339     go_through_object_cl (method_table (obj), o, s, oo,
33340                                 {
33341                                     uint8_t* child_o = *oo;
33342                                     if (child_o)
33343                                     {
33344                                         dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
33345                                         MethodTable *pMT = method_table (child_o);
33346                                         if (!pMT->SanityCheck()) {
33347                                             dprintf (3, ("Bad member of %Ix %Ix",
33348                                                         (size_t)oo, (size_t)child_o));
33349                                             FATAL_GC_ERROR();
33350                                         }
33351                                     }
33352                                 } );
33353 #endif // VERIFY_HEAP
33354 }
33355 
33356 void DestructObject (CObjectHeader* hdr)
33357 {
33358     UNREFERENCED_PARAMETER(hdr); // compiler bug? -- this *is*, indeed, referenced
33359     hdr->~CObjectHeader();
33360 }
33361 
33362 HRESULT GCHeap::Shutdown ()
33363 {
33364     deleteGCShadow();
33365 
33366     GCScan::GcRuntimeStructuresValid (FALSE);
33367 
33368     // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
33369     // threads except the one performing the shutdown.
33370     // ASSERT( !GcInProgress );
33371 
33372     // Guard against any more GC occurring and against any threads blocking
33373     // for GC to complete when the GC heap is gone.  This fixes a race condition
33374     // where a thread in GC is destroyed as part of process destruction and
33375     // the remaining threads block for GC complete.
33376 
33377     //GCTODO
33378     //EnterAllocLock();
33379     //Enter();
33380     //EnterFinalizeLock();
33381     //SetGCDone();
33382 
33383     // during shutdown lot of threads are suspended
33384     // on this even, we don't want to wake them up just yet
33385     //CloseHandle (WaitForGCEvent);
33386 
33387     //find out if the global card table hasn't been used yet
33388     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
33389     if (card_table_refcount (ct) == 0)
33390     {
33391         destroy_card_table (ct);
33392         g_gc_card_table = 0;
33393 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33394         SoftwareWriteWatch::StaticClose();
33395 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33396     }
33397 
33398     //destroy all segments on the standby list
33399     while(gc_heap::segment_standby_list != 0)
33400     {
33401         heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
33402 #ifdef MULTIPLE_HEAPS
33403         (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33404 #else //MULTIPLE_HEAPS
33405         pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33406 #endif //MULTIPLE_HEAPS
33407         gc_heap::segment_standby_list = next_seg;
33408     }
33409 
33410 
33411 #ifdef MULTIPLE_HEAPS
33412 
33413     for (int i = 0; i < gc_heap::n_heaps; i ++)
33414     {
33415         delete gc_heap::g_heaps[i]->vm_heap;
33416         //destroy pure GC stuff
33417         gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
33418     }
33419 #else
33420     gc_heap::destroy_gc_heap (pGenGCHeap);
33421 
33422 #endif //MULTIPLE_HEAPS
33423     gc_heap::shutdown_gc();
33424 
33425     return S_OK;
33426 }
33427 
33428 // Wait until a garbage collection is complete
33429 // returns NOERROR if wait was OK, other error code if failure.
33430 // WARNING: This will not undo the must complete state. If you are
33431 // in a must complete when you call this, you'd better know what you're
33432 // doing.
33433 
33434 #ifdef FEATURE_PREMORTEM_FINALIZATION
33435 static
33436 HRESULT AllocateCFinalize(CFinalize **pCFinalize)
33437 {
33438     *pCFinalize = new (nothrow) CFinalize();
33439     if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
33440         return E_OUTOFMEMORY;
33441 
33442     return S_OK;
33443 }
33444 #endif // FEATURE_PREMORTEM_FINALIZATION
33445 
33446 // init the instance heap
33447 HRESULT GCHeap::Init(size_t hn)
33448 {
33449     HRESULT hres = S_OK;
33450 
33451     //Initialize all of the instance members.
33452 
33453 #ifdef MULTIPLE_HEAPS
33454     m_GCLock                = -1;
33455 #endif //MULTIPLE_HEAPS
33456 
33457     // Rest of the initialization
33458 
33459 #ifdef MULTIPLE_HEAPS
33460     if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
33461         hres = E_OUTOFMEMORY;
33462 #else
33463     UNREFERENCED_PARAMETER(hn);
33464     if (!gc_heap::make_gc_heap())
33465         hres = E_OUTOFMEMORY;
33466 #endif //MULTIPLE_HEAPS
33467 
33468     // Failed.
33469     return hres;
33470 }
33471 
33472 //System wide initialization
33473 HRESULT GCHeap::Initialize ()
33474 {
33475 
33476     HRESULT hr = S_OK;
33477 
33478     if (!GCToOSInterface::Initialize())
33479     {
33480         return E_FAIL;
33481     }
33482 
33483 //Initialize the static members.
33484 #ifdef TRACE_GC
33485     GcDuration = 0;
33486     CreatedObjectCount = 0;
33487 #endif //TRACE_GC
33488 
33489     size_t seg_size = get_valid_segment_size();
33490     size_t large_seg_size = get_valid_segment_size(TRUE);
33491     gc_heap::min_segment_size = min (seg_size, large_seg_size);
33492 
33493 #ifdef MULTIPLE_HEAPS
33494     if (g_pConfig->GetGCNoAffinitize())
33495         gc_heap::gc_thread_no_affinitize_p = true;
33496 
33497     uint32_t nhp_from_config = g_pConfig->GetGCHeapCount();
33498     // GetGCProcessCpuCount only returns up to 64 procs.
33499     uint32_t nhp_from_process = CPUGroupInfo::CanEnableGCCPUGroups() ?
33500                                 CPUGroupInfo::GetNumActiveProcessors():
33501                                 GCToOSInterface::GetCurrentProcessCpuCount();
33502 
33503     uint32_t nhp = ((nhp_from_config == 0) ? nhp_from_process :
33504                                              (min (nhp_from_config, nhp_from_process)));
33505 
33506     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
33507 #else
33508     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
33509 #endif //MULTIPLE_HEAPS
33510 
33511     if (hr != S_OK)
33512         return hr;
33513 
33514     gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit();
33515 
33516     gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
33517 #ifndef MULTIPLE_HEAPS
33518     gc_heap::mem_one_percent /= g_SystemInfo.dwNumberOfProcessors;
33519 #endif //!MULTIPLE_HEAPS
33520 
33521     // We should only use this if we are in the "many process" mode which really is only applicable
33522     // to very powerful machines - before that's implemented, temporarily I am only enabling this for 80GB+ memory.
33523     // For now I am using an estimate to calculate these numbers but this should really be obtained
33524     // programmatically going forward.
33525     // I am assuming 47 processes using WKS GC and 3 using SVR GC.
33526     // I am assuming 3 in part due to the "very high memory load" is 97%.
33527     int available_mem_th = 10;
33528     if (gc_heap::total_physical_mem >= ((uint64_t)80 * 1024 * 1024 * 1024))
33529     {
33530         int adjusted_available_mem_th = 3 + (int)((float)47 / (float)(g_SystemInfo.dwNumberOfProcessors));
33531         available_mem_th = min (available_mem_th, adjusted_available_mem_th);
33532     }
33533 
33534     gc_heap::high_memory_load_th = 100 - available_mem_th;
33535 
33536 #if defined(BIT64)
33537     gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
33538 #endif // BIT64
33539 
33540     WaitForGCEvent = new (nothrow) CLREvent;
33541 
33542     if (!WaitForGCEvent)
33543     {
33544         return E_OUTOFMEMORY;
33545     }
33546 
33547     if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE))
33548     {
33549         return E_FAIL;
33550     }
33551 
33552     stomp_write_barrier_initialize();
33553 
33554 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33555 #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
33556     if (GCStress<cfg_any>::IsEnabled())  {
33557         for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
33558             m_StressObjs[i] = CreateGlobalHandle(0);
33559         m_CurStressObj = 0;
33560     }
33561 #endif //STRESS_HEAP && !MULTIPLE_HEAPS
33562 #endif // FEATURE_REDHAWK
33563 
33564     initGCShadow();         // If we are debugging write barriers, initialize heap shadow
33565 
33566 #ifdef MULTIPLE_HEAPS
33567 
33568     for (unsigned i = 0; i < nhp; i++)
33569     {
33570         GCHeap* Hp = new (nothrow) GCHeap();
33571         if (!Hp)
33572             return E_OUTOFMEMORY;
33573 
33574         if ((hr = Hp->Init (i))!= S_OK)
33575         {
33576             return hr;
33577         }
33578     }
33579     // initialize numa node to heap map
33580     heap_select::init_numa_node_to_heap_map(nhp);
33581 #else
33582     hr = Init (0);
33583 #endif //MULTIPLE_HEAPS
33584 
33585     if (hr == S_OK)
33586     {
33587         GCScan::GcRuntimeStructuresValid (TRUE);
33588 
33589         GCToEEInterface::DiagUpdateGenerationBounds();
33590     }
33591 
33592     return hr;
33593 };
33594 
33595 ////
33596 // GC callback functions
33597 BOOL GCHeap::IsPromoted(Object* object)
33598 {
33599 #ifdef _DEBUG
33600     ((CObjectHeader*)object)->Validate();
33601 #endif //_DEBUG
33602 
33603     uint8_t* o = (uint8_t*)object;
33604 
33605     if (gc_heap::settings.condemned_generation == max_generation)
33606     {
33607 #ifdef MULTIPLE_HEAPS
33608         gc_heap* hp = gc_heap::g_heaps[0];
33609 #else
33610         gc_heap* hp = pGenGCHeap;
33611 #endif //MULTIPLE_HEAPS
33612 
33613 #ifdef BACKGROUND_GC
33614         if (gc_heap::settings.concurrent)
33615         {
33616             BOOL is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
33617                             hp->background_marked (o));
33618             return is_marked;
33619         }
33620         else
33621 #endif //BACKGROUND_GC
33622         {
33623             return (!((o < hp->highest_address) && (o >= hp->lowest_address))
33624                     || hp->is_mark_set (o));
33625         }
33626     }
33627     else
33628     {
33629         gc_heap* hp = gc_heap::heap_of (o);
33630         return (!((o < hp->gc_high) && (o >= hp->gc_low))
33631                 || hp->is_mark_set (o));
33632     }
33633 }
33634 
33635 size_t GCHeap::GetPromotedBytes(int heap_index)
33636 {
33637 #ifdef BACKGROUND_GC
33638     if (gc_heap::settings.concurrent)
33639     {
33640         return gc_heap::bpromoted_bytes (heap_index);
33641     }
33642     else
33643 #endif //BACKGROUND_GC
33644     {
33645         return gc_heap::promoted_bytes (heap_index);
33646     }
33647 }
33648 
33649 unsigned int GCHeap::WhichGeneration (Object* object)
33650 {
33651     gc_heap* hp = gc_heap::heap_of ((uint8_t*)object);
33652     unsigned int g = hp->object_gennum ((uint8_t*)object);
33653     dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
33654     return g;
33655 }
33656 
33657 BOOL    GCHeap::IsEphemeral (Object* object)
33658 {
33659     uint8_t* o = (uint8_t*)object;
33660     gc_heap* hp = gc_heap::heap_of (o);
33661     return hp->ephemeral_pointer_p (o);
33662 }
33663 
33664 // Return NULL if can't find next object. When EE is not suspended,
33665 // the result is not accurate: if the input arg is in gen0, the function could
33666 // return zeroed out memory as next object
33667 Object * GCHeap::NextObj (Object * object)
33668 {
33669 #ifdef VERIFY_HEAP
33670     uint8_t* o = (uint8_t*)object;
33671 
33672 #ifndef FEATURE_BASICFREEZE
33673     if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)))
33674     {
33675         return NULL;
33676     }
33677 #endif //!FEATURE_BASICFREEZE
33678 
33679     heap_segment * hs = gc_heap::find_segment (o, FALSE);
33680     if (!hs)
33681     {
33682         return NULL;
33683     }
33684 
33685     BOOL large_object_p = heap_segment_loh_p (hs);
33686     if (large_object_p)
33687         return NULL; //could be racing with another core allocating.
33688 #ifdef MULTIPLE_HEAPS
33689     gc_heap* hp = heap_segment_heap (hs);
33690 #else //MULTIPLE_HEAPS
33691     gc_heap* hp = 0;
33692 #endif //MULTIPLE_HEAPS
33693     unsigned int g = hp->object_gennum ((uint8_t*)object);
33694     if ((g == 0) && hp->settings.demotion)
33695         return NULL;//could be racing with another core allocating.
33696     int align_const = get_alignment_constant (!large_object_p);
33697     uint8_t* nextobj = o + Align (size (o), align_const);
33698     if (nextobj <= o) // either overflow or 0 sized object.
33699     {
33700         return NULL;
33701     }
33702 
33703     if ((nextobj < heap_segment_mem(hs)) ||
33704         (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) ||
33705         (nextobj >= hp->alloc_allocated))
33706     {
33707         return NULL;
33708     }
33709 
33710     return (Object *)nextobj;
33711 #else
33712     return nullptr;
33713 #endif // VERIFY_HEAP
33714 }
33715 
33716 #ifdef VERIFY_HEAP
33717 
33718 #ifdef FEATURE_BASICFREEZE
33719 BOOL GCHeap::IsInFrozenSegment (Object * object)
33720 {
33721     uint8_t* o = (uint8_t*)object;
33722     heap_segment * hs = gc_heap::find_segment (o, FALSE);
33723     //We create a frozen object for each frozen segment before the segment is inserted
33724     //to segment list; during ngen, we could also create frozen objects in segments which
33725     //don't belong to current GC heap.
33726     //So we return true if hs is NULL. It might create a hole about detecting invalidate
33727     //object. But given all other checks present, the hole should be very small
33728     return !hs || heap_segment_read_only_p (hs);
33729 }
33730 #endif //FEATURE_BASICFREEZE
33731 
33732 #endif //VERIFY_HEAP
33733 
33734 // returns TRUE if the pointer is in one of the GC heaps.
33735 BOOL GCHeap::IsHeapPointer (void* vpObject, BOOL small_heap_only)
33736 {
33737     STATIC_CONTRACT_SO_TOLERANT;
33738 
33739     // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment
33740     // no longer calls CLREvent::Wait which eventually takes a lock.
33741 
33742     uint8_t* object = (uint8_t*) vpObject;
33743 #ifndef FEATURE_BASICFREEZE
33744     if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address)))
33745         return FALSE;
33746 #endif //!FEATURE_BASICFREEZE
33747 
33748     heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
33749     return !!hs;
33750 }
33751 
33752 #ifdef STRESS_PINNING
33753 static n_promote = 0;
33754 #endif //STRESS_PINNING
33755 // promote an object
33756 void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
33757 {
33758     THREAD_NUMBER_FROM_CONTEXT;
33759 #ifndef MULTIPLE_HEAPS
33760     const int thread = 0;
33761 #endif //!MULTIPLE_HEAPS
33762 
33763     uint8_t* o = (uint8_t*)*ppObject;
33764 
33765     if (o == 0)
33766         return;
33767 
33768 #ifdef DEBUG_DestroyedHandleValue
33769     // we can race with destroy handle during concurrent scan
33770     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
33771         return;
33772 #endif //DEBUG_DestroyedHandleValue
33773 
33774     HEAP_FROM_THREAD;
33775 
33776     gc_heap* hp = gc_heap::heap_of (o);
33777 
33778     dprintf (3, ("Promote %Ix", (size_t)o));
33779 
33780 #ifdef INTERIOR_POINTERS
33781     if (flags & GC_CALL_INTERIOR)
33782     {
33783         if ((o < hp->gc_low) || (o >= hp->gc_high))
33784         {
33785             return;
33786         }
33787         if ( (o = hp->find_object (o, hp->gc_low)) == 0)
33788         {
33789             return;
33790         }
33791 
33792     }
33793 #endif //INTERIOR_POINTERS
33794 
33795 #ifdef FEATURE_CONSERVATIVE_GC
33796     // For conservative GC, a value on stack may point to middle of a free object.
33797     // In this case, we don't need to promote the pointer.
33798     if (g_pConfig->GetGCConservative()
33799         && ((CObjectHeader*)o)->IsFree())
33800     {
33801         return;
33802     }
33803 #endif
33804 
33805 #ifdef _DEBUG
33806     ((CObjectHeader*)o)->ValidatePromote(sc, flags);
33807 #else
33808     UNREFERENCED_PARAMETER(sc);
33809 #endif //_DEBUG
33810 
33811     if (flags & GC_CALL_PINNED)
33812         hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
33813 
33814 #ifdef STRESS_PINNING
33815     if ((++n_promote % 20) == 1)
33816             hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
33817 #endif //STRESS_PINNING
33818 
33819 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33820     size_t promoted_size_begin = hp->promoted_bytes (thread);
33821 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33822 
33823     if ((o >= hp->gc_low) && (o < hp->gc_high))
33824     {
33825         hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
33826     }
33827 
33828 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
33829     size_t promoted_size_end = hp->promoted_bytes (thread);
33830     if (g_fEnableARM)
33831     {
33832         if (sc->pCurrentDomain)
33833         {
33834             sc->pCurrentDomain->RecordSurvivedBytes ((promoted_size_end - promoted_size_begin), thread);
33835         }
33836     }
33837 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
33838 
33839     STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
33840 }
33841 
33842 void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
33843                        uint32_t flags)
33844 {
33845     UNREFERENCED_PARAMETER(sc);
33846 
33847     uint8_t* object = (uint8_t*)(Object*)(*ppObject);
33848 
33849     THREAD_NUMBER_FROM_CONTEXT;
33850 
33851     //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
33852     dprintf (3, ("R: %Ix", (size_t)ppObject));
33853 
33854     if (object == 0)
33855         return;
33856 
33857     gc_heap* hp = gc_heap::heap_of (object);
33858 
33859 #ifdef _DEBUG
33860     if (!(flags & GC_CALL_INTERIOR))
33861     {
33862         // We cannot validate this object if it's in the condemned gen because it could
33863         // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
33864         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
33865         {
33866             ((CObjectHeader*)object)->Validate(FALSE);
33867         }
33868     }
33869 #endif //_DEBUG
33870 
33871     dprintf (3, ("Relocate %Ix\n", (size_t)object));
33872 
33873     uint8_t* pheader;
33874 
33875     if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
33876     {
33877         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
33878         {
33879             return;
33880         }
33881 
33882         if (gc_heap::loh_object_p (object))
33883         {
33884             pheader = hp->find_object (object, 0);
33885             if (pheader == 0)
33886             {
33887                 return;
33888             }
33889 
33890             ptrdiff_t ref_offset = object - pheader;
33891             hp->relocate_address(&pheader THREAD_NUMBER_ARG);
33892             *ppObject = (Object*)(pheader + ref_offset);
33893             return;
33894         }
33895     }
33896 
33897     {
33898         pheader = object;
33899         hp->relocate_address(&pheader THREAD_NUMBER_ARG);
33900         *ppObject = (Object*)pheader;
33901     }
33902 
33903     STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
33904 }
33905 
33906 /*static*/ BOOL GCHeap::IsObjectInFixedHeap(Object *pObj)
33907 {
33908     // For now we simply look at the size of the object to determine if it in the
33909     // fixed heap or not. If the bit indicating this gets set at some point
33910     // we should key off that instead.
33911     return size( pObj ) >= LARGE_OBJECT_SIZE;
33912 }
33913 
33914 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33915 #ifdef STRESS_HEAP
33916 
33917 void StressHeapDummy ();
33918 
33919 static int32_t GCStressStartCount = -1;
33920 static int32_t GCStressCurCount = 0;
33921 static int32_t GCStressStartAtJit = -1;
33922 
33923 // the maximum number of foreground GCs we'll induce during one BGC
33924 // (this number does not include "naturally" occuring GCs).
33925 static int32_t GCStressMaxFGCsPerBGC = -1;
33926 
33927 // CLRRandom implementation can produce FPU exceptions if
33928 // the test/application run by CLR is enabling any FPU exceptions.
33929 // We want to avoid any unexpected exception coming from stress
33930 // infrastructure, so CLRRandom is not an option.
33931 // The code below is a replicate of CRT rand() implementation.
33932 // Using CRT rand() is not an option because we will interfere with the user application
33933 // that may also use it.
33934 int StressRNG(int iMaxValue)
33935 {
33936     static BOOL bisRandInit = FALSE;
33937     static int lHoldrand = 1L;
33938 
33939     if (!bisRandInit)
33940     {
33941         lHoldrand = (int)time(NULL);
33942         bisRandInit = TRUE;
33943     }
33944     int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
33945     return randValue % iMaxValue;
33946 }
33947 #endif // STRESS_HEAP
33948 #endif // !FEATURE_REDHAWK
33949 
33950 // free up object so that things will move and then do a GC
33951 //return TRUE if GC actually happens, otherwise FALSE
33952 BOOL GCHeap::StressHeap(gc_alloc_context * context)
33953 {
33954 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
33955     alloc_context* acontext = static_cast<alloc_context*>(context);
33956 
33957     // if GC stress was dynamically disabled during this run we return FALSE
33958     if (!GCStressPolicy::IsEnabled())
33959         return FALSE;
33960 
33961 #ifdef _DEBUG
33962     if (g_pConfig->FastGCStressLevel() && !GetThread()->StressHeapIsEnabled()) {
33963         return FALSE;
33964     }
33965 
33966 #endif //_DEBUG
33967 
33968     if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
33969 #ifdef _DEBUG
33970         || g_pConfig->FastGCStressLevel() > 1
33971 #endif //_DEBUG
33972         ) {
33973         if (!Thread::UniqueStack(&acontext)) {
33974             return FALSE;
33975         }
33976     }
33977 
33978 #ifdef BACKGROUND_GC
33979         // don't trigger a GC from the GC threads but still trigger GCs from user threads.
33980         if (IsGCSpecialThread())
33981         {
33982             return FALSE;
33983         }
33984 #endif //BACKGROUND_GC
33985 
33986         if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
33987         {
33988             GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
33989             GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
33990         }
33991 
33992         if (GCStressMaxFGCsPerBGC == -1)
33993         {
33994             GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
33995             if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
33996                 GCStressMaxFGCsPerBGC = 6;
33997         }
33998 
33999 #ifdef _DEBUG
34000         if (g_JitCount < GCStressStartAtJit)
34001             return FALSE;
34002 #endif //_DEBUG
34003 
34004         // Allow programmer to skip the first N Stress GCs so that you can
34005         // get to the interesting ones faster.
34006         Interlocked::Increment(&GCStressCurCount);
34007         if (GCStressCurCount < GCStressStartCount)
34008             return FALSE;
34009 
34010         // throttle the number of stress-induced GCs by a factor given by GCStressStep
34011         if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
34012         {
34013             return FALSE;
34014         }
34015 
34016 #ifdef BACKGROUND_GC
34017         if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
34018         {
34019             // allow a maximum number of stress induced FGCs during one BGC
34020             if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
34021                 return FALSE;
34022             ++gc_stress_fgcs_in_bgc;
34023         }
34024 #endif // BACKGROUND_GC
34025 
34026     if (g_pStringClass == 0)
34027     {
34028         // If the String class has not been loaded, dont do any stressing. This should
34029         // be kept to a minimum to get as complete coverage as possible.
34030         _ASSERTE(g_fEEInit);
34031         return FALSE;
34032     }
34033 
34034 #ifndef MULTIPLE_HEAPS
34035     static int32_t OneAtATime = -1;
34036 
34037     if (acontext == 0)
34038         acontext = generation_alloc_context (pGenGCHeap->generation_of(0));
34039 
34040     // Only bother with this if the stress level is big enough and if nobody else is
34041     // doing it right now.  Note that some callers are inside the AllocLock and are
34042     // guaranteed synchronized.  But others are using AllocationContexts and have no
34043     // particular synchronization.
34044     //
34045     // For this latter case, we want a very high-speed way of limiting this to one
34046     // at a time.  A secondary advantage is that we release part of our StressObjs
34047     // buffer sparingly but just as effectively.
34048 
34049     if (Interlocked::Increment(&OneAtATime) == 0 &&
34050         !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
34051     {
34052         StringObject* str;
34053 
34054         // If the current string is used up
34055         if (ObjectFromHandle(m_StressObjs[m_CurStressObj]) == 0)
34056         {
34057             // Populate handles with strings
34058             int i = m_CurStressObj;
34059             while(ObjectFromHandle(m_StressObjs[i]) == 0)
34060             {
34061                 _ASSERTE(m_StressObjs[i] != 0);
34062                 unsigned strLen = (LARGE_OBJECT_SIZE - 32) / sizeof(WCHAR);
34063                 unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
34064 
34065                 // update the cached type handle before allocating
34066                 SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
34067                 str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
34068                 if (str)
34069                 {
34070                     str->SetMethodTable (g_pStringClass);
34071                     str->SetStringLength (strLen);
34072 
34073 #if CHECK_APP_DOMAIN_LEAKS
34074                     if (g_pConfig->AppDomainLeaks() && str->SetAppDomainNoThrow())
34075                     {
34076 #endif
34077                         StoreObjectInHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
34078 #if CHECK_APP_DOMAIN_LEAKS
34079                     }
34080 #endif
34081                 }
34082                 i = (i + 1) % NUM_HEAP_STRESS_OBJS;
34083                 if (i == m_CurStressObj) break;
34084             }
34085 
34086             // advance the current handle to the next string
34087             m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
34088         }
34089 
34090         // Get the current string
34091         str = (StringObject*) OBJECTREFToObject(ObjectFromHandle(m_StressObjs[m_CurStressObj]));
34092         if (str)
34093         {
34094             // Chop off the end of the string and form a new object out of it.
34095             // This will 'free' an object at the begining of the heap, which will
34096             // force data movement.  Note that we can only do this so many times.
34097             // before we have to move on to the next string.
34098             unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
34099             if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
34100             {
34101                 unsigned sizeToNextObj = (unsigned)Align(size(str));
34102                 uint8_t* freeObj = ((uint8_t*) str) + sizeToNextObj - sizeOfNewObj;
34103                 pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);
34104                 str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
34105             }
34106             else
34107             {
34108                 // Let the string itself become garbage.
34109                 // will be realloced next time around
34110                 StoreObjectInHandle(m_StressObjs[m_CurStressObj], 0);
34111             }
34112         }
34113     }
34114     Interlocked::Decrement(&OneAtATime);
34115 #endif // !MULTIPLE_HEAPS
34116     if (IsConcurrentGCEnabled())
34117     {
34118         int rgen = StressRNG(10);
34119 
34120         // gen0:gen1:gen2 distribution: 40:40:20
34121         if (rgen >= 8)
34122             rgen = 2;
34123         else if (rgen >= 4)
34124             rgen = 1;
34125     else
34126             rgen = 0;
34127 
34128         GarbageCollectTry (rgen, FALSE, collection_gcstress);
34129     }
34130     else
34131     {
34132         GarbageCollect(max_generation, FALSE, collection_gcstress);
34133     }
34134 
34135     return TRUE;
34136 #else
34137     UNREFERENCED_PARAMETER(context);
34138     return FALSE;
34139 #endif // defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34140 }
34141 
34142 
34143 #ifdef FEATURE_PREMORTEM_FINALIZATION
34144 #define REGISTER_FOR_FINALIZATION(_object, _size) \
34145     hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
34146 #else // FEATURE_PREMORTEM_FINALIZATION
34147 #define REGISTER_FOR_FINALIZATION(_object, _size) true
34148 #endif // FEATURE_PREMORTEM_FINALIZATION
34149 
34150 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {  \
34151     if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size)))   \
34152     {                                                                                       \
34153         STRESS_LOG_OOM_STACK(_size);                                                        \
34154         return NULL;                                                                        \
34155     }                                                                                       \
34156 } while (false)
34157 
34158 //
34159 // Small Object Allocator
34160 //
34161 //
34162 Object *
34163 GCHeap::Alloc( size_t size, uint32_t flags REQD_ALIGN_DCL)
34164 {
34165     CONTRACTL {
34166         NOTHROW;
34167         GC_TRIGGERS;
34168     } CONTRACTL_END;
34169 
34170     TRIGGERSGC();
34171 
34172     Object* newAlloc = NULL;
34173 
34174 #ifdef TRACE_GC
34175 #ifdef COUNT_CYCLES
34176     AllocStart = GetCycleCount32();
34177     unsigned finish;
34178 #elif defined(ENABLE_INSTRUMENTATION)
34179     unsigned AllocStart = GetInstLogTime();
34180     unsigned finish;
34181 #endif //COUNT_CYCLES
34182 #endif //TRACE_GC
34183 
34184 #ifdef MULTIPLE_HEAPS
34185     //take the first heap....
34186     gc_heap* hp = gc_heap::g_heaps[0];
34187 #else
34188     gc_heap* hp = pGenGCHeap;
34189 #ifdef _PREFAST_
34190     // prefix complains about us dereferencing hp in wks build even though we only access static members
34191     // this way. not sure how to shut it up except for this ugly workaround:
34192     PREFIX_ASSUME(hp != NULL);
34193 #endif //_PREFAST_
34194 #endif //MULTIPLE_HEAPS
34195 
34196     {
34197         AllocLockHolder lh;
34198 
34199 #ifndef FEATURE_REDHAWK
34200         GCStress<gc_on_alloc>::MaybeTrigger(generation_alloc_context(hp->generation_of(0)));
34201 #endif // FEATURE_REDHAWK
34202 
34203         alloc_context* acontext = 0;
34204 
34205         if (size < LARGE_OBJECT_SIZE)
34206         {
34207             acontext = generation_alloc_context (hp->generation_of (0));
34208 
34209 #ifdef TRACE_GC
34210             AllocSmallCount++;
34211 #endif //TRACE_GC
34212             newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
34213 #ifdef FEATURE_STRUCTALIGN
34214             newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
34215 #endif // FEATURE_STRUCTALIGN
34216             // ASSERT (newAlloc);
34217         }
34218         else
34219         {
34220             acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34221 
34222             newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34223 #ifdef FEATURE_STRUCTALIGN
34224             newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34225 #endif // FEATURE_STRUCTALIGN
34226         }
34227     }
34228 
34229     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34230 
34231 #ifdef TRACE_GC
34232 #ifdef COUNT_CYCLES
34233     finish = GetCycleCount32();
34234 #elif defined(ENABLE_INSTRUMENTATION)
34235     finish = GetInstLogTime();
34236 #endif //COUNT_CYCLES
34237     AllocDuration += finish - AllocStart;
34238     AllocCount++;
34239 #endif //TRACE_GC
34240     return newAlloc;
34241 }
34242 
34243 // Allocate small object with an alignment requirement of 8-bytes. Non allocation context version.
34244 Object *
34245 GCHeap::AllocAlign8( size_t size, uint32_t flags)
34246 {
34247 #ifdef FEATURE_64BIT_ALIGNMENT
34248     CONTRACTL {
34249         NOTHROW;
34250         GC_TRIGGERS;
34251     } CONTRACTL_END;
34252 
34253     Object* newAlloc = NULL;
34254 
34255     {
34256         AllocLockHolder lh;
34257 
34258 #ifdef MULTIPLE_HEAPS
34259         //take the first heap....
34260         gc_heap* hp = gc_heap::g_heaps[0];
34261 #else
34262         gc_heap* hp = pGenGCHeap;
34263 #endif //MULTIPLE_HEAPS
34264 
34265         newAlloc = AllocAlign8Common(hp, generation_alloc_context (hp->generation_of (0)), size, flags);
34266     }
34267 
34268     return newAlloc;
34269 #else
34270     UNREFERENCED_PARAMETER(size);
34271     UNREFERENCED_PARAMETER(flags);
34272     assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!");
34273     return nullptr;
34274 #endif  //FEATURE_64BIT_ALIGNMENT
34275 }
34276 
34277 // Allocate small object with an alignment requirement of 8-bytes. Allocation context version.
34278 Object*
34279 GCHeap::AllocAlign8(gc_alloc_context* ctx, size_t size, uint32_t flags )
34280 {
34281 #ifdef FEATURE_64BIT_ALIGNMENT
34282     CONTRACTL {
34283         NOTHROW;
34284         GC_TRIGGERS;
34285     } CONTRACTL_END;
34286 
34287     alloc_context* acontext = static_cast<alloc_context*>(ctx);
34288 
34289 #ifdef MULTIPLE_HEAPS
34290     if (acontext->get_alloc_heap() == 0)
34291     {
34292         AssignHeap (acontext);
34293         assert (acontext->get_alloc_heap());
34294     }
34295 
34296     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34297 #else
34298     gc_heap* hp = pGenGCHeap;
34299 #endif //MULTIPLE_HEAPS
34300 
34301     return AllocAlign8Common(hp, acontext, size, flags);
34302 #else
34303     UNREFERENCED_PARAMETER(ctx);
34304     UNREFERENCED_PARAMETER(size);
34305     UNREFERENCED_PARAMETER(flags);
34306     assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!");
34307     return nullptr;
34308 #endif  //FEATURE_64BIT_ALIGNMENT
34309 }
34310 
34311 // Common code used by both variants of AllocAlign8 above.
34312 Object*
34313 GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, uint32_t flags)
34314 {
34315 #ifdef FEATURE_64BIT_ALIGNMENT
34316     CONTRACTL {
34317         NOTHROW;
34318         GC_TRIGGERS;
34319     } CONTRACTL_END;
34320 
34321     gc_heap* hp = (gc_heap*)_hp;
34322 
34323     TRIGGERSGC();
34324 
34325     Object* newAlloc = NULL;
34326 
34327 #ifdef TRACE_GC
34328 #ifdef COUNT_CYCLES
34329     AllocStart = GetCycleCount32();
34330     unsigned finish;
34331 #elif defined(ENABLE_INSTRUMENTATION)
34332     unsigned AllocStart = GetInstLogTime();
34333     unsigned finish;
34334 #endif //COUNT_CYCLES
34335 #endif //TRACE_GC
34336 
34337 #ifndef FEATURE_REDHAWK
34338     GCStress<gc_on_alloc>::MaybeTrigger(acontext);
34339 #endif // FEATURE_REDHAWK
34340 
34341     if (size < LARGE_OBJECT_SIZE)
34342     {
34343 #ifdef TRACE_GC
34344         AllocSmallCount++;
34345 #endif //TRACE_GC
34346 
34347         // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
34348         // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
34349         // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
34350         size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
34351 
34352         // Retrieve the address of the next allocation from the context (note that we're inside the alloc
34353         // lock at this point).
34354         uint8_t*  result = acontext->alloc_ptr;
34355 
34356         // Will an allocation at this point yield the correct alignment and fit into the remainder of the
34357         // context?
34358         if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
34359         {
34360             // Yes, we can just go ahead and make the allocation.
34361             newAlloc = (Object*) hp->allocate (size, acontext);
34362             ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34363         }
34364         else
34365         {
34366             // No, either the next available address is not aligned in the way we require it or there's
34367             // not enough space to allocate an object of the required size. In both cases we allocate a
34368             // padding object (marked as a free object). This object's size is such that it will reverse
34369             // the alignment of the next header (asserted below).
34370             //
34371             // We allocate both together then decide based on the result whether we'll format the space as
34372             // free object + real object or real object + free object.
34373             ASSERT((Align(min_obj_size) & 7) == 4);
34374             CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
34375             if (freeobj)
34376             {
34377                 if (((size_t)freeobj & 7) == desiredAlignment)
34378                 {
34379                     // New allocation has desired alignment, return this one and place the free object at the
34380                     // end of the allocated space.
34381                     newAlloc = (Object*)freeobj;
34382                     freeobj = (CObjectHeader*)((uint8_t*)freeobj + Align(size));
34383                 }
34384                 else
34385                 {
34386                     // New allocation is still mis-aligned, format the initial space as a free object and the
34387                     // rest of the space should be correctly aligned for the real object.
34388                     newAlloc = (Object*)((uint8_t*)freeobj + Align(min_obj_size));
34389                     ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34390                 }
34391                 freeobj->SetFree(min_obj_size);
34392             }
34393         }
34394     }
34395     else
34396     {
34397         // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
34398         // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
34399         // we've managed to arrange things so the only case where we see a bias is for boxed value types and
34400         // these can never get large enough to be allocated on the LOH.
34401         ASSERT(65536 < LARGE_OBJECT_SIZE);
34402         ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
34403 
34404         alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34405 
34406         newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
34407         ASSERT(((size_t)newAlloc & 7) == 0);
34408     }
34409 
34410     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34411 
34412 #ifdef TRACE_GC
34413 #ifdef COUNT_CYCLES
34414     finish = GetCycleCount32();
34415 #elif defined(ENABLE_INSTRUMENTATION)
34416     finish = GetInstLogTime();
34417 #endif //COUNT_CYCLES
34418     AllocDuration += finish - AllocStart;
34419     AllocCount++;
34420 #endif //TRACE_GC
34421     return newAlloc;
34422 #else
34423     UNREFERENCED_PARAMETER(_hp);
34424     UNREFERENCED_PARAMETER(acontext);
34425     UNREFERENCED_PARAMETER(size);
34426     UNREFERENCED_PARAMETER(flags);
34427     assert(!"Should not call GCHeap::AllocAlign8Common without FEATURE_64BIT_ALIGNMENT defined!");
34428     return nullptr;
34429 #endif // FEATURE_64BIT_ALIGNMENT
34430 }
34431 
34432 Object *
34433 GCHeap::AllocLHeap( size_t size, uint32_t flags REQD_ALIGN_DCL)
34434 {
34435     CONTRACTL {
34436         NOTHROW;
34437         GC_TRIGGERS;
34438     } CONTRACTL_END;
34439 
34440     TRIGGERSGC();
34441 
34442     Object* newAlloc = NULL;
34443 
34444 #ifdef TRACE_GC
34445 #ifdef COUNT_CYCLES
34446     AllocStart = GetCycleCount32();
34447     unsigned finish;
34448 #elif defined(ENABLE_INSTRUMENTATION)
34449     unsigned AllocStart = GetInstLogTime();
34450     unsigned finish;
34451 #endif //COUNT_CYCLES
34452 #endif //TRACE_GC
34453 
34454 #ifdef MULTIPLE_HEAPS
34455     //take the first heap....
34456     gc_heap* hp = gc_heap::g_heaps[0];
34457 #else
34458     gc_heap* hp = pGenGCHeap;
34459 #ifdef _PREFAST_
34460     // prefix complains about us dereferencing hp in wks build even though we only access static members
34461     // this way. not sure how to shut it up except for this ugly workaround:
34462     PREFIX_ASSUME(hp != NULL);
34463 #endif //_PREFAST_
34464 #endif //MULTIPLE_HEAPS
34465 
34466 #ifndef FEATURE_REDHAWK
34467     GCStress<gc_on_alloc>::MaybeTrigger(generation_alloc_context(hp->generation_of(0)));
34468 #endif // FEATURE_REDHAWK
34469 
34470     alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34471 
34472     newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34473 #ifdef FEATURE_STRUCTALIGN
34474     newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34475 #endif // FEATURE_STRUCTALIGN
34476     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34477 
34478 #ifdef TRACE_GC
34479 #ifdef COUNT_CYCLES
34480     finish = GetCycleCount32();
34481 #elif defined(ENABLE_INSTRUMENTATION)
34482     finish = GetInstLogTime();
34483 #endif //COUNT_CYCLES
34484     AllocDuration += finish - AllocStart;
34485     AllocCount++;
34486 #endif //TRACE_GC
34487     return newAlloc;
34488 }
34489 
34490 Object*
34491 GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_DCL)
34492 {
34493     CONTRACTL {
34494         NOTHROW;
34495         GC_TRIGGERS;
34496     } CONTRACTL_END;
34497 
34498     TRIGGERSGC();
34499 
34500     Object* newAlloc = NULL;
34501     alloc_context* acontext = static_cast<alloc_context*>(context);
34502 
34503 #ifdef TRACE_GC
34504 #ifdef COUNT_CYCLES
34505     AllocStart = GetCycleCount32();
34506     unsigned finish;
34507 #elif defined(ENABLE_INSTRUMENTATION)
34508     unsigned AllocStart = GetInstLogTime();
34509     unsigned finish;
34510 #endif //COUNT_CYCLES
34511 #endif //TRACE_GC
34512 
34513 #ifdef MULTIPLE_HEAPS
34514     if (acontext->get_alloc_heap() == 0)
34515     {
34516         AssignHeap (acontext);
34517         assert (acontext->get_alloc_heap());
34518     }
34519 #endif //MULTIPLE_HEAPS
34520 
34521 #ifndef FEATURE_REDHAWK
34522     GCStress<gc_on_alloc>::MaybeTrigger(acontext);
34523 #endif // FEATURE_REDHAWK
34524 
34525 #ifdef MULTIPLE_HEAPS
34526     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34527 #else
34528     gc_heap* hp = pGenGCHeap;
34529 #ifdef _PREFAST_
34530     // prefix complains about us dereferencing hp in wks build even though we only access static members
34531     // this way. not sure how to shut it up except for this ugly workaround:
34532     PREFIX_ASSUME(hp != NULL);
34533 #endif //_PREFAST_
34534 #endif //MULTIPLE_HEAPS
34535 
34536     if (size < LARGE_OBJECT_SIZE)
34537     {
34538 
34539 #ifdef TRACE_GC
34540         AllocSmallCount++;
34541 #endif //TRACE_GC
34542         newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
34543 #ifdef FEATURE_STRUCTALIGN
34544         newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
34545 #endif // FEATURE_STRUCTALIGN
34546 //        ASSERT (newAlloc);
34547     }
34548     else
34549     {
34550         newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34551 #ifdef FEATURE_STRUCTALIGN
34552         newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34553 #endif // FEATURE_STRUCTALIGN
34554     }
34555 
34556     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34557 
34558 #ifdef TRACE_GC
34559 #ifdef COUNT_CYCLES
34560     finish = GetCycleCount32();
34561 #elif defined(ENABLE_INSTRUMENTATION)
34562     finish = GetInstLogTime();
34563 #endif //COUNT_CYCLES
34564     AllocDuration += finish - AllocStart;
34565     AllocCount++;
34566 #endif //TRACE_GC
34567     return newAlloc;
34568 }
34569 
34570 void
34571 GCHeap::FixAllocContext (gc_alloc_context* context, BOOL lockp, void* arg, void *heap)
34572 {
34573     alloc_context* acontext = static_cast<alloc_context*>(context);
34574 #ifdef MULTIPLE_HEAPS
34575 
34576     if (arg != 0)
34577         acontext->alloc_count = 0;
34578 
34579     uint8_t * alloc_ptr = acontext->alloc_ptr;
34580 
34581     if (!alloc_ptr)
34582         return;
34583 
34584     // The acontext->alloc_heap can be out of sync with the ptrs because
34585     // of heap re-assignment in allocate
34586     gc_heap* hp = gc_heap::heap_of (alloc_ptr);
34587 #else
34588     gc_heap* hp = pGenGCHeap;
34589 #endif //MULTIPLE_HEAPS
34590 
34591     if (heap == NULL || heap == hp)
34592     {
34593         if (lockp)
34594         {
34595             enter_spin_lock (&hp->more_space_lock);
34596         }
34597         hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
34598                                 get_alignment_constant(TRUE));
34599         if (lockp)
34600         {
34601             leave_spin_lock (&hp->more_space_lock);
34602         }
34603     }
34604 }
34605 
34606 Object*
34607 GCHeap::GetContainingObject (void *pInteriorPtr)
34608 {
34609     uint8_t *o = (uint8_t*)pInteriorPtr;
34610 
34611     gc_heap* hp = gc_heap::heap_of (o);
34612     if (o >= hp->lowest_address && o < hp->highest_address)
34613     {
34614         o = hp->find_object (o, hp->gc_low);
34615     }
34616     else
34617     {
34618         o = NULL;
34619     }
34620 
34621     return (Object *)o;
34622 }
34623 
34624 BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
34625 {
34626     if (dd_new_allocation (dd) < 0)
34627     {
34628         return TRUE;
34629     }
34630 
34631     if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
34632     {
34633         return TRUE;
34634     }
34635 
34636     return FALSE;
34637 }
34638 
34639 //----------------------------------------------------------------------------
34640 // #GarbageCollector
34641 //
34642 //  API to ensure that a complete new garbage collection takes place
34643 //
34644 HRESULT
34645 GCHeap::GarbageCollect (int generation, BOOL low_memory_p, int mode)
34646 {
34647 #if defined(BIT64)
34648     if (low_memory_p)
34649     {
34650         size_t total_allocated = 0;
34651         size_t total_desired = 0;
34652 #ifdef MULTIPLE_HEAPS
34653         int hn = 0;
34654         for (hn = 0; hn < gc_heap::n_heaps; hn++)
34655         {
34656             gc_heap* hp = gc_heap::g_heaps [hn];
34657             total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
34658             total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
34659                 dd_new_allocation (hp->dynamic_data_of (0));
34660         }
34661 #else
34662         gc_heap* hp = pGenGCHeap;
34663         total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
34664         total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
34665             dd_new_allocation (hp->dynamic_data_of (0));
34666 #endif //MULTIPLE_HEAPS
34667 
34668         if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
34669         {
34670             dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
34671                          total_allocated, total_desired));
34672 
34673             return S_OK;
34674         }
34675     }
34676 #endif // BIT64
34677 
34678 #ifdef MULTIPLE_HEAPS
34679     gc_heap* hpt = gc_heap::g_heaps[0];
34680 #else
34681     gc_heap* hpt = 0;
34682 #endif //MULTIPLE_HEAPS
34683 
34684     generation = (generation < 0) ? max_generation : min (generation, max_generation);
34685     dynamic_data* dd = hpt->dynamic_data_of (generation);
34686 
34687 #ifdef BACKGROUND_GC
34688     if (recursive_gc_sync::background_running_p())
34689     {
34690         if ((mode == collection_optimized) || (mode & collection_non_blocking))
34691         {
34692             return S_OK;
34693         }
34694         if (mode & collection_blocking)
34695         {
34696             pGenGCHeap->background_gc_wait();
34697             if (mode & collection_optimized)
34698             {
34699                 return S_OK;
34700             }
34701         }
34702     }
34703 #endif //BACKGROUND_GC
34704 
34705     if (mode & collection_optimized)
34706     {
34707         if (pGenGCHeap->gc_started)
34708         {
34709             return S_OK;
34710         }
34711         else
34712         {
34713             BOOL should_collect = FALSE;
34714             BOOL should_check_loh = (generation == max_generation);
34715 #ifdef MULTIPLE_HEAPS
34716             for (int i = 0; i < gc_heap::n_heaps; i++)
34717             {
34718                 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
34719                 dynamic_data* dd2 = (should_check_loh ?
34720                                      (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
34721                                      0);
34722 
34723                 if (should_collect_optimized (dd1, low_memory_p))
34724                 {
34725                     should_collect = TRUE;
34726                     break;
34727                 }
34728                 if (dd2 && should_collect_optimized (dd2, low_memory_p))
34729                 {
34730                     should_collect = TRUE;
34731                     break;
34732                 }
34733             }
34734 #else
34735             should_collect = should_collect_optimized (dd, low_memory_p);
34736             if (!should_collect && should_check_loh)
34737             {
34738                 should_collect =
34739                     should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
34740             }
34741 #endif //MULTIPLE_HEAPS
34742             if (!should_collect)
34743             {
34744                 return S_OK;
34745             }
34746         }
34747     }
34748 
34749     size_t CollectionCountAtEntry = dd_collection_count (dd);
34750     size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
34751     size_t CurrentCollectionCount = 0;
34752 
34753 retry:
34754 
34755     CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
34756 
34757     if ((mode & collection_blocking) &&
34758         (generation == max_generation) &&
34759         (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
34760     {
34761 #ifdef BACKGROUND_GC
34762         if (recursive_gc_sync::background_running_p())
34763         {
34764             pGenGCHeap->background_gc_wait();
34765         }
34766 #endif //BACKGROUND_GC
34767 
34768         goto retry;
34769     }
34770 
34771     if (CollectionCountAtEntry == CurrentCollectionCount)
34772     {
34773         goto retry;
34774     }
34775 
34776     return S_OK;
34777 }
34778 
34779 size_t
34780 GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
34781 {
34782     int gen = (generation < 0) ?
34783                max_generation : min (generation, max_generation);
34784 
34785     gc_reason reason = reason_empty;
34786 
34787     if (low_memory_p)
34788     {
34789         if (mode & collection_blocking)
34790             reason = reason_lowmemory_blocking;
34791         else
34792             reason = reason_lowmemory;
34793     }
34794     else
34795         reason = reason_induced;
34796 
34797     if (reason == reason_induced)
34798     {
34799         if (mode & collection_compacting)
34800         {
34801             reason = reason_induced_compacting;
34802         }
34803         else if (mode & collection_non_blocking)
34804         {
34805             reason = reason_induced_noforce;
34806         }
34807 #ifdef STRESS_HEAP
34808         else if (mode & collection_gcstress)
34809         {
34810             reason = reason_gcstress;
34811         }
34812 #endif
34813     }
34814 
34815     return GarbageCollectGeneration (gen, reason);
34816 }
34817 
34818 void gc_heap::do_pre_gc()
34819 {
34820     STRESS_LOG_GC_STACK;
34821 
34822 #ifdef STRESS_LOG
34823     STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
34824                         (uint32_t)settings.condemned_generation,
34825                         (uint32_t)settings.reason);
34826 #endif // STRESS_LOG
34827 
34828 #ifdef MULTIPLE_HEAPS
34829     gc_heap* hp = g_heaps[0];
34830 #else
34831     gc_heap* hp = 0;
34832 #endif //MULTIPLE_HEAPS
34833 
34834 #ifdef BACKGROUND_GC
34835     settings.b_state = hp->current_bgc_state;
34836 #endif //BACKGROUND_GC
34837 
34838 #ifdef BACKGROUND_GC
34839     dprintf (1, ("*GC* %d(gen0:%d)(%d)(%s)(%d)",
34840         VolatileLoad(&settings.gc_index),
34841         dd_collection_count (hp->dynamic_data_of (0)),
34842         settings.condemned_generation,
34843         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
34844         settings.b_state));
34845 #else
34846     dprintf (1, ("*GC* %d(gen0:%d)(%d)",
34847         VolatileLoad(&settings.gc_index),
34848         dd_collection_count(hp->dynamic_data_of(0)),
34849         settings.condemned_generation));
34850 #endif //BACKGROUND_GC
34851 
34852     // TODO: this can happen...it's because of the way we are calling
34853     // do_pre_gc, will fix later.
34854     //if (last_gc_index > VolatileLoad(&settings.gc_index))
34855     //{
34856     //    FATAL_GC_ERROR();
34857     //}
34858 
34859     last_gc_index = VolatileLoad(&settings.gc_index);
34860     GCHeap::UpdatePreGCCounters();
34861 
34862     if (settings.concurrent)
34863     {
34864 #ifdef BACKGROUND_GC
34865         full_gc_counts[gc_type_background]++;
34866 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34867         GCHeap::gc_stress_fgcs_in_bgc = 0;
34868 #endif // STRESS_HEAP && !FEATURE_REDHAWK
34869 #endif // BACKGROUND_GC
34870     }
34871     else
34872     {
34873         if (settings.condemned_generation == max_generation)
34874         {
34875             full_gc_counts[gc_type_blocking]++;
34876         }
34877         else
34878         {
34879 #ifdef BACKGROUND_GC
34880             if (settings.background_p)
34881             {
34882                 ephemeral_fgc_counts[settings.condemned_generation]++;
34883             }
34884 #endif //BACKGROUND_GC
34885         }
34886     }
34887 
34888 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34889     if (g_fEnableARM)
34890     {
34891         SystemDomain::ResetADSurvivedBytes();
34892     }
34893 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34894 }
34895 
34896 #ifdef GC_CONFIG_DRIVEN
34897 void gc_heap::record_interesting_info_per_heap()
34898 {
34899     // datapoints are always from the last blocking GC so don't record again
34900     // for BGCs.
34901     if (!(settings.concurrent))
34902     {
34903         for (int i = 0; i < max_idp_count; i++)
34904         {
34905             interesting_data_per_heap[i] += interesting_data_per_gc[i];
34906         }
34907     }
34908 
34909     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
34910     if (compact_reason >= 0)
34911         (compact_reasons_per_heap[compact_reason])++;
34912     int expand_mechanism = get_gc_data_per_heap()->get_mechanism (gc_heap_expand);
34913     if (expand_mechanism >= 0)
34914         (expand_mechanisms_per_heap[expand_mechanism])++;
34915 
34916     for (int i = 0; i < max_gc_mechanism_bits_count; i++)
34917     {
34918         if (get_gc_data_per_heap()->is_mechanism_bit_set ((gc_mechanism_bit_per_heap)i))
34919             (interesting_mechanism_bits_per_heap[i])++;
34920     }
34921 
34922     //         h#  | GC  | gen | C   | EX  | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
34923     cprintf (("%2d | %6d | %1d | %1s | %2s | %2s | %2s | %2s | %2s || %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id |",
34924             heap_number,
34925             (size_t)settings.gc_index,
34926             settings.condemned_generation,
34927             // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs.
34928             (settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W") : ""), // compaction
34929             ((expand_mechanism >= 0)? "X" : ""), // EX
34930             ((expand_mechanism == expand_reuse_normal) ? "X" : ""), // NF
34931             ((expand_mechanism == expand_reuse_bestfit) ? "X" : ""), // BF
34932             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_mark_list_bit) ? "X" : ""), // ML
34933             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_demotion_bit) ? "X" : ""), // DM
34934             interesting_data_per_gc[idp_pre_short],
34935             interesting_data_per_gc[idp_post_short],
34936             interesting_data_per_gc[idp_merged_pin],
34937             interesting_data_per_gc[idp_converted_pin],
34938             interesting_data_per_gc[idp_pre_pin],
34939             interesting_data_per_gc[idp_post_pin],
34940             interesting_data_per_gc[idp_pre_and_post_pin],
34941             interesting_data_per_gc[idp_pre_short_padded],
34942             interesting_data_per_gc[idp_post_short_padded]));
34943 }
34944 
34945 void gc_heap::record_global_mechanisms()
34946 {
34947     for (int i = 0; i < max_global_mechanisms_count; i++)
34948     {
34949         if (gc_data_global.get_mechanism_p ((gc_global_mechanism_p)i))
34950         {
34951             ::record_global_mechanism (i);
34952         }
34953     }
34954 }
34955 
34956 BOOL gc_heap::should_do_sweeping_gc (BOOL compact_p)
34957 {
34958     if (!compact_ratio)
34959         return (!compact_p);
34960 
34961     size_t compact_count = compact_or_sweep_gcs[0];
34962     size_t sweep_count = compact_or_sweep_gcs[1];
34963 
34964     size_t total_count = compact_count + sweep_count;
34965     BOOL should_compact = compact_p;
34966     if (total_count > 3)
34967     {
34968         if (compact_p)
34969         {
34970             int temp_ratio = (int)((compact_count + 1) * 100 / (total_count + 1));
34971             if (temp_ratio > compact_ratio)
34972             {
34973                 // cprintf (("compact would be: %d, total_count: %d, ratio would be %d%% > target\n",
34974                 //     (compact_count + 1), (total_count + 1), temp_ratio));
34975                 should_compact = FALSE;
34976             }
34977         }
34978         else
34979         {
34980             int temp_ratio = (int)((sweep_count + 1) * 100 / (total_count + 1));
34981             if (temp_ratio > (100 - compact_ratio))
34982             {
34983                 // cprintf (("sweep would be: %d, total_count: %d, ratio would be %d%% > target\n",
34984                 //     (sweep_count + 1), (total_count + 1), temp_ratio));
34985                 should_compact = TRUE;
34986             }
34987         }
34988     }
34989 
34990     return !should_compact;
34991 }
34992 #endif //GC_CONFIG_DRIVEN
34993 
34994 void gc_heap::do_post_gc()
34995 {
34996     if (!settings.concurrent)
34997     {
34998         initGCShadow();
34999     }
35000 
35001 #ifdef TRACE_GC
35002 #ifdef COUNT_CYCLES
35003     AllocStart = GetCycleCount32();
35004 #else
35005     AllocStart = clock();
35006 #endif //COUNT_CYCLES
35007 #endif //TRACE_GC
35008 
35009 #ifdef MULTIPLE_HEAPS
35010     gc_heap* hp = g_heaps[0];
35011 #else
35012     gc_heap* hp = 0;
35013 #endif //MULTIPLE_HEAPS
35014 
35015     GCToEEInterface::GcDone(settings.condemned_generation);
35016 
35017     GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index),
35018                          (uint32_t)settings.condemned_generation,
35019                          (uint32_t)settings.reason,
35020                          !!settings.concurrent);
35021 
35022     //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)",
35023     dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)",
35024         VolatileLoad(&settings.gc_index),
35025         dd_collection_count(hp->dynamic_data_of(0)),
35026         settings.condemned_generation,
35027         (settings.concurrent ? "BGC" : "GC")));
35028 
35029     GCHeap::UpdatePostGCCounters();
35030 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
35031     //if (g_fEnableARM)
35032     //{
35033     //    SystemDomain::GetADSurvivedBytes();
35034     //}
35035 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
35036 
35037 #ifdef STRESS_LOG
35038     STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
35039                       (uint32_t)settings.condemned_generation,
35040                       (uint32_t)settings.reason);
35041 #endif // STRESS_LOG
35042 
35043 #ifdef GC_CONFIG_DRIVEN
35044     if (!settings.concurrent)
35045     {
35046         if (settings.compaction)
35047             (compact_or_sweep_gcs[0])++;
35048         else
35049             (compact_or_sweep_gcs[1])++;
35050     }
35051 
35052 #ifdef MULTIPLE_HEAPS
35053     for (int i = 0; i < n_heaps; i++)
35054         g_heaps[i]->record_interesting_info_per_heap();
35055 #else
35056     record_interesting_info_per_heap();
35057 #endif //MULTIPLE_HEAPS
35058     record_global_mechanisms();
35059 #endif //GC_CONFIG_DRIVEN
35060 }
35061 
35062 unsigned GCHeap::GetGcCount()
35063 {
35064     return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
35065 }
35066 
35067 size_t
35068 GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
35069 {
35070     dprintf (2, ("triggered a GC!"));
35071 
35072 #ifdef MULTIPLE_HEAPS
35073     gc_heap* hpt = gc_heap::g_heaps[0];
35074 #else
35075     gc_heap* hpt = 0;
35076 #endif //MULTIPLE_HEAPS
35077     Thread* current_thread = GetThread();
35078     BOOL cooperative_mode = TRUE;
35079     dynamic_data* dd = hpt->dynamic_data_of (gen);
35080     size_t localCount = dd_collection_count (dd);
35081 
35082     enter_spin_lock (&gc_heap::gc_lock);
35083     dprintf (SPINLOCK_LOG, ("GC Egc"));
35084     ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
35085 
35086     //don't trigger another GC if one was already in progress
35087     //while waiting for the lock
35088     {
35089         size_t col_count = dd_collection_count (dd);
35090 
35091         if (localCount != col_count)
35092         {
35093 #ifdef SYNCHRONIZATION_STATS
35094             gc_lock_contended++;
35095 #endif //SYNCHRONIZATION_STATS
35096             dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
35097             leave_spin_lock (&gc_heap::gc_lock);
35098 
35099             // We don't need to release msl here 'cause this means a GC
35100             // has happened and would have release all msl's.
35101             return col_count;
35102          }
35103     }
35104 
35105 #ifdef COUNT_CYCLES
35106     int gc_start = GetCycleCount32();
35107 #endif //COUNT_CYCLES
35108 
35109 #ifdef TRACE_GC
35110 #ifdef COUNT_CYCLES
35111     AllocDuration += GetCycleCount32() - AllocStart;
35112 #else
35113     AllocDuration += clock() - AllocStart;
35114 #endif //COUNT_CYCLES
35115 #endif //TRACE_GC
35116 
35117     gc_heap::g_low_memory_status = (reason == reason_lowmemory) ||
35118                                    (reason == reason_lowmemory_blocking) ||
35119                                    g_bLowMemoryFromHost;
35120 
35121     if (g_bLowMemoryFromHost)
35122         reason = reason_lowmemory_host;
35123 
35124     gc_trigger_reason = reason;
35125 
35126 #ifdef MULTIPLE_HEAPS
35127     for (int i = 0; i < gc_heap::n_heaps; i++)
35128     {
35129         gc_heap::g_heaps[i]->reset_gc_done();
35130     }
35131 #else
35132     gc_heap::reset_gc_done();
35133 #endif //MULTIPLE_HEAPS
35134 
35135     gc_heap::gc_started = TRUE;
35136 
35137     {
35138         init_sync_log_stats();
35139 
35140 #ifndef MULTIPLE_HEAPS
35141         cooperative_mode = gc_heap::enable_preemptive (current_thread);
35142 
35143         dprintf (2, ("Suspending EE"));
35144         BEGIN_TIMING(suspend_ee_during_log);
35145         GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
35146         END_TIMING(suspend_ee_during_log);
35147         gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();
35148         gc_heap::disable_preemptive (current_thread, cooperative_mode);
35149         if (gc_heap::proceed_with_gc_p)
35150             pGenGCHeap->settings.init_mechanisms();
35151         else
35152             gc_heap::update_collection_counts_for_no_gc();
35153 
35154 #endif //!MULTIPLE_HEAPS
35155     }
35156 
35157 // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
35158 
35159 #ifdef TRACE_GC
35160 #ifdef COUNT_CYCLES
35161     unsigned start;
35162     unsigned finish;
35163     start = GetCycleCount32();
35164 #else
35165     clock_t start;
35166     clock_t finish;
35167     start = clock();
35168 #endif //COUNT_CYCLES
35169     PromotedObjectCount = 0;
35170 #endif //TRACE_GC
35171 
35172     unsigned int condemned_generation_number = gen;
35173 
35174     // We want to get a stack from the user thread that triggered the GC
35175     // instead of on the GC thread which is the case for Server GC.
35176     // But we are doing it for Workstation GC as well to be uniform.
35177     FireEtwGCTriggered((int) reason, GetClrInstanceId());
35178 
35179 #ifdef MULTIPLE_HEAPS
35180     GcCondemnedGeneration = condemned_generation_number;
35181 
35182     cooperative_mode = gc_heap::enable_preemptive (current_thread);
35183 
35184     BEGIN_TIMING(gc_during_log);
35185     gc_heap::ee_suspend_event.Set();
35186     gc_heap::wait_for_gc_done();
35187     END_TIMING(gc_during_log);
35188 
35189     gc_heap::disable_preemptive (current_thread, cooperative_mode);
35190 
35191     condemned_generation_number = GcCondemnedGeneration;
35192 #else
35193     if (gc_heap::proceed_with_gc_p)
35194     {
35195         BEGIN_TIMING(gc_during_log);
35196         pGenGCHeap->garbage_collect (condemned_generation_number);
35197         END_TIMING(gc_during_log);
35198     }
35199 #endif //MULTIPLE_HEAPS
35200 
35201 #ifdef TRACE_GC
35202 #ifdef COUNT_CYCLES
35203     finish = GetCycleCount32();
35204 #else
35205     finish = clock();
35206 #endif //COUNT_CYCLES
35207     GcDuration += finish - start;
35208     dprintf (3,
35209              ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
35210               VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
35211               finish - start, GcDuration,
35212               AllocCount ? (AllocDuration / AllocCount) : 0,
35213               AllocSmallCount, AllocBigCount));
35214     AllocCount = 0;
35215     AllocDuration = 0;
35216 #endif // TRACE_GC
35217 
35218 #ifdef BACKGROUND_GC
35219     // We are deciding whether we should fire the alloc wait end event here
35220     // because in begin_foreground we could be calling end_foreground
35221     // if we need to retry.
35222     if (gc_heap::alloc_wait_event_p)
35223     {
35224         hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
35225         gc_heap::alloc_wait_event_p = FALSE;
35226     }
35227 #endif //BACKGROUND_GC
35228 
35229 #ifndef MULTIPLE_HEAPS
35230 #ifdef BACKGROUND_GC
35231     if (!gc_heap::dont_restart_ee_p)
35232     {
35233 #endif //BACKGROUND_GC
35234         BEGIN_TIMING(restart_ee_during_log);
35235         GCToEEInterface::RestartEE(TRUE);
35236         END_TIMING(restart_ee_during_log);
35237 #ifdef BACKGROUND_GC
35238     }
35239 #endif //BACKGROUND_GC
35240 #endif //!MULTIPLE_HEAPS
35241 
35242 #ifdef COUNT_CYCLES
35243     printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
35244             GetCycleCount32() - gc_start);
35245 #endif //COUNT_CYCLES
35246 
35247 #ifndef MULTIPLE_HEAPS
35248     process_sync_log_stats();
35249     gc_heap::gc_started = FALSE;
35250     gc_heap::set_gc_done();
35251     dprintf (SPINLOCK_LOG, ("GC Lgc"));
35252     leave_spin_lock (&gc_heap::gc_lock);
35253 #endif //!MULTIPLE_HEAPS
35254 
35255 #ifdef FEATURE_PREMORTEM_FINALIZATION
35256     GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers);
35257 #endif // FEATURE_PREMORTEM_FINALIZATION
35258 
35259     return dd_collection_count (dd);
35260 }
35261 
35262 size_t      GCHeap::GetTotalBytesInUse ()
35263 {
35264 #ifdef MULTIPLE_HEAPS
35265     //enumarate all the heaps and get their size.
35266     size_t tot_size = 0;
35267     for (int i = 0; i < gc_heap::n_heaps; i++)
35268     {
35269         GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
35270         tot_size += Hp->ApproxTotalBytesInUse (FALSE);
35271     }
35272     return tot_size;
35273 #else
35274     return ApproxTotalBytesInUse ();
35275 #endif //MULTIPLE_HEAPS
35276 }
35277 
35278 int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
35279 {
35280     if (get_bgc_fgc_count != 0)
35281     {
35282 #ifdef BACKGROUND_GC
35283         if (generation == max_generation)
35284         {
35285             return (int)(gc_heap::full_gc_counts[gc_type_background]);
35286         }
35287         else
35288         {
35289             return (int)(gc_heap::ephemeral_fgc_counts[generation]);
35290         }
35291 #else
35292         return 0;
35293 #endif //BACKGROUND_GC
35294     }
35295 
35296 #ifdef MULTIPLE_HEAPS
35297     gc_heap* hp = gc_heap::g_heaps [0];
35298 #else  //MULTIPLE_HEAPS
35299     gc_heap* hp = pGenGCHeap;
35300 #endif //MULTIPLE_HEAPS
35301     if (generation > max_generation)
35302         return 0;
35303     else
35304         return (int)dd_collection_count (hp->dynamic_data_of (generation));
35305 }
35306 
35307 size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
35308 {
35309     size_t totsize = 0;
35310     //GCTODO
35311     //ASSERT(InMustComplete());
35312     enter_spin_lock (&pGenGCHeap->gc_lock);
35313 
35314     heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
35315     // Get small block heap size info
35316     totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
35317     heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
35318     while (seg1 != eph_seg)
35319     {
35320         totsize += heap_segment_allocated (seg1) -
35321             heap_segment_mem (seg1);
35322         seg1 = heap_segment_next (seg1);
35323     }
35324 
35325     //discount the fragmentation
35326     for (int i = 0; i <= max_generation; i++)
35327     {
35328         generation* gen = pGenGCHeap->generation_of (i);
35329         totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
35330     }
35331 
35332     if (!small_heap_only)
35333     {
35334         heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
35335 
35336         while (seg2 != 0)
35337         {
35338             totsize += heap_segment_allocated (seg2) -
35339                 heap_segment_mem (seg2);
35340             seg2 = heap_segment_next (seg2);
35341         }
35342 
35343         //discount the fragmentation
35344         generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
35345         size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
35346         totsize -= frag;
35347     }
35348     leave_spin_lock (&pGenGCHeap->gc_lock);
35349     return totsize;
35350 }
35351 
35352 #ifdef MULTIPLE_HEAPS
35353 void GCHeap::AssignHeap (alloc_context* acontext)
35354 {
35355     // Assign heap based on processor
35356     acontext->set_alloc_heap(GetHeap(heap_select::select_heap(acontext, 0)));
35357     acontext->set_home_heap(acontext->get_alloc_heap());
35358 }
35359 GCHeap* GCHeap::GetHeap (int n)
35360 {
35361     assert (n < gc_heap::n_heaps);
35362     return gc_heap::g_heaps [n]->vm_heap;
35363 }
35364 #endif //MULTIPLE_HEAPS
35365 
35366 bool GCHeap::IsThreadUsingAllocationContextHeap(gc_alloc_context* context, int thread_number)
35367 {
35368     alloc_context* acontext = static_cast<alloc_context*>(context);
35369 #ifdef MULTIPLE_HEAPS
35370     return ((acontext->get_home_heap() == GetHeap(thread_number)) ||
35371             ((acontext->get_home_heap() == 0) && (thread_number == 0)));
35372 #else
35373     UNREFERENCED_PARAMETER(acontext);
35374     UNREFERENCED_PARAMETER(thread_number);
35375     return true;
35376 #endif //MULTIPLE_HEAPS
35377 }
35378 
35379 // Returns the number of processors required to trigger the use of thread based allocation contexts
35380 int GCHeap::GetNumberOfHeaps ()
35381 {
35382 #ifdef MULTIPLE_HEAPS
35383     return gc_heap::n_heaps;
35384 #else
35385     return 1;
35386 #endif //MULTIPLE_HEAPS
35387 }
35388 
35389 /*
35390   in this way we spend extra time cycling through all the heaps while create the handle
35391   it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
35392 */
35393 int GCHeap::GetHomeHeapNumber ()
35394 {
35395 #ifdef MULTIPLE_HEAPS
35396     Thread *pThread = GetThread();
35397     for (int i = 0; i < gc_heap::n_heaps; i++)
35398     {
35399         if (pThread)
35400         {
35401             gc_alloc_context* ctx = GCToEEInterface::GetAllocContext(pThread);
35402             GCHeap *hp = static_cast<alloc_context*>(ctx)->get_home_heap();
35403             if (hp == gc_heap::g_heaps[i]->vm_heap) return i;
35404         }
35405     }
35406     return 0;
35407 #else
35408     return 0;
35409 #endif //MULTIPLE_HEAPS
35410 }
35411 
35412 unsigned int GCHeap::GetCondemnedGeneration()
35413 {
35414     return gc_heap::settings.condemned_generation;
35415 }
35416 
35417 int GCHeap::GetGcLatencyMode()
35418 {
35419     return (int)(pGenGCHeap->settings.pause_mode);
35420 }
35421 
35422 int GCHeap::SetGcLatencyMode (int newLatencyMode)
35423 {
35424     if (gc_heap::settings.pause_mode == pause_no_gc)
35425         return (int)set_pause_mode_no_gc;
35426 
35427     gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
35428 
35429     if (new_mode == pause_low_latency)
35430     {
35431 #ifndef MULTIPLE_HEAPS
35432         pGenGCHeap->settings.pause_mode = new_mode;
35433 #endif //!MULTIPLE_HEAPS
35434     }
35435     else if (new_mode == pause_sustained_low_latency)
35436     {
35437 #ifdef BACKGROUND_GC
35438         if (gc_heap::gc_can_use_concurrent)
35439         {
35440             pGenGCHeap->settings.pause_mode = new_mode;
35441         }
35442 #endif //BACKGROUND_GC
35443     }
35444     else
35445     {
35446         pGenGCHeap->settings.pause_mode = new_mode;
35447     }
35448 
35449 #ifdef BACKGROUND_GC
35450     if (recursive_gc_sync::background_running_p())
35451     {
35452         // If we get here, it means we are doing an FGC. If the pause
35453         // mode was altered we will need to save it in the BGC settings.
35454         if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
35455         {
35456             gc_heap::saved_bgc_settings.pause_mode = new_mode;
35457         }
35458     }
35459 #endif //BACKGROUND_GC
35460 
35461     return (int)set_pause_mode_success;
35462 }
35463 
35464 int GCHeap::GetLOHCompactionMode()
35465 {
35466     return pGenGCHeap->loh_compaction_mode;
35467 }
35468 
35469 void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
35470 {
35471 #ifdef FEATURE_LOH_COMPACTION
35472     pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
35473 #endif //FEATURE_LOH_COMPACTION
35474 }
35475 
35476 BOOL GCHeap::RegisterForFullGCNotification(uint32_t gen2Percentage,
35477                                            uint32_t lohPercentage)
35478 {
35479 #ifdef MULTIPLE_HEAPS
35480     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35481     {
35482         gc_heap* hp = gc_heap::g_heaps [hn];
35483         hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
35484     }
35485 #else //MULTIPLE_HEAPS
35486     pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
35487 #endif //MULTIPLE_HEAPS
35488 
35489     pGenGCHeap->full_gc_approach_event.Reset();
35490     pGenGCHeap->full_gc_end_event.Reset();
35491     pGenGCHeap->full_gc_approach_event_set = false;
35492 
35493     pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
35494     pGenGCHeap->fgn_loh_percent = lohPercentage;
35495 
35496     return TRUE;
35497 }
35498 
35499 BOOL GCHeap::CancelFullGCNotification()
35500 {
35501     pGenGCHeap->fgn_maxgen_percent = 0;
35502     pGenGCHeap->fgn_loh_percent = 0;
35503 
35504     pGenGCHeap->full_gc_approach_event.Set();
35505     pGenGCHeap->full_gc_end_event.Set();
35506 
35507     return TRUE;
35508 }
35509 
35510 int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
35511 {
35512     dprintf (2, ("WFGA: Begin wait"));
35513     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
35514     dprintf (2, ("WFGA: End wait"));
35515     return result;
35516 }
35517 
35518 int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
35519 {
35520     dprintf (2, ("WFGE: Begin wait"));
35521     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
35522     dprintf (2, ("WFGE: End wait"));
35523     return result;
35524 }
35525 
35526 int GCHeap::StartNoGCRegion(uint64_t totalSize, BOOL lohSizeKnown, uint64_t lohSize, BOOL disallowFullBlockingGC)
35527 {
35528     AllocLockHolder lh;
35529 
35530     dprintf (1, ("begin no gc called"));
35531     start_no_gc_region_status status = gc_heap::prepare_for_no_gc_region (totalSize, lohSizeKnown, lohSize, disallowFullBlockingGC);
35532     if (status == start_no_gc_success)
35533     {
35534         GarbageCollect (max_generation);
35535         status = gc_heap::get_start_no_gc_region_status();
35536     }
35537 
35538     if (status != start_no_gc_success)
35539         gc_heap::handle_failure_for_no_gc();
35540 
35541     return (int)status;
35542 }
35543 
35544 int GCHeap::EndNoGCRegion()
35545 {
35546     AllocLockHolder lh;
35547     return (int)gc_heap::end_no_gc_region();
35548 }
35549 
35550 void GCHeap::PublishObject (uint8_t* Obj)
35551 {
35552 #ifdef BACKGROUND_GC
35553     gc_heap* hp = gc_heap::heap_of (Obj);
35554     hp->bgc_alloc_lock->loh_alloc_done (Obj);
35555 #endif //BACKGROUND_GC
35556 }
35557 
35558 // The spec for this one isn't clear. This function
35559 // returns the size that can be allocated without
35560 // triggering a GC of any kind.
35561 size_t GCHeap::ApproxFreeBytes()
35562 {
35563     //GCTODO
35564     //ASSERT(InMustComplete());
35565     enter_spin_lock (&pGenGCHeap->gc_lock);
35566 
35567     generation* gen = pGenGCHeap->generation_of (0);
35568     size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
35569 
35570     leave_spin_lock (&pGenGCHeap->gc_lock);
35571 
35572     return res;
35573 }
35574 
35575 HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
35576 {
35577     if ((gen < 0) || (gen > max_generation))
35578         return E_FAIL;
35579 #ifdef MULTIPLE_HEAPS
35580     counters->current_size = 0;
35581     counters->promoted_size = 0;
35582     counters->collection_count = 0;
35583 
35584     //enumarate all the heaps and get their counters.
35585     for (int i = 0; i < gc_heap::n_heaps; i++)
35586     {
35587         dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
35588 
35589         counters->current_size += dd_current_size (dd);
35590         counters->promoted_size += dd_promoted_size (dd);
35591         if (i == 0)
35592         counters->collection_count += dd_collection_count (dd);
35593     }
35594 #else
35595     dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
35596     counters->current_size = dd_current_size (dd);
35597     counters->promoted_size = dd_promoted_size (dd);
35598     counters->collection_count = dd_collection_count (dd);
35599 #endif //MULTIPLE_HEAPS
35600     return S_OK;
35601 }
35602 
35603 // Get the segment size to use, making sure it conforms.
35604 size_t GCHeap::GetValidSegmentSize(BOOL large_seg)
35605 {
35606     return get_valid_segment_size (large_seg);
35607 }
35608 
35609 // Get the max gen0 heap size, making sure it conforms.
35610 size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
35611 {
35612     size_t gen0size = g_pConfig->GetGCgen0size();
35613 
35614     if ((gen0size == 0) || !g_theGCHeap->IsValidGen0MaxSize(gen0size))
35615     {
35616 #ifdef SERVER_GC
35617         // performance data seems to indicate halving the size results
35618         // in optimal perf.  Ask for adjusted gen0 size.
35619         gen0size = max(GCToOSInterface::GetLargestOnDieCacheSize(FALSE)/GCToOSInterface::GetLogicalCpuCount(),(256*1024));
35620 #if (defined(_TARGET_AMD64_))
35621         // if gen0 size is too large given the available memory, reduce it.
35622         // Get true cache size, as we don't want to reduce below this.
35623         size_t trueSize = max(GCToOSInterface::GetLargestOnDieCacheSize(TRUE)/GCToOSInterface::GetLogicalCpuCount(),(256*1024));
35624         dprintf (2, ("cache: %Id-%Id, cpu: %Id",
35625             GCToOSInterface::GetLargestOnDieCacheSize(FALSE),
35626             GCToOSInterface::GetLargestOnDieCacheSize(TRUE),
35627             GCToOSInterface::GetLogicalCpuCount()));
35628 
35629         // if the total min GC across heaps will exceed 1/6th of available memory,
35630         // then reduce the min GC size until it either fits or has been reduced to cache size.
35631         while ((gen0size * gc_heap::n_heaps) > GCToOSInterface::GetPhysicalMemoryLimit() / 6)
35632         {
35633             gen0size = gen0size / 2;
35634             if (gen0size <= trueSize)
35635             {
35636                 gen0size = trueSize;
35637                 break;
35638             }
35639         }
35640 #endif //_TARGET_AMD64_
35641 
35642 #else //SERVER_GC
35643         gen0size = max((4*GCToOSInterface::GetLargestOnDieCacheSize(TRUE)/5),(256*1024));
35644 #endif //SERVER_GC
35645     }
35646 
35647     // Generation 0 must never be more than 1/2 the segment size.
35648     if (gen0size >= (seg_size / 2))
35649         gen0size = seg_size / 2;
35650 
35651     return (gen0size);
35652 }
35653 
35654 void GCHeap::SetReservedVMLimit (size_t vmlimit)
35655 {
35656     gc_heap::reserved_memory_limit = vmlimit;
35657 }
35658 
35659 
35660 //versions of same method on each heap
35661 
35662 #ifdef FEATURE_PREMORTEM_FINALIZATION
35663 
35664 Object* GCHeap::GetNextFinalizableObject()
35665 {
35666 
35667 #ifdef MULTIPLE_HEAPS
35668 
35669     //return the first non critical one in the first queue.
35670     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35671     {
35672         gc_heap* hp = gc_heap::g_heaps [hn];
35673         Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
35674         if (O)
35675             return O;
35676     }
35677     //return the first non crtitical/critical one in the first queue.
35678     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35679     {
35680         gc_heap* hp = gc_heap::g_heaps [hn];
35681         Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
35682         if (O)
35683             return O;
35684     }
35685     return 0;
35686 
35687 
35688 #else //MULTIPLE_HEAPS
35689     return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
35690 #endif //MULTIPLE_HEAPS
35691 
35692 }
35693 
35694 size_t GCHeap::GetNumberFinalizableObjects()
35695 {
35696 #ifdef MULTIPLE_HEAPS
35697     size_t cnt = 0;
35698     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35699     {
35700         gc_heap* hp = gc_heap::g_heaps [hn];
35701         cnt += hp->finalize_queue->GetNumberFinalizableObjects();
35702     }
35703     return cnt;
35704 
35705 
35706 #else //MULTIPLE_HEAPS
35707     return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
35708 #endif //MULTIPLE_HEAPS
35709 }
35710 
35711 size_t GCHeap::GetFinalizablePromotedCount()
35712 {
35713 #ifdef MULTIPLE_HEAPS
35714     size_t cnt = 0;
35715 
35716     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35717     {
35718         gc_heap* hp = gc_heap::g_heaps [hn];
35719         cnt += hp->finalize_queue->GetPromotedCount();
35720     }
35721     return cnt;
35722 
35723 #else //MULTIPLE_HEAPS
35724     return pGenGCHeap->finalize_queue->GetPromotedCount();
35725 #endif //MULTIPLE_HEAPS
35726 }
35727 
35728 BOOL GCHeap::FinalizeAppDomain(AppDomain *pDomain, BOOL fRunFinalizers)
35729 {
35730 #ifdef MULTIPLE_HEAPS
35731     BOOL foundp = FALSE;
35732     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35733     {
35734         gc_heap* hp = gc_heap::g_heaps [hn];
35735         if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
35736             foundp = TRUE;
35737     }
35738     return foundp;
35739 
35740 #else //MULTIPLE_HEAPS
35741     return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
35742 #endif //MULTIPLE_HEAPS
35743 }
35744 
35745 BOOL GCHeap::ShouldRestartFinalizerWatchDog()
35746 {
35747     // This condition was historically used as part of the condition to detect finalizer thread timeouts
35748     return gc_heap::gc_lock.lock != -1;
35749 }
35750 
35751 void GCHeap::SetFinalizeQueueForShutdown(BOOL fHasLock)
35752 {
35753 #ifdef MULTIPLE_HEAPS
35754     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35755     {
35756         gc_heap* hp = gc_heap::g_heaps [hn];
35757         hp->finalize_queue->SetSegForShutDown(fHasLock);
35758     }
35759 
35760 #else //MULTIPLE_HEAPS
35761     pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
35762 #endif //MULTIPLE_HEAPS
35763 }
35764 
35765 //---------------------------------------------------------------------------
35766 // Finalized class tracking
35767 //---------------------------------------------------------------------------
35768 
35769 bool GCHeap::RegisterForFinalization (int gen, Object* obj)
35770 {
35771     if (gen == -1)
35772         gen = 0;
35773     if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
35774     {
35775         //just reset the bit
35776         ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
35777         return true;
35778     }
35779     else
35780     {
35781         gc_heap* hp = gc_heap::heap_of ((uint8_t*)obj);
35782         return hp->finalize_queue->RegisterForFinalization (gen, obj);
35783     }
35784 }
35785 
35786 void GCHeap::SetFinalizationRun (Object* obj)
35787 {
35788     ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
35789 }
35790 
35791 #endif // FEATURE_PREMORTEM_FINALIZATION
35792 
35793 #ifdef FEATURE_PREMORTEM_FINALIZATION
35794 
35795 //--------------------------------------------------------------------
35796 //
35797 //          Support for finalization
35798 //
35799 //--------------------------------------------------------------------
35800 
35801 inline
35802 unsigned int gen_segment (int gen)
35803 {
35804     assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
35805     return (NUMBERGENERATIONS - gen - 1);
35806 }
35807 
35808 bool CFinalize::Initialize()
35809 {
35810     CONTRACTL {
35811         NOTHROW;
35812         GC_NOTRIGGER;
35813     } CONTRACTL_END;
35814 
35815     m_Array = new (nothrow)(Object*[100]);
35816 
35817     if (!m_Array)
35818     {
35819         ASSERT (m_Array);
35820         STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
35821         if (g_pConfig->IsGCBreakOnOOMEnabled())
35822         {
35823             GCToOSInterface::DebugBreak();
35824         }
35825         return false;
35826     }
35827     m_EndArray = &m_Array[100];
35828 
35829     for (int i =0; i < FreeList; i++)
35830     {
35831         SegQueueLimit (i) = m_Array;
35832     }
35833     m_PromotedCount = 0;
35834     lock = -1;
35835 #ifdef _DEBUG
35836     lockowner_threadid.Clear();
35837 #endif // _DEBUG
35838 
35839     return true;
35840 }
35841 
35842 CFinalize::~CFinalize()
35843 {
35844     delete m_Array;
35845 }
35846 
35847 size_t CFinalize::GetPromotedCount ()
35848 {
35849     return m_PromotedCount;
35850 }
35851 
35852 inline
35853 void CFinalize::EnterFinalizeLock()
35854 {
35855     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
35856              GetThread() == 0 ||
35857              GCToEEInterface::IsPreemptiveGCDisabled(GetThread()));
35858 
35859 retry:
35860     if (Interlocked::Exchange (&lock, 0) >= 0)
35861     {
35862         unsigned int i = 0;
35863         while (lock >= 0)
35864         {
35865             YieldProcessor();           // indicate to the processor that we are spining
35866             if (++i & 7)
35867                 GCToOSInterface::YieldThread (0);
35868             else
35869                 GCToOSInterface::Sleep (5);
35870         }
35871         goto retry;
35872     }
35873 
35874 #ifdef _DEBUG
35875     lockowner_threadid.SetToCurrentThread();
35876 #endif // _DEBUG
35877 }
35878 
35879 inline
35880 void CFinalize::LeaveFinalizeLock()
35881 {
35882     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
35883              GetThread() == 0 ||
35884              GCToEEInterface::IsPreemptiveGCDisabled(GetThread()));
35885 
35886 #ifdef _DEBUG
35887     lockowner_threadid.Clear();
35888 #endif // _DEBUG
35889     lock = -1;
35890 }
35891 
35892 bool
35893 CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
35894 {
35895     CONTRACTL {
35896         NOTHROW;
35897         GC_NOTRIGGER;
35898     } CONTRACTL_END;
35899 
35900     EnterFinalizeLock();
35901     // Adjust gen
35902     unsigned int dest = 0;
35903 
35904     if (g_fFinalizerRunOnShutDown)
35905     {
35906         //no method table available yet,
35907         //put it in the finalizer queue and sort out when
35908         //dequeueing
35909         dest = FinalizerListSeg;
35910     }
35911 
35912     else
35913         dest = gen_segment (gen);
35914 
35915     // Adjust boundary for segments so that GC will keep objects alive.
35916     Object*** s_i = &SegQueue (FreeList);
35917     if ((*s_i) == m_EndArray)
35918     {
35919         if (!GrowArray())
35920         {
35921             LeaveFinalizeLock();
35922             if (method_table(obj) == NULL)
35923             {
35924                 // If the object is uninitialized, a valid size should have been passed.
35925                 assert (size >= Align (min_obj_size));
35926                 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
35927                 ((CObjectHeader*)obj)->SetFree(size);
35928             }
35929             STRESS_LOG_OOM_STACK(0);
35930             if (g_pConfig->IsGCBreakOnOOMEnabled())
35931             {
35932                 GCToOSInterface::DebugBreak();
35933             }
35934             return false;
35935         }
35936     }
35937     Object*** end_si = &SegQueueLimit (dest);
35938     do
35939     {
35940         //is the segment empty?
35941         if (!(*s_i == *(s_i-1)))
35942         {
35943             //no, swap the end elements.
35944             *(*s_i) = *(*(s_i-1));
35945         }
35946         //increment the fill pointer
35947         (*s_i)++;
35948         //go to the next segment.
35949         s_i--;
35950     } while (s_i > end_si);
35951 
35952     // We have reached the destination segment
35953     // store the object
35954     **s_i = obj;
35955     // increment the fill pointer
35956     (*s_i)++;
35957 
35958     LeaveFinalizeLock();
35959 
35960     return true;
35961 }
35962 
35963 Object*
35964 CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
35965 {
35966     Object* obj = 0;
35967     //serialize
35968     EnterFinalizeLock();
35969 
35970 retry:
35971     if (!IsSegEmpty(FinalizerListSeg))
35972     {
35973         if (g_fFinalizerRunOnShutDown)
35974         {
35975             obj = *(SegQueueLimit (FinalizerListSeg)-1);
35976             if (method_table(obj)->HasCriticalFinalizer())
35977             {
35978                 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
35979                           FinalizerListSeg, CriticalFinalizerListSeg);
35980                 goto retry;
35981             }
35982             else
35983                 --SegQueueLimit (FinalizerListSeg);
35984         }
35985         else
35986             obj =  *(--SegQueueLimit (FinalizerListSeg));
35987 
35988     }
35989     else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
35990     {
35991         //the FinalizerList is empty, we can adjust both
35992         // limit instead of moving the object to the free list
35993         obj =  *(--SegQueueLimit (CriticalFinalizerListSeg));
35994         --SegQueueLimit (FinalizerListSeg);
35995     }
35996     if (obj)
35997     {
35998         dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
35999     }
36000     LeaveFinalizeLock();
36001     return obj;
36002 }
36003 
36004 void
36005 CFinalize::SetSegForShutDown(BOOL fHasLock)
36006 {
36007     int i;
36008 
36009     if (!fHasLock)
36010         EnterFinalizeLock();
36011     for (i = 0; i <= max_generation; i++)
36012     {
36013         unsigned int seg = gen_segment (i);
36014         Object** startIndex = SegQueueLimit (seg)-1;
36015         Object** stopIndex  = SegQueue (seg);
36016         for (Object** po = startIndex; po >= stopIndex; po--)
36017         {
36018             Object* obj = *po;
36019             if (method_table(obj)->HasCriticalFinalizer())
36020             {
36021                 MoveItem (po, seg, CriticalFinalizerListSeg);
36022             }
36023             else
36024             {
36025                 MoveItem (po, seg, FinalizerListSeg);
36026             }
36027         }
36028     }
36029     if (!fHasLock)
36030         LeaveFinalizeLock();
36031 }
36032 
36033 void
36034 CFinalize::DiscardNonCriticalObjects()
36035 {
36036     //empty the finalization queue
36037     Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
36038     Object** stopIndex  = SegQueue (FinalizerListSeg);
36039     for (Object** po = startIndex; po >= stopIndex; po--)
36040     {
36041         MoveItem (po, FinalizerListSeg, FreeList);
36042     }
36043 }
36044 
36045 size_t
36046 CFinalize::GetNumberFinalizableObjects()
36047 {
36048     return SegQueueLimit (FinalizerListSeg) -
36049         (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
36050 }
36051 
36052 BOOL
36053 CFinalize::FinalizeSegForAppDomain (AppDomain *pDomain,
36054                                     BOOL fRunFinalizers,
36055                                     unsigned int Seg)
36056 {
36057     BOOL finalizedFound = FALSE;
36058     Object** endIndex = SegQueue (Seg);
36059     for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36060     {
36061         CObjectHeader* obj = (CObjectHeader*)*i;
36062 
36063         // Objects are put into the finalization queue before they are complete (ie their methodtable
36064         // may be null) so we must check that the object we found has a method table before checking
36065         // if it has the index we are looking for. If the methodtable is null, it can't be from the
36066         // unloading domain, so skip it.
36067         if (method_table(obj) == NULL)
36068             continue;
36069 
36070         // eagerly finalize all objects except those that may be agile.
36071         if (obj->GetAppDomainIndex() != pDomain->GetIndex())
36072             continue;
36073 
36074 #ifndef FEATURE_REDHAWK
36075         if (method_table(obj)->IsAgileAndFinalizable())
36076         {
36077             // If an object is both agile & finalizable, we leave it in the
36078             // finalization queue during unload.  This is OK, since it's agile.
36079             // Right now only threads can be this way, so if that ever changes, change
36080             // the assert to just continue if not a thread.
36081             _ASSERTE(method_table(obj) == g_pThreadClass);
36082 
36083             if (method_table(obj) == g_pThreadClass)
36084             {
36085                 // However, an unstarted thread should be finalized. It could be holding a delegate
36086                 // in the domain we want to unload. Once the thread has been started, its
36087                 // delegate is cleared so only unstarted threads are a problem.
36088                 Thread *pThread = ((THREADBASEREF)ObjectToOBJECTREF(obj))->GetInternal();
36089                 if (! pThread || ! pThread->IsUnstarted())
36090                 {
36091                     // This appdomain is going to be gone soon so let us assign
36092                     // it the appdomain that's guaranteed to exist
36093                     // The object is agile and the delegate should be null so we can do it
36094                     obj->GetHeader()->ResetAppDomainIndexNoFailure(SystemDomain::System()->DefaultDomain()->GetIndex());
36095                     continue;
36096                 }
36097             }
36098             else
36099             {
36100                 obj->GetHeader()->ResetAppDomainIndexNoFailure(SystemDomain::System()->DefaultDomain()->GetIndex());
36101                 continue;
36102             }
36103         }
36104 #endif //!FEATURE_REDHAWK
36105 
36106         if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36107         {
36108             //remove the object because we don't want to
36109             //run the finalizer
36110             MoveItem (i, Seg, FreeList);
36111             //Reset the bit so it will be put back on the queue
36112             //if resurrected and re-registered.
36113             obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36114         }
36115         else
36116         {
36117             if (method_table(obj)->HasCriticalFinalizer())
36118             {
36119                 finalizedFound = TRUE;
36120                 MoveItem (i, Seg, CriticalFinalizerListSeg);
36121             }
36122             else
36123             {
36124                 if (pDomain->IsRudeUnload())
36125                 {
36126                     MoveItem (i, Seg, FreeList);
36127                 }
36128                 else
36129                 {
36130                     finalizedFound = TRUE;
36131                     MoveItem (i, Seg, FinalizerListSeg);
36132                 }
36133             }
36134         }
36135     }
36136 
36137     return finalizedFound;
36138 }
36139 
36140 BOOL
36141 CFinalize::FinalizeAppDomain (AppDomain *pDomain, BOOL fRunFinalizers)
36142 {
36143     BOOL finalizedFound = FALSE;
36144 
36145     unsigned int startSeg = gen_segment (max_generation);
36146 
36147     EnterFinalizeLock();
36148 
36149     for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
36150     {
36151         if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
36152         {
36153             finalizedFound = TRUE;
36154         }
36155     }
36156 
36157     LeaveFinalizeLock();
36158 
36159     return finalizedFound;
36160 }
36161 
36162 void
36163 CFinalize::MoveItem (Object** fromIndex,
36164                      unsigned int fromSeg,
36165                      unsigned int toSeg)
36166 {
36167 
36168     int step;
36169     ASSERT (fromSeg != toSeg);
36170     if (fromSeg > toSeg)
36171         step = -1;
36172     else
36173         step = +1;
36174     // Place the element at the boundary closest to dest
36175     Object** srcIndex = fromIndex;
36176     for (unsigned int i = fromSeg; i != toSeg; i+= step)
36177     {
36178         Object**& destFill = m_FillPointers[i+(step - 1 )/2];
36179         Object** destIndex = destFill - (step + 1)/2;
36180         if (srcIndex != destIndex)
36181         {
36182             Object* tmp = *srcIndex;
36183             *srcIndex = *destIndex;
36184             *destIndex = tmp;
36185         }
36186         destFill -= step;
36187         srcIndex = destIndex;
36188     }
36189 }
36190 
36191 void
36192 CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
36193 {
36194     ScanContext sc;
36195     if (pSC == 0)
36196         pSC = &sc;
36197 
36198     pSC->thread_number = hn;
36199 
36200     //scan the finalization queue
36201     Object** startIndex  = SegQueue (CriticalFinalizerListSeg);
36202     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
36203 
36204     for (Object** po = startIndex; po < stopIndex; po++)
36205     {
36206         Object* o = *po;
36207         //dprintf (3, ("scan freacheable %Ix", (size_t)o));
36208         dprintf (3, ("scan f %Ix", (size_t)o));
36209 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
36210         if (g_fEnableARM)
36211         {
36212             pSC->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(o->GetAppDomainIndex());
36213         }
36214 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
36215 
36216         (*fn)(po, pSC, 0);
36217     }
36218 }
36219 
36220 void CFinalize::WalkFReachableObjects (fq_walk_fn fn)
36221 {
36222     Object** startIndex = SegQueue (CriticalFinalizerListSeg);
36223     Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
36224     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
36225     for (Object** po = startIndex; po < stopIndex; po++)
36226     {
36227         //report *po
36228         fn(po < stopCriticalIndex, *po);
36229     }
36230 }
36231 
36232 BOOL
36233 CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
36234                                 gc_heap* hp)
36235 {
36236     ScanContext sc;
36237     sc.promotion = TRUE;
36238 #ifdef MULTIPLE_HEAPS
36239     sc.thread_number = hp->heap_number;
36240 #else
36241     UNREFERENCED_PARAMETER(hp);
36242 #endif //MULTIPLE_HEAPS
36243 
36244     BOOL finalizedFound = FALSE;
36245 
36246     //start with gen and explore all the younger generations.
36247     unsigned int startSeg = gen_segment (gen);
36248     {
36249         m_PromotedCount = 0;
36250         for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
36251         {
36252             Object** endIndex = SegQueue (Seg);
36253             for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36254             {
36255                 CObjectHeader* obj = (CObjectHeader*)*i;
36256                 dprintf (3, ("scanning: %Ix", (size_t)obj));
36257                 if (!g_theGCHeap->IsPromoted (obj))
36258                 {
36259                     dprintf (3, ("freacheable: %Ix", (size_t)obj));
36260 
36261                     assert (method_table(obj)->HasFinalizer());
36262 
36263 #ifndef FEATURE_REDHAWK
36264                     if (method_table(obj) == pWeakReferenceMT || method_table(obj)->GetCanonicalMethodTable() == pWeakReferenceOfTCanonMT)
36265                     {
36266                         //destruct the handle right there.
36267                         FinalizeWeakReference (obj);
36268                         MoveItem (i, Seg, FreeList);
36269                     }
36270                     else
36271 #endif //!FEATURE_REDHAWK
36272                     if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36273                     {
36274                         //remove the object because we don't want to
36275                         //run the finalizer
36276 
36277                         MoveItem (i, Seg, FreeList);
36278 
36279                         //Reset the bit so it will be put back on the queue
36280                         //if resurrected and re-registered.
36281                         obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36282 
36283                     }
36284                     else
36285                     {
36286                         m_PromotedCount++;
36287 
36288                         if (method_table(obj)->HasCriticalFinalizer())
36289                         {
36290                             MoveItem (i, Seg, CriticalFinalizerListSeg);
36291                         }
36292                         else
36293                         {
36294                             MoveItem (i, Seg, FinalizerListSeg);
36295                         }
36296                     }
36297                 }
36298 #ifdef BACKGROUND_GC
36299                 else
36300                 {
36301                     if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
36302                     {
36303                         // TODO - fix the following line.
36304                         //assert (gc_heap::background_object_marked ((uint8_t*)obj, FALSE));
36305                         dprintf (3, ("%Ix is marked", (size_t)obj));
36306                     }
36307                 }
36308 #endif //BACKGROUND_GC
36309             }
36310         }
36311     }
36312     finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
36313                      !IsSegEmpty(CriticalFinalizerListSeg);
36314 
36315     if (finalizedFound)
36316     {
36317         //Promote the f-reachable objects
36318         GcScanRoots (pfn,
36319 #ifdef MULTIPLE_HEAPS
36320                      hp->heap_number
36321 #else
36322                      0
36323 #endif //MULTIPLE_HEAPS
36324                      , 0);
36325 
36326         hp->settings.found_finalizers = TRUE;
36327 
36328 #ifdef BACKGROUND_GC
36329         if (hp->settings.concurrent)
36330         {
36331             hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
36332         }
36333 #endif //BACKGROUND_GC
36334         if (hp->settings.concurrent && hp->settings.found_finalizers)
36335         {
36336             if (!mark_only_p)
36337                 GCToEEInterface::EnableFinalization(true);
36338         }
36339     }
36340 
36341     return finalizedFound;
36342 }
36343 
36344 //Relocates all of the objects in the finalization array
36345 void
36346 CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
36347 {
36348     ScanContext sc;
36349     sc.promotion = FALSE;
36350 #ifdef MULTIPLE_HEAPS
36351     sc.thread_number = hp->heap_number;
36352 #else
36353     UNREFERENCED_PARAMETER(hp);
36354 #endif //MULTIPLE_HEAPS
36355 
36356     unsigned int Seg = gen_segment (gen);
36357 
36358     Object** startIndex = SegQueue (Seg);
36359     for (Object** po = startIndex; po < SegQueue (FreeList);po++)
36360     {
36361         GCHeap::Relocate (po, &sc);
36362     }
36363 }
36364 
36365 void
36366 CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
36367 {
36368     // update the generation fill pointers.
36369     // if gen_0_empty is FALSE, test each object to find out if
36370     // it was promoted or not
36371     if (gen_0_empty_p)
36372     {
36373         for (int i = min (gen+1, max_generation); i > 0; i--)
36374         {
36375             m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
36376         }
36377     }
36378     else
36379     {
36380         //Look for demoted or promoted plugs
36381 
36382         for (int i = gen; i >= 0; i--)
36383         {
36384             unsigned int Seg = gen_segment (i);
36385             Object** startIndex = SegQueue (Seg);
36386 
36387             for (Object** po = startIndex;
36388                  po < SegQueueLimit (gen_segment(i)); po++)
36389             {
36390                 int new_gen = g_theGCHeap->WhichGeneration (*po);
36391                 if (new_gen != i)
36392                 {
36393                     if (new_gen > i)
36394                     {
36395                         //promotion
36396                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
36397                     }
36398                     else
36399                     {
36400                         //demotion
36401                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
36402                         //back down in order to see all objects.
36403                         po--;
36404                     }
36405                 }
36406 
36407             }
36408         }
36409     }
36410 }
36411 
36412 BOOL
36413 CFinalize::GrowArray()
36414 {
36415     size_t oldArraySize = (m_EndArray - m_Array);
36416     size_t newArraySize =  (size_t)(((float)oldArraySize / 10) * 12);
36417 
36418     Object** newArray = new (nothrow) Object*[newArraySize];
36419     if (!newArray)
36420     {
36421         // It's not safe to throw here, because of the FinalizeLock.  Tell our caller
36422         // to throw for us.
36423 //        ASSERT (newArray);
36424         return FALSE;
36425     }
36426     memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
36427 
36428     //adjust the fill pointers
36429     for (int i = 0; i < FreeList; i++)
36430     {
36431         m_FillPointers [i] += (newArray - m_Array);
36432     }
36433     delete m_Array;
36434     m_Array = newArray;
36435     m_EndArray = &m_Array [newArraySize];
36436 
36437     return TRUE;
36438 }
36439 
36440 #ifdef VERIFY_HEAP
36441 void CFinalize::CheckFinalizerObjects()
36442 {
36443     for (int i = 0; i <= max_generation; i++)
36444     {
36445         Object **startIndex = SegQueue (gen_segment (i));
36446         Object **stopIndex  = SegQueueLimit (gen_segment (i));
36447 
36448         for (Object **po = startIndex; po < stopIndex; po++)
36449         {
36450             if ((int)g_theGCHeap->WhichGeneration (*po) < i)
36451                 FATAL_GC_ERROR ();
36452             ((CObjectHeader*)*po)->Validate();
36453         }
36454     }
36455 }
36456 #endif //VERIFY_HEAP
36457 
36458 #endif // FEATURE_PREMORTEM_FINALIZATION
36459 
36460 
36461 //------------------------------------------------------------------------------
36462 //
36463 //                      End of VM specific support
36464 //
36465 //------------------------------------------------------------------------------
36466 void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36467 {
36468     generation* gen = gc_heap::generation_of (gen_number);
36469     heap_segment*    seg = generation_start_segment (gen);
36470     uint8_t*       x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
36471                      generation_allocation_start (gen));
36472 
36473     uint8_t*       end = heap_segment_allocated (seg);
36474     BOOL small_object_segments = TRUE;
36475     int align_const = get_alignment_constant (small_object_segments);
36476 
36477     while (1)
36478 
36479     {
36480         if (x >= end)
36481         {
36482             if ((seg = heap_segment_next (seg)) != 0)
36483             {
36484                 x = heap_segment_mem (seg);
36485                 end = heap_segment_allocated (seg);
36486                 continue;
36487             }
36488             else
36489             {
36490                 if (small_object_segments && walk_large_object_heap_p)
36491 
36492                 {
36493                     small_object_segments = FALSE;
36494                     align_const = get_alignment_constant (small_object_segments);
36495                     seg = generation_start_segment (large_object_generation);
36496                     x = heap_segment_mem (seg);
36497                     end = heap_segment_allocated (seg);
36498                     continue;
36499                 }
36500                 else
36501                 {
36502                     break;
36503                 }
36504             }
36505         }
36506 
36507         size_t s = size (x);
36508         CObjectHeader* o = (CObjectHeader*)x;
36509 
36510         if (!o->IsFree())
36511 
36512         {
36513             _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
36514 
36515             if (!fn (o->GetObjectBase(), context))
36516                 return;
36517         }
36518         x = x + Align (s, align_const);
36519     }
36520 }
36521 
36522 void gc_heap::walk_finalize_queue (fq_walk_fn fn)
36523 {
36524 #ifdef FEATURE_PREMORTEM_FINALIZATION
36525     finalize_queue->WalkFReachableObjects (fn);
36526 #endif //FEATURE_PREMORTEM_FINALIZATION
36527 }
36528 
36529 void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36530 {
36531 #ifdef MULTIPLE_HEAPS
36532     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36533     {
36534         gc_heap* hp = gc_heap::g_heaps [hn];
36535 
36536         hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p);
36537     }
36538 #else
36539     walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p);
36540 #endif //MULTIPLE_HEAPS
36541 }
36542 
36543 void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context)
36544 {
36545     uint8_t* o = (uint8_t*)obj;
36546     if (o)
36547     {
36548         go_through_object_cl (method_table (o), o, size(o), oo,
36549                                     {
36550                                         if (*oo)
36551                                         {
36552                                             Object *oh = (Object*)*oo;
36553                                             if (!fn (oh, context))
36554                                                 return;
36555                                         }
36556                                     }
36557             );
36558     }
36559 }
36560 
36561 void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, size_t diag_context, walk_surv_type type)
36562 {
36563     gc_heap* hp = (gc_heap*)gc_context;
36564     hp->walk_survivors (fn, diag_context, type);
36565 }
36566 
36567 void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36568 {
36569     gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
36570 }
36571 
36572 void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
36573 {
36574     gc_heap* hp = (gc_heap*)gc_context;
36575     hp->walk_finalize_queue (fn);
36576 }
36577 
36578 void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc)
36579 {
36580 #ifdef MULTIPLE_HEAPS
36581     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36582     {
36583         gc_heap* hp = gc_heap::g_heaps [hn];
36584         hp->finalize_queue->GcScanRoots(fn, hn, sc);
36585     }
36586 #else
36587         pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc);
36588 #endif //MULTIPLE_HEAPS
36589 }
36590 
36591 void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36592 {
36593     UNREFERENCED_PARAMETER(gen_number);
36594     GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn);
36595 }
36596 
36597 void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36598 {
36599     UNREFERENCED_PARAMETER(gen_number);
36600     GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn);
36601 }
36602 
36603 // Go through and touch (read) each page straddled by a memory block.
36604 void TouchPages(void * pStart, size_t cb)
36605 {
36606     const uint32_t pagesize = OS_PAGE_SIZE;
36607     _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
36608     if (cb)
36609     {
36610         VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
36611         VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) -  (((size_t)pStart) & (pagesize-1)));
36612         while (p < pEnd)
36613         {
36614             char a;
36615             a = VolatileLoad(p);
36616             //printf("Touching page %lxh\n", (uint32_t)p);
36617             p += pagesize;
36618         }
36619     }
36620 }
36621 
36622 #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
36623     // This code is designed to catch the failure to update the write barrier
36624     // The way it works is to copy the whole heap right after every GC.  The write
36625     // barrier code has been modified so that it updates the shadow as well as the
36626     // real GC heap.  Before doing the next GC, we walk the heap, looking for pointers
36627     // that were updated in the real heap, but not the shadow.  A mismatch indicates
36628     // an error.  The offending code can be found by breaking after the correct GC,
36629     // and then placing a data breakpoint on the Heap location that was updated without
36630     // going through the write barrier.
36631 
36632     // Called at process shutdown
36633 void deleteGCShadow()
36634 {
36635     if (g_GCShadow != 0)
36636         GCToOSInterface::VirtualRelease (g_GCShadow, g_GCShadowEnd - g_GCShadow);
36637     g_GCShadow = 0;
36638     g_GCShadowEnd = 0;
36639 }
36640 
36641     // Called at startup and right after a GC, get a snapshot of the GC Heap
36642 void initGCShadow()
36643 {
36644     if (!(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK))
36645         return;
36646 
36647     size_t len = g_gc_highest_address - g_gc_lowest_address;
36648     if (len > (size_t)(g_GCShadowEnd - g_GCShadow))
36649     {
36650         deleteGCShadow();
36651         g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None);
36652         if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len))
36653         {
36654             _ASSERTE(!"Not enough memory to run HeapVerify level 2");
36655             // If after the assert we decide to allow the program to continue
36656             // running we need to be in a state that will not trigger any
36657             // additional AVs while we fail to allocate a shadow segment, i.e.
36658             // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
36659             deleteGCShadow();
36660             return;
36661         }
36662 
36663         g_GCShadowEnd += len;
36664     }
36665 
36666     // save the value of g_gc_lowest_address at this time.  If this value changes before
36667     // the next call to checkGCWriteBarrier() it means we extended the heap (with a
36668     // large object segment most probably), and the whole shadow segment is inconsistent.
36669     g_shadow_lowest_address = g_gc_lowest_address;
36670 
36671         //****** Copy the whole GC heap ******
36672     //
36673     // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
36674     // can produce a NULL result.  This is because the initialization has not completed.
36675     //
36676     generation* gen = gc_heap::generation_of (max_generation);
36677     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36678 
36679     ptrdiff_t delta = g_GCShadow - g_gc_lowest_address;
36680     BOOL small_object_segments = TRUE;
36681     while(1)
36682     {
36683         if (!seg)
36684         {
36685             if (small_object_segments)
36686             {
36687                 small_object_segments = FALSE;
36688                 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
36689                 continue;
36690             }
36691             else
36692                 break;
36693         }
36694             // Copy the segment
36695         uint8_t* start = heap_segment_mem(seg);
36696         uint8_t* end = heap_segment_allocated (seg);
36697         memcpy(start + delta, start, end - start);
36698         seg = heap_segment_next_rw (seg);
36699     }
36700 }
36701 
36702 #define INVALIDGCVALUE (void*)((size_t)0xcccccccd)
36703 
36704     // test to see if 'ptr' was only updated via the write barrier.
36705 inline void testGCShadow(Object** ptr)
36706 {
36707     Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)];
36708     if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow)
36709     {
36710 
36711         // If you get this assertion, someone updated a GC poitner in the heap without
36712         // using the write barrier.  To find out who, check the value of
36713         // dd_collection_count (dynamic_data_of (0)). Also
36714         // note the value of 'ptr'.  Rerun the App that the previous GC just occurred.
36715         // Then put a data breakpoint for the value of 'ptr'  Then check every write
36716         // to pointer between the two GCs.  The last one is not using the write barrier.
36717 
36718         // If the memory of interest does not exist at system startup,
36719         // you need to set the data breakpoint right after the memory gets committed
36720         // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
36721         // in the memory window.  run until the memory gets mapped. Then you can set
36722         // your breakpoint
36723 
36724         // Note a recent change, we've identified race conditions when updating the gc shadow.
36725         // Throughout the runtime, code will update an address in the gc heap, then erect the
36726         // write barrier, which calls updateGCShadow. With an app that pounds one heap location
36727         // from multiple threads, you can hit this assert even though all involved are using the
36728         // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
36729         // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
36730         // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
36731         // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
36732         // TODO: erroneous asserts in here.
36733 
36734         if(*shadow!=INVALIDGCVALUE)
36735         {
36736 #ifdef FEATURE_BASICFREEZE
36737             // Write barriers for stores of references to frozen objects may be optimized away.
36738             if (!gc_heap::frozen_object_p(*ptr))
36739 #endif // FEATURE_BASICFREEZE
36740             {
36741                 _ASSERTE(!"Pointer updated without using write barrier");
36742             }
36743         }
36744         /*
36745         else
36746         {
36747              printf("saw a INVALIDGCVALUE. (just to let you know)\n");
36748         }
36749         */
36750     }
36751 }
36752 
36753 void testGCShadowHelper (uint8_t* x)
36754 {
36755     size_t s = size (x);
36756     if (contain_pointers (x))
36757     {
36758         go_through_object_nostart (method_table(x), x, s, oo,
36759                            { testGCShadow((Object**) oo); });
36760     }
36761 }
36762 
36763     // Walk the whole heap, looking for pointers that were not updated with the write barrier.
36764 void checkGCWriteBarrier()
36765 {
36766     // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment
36767     // and the GC shadow segment did not track that change!
36768     if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address)
36769     {
36770         // No shadow stack, nothing to check.
36771         return;
36772     }
36773 
36774     {
36775         generation* gen = gc_heap::generation_of (max_generation);
36776         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36777 
36778         PREFIX_ASSUME(seg != NULL);
36779 
36780         while(seg)
36781         {
36782             uint8_t* x = heap_segment_mem(seg);
36783             while (x < heap_segment_allocated (seg))
36784             {
36785                 size_t s = size (x);
36786                 testGCShadowHelper (x);
36787                 x = x + Align (s);
36788             }
36789             seg = heap_segment_next_rw (seg);
36790         }
36791     }
36792 
36793     {
36794         // go through large object heap
36795         int alignment = get_alignment_constant(FALSE);
36796         generation* gen = gc_heap::generation_of (max_generation+1);
36797         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
36798 
36799         PREFIX_ASSUME(seg != NULL);
36800 
36801         while(seg)
36802         {
36803             uint8_t* x = heap_segment_mem(seg);
36804             while (x < heap_segment_allocated (seg))
36805             {
36806                 size_t s = size (x);
36807                 testGCShadowHelper (x);
36808                 x = x + Align (s, alignment);
36809             }
36810             seg = heap_segment_next_rw (seg);
36811         }
36812     }
36813 }
36814 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
36815 
36816 #endif // !DACCESS_COMPILE
36817 
36818 #ifdef FEATURE_BASICFREEZE
36819 void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
36820 {
36821 #ifdef DACCESS_COMPILE
36822     UNREFERENCED_PARAMETER(seg);
36823     UNREFERENCED_PARAMETER(pvContext);
36824     UNREFERENCED_PARAMETER(pfnMethodTable);
36825     UNREFERENCED_PARAMETER(pfnObjRef);
36826 #else
36827     uint8_t *o = heap_segment_mem(seg);
36828 
36829     // small heap alignment constant
36830     int alignment = get_alignment_constant(TRUE);
36831 
36832     while (o < heap_segment_allocated(seg))
36833     {
36834         pfnMethodTable(pvContext, o);
36835 
36836         if (contain_pointers (o))
36837         {
36838             go_through_object_nostart (method_table (o), o, size(o), oo,
36839                    {
36840                        if (*oo)
36841                            pfnObjRef(pvContext, oo);
36842                    }
36843             );
36844         }
36845 
36846         o += Align(size(o), alignment);
36847     }
36848 #endif //!DACCESS_COMPILE
36849 }
36850 #endif // FEATURE_BASICFREEZE
36851 
36852 #ifndef DACCESS_COMPILE
36853 HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
36854 {
36855 #ifdef BACKGROUND_GC
36856     if (recursive_gc_sync::background_running_p())
36857     {
36858         uint32_t dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
36859         if (dwRet == WAIT_OBJECT_0)
36860             return S_OK;
36861         else if (dwRet == WAIT_TIMEOUT)
36862             return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
36863         else
36864             return E_FAIL;      // It is not clear if what the last error would be if the wait failed,
36865                                 // as there are too many layers in between. The best we can do is to return E_FAIL;
36866     }
36867 #endif
36868 
36869     return S_OK;
36870 }
36871 #endif // !DACCESS_COMPILE
36872 
36873 void GCHeap::TemporaryEnableConcurrentGC()
36874 {
36875 #ifdef BACKGROUND_GC
36876     gc_heap::temp_disable_concurrent_p = false;
36877 #endif //BACKGROUND_GC
36878 }
36879 
36880 void GCHeap::TemporaryDisableConcurrentGC()
36881 {
36882 #ifdef BACKGROUND_GC
36883     gc_heap::temp_disable_concurrent_p = true;
36884 #endif //BACKGROUND_GC
36885 }
36886 
36887 BOOL GCHeap::IsConcurrentGCEnabled()
36888 {
36889 #ifdef BACKGROUND_GC
36890     return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
36891 #else
36892     return FALSE;
36893 #endif //BACKGROUND_GC
36894 }
36895