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// We just needed a simple random number generator for testing.
25class gc_rand
26{
27public:
28 static uint64_t x;
29
30 static uint64_t get_rand()
31 {
32 x = (314159269*x+278281) & 0x7FFFFFFF;
33 return x;
34 }
35
36 // obtain random number in the range 0 .. r-1
37 static uint64_t get_rand(uint64_t r) {
38 // require r >= 0
39 uint64_t x = (uint64_t)((get_rand() * r) >> 31);
40 return x;
41 }
42};
43
44uint64_t gc_rand::x = 0;
45
46#if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
47BOOL bgc_heap_walk_for_etw_p = FALSE;
48#endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
49
50#if defined(FEATURE_REDHAWK)
51#define MAYBE_UNUSED_VAR(v) v = v
52#else
53#define MAYBE_UNUSED_VAR(v)
54#endif // FEATURE_REDHAWK
55
56#define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0))
57
58#ifdef SERVER_GC
59#define partial_size_th 100
60#define num_partial_refs 64
61#else //SERVER_GC
62#define partial_size_th 100
63#define num_partial_refs 32
64#endif //SERVER_GC
65
66#define demotion_plug_len_th (6*1024*1024)
67
68#ifdef BIT64
69#define MARK_STACK_INITIAL_LENGTH 1024
70#else
71#define MARK_STACK_INITIAL_LENGTH 128
72#endif // BIT64
73
74#define LOH_PIN_QUEUE_LENGTH 100
75#define LOH_PIN_DECAY 10
76
77#ifdef BIT64
78// Right now we support maximum 1024 procs - meaning that we will create at most
79// that many GC threads and GC heaps.
80#define MAX_SUPPORTED_CPUS 1024
81#else
82#define MAX_SUPPORTED_CPUS 64
83#endif // BIT64
84
85uint32_t yp_spin_count_unit = 0;
86size_t loh_size_threshold = LARGE_OBJECT_SIZE;
87
88#ifdef GC_CONFIG_DRIVEN
89int compact_ratio = 0;
90#endif //GC_CONFIG_DRIVEN
91
92// See comments in reset_memory.
93BOOL reset_mm_p = TRUE;
94
95bool g_fFinalizerRunOnShutDown = false;
96
97#ifdef FEATURE_SVR_GC
98bool g_built_with_svr_gc = true;
99#else
100bool g_built_with_svr_gc = false;
101#endif // FEATURE_SVR_GC
102
103#if defined(BUILDENV_DEBUG)
104uint8_t g_build_variant = 0;
105#elif defined(BUILDENV_CHECKED)
106uint8_t g_build_variant = 1;
107#else
108uint8_t g_build_variant = 2;
109#endif // defined(BUILDENV_DEBUG)
110
111VOLATILE(int32_t) g_no_gc_lock = -1;
112
113#if defined (TRACE_GC) && !defined (DACCESS_COMPILE)
114const char * const allocation_state_str[] = {
115 "start",
116 "can_allocate",
117 "cant_allocate",
118 "try_fit",
119 "try_fit_new_seg",
120 "try_fit_new_seg_after_cg",
121 "try_fit_no_seg",
122 "try_fit_after_cg",
123 "try_fit_after_bgc",
124 "try_free_full_seg_in_bgc",
125 "try_free_after_bgc",
126 "try_seg_end",
127 "acquire_seg",
128 "acquire_seg_after_cg",
129 "acquire_seg_after_bgc",
130 "check_and_wait_for_bgc",
131 "trigger_full_compact_gc",
132 "trigger_ephemeral_gc",
133 "trigger_2nd_ephemeral_gc",
134 "check_retry_seg"
135};
136
137const char * const msl_take_state_str[] = {
138 "get_large_seg",
139 "bgc_loh_sweep",
140 "wait_bgc",
141 "block_gc",
142 "clr_mem",
143 "clr_large_mem",
144 "t_eph_gc",
145 "t_full_gc",
146 "alloc_small",
147 "alloc_large",
148 "alloc_small_cant",
149 "alloc_large_cant",
150 "try_alloc",
151 "try_budget"
152};
153#endif //TRACE_GC && !DACCESS_COMPILE
154
155
156// Keep this in sync with the definition of gc_reason
157#if (defined(DT_LOG) || defined(TRACE_GC)) && !defined (DACCESS_COMPILE)
158static const char* const str_gc_reasons[] =
159{
160 "alloc_soh",
161 "induced",
162 "lowmem",
163 "empty",
164 "alloc_loh",
165 "oos_soh",
166 "oos_loh",
167 "induced_noforce",
168 "gcstress",
169 "induced_lowmem",
170 "induced_compacting",
171 "lowmemory_host",
172 "pm_full_gc",
173 "lowmemory_host_blocking"
174};
175
176static const char* const str_gc_pause_modes[] =
177{
178 "batch",
179 "interactive",
180 "low_latency",
181 "sustained_low_latency",
182 "no_gc"
183};
184#endif // defined(DT_LOG) || defined(TRACE_GC)
185
186inline
187BOOL is_induced (gc_reason reason)
188{
189 return ((reason == reason_induced) ||
190 (reason == reason_induced_noforce) ||
191 (reason == reason_lowmemory) ||
192 (reason == reason_lowmemory_blocking) ||
193 (reason == reason_induced_compacting) ||
194 (reason == reason_lowmemory_host) ||
195 (reason == reason_lowmemory_host_blocking));
196}
197
198inline
199BOOL is_induced_blocking (gc_reason reason)
200{
201 return ((reason == reason_induced) ||
202 (reason == reason_lowmemory_blocking) ||
203 (reason == reason_induced_compacting) ||
204 (reason == reason_lowmemory_host_blocking));
205}
206
207#ifndef DACCESS_COMPILE
208int64_t qpf;
209
210size_t GetHighPrecisionTimeStamp()
211{
212 int64_t ts = GCToOSInterface::QueryPerformanceCounter();
213
214 return (size_t)(ts / (qpf / 1000));
215}
216#endif
217
218#ifdef GC_STATS
219// There is a current and a prior copy of the statistics. This allows us to display deltas per reporting
220// interval, as well as running totals. The 'min' and 'max' values require special treatment. They are
221// Reset (zeroed) in the current statistics when we begin a new interval and they are updated via a
222// comparison with the global min/max.
223GCStatistics g_GCStatistics;
224GCStatistics g_LastGCStatistics;
225
226char* GCStatistics::logFileName = NULL;
227FILE* GCStatistics::logFile = NULL;
228
229void GCStatistics::AddGCStats(const gc_mechanisms& settings, size_t timeInMSec)
230{
231#ifdef BACKGROUND_GC
232 if (settings.concurrent)
233 {
234 bgc.Accumulate((uint32_t)timeInMSec*1000);
235 cntBGC++;
236 }
237 else if (settings.background_p)
238 {
239 fgc.Accumulate((uint32_t)timeInMSec*1000);
240 cntFGC++;
241 if (settings.compaction)
242 cntCompactFGC++;
243 assert(settings.condemned_generation < max_generation);
244 cntFGCGen[settings.condemned_generation]++;
245 }
246 else
247#endif // BACKGROUND_GC
248 {
249 ngc.Accumulate((uint32_t)timeInMSec*1000);
250 cntNGC++;
251 if (settings.compaction)
252 cntCompactNGC++;
253 cntNGCGen[settings.condemned_generation]++;
254 }
255
256 if (is_induced (settings.reason))
257 cntReasons[(int)reason_induced]++;
258 else if (settings.stress_induced)
259 cntReasons[(int)reason_gcstress]++;
260 else
261 cntReasons[(int)settings.reason]++;
262
263#ifdef BACKGROUND_GC
264 if (settings.concurrent || !settings.background_p)
265 {
266#endif // BACKGROUND_GC
267 RollOverIfNeeded();
268#ifdef BACKGROUND_GC
269 }
270#endif // BACKGROUND_GC
271}
272
273void GCStatistics::Initialize()
274{
275 LIMITED_METHOD_CONTRACT;
276 // for efficiency sake we're taking a dependency on the layout of a C++ object
277 // with a vtable. protect against violations of our premise:
278 static_assert(offsetof(GCStatistics, cntDisplay) == sizeof(void*),
279 "The first field of GCStatistics follows the pointer sized vtable");
280
281 int podOffs = offsetof(GCStatistics, cntDisplay); // offset of the first POD field
282 memset((uint8_t*)(&g_GCStatistics)+podOffs, 0, sizeof(g_GCStatistics)-podOffs);
283 memset((uint8_t*)(&g_LastGCStatistics)+podOffs, 0, sizeof(g_LastGCStatistics)-podOffs);
284}
285
286void GCStatistics::DisplayAndUpdate()
287{
288 LIMITED_METHOD_CONTRACT;
289
290 if (logFileName == NULL || logFile == NULL)
291 return;
292
293 {
294 if (cntDisplay == 0)
295 fprintf(logFile, "\nGCMix **** Initialize *****\n\n");
296
297 fprintf(logFile, "GCMix **** Summary ***** %d\n", cntDisplay);
298
299 // NGC summary (total, timing info)
300 ngc.DisplayAndUpdate(logFile, "NGC ", &g_LastGCStatistics.ngc, cntNGC, g_LastGCStatistics.cntNGC, msec);
301
302 // FGC summary (total, timing info)
303 fgc.DisplayAndUpdate(logFile, "FGC ", &g_LastGCStatistics.fgc, cntFGC, g_LastGCStatistics.cntFGC, msec);
304
305 // BGC summary
306 bgc.DisplayAndUpdate(logFile, "BGC ", &g_LastGCStatistics.bgc, cntBGC, g_LastGCStatistics.cntBGC, msec);
307
308 // NGC/FGC break out by generation & compacting vs. sweeping
309 fprintf(logFile, "NGC ");
310 for (int i = max_generation; i >= 0; --i)
311 fprintf(logFile, "gen%d %d (%d). ", i, cntNGCGen[i]-g_LastGCStatistics.cntNGCGen[i], cntNGCGen[i]);
312 fprintf(logFile, "\n");
313
314 fprintf(logFile, "FGC ");
315 for (int i = max_generation-1; i >= 0; --i)
316 fprintf(logFile, "gen%d %d (%d). ", i, cntFGCGen[i]-g_LastGCStatistics.cntFGCGen[i], cntFGCGen[i]);
317 fprintf(logFile, "\n");
318
319 // Compacting vs. Sweeping break out
320 int _cntSweep = cntNGC-cntCompactNGC;
321 int _cntLastSweep = g_LastGCStatistics.cntNGC-g_LastGCStatistics.cntCompactNGC;
322 fprintf(logFile, "NGC Sweeping %d (%d) Compacting %d (%d)\n",
323 _cntSweep - _cntLastSweep, _cntSweep,
324 cntCompactNGC - g_LastGCStatistics.cntCompactNGC, cntCompactNGC);
325
326 _cntSweep = cntFGC-cntCompactFGC;
327 _cntLastSweep = g_LastGCStatistics.cntFGC-g_LastGCStatistics.cntCompactFGC;
328 fprintf(logFile, "FGC Sweeping %d (%d) Compacting %d (%d)\n",
329 _cntSweep - _cntLastSweep, _cntSweep,
330 cntCompactFGC - g_LastGCStatistics.cntCompactFGC, cntCompactFGC);
331
332#ifdef TRACE_GC
333 // GC reasons...
334 for (int reason=(int)reason_alloc_soh; reason <= (int)reason_gcstress; ++reason)
335 {
336 if (cntReasons[reason] != 0)
337 fprintf(logFile, "%s %d (%d). ", str_gc_reasons[reason],
338 cntReasons[reason]-g_LastGCStatistics.cntReasons[reason], cntReasons[reason]);
339 }
340#endif // TRACE_GC
341 fprintf(logFile, "\n\n");
342
343 // flush the log file...
344 fflush(logFile);
345 }
346
347 g_LastGCStatistics = *this;
348
349 ngc.Reset();
350 fgc.Reset();
351 bgc.Reset();
352}
353
354#endif // GC_STATS
355
356inline
357size_t round_up_power2 (size_t size)
358{
359 // Get the 0-based index of the most-significant bit in size-1.
360 // If the call failed (because size-1 is zero), size must be 1,
361 // so return 1 (because 1 rounds up to itself).
362 DWORD highest_set_bit_index;
363 if (0 ==
364#ifdef BIT64
365 BitScanReverse64(
366#else
367 BitScanReverse(
368#endif
369 &highest_set_bit_index, size - 1)) { return 1; }
370
371 // The size == 0 case (which would have overflowed to SIZE_MAX when decremented)
372 // is handled below by relying on the fact that highest_set_bit_index is the maximum value
373 // (31 or 63, depending on sizeof(size_t)) and left-shifting a value >= 2 by that
374 // number of bits shifts in zeros from the right, resulting in an output of zero.
375 return static_cast<size_t>(2) << highest_set_bit_index;
376}
377
378inline
379size_t round_down_power2 (size_t size)
380{
381 // Get the 0-based index of the most-significant bit in size.
382 // If the call failed, size must be zero so return zero.
383 DWORD highest_set_bit_index;
384 if (0 ==
385#ifdef BIT64
386 BitScanReverse64(
387#else
388 BitScanReverse(
389#endif
390 &highest_set_bit_index, size)) { return 0; }
391
392 // Left-shift 1 by highest_set_bit_index to get back a value containing only
393 // the most-significant set bit of size, i.e. size rounded down
394 // to the next power-of-two value.
395 return static_cast<size_t>(1) << highest_set_bit_index;
396}
397
398// Get the 0-based index of the most-significant bit in the value.
399// Returns -1 if the input value is zero (i.e. has no set bits).
400inline
401int index_of_highest_set_bit (size_t value)
402{
403 // Get the 0-based index of the most-significant bit in the value.
404 // If the call failed (because value is zero), return -1.
405 DWORD highest_set_bit_index;
406 return (0 ==
407#ifdef BIT64
408 BitScanReverse64(
409#else
410 BitScanReverse(
411#endif
412 &highest_set_bit_index, value)) ? -1 : static_cast<int>(highest_set_bit_index);
413}
414
415inline
416int relative_index_power2_plug (size_t power2)
417{
418 int index = index_of_highest_set_bit (power2);
419 assert (index <= MAX_INDEX_POWER2);
420
421 return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
422}
423
424inline
425int relative_index_power2_free_space (size_t power2)
426{
427 int index = index_of_highest_set_bit (power2);
428 assert (index <= MAX_INDEX_POWER2);
429
430 return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
431}
432
433#ifdef BACKGROUND_GC
434uint32_t bgc_alloc_spin_count = 140;
435uint32_t bgc_alloc_spin_count_loh = 16;
436uint32_t bgc_alloc_spin = 2;
437
438
439inline
440void c_write (uint32_t& place, uint32_t value)
441{
442 Interlocked::Exchange (&place, value);
443 //place = value;
444}
445
446#ifndef DACCESS_COMPILE
447// If every heap's gen2 or gen3 size is less than this threshold we will do a blocking GC.
448const size_t bgc_min_per_heap = 4*1024*1024;
449
450int gc_heap::gchist_index = 0;
451gc_mechanisms_store gc_heap::gchist[max_history_count];
452
453#ifndef MULTIPLE_HEAPS
454size_t gc_heap::total_promoted_bytes = 0;
455VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process;
456int gc_heap::gchist_index_per_heap = 0;
457gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count];
458#endif //MULTIPLE_HEAPS
459
460void gc_heap::add_to_history_per_heap()
461{
462#ifdef GC_HISTORY
463 gc_history* current_hist = &gchist_per_heap[gchist_index_per_heap];
464 current_hist->gc_index = settings.gc_index;
465 current_hist->current_bgc_state = current_bgc_state;
466 size_t elapsed = dd_gc_elapsed_time (dynamic_data_of (0));
467 current_hist->gc_time_ms = (uint32_t)elapsed;
468 current_hist->gc_efficiency = (elapsed ? (total_promoted_bytes / elapsed) : total_promoted_bytes);
469 current_hist->eph_low = generation_allocation_start (generation_of (max_generation-1));
470 current_hist->gen0_start = generation_allocation_start (generation_of (0));
471 current_hist->eph_high = heap_segment_allocated (ephemeral_heap_segment);
472#ifdef BACKGROUND_GC
473 current_hist->bgc_lowest = background_saved_lowest_address;
474 current_hist->bgc_highest = background_saved_highest_address;
475#endif //BACKGROUND_GC
476 current_hist->fgc_lowest = lowest_address;
477 current_hist->fgc_highest = highest_address;
478 current_hist->g_lowest = g_gc_lowest_address;
479 current_hist->g_highest = g_gc_highest_address;
480
481 gchist_index_per_heap++;
482 if (gchist_index_per_heap == max_history_count)
483 {
484 gchist_index_per_heap = 0;
485 }
486#endif //GC_HISTORY
487}
488
489void gc_heap::add_to_history()
490{
491#ifdef GC_HISTORY
492 gc_mechanisms_store* current_settings = &gchist[gchist_index];
493 current_settings->store (&settings);
494
495 gchist_index++;
496 if (gchist_index == max_history_count)
497 {
498 gchist_index = 0;
499 }
500#endif //GC_HISTORY
501}
502
503#endif //DACCESS_COMPILE
504#endif //BACKGROUND_GC
505
506#if defined(TRACE_GC) && !defined(DACCESS_COMPILE)
507BOOL gc_log_on = TRUE;
508FILE* gc_log = NULL;
509size_t gc_log_file_size = 0;
510
511size_t gc_buffer_index = 0;
512size_t max_gc_buffers = 0;
513
514static CLRCriticalSection gc_log_lock;
515
516// we keep this much in a buffer and only flush when the buffer is full
517#define gc_log_buffer_size (1024*1024)
518uint8_t* gc_log_buffer = 0;
519size_t gc_log_buffer_offset = 0;
520
521void log_va_msg(const char *fmt, va_list args)
522{
523 gc_log_lock.Enter();
524
525 const int BUFFERSIZE = 512;
526 static char rgchBuffer[BUFFERSIZE];
527 char * pBuffer = &rgchBuffer[0];
528
529 pBuffer[0] = '\n';
530 int buffer_start = 1;
531 int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging());
532 buffer_start += pid_len;
533 memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start);
534 int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args);
535 if (msg_len == -1)
536 {
537 msg_len = BUFFERSIZE - buffer_start;
538 }
539
540 msg_len += buffer_start;
541
542 if ((gc_log_buffer_offset + msg_len) > (gc_log_buffer_size - 12))
543 {
544 char index_str[8];
545 memset (index_str, '-', 8);
546 sprintf_s (index_str, _countof(index_str), "%d", (int)gc_buffer_index);
547 gc_log_buffer[gc_log_buffer_offset] = '\n';
548 memcpy (gc_log_buffer + (gc_log_buffer_offset + 1), index_str, 8);
549
550 gc_buffer_index++;
551 if (gc_buffer_index > max_gc_buffers)
552 {
553 fseek (gc_log, 0, SEEK_SET);
554 gc_buffer_index = 0;
555 }
556 fwrite(gc_log_buffer, gc_log_buffer_size, 1, gc_log);
557 fflush(gc_log);
558 memset (gc_log_buffer, '*', gc_log_buffer_size);
559 gc_log_buffer_offset = 0;
560 }
561
562 memcpy (gc_log_buffer + gc_log_buffer_offset, pBuffer, msg_len);
563 gc_log_buffer_offset += msg_len;
564
565 gc_log_lock.Leave();
566}
567
568void GCLog (const char *fmt, ... )
569{
570 if (gc_log_on && (gc_log != NULL))
571 {
572 va_list args;
573 va_start(args, fmt);
574 log_va_msg (fmt, args);
575 va_end(args);
576 }
577}
578#endif // TRACE_GC && !DACCESS_COMPILE
579
580#if defined(GC_CONFIG_DRIVEN) && !defined(DACCESS_COMPILE)
581
582BOOL gc_config_log_on = FALSE;
583FILE* gc_config_log = NULL;
584
585// we keep this much in a buffer and only flush when the buffer is full
586#define gc_config_log_buffer_size (1*1024) // TEMP
587uint8_t* gc_config_log_buffer = 0;
588size_t gc_config_log_buffer_offset = 0;
589
590// For config since we log so little we keep the whole history. Also it's only
591// ever logged by one thread so no need to synchronize.
592void log_va_msg_config(const char *fmt, va_list args)
593{
594 const int BUFFERSIZE = 256;
595 static char rgchBuffer[BUFFERSIZE];
596 char * pBuffer = &rgchBuffer[0];
597
598 pBuffer[0] = '\n';
599 int buffer_start = 1;
600 int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
601 assert (msg_len != -1);
602 msg_len += buffer_start;
603
604 if ((gc_config_log_buffer_offset + msg_len) > gc_config_log_buffer_size)
605 {
606 fwrite(gc_config_log_buffer, gc_config_log_buffer_offset, 1, gc_config_log);
607 fflush(gc_config_log);
608 gc_config_log_buffer_offset = 0;
609 }
610
611 memcpy (gc_config_log_buffer + gc_config_log_buffer_offset, pBuffer, msg_len);
612 gc_config_log_buffer_offset += msg_len;
613}
614
615void GCLogConfig (const char *fmt, ... )
616{
617 if (gc_config_log_on && (gc_config_log != NULL))
618 {
619 va_list args;
620 va_start( args, fmt );
621 log_va_msg_config (fmt, args);
622 }
623}
624#endif // GC_CONFIG_DRIVEN && !DACCESS_COMPILE
625
626#ifdef SYNCHRONIZATION_STATS
627
628// Number of GCs have we done since we last logged.
629static unsigned int gc_count_during_log;
630 // In ms. This is how often we print out stats.
631static const unsigned int log_interval = 5000;
632// Time (in ms) when we start a new log interval.
633static unsigned int log_start_tick;
634static unsigned int gc_lock_contended;
635static int64_t log_start_hires;
636// Cycles accumulated in SuspendEE during log_interval.
637static uint64_t suspend_ee_during_log;
638// Cycles accumulated in RestartEE during log_interval.
639static uint64_t restart_ee_during_log;
640static uint64_t gc_during_log;
641
642#endif //SYNCHRONIZATION_STATS
643
644void
645init_sync_log_stats()
646{
647#ifdef SYNCHRONIZATION_STATS
648 if (gc_count_during_log == 0)
649 {
650 gc_heap::init_sync_stats();
651 suspend_ee_during_log = 0;
652 restart_ee_during_log = 0;
653 gc_during_log = 0;
654 gc_lock_contended = 0;
655
656 log_start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
657 log_start_hires = GCToOSInterface::QueryPerformanceCounter();
658 }
659 gc_count_during_log++;
660#endif //SYNCHRONIZATION_STATS
661}
662
663void
664process_sync_log_stats()
665{
666#ifdef SYNCHRONIZATION_STATS
667
668 unsigned int log_elapsed = GCToOSInterface::GetLowPrecisionTimeStamp() - log_start_tick;
669
670 if (log_elapsed > log_interval)
671 {
672 uint64_t total = GCToOSInterface::QueryPerformanceCounter() - log_start_hires;
673 // Print out the cycles we spent on average in each suspend and restart.
674 printf("\n_________________________________________________________________________________\n"
675 "Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n"
676 "SuspendEE: %8u; RestartEE: %8u GC %.3f%%\n",
677 log_interval / 1000,
678 gc_count_during_log,
679 gc_lock_contended,
680 (unsigned int)(gc_during_log / gc_count_during_log),
681 (unsigned int)(suspend_ee_during_log / gc_count_during_log),
682 (unsigned int)(restart_ee_during_log / gc_count_during_log),
683 (double)(100.0f * gc_during_log / total));
684 gc_heap::print_sync_stats(gc_count_during_log);
685
686 gc_count_during_log = 0;
687 }
688#endif //SYNCHRONIZATION_STATS
689}
690
691#ifdef MULTIPLE_HEAPS
692
693enum gc_join_stage
694{
695 gc_join_init_cpu_mapping = 0,
696 gc_join_done = 1,
697 gc_join_generation_determined = 2,
698 gc_join_begin_mark_phase = 3,
699 gc_join_scan_dependent_handles = 4,
700 gc_join_rescan_dependent_handles = 5,
701 gc_join_scan_sizedref_done = 6,
702 gc_join_null_dead_short_weak = 7,
703 gc_join_scan_finalization = 8,
704 gc_join_null_dead_long_weak = 9,
705 gc_join_null_dead_syncblk = 10,
706 gc_join_decide_on_compaction = 11,
707 gc_join_rearrange_segs_compaction = 12,
708 gc_join_adjust_handle_age_compact = 13,
709 gc_join_adjust_handle_age_sweep = 14,
710 gc_join_begin_relocate_phase = 15,
711 gc_join_relocate_phase_done = 16,
712 gc_join_verify_objects_done = 17,
713 gc_join_start_bgc = 18,
714 gc_join_restart_ee = 19,
715 gc_join_concurrent_overflow = 20,
716 gc_join_suspend_ee = 21,
717 gc_join_bgc_after_ephemeral = 22,
718 gc_join_allow_fgc = 23,
719 gc_join_bgc_sweep = 24,
720 gc_join_suspend_ee_verify = 25,
721 gc_join_restart_ee_verify = 26,
722 gc_join_set_state_free = 27,
723 gc_r_join_update_card_bundle = 28,
724 gc_join_after_absorb = 29,
725 gc_join_verify_copy_table = 30,
726 gc_join_after_reset = 31,
727 gc_join_after_ephemeral_sweep = 32,
728 gc_join_after_profiler_heap_walk = 33,
729 gc_join_minimal_gc = 34,
730 gc_join_after_commit_soh_no_gc = 35,
731 gc_join_expand_loh_no_gc = 36,
732 gc_join_final_no_gc = 37,
733 gc_join_disable_software_write_watch = 38,
734 gc_join_max = 39
735};
736
737enum gc_join_flavor
738{
739 join_flavor_server_gc = 0,
740 join_flavor_bgc = 1
741};
742
743#define first_thread_arrived 2
744#pragma warning(push)
745#pragma warning(disable:4324) // don't complain if DECLSPEC_ALIGN actually pads
746struct DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE) join_structure
747{
748 // Shared non volatile keep on separate line to prevent eviction
749 int n_threads;
750
751 // Keep polling/wait structures on separate line write once per join
752 DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
753 GCEvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
754 Volatile<int> lock_color;
755 VOLATILE(BOOL) wait_done;
756 VOLATILE(BOOL) joined_p;
757
758 // Keep volatile counted locks on separate cache line write many per join
759 DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
760 VOLATILE(int32_t) join_lock;
761 VOLATILE(int32_t) r_join_lock;
762
763};
764#pragma warning(pop)
765
766enum join_type
767{
768 type_last_join = 0,
769 type_join = 1,
770 type_restart = 2,
771 type_first_r_join = 3,
772 type_r_join = 4
773};
774
775enum join_time
776{
777 time_start = 0,
778 time_end = 1
779};
780
781enum join_heap_index
782{
783 join_heap_restart = 100,
784 join_heap_r_restart = 200
785};
786
787struct join_event
788{
789 uint32_t heap;
790 join_time time;
791 join_type type;
792};
793
794class t_join
795{
796 join_structure join_struct;
797
798 int id;
799 gc_join_flavor flavor;
800
801#ifdef JOIN_STATS
802 uint64_t start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
803 // remember join id and last thread to arrive so restart can use these
804 int thd;
805 // we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval
806 uint32_t start_tick;
807 // counters for joins, in 1000's of clock cycles
808 uint64_t elapsed_total[gc_join_max], wake_total[gc_join_max], seq_loss_total[gc_join_max], par_loss_total[gc_join_max], in_join_total[gc_join_max];
809#endif //JOIN_STATS
810
811public:
812 BOOL init (int n_th, gc_join_flavor f)
813 {
814 dprintf (JOIN_LOG, ("Initializing join structure"));
815 join_struct.n_threads = n_th;
816 join_struct.lock_color = 0;
817 for (int i = 0; i < 3; i++)
818 {
819 if (!join_struct.joined_event[i].IsValid())
820 {
821 join_struct.joined_p = FALSE;
822 dprintf (JOIN_LOG, ("Creating join event %d", i));
823 // TODO - changing this to a non OS event
824 // because this is also used by BGC threads which are
825 // managed threads and WaitEx does not allow you to wait
826 // for an OS event on a managed thread.
827 // But we are not sure if this plays well in the hosting
828 // environment.
829 //join_struct.joined_event[i].CreateOSManualEventNoThrow(FALSE);
830 if (!join_struct.joined_event[i].CreateManualEventNoThrow(FALSE))
831 return FALSE;
832 }
833 }
834 join_struct.join_lock = join_struct.n_threads;
835 join_struct.r_join_lock = join_struct.n_threads;
836 join_struct.wait_done = FALSE;
837 flavor = f;
838
839#ifdef JOIN_STATS
840 start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
841#endif //JOIN_STATS
842
843 return TRUE;
844 }
845
846 void destroy ()
847 {
848 dprintf (JOIN_LOG, ("Destroying join structure"));
849 for (int i = 0; i < 3; i++)
850 {
851 if (join_struct.joined_event[i].IsValid())
852 join_struct.joined_event[i].CloseEvent();
853 }
854 }
855
856 inline void fire_event (int heap, join_time time, join_type type, int join_id)
857 {
858 FIRE_EVENT(GCJoin_V2, heap, time, type, join_id);
859 }
860
861 void join (gc_heap* gch, int join_id)
862 {
863#ifdef JOIN_STATS
864 // parallel execution ends here
865 end[gch->heap_number] = get_ts();
866#endif //JOIN_STATS
867
868 assert (!join_struct.joined_p);
869 int color = join_struct.lock_color.LoadWithoutBarrier();
870
871 if (Interlocked::Decrement(&join_struct.join_lock) != 0)
872 {
873 dprintf (JOIN_LOG, ("join%d(%d): Join() Waiting...join_lock is now %d",
874 flavor, join_id, (int32_t)(join_struct.join_lock)));
875
876 fire_event (gch->heap_number, time_start, type_join, join_id);
877
878 //busy wait around the color
879 if (color == join_struct.lock_color.LoadWithoutBarrier())
880 {
881respin:
882 int spin_count = 128 * yp_spin_count_unit;
883 for (int j = 0; j < spin_count; j++)
884 {
885 if (color != join_struct.lock_color.LoadWithoutBarrier())
886 {
887 break;
888 }
889 YieldProcessor(); // indicate to the processor that we are spinning
890 }
891
892 // we've spun, and if color still hasn't changed, fall into hard wait
893 if (color == join_struct.lock_color.LoadWithoutBarrier())
894 {
895 dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d",
896 flavor, join_id, color, (int32_t)(join_struct.join_lock)));
897
898 //Thread* current_thread = GCToEEInterface::GetThread();
899 //BOOL cooperative_mode = gc_heap::enable_preemptive ();
900 uint32_t dwJoinWait = join_struct.joined_event[color].Wait(INFINITE, FALSE);
901 //gc_heap::disable_preemptive (cooperative_mode);
902
903 if (dwJoinWait != WAIT_OBJECT_0)
904 {
905 STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
906 FATAL_GC_ERROR ();
907 }
908 }
909
910 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
911 if (color == join_struct.lock_color.LoadWithoutBarrier())
912 {
913 goto respin;
914 }
915
916 dprintf (JOIN_LOG, ("join%d(%d): Join() done, join_lock is %d",
917 flavor, join_id, (int32_t)(join_struct.join_lock)));
918 }
919
920 fire_event (gch->heap_number, time_end, type_join, join_id);
921
922#ifdef JOIN_STATS
923 // parallel execution starts here
924 start[gch->heap_number] = get_ts();
925 Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number]));
926#endif //JOIN_STATS
927 }
928 else
929 {
930 fire_event (gch->heap_number, time_start, type_last_join, join_id);
931
932 join_struct.joined_p = TRUE;
933 dprintf (JOIN_LOG, ("join%d(%d): Last thread to complete the join, setting id", flavor, join_id));
934 join_struct.joined_event[!color].Reset();
935 id = join_id;
936 // this one is alone so it can proceed
937#ifdef JOIN_STATS
938 // remember the join id, the last thread arriving, the start of the sequential phase,
939 // and keep track of the cycles spent waiting in the join
940 thd = gch->heap_number;
941 start_seq = get_ts();
942 Interlocked::ExchangeAdd(&in_join_total[join_id], (start_seq - end[gch->heap_number]));
943#endif //JOIN_STATS
944 }
945 }
946
947 // Reverse join - first thread gets here does the work; other threads will only proceed
948 // after the work is done.
949 // Note that you cannot call this twice in a row on the same thread. Plus there's no
950 // need to call it twice in row - you should just merge the work.
951 BOOL r_join (gc_heap* gch, int join_id)
952 {
953
954 if (join_struct.n_threads == 1)
955 {
956 return TRUE;
957 }
958
959 if (Interlocked::CompareExchange(&join_struct.r_join_lock, 0, join_struct.n_threads) == 0)
960 {
961 if (!join_struct.wait_done)
962 {
963 dprintf (JOIN_LOG, ("r_join() Waiting..."));
964
965 fire_event (gch->heap_number, time_start, type_join, join_id);
966
967 //busy wait around the color
968 if (!join_struct.wait_done)
969 {
970 respin:
971 int spin_count = 256 * yp_spin_count_unit;
972 for (int j = 0; j < spin_count; j++)
973 {
974 if (join_struct.wait_done)
975 {
976 break;
977 }
978 YieldProcessor(); // indicate to the processor that we are spinning
979 }
980
981 // we've spun, and if color still hasn't changed, fall into hard wait
982 if (!join_struct.wait_done)
983 {
984 dprintf (JOIN_LOG, ("Join() hard wait on reset event %d", first_thread_arrived));
985 uint32_t dwJoinWait = join_struct.joined_event[first_thread_arrived].Wait(INFINITE, FALSE);
986 if (dwJoinWait != WAIT_OBJECT_0)
987 {
988 STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
989 FATAL_GC_ERROR ();
990 }
991 }
992
993 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
994 if (!join_struct.wait_done)
995 {
996 goto respin;
997 }
998
999 dprintf (JOIN_LOG, ("r_join() done"));
1000 }
1001
1002 fire_event (gch->heap_number, time_end, type_join, join_id);
1003 }
1004
1005 return FALSE;
1006 }
1007 else
1008 {
1009 fire_event (gch->heap_number, time_start, type_first_r_join, join_id);
1010 return TRUE;
1011 }
1012 }
1013
1014#ifdef JOIN_STATS
1015 uint64_t get_ts()
1016 {
1017 return GCToOSInterface::QueryPerformanceCounter();
1018 }
1019
1020 void start_ts (gc_heap* gch)
1021 {
1022 // parallel execution ends here
1023 start[gch->heap_number] = get_ts();
1024 }
1025#endif //JOIN_STATS
1026
1027 void restart()
1028 {
1029#ifdef JOIN_STATS
1030 uint64_t elapsed_seq = get_ts() - start_seq;
1031 uint64_t max = 0, sum = 0, wake = 0;
1032 uint64_t min_ts = start[0];
1033 for (int i = 1; i < join_struct.n_threads; i++)
1034 {
1035 if(min_ts > start[i]) min_ts = start[i];
1036 }
1037
1038 for (int i = 0; i < join_struct.n_threads; i++)
1039 {
1040 uint64_t wake_delay = start[i] - min_ts;
1041 uint64_t elapsed = end[i] - start[i];
1042 if (max < elapsed)
1043 max = elapsed;
1044 sum += elapsed;
1045 wake += wake_delay;
1046 }
1047 uint64_t seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
1048 uint64_t par_loss = join_struct.n_threads*max - sum;
1049 double efficiency = 0.0;
1050 if (max > 0)
1051 efficiency = sum*100.0/(join_struct.n_threads*max);
1052
1053 const double ts_scale = 1e-6;
1054
1055 // enable this printf to get statistics on each individual join as it occurs
1056// printf("join #%3d seq_loss = %5g par_loss = %5g efficiency = %3.0f%%\n", join_id, ts_scale*seq_loss, ts_scale*par_loss, efficiency);
1057
1058 elapsed_total[id] += sum;
1059 wake_total[id] += wake;
1060 seq_loss_total[id] += seq_loss;
1061 par_loss_total[id] += par_loss;
1062
1063 // every 10 seconds, print a summary of the time spent in each type of join
1064 if (GCToOSInterface::GetLowPrecisionTimeStamp() - start_tick > 10*1000)
1065 {
1066 printf("**** summary *****\n");
1067 for (int i = 0; i < 16; i++)
1068 {
1069 printf("join #%3d elapsed_total = %8g wake_loss = %8g seq_loss = %8g par_loss = %8g in_join_total = %8g\n",
1070 i,
1071 ts_scale*elapsed_total[i],
1072 ts_scale*wake_total[i],
1073 ts_scale*seq_loss_total[i],
1074 ts_scale*par_loss_total[i],
1075 ts_scale*in_join_total[i]);
1076 elapsed_total[i] = wake_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
1077 }
1078 start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
1079 }
1080#endif //JOIN_STATS
1081
1082 fire_event (join_heap_restart, time_start, type_restart, -1);
1083 assert (join_struct.joined_p);
1084 join_struct.joined_p = FALSE;
1085 join_struct.join_lock = join_struct.n_threads;
1086 dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
1087// printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start);
1088 int color = join_struct.lock_color.LoadWithoutBarrier();
1089 join_struct.lock_color = !color;
1090 join_struct.joined_event[color].Set();
1091
1092// printf("Set joined_event %d\n", !join_struct.lock_color);
1093
1094 fire_event (join_heap_restart, time_end, type_restart, -1);
1095
1096#ifdef JOIN_STATS
1097 start[thd] = get_ts();
1098#endif //JOIN_STATS
1099 }
1100
1101 BOOL joined()
1102 {
1103 dprintf (JOIN_LOG, ("join%d(%d): joined, join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
1104 return join_struct.joined_p;
1105 }
1106
1107 void r_restart()
1108 {
1109 if (join_struct.n_threads != 1)
1110 {
1111 fire_event (join_heap_r_restart, time_start, type_restart, -1);
1112 join_struct.wait_done = TRUE;
1113 join_struct.joined_event[first_thread_arrived].Set();
1114 fire_event (join_heap_r_restart, time_end, type_restart, -1);
1115 }
1116 }
1117
1118 void r_init()
1119 {
1120 if (join_struct.n_threads != 1)
1121 {
1122 join_struct.r_join_lock = join_struct.n_threads;
1123 join_struct.wait_done = FALSE;
1124 join_struct.joined_event[first_thread_arrived].Reset();
1125 }
1126 }
1127};
1128
1129t_join gc_t_join;
1130
1131#ifdef BACKGROUND_GC
1132t_join bgc_t_join;
1133#endif //BACKGROUND_GC
1134
1135#endif //MULTIPLE_HEAPS
1136
1137#define spin_and_switch(count_to_spin, expr) \
1138{ \
1139 for (int j = 0; j < count_to_spin; j++) \
1140 { \
1141 if (expr) \
1142 { \
1143 break;\
1144 } \
1145 YieldProcessor(); \
1146 } \
1147 if (!(expr)) \
1148 { \
1149 GCToOSInterface::YieldThread(0); \
1150 } \
1151}
1152
1153#ifndef DACCESS_COMPILE
1154#ifdef BACKGROUND_GC
1155
1156#define max_pending_allocs 64
1157
1158class exclusive_sync
1159{
1160 // TODO - verify that this is the right syntax for Volatile.
1161 VOLATILE(uint8_t*) rwp_object;
1162 VOLATILE(int32_t) needs_checking;
1163
1164 int spin_count;
1165
1166 uint8_t cache_separator[HS_CACHE_LINE_SIZE - sizeof (int) - sizeof (int32_t)];
1167
1168 // TODO - perhaps each object should be on its own cache line...
1169 VOLATILE(uint8_t*) alloc_objects[max_pending_allocs];
1170
1171 int find_free_index ()
1172 {
1173 for (int i = 0; i < max_pending_allocs; i++)
1174 {
1175 if (alloc_objects [i] == (uint8_t*)0)
1176 {
1177 return i;
1178 }
1179 }
1180
1181 return -1;
1182 }
1183
1184public:
1185 void init()
1186 {
1187 spin_count = 32 * (g_num_processors - 1);
1188 rwp_object = 0;
1189 needs_checking = 0;
1190 for (int i = 0; i < max_pending_allocs; i++)
1191 {
1192 alloc_objects [i] = (uint8_t*)0;
1193 }
1194 }
1195
1196 void check()
1197 {
1198 for (int i = 0; i < max_pending_allocs; i++)
1199 {
1200 if (alloc_objects [i] != (uint8_t*)0)
1201 {
1202 GCToOSInterface::DebugBreak();
1203 }
1204 }
1205 }
1206
1207 void bgc_mark_set (uint8_t* obj)
1208 {
1209 dprintf (3, ("cm: probing %Ix", obj));
1210retry:
1211 if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
1212 {
1213 // If we spend too much time spending all the allocs,
1214 // consider adding a high water mark and scan up
1215 // to that; we'll need to interlock in done when
1216 // we update the high watermark.
1217 for (int i = 0; i < max_pending_allocs; i++)
1218 {
1219 if (obj == alloc_objects[i])
1220 {
1221 needs_checking = 0;
1222 dprintf (3, ("cm: will spin"));
1223 spin_and_switch (spin_count, (obj != alloc_objects[i]));
1224 goto retry;
1225 }
1226 }
1227
1228 rwp_object = obj;
1229 needs_checking = 0;
1230 dprintf (3, ("cm: set %Ix", obj));
1231 return;
1232 }
1233 else
1234 {
1235 spin_and_switch (spin_count, (needs_checking == 0));
1236 goto retry;
1237 }
1238 }
1239
1240 int loh_alloc_set (uint8_t* obj)
1241 {
1242 if (!gc_heap::cm_in_progress)
1243 {
1244 return -1;
1245 }
1246
1247retry:
1248 dprintf (3, ("loh alloc: probing %Ix", obj));
1249
1250 if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
1251 {
1252 if (obj == rwp_object)
1253 {
1254 needs_checking = 0;
1255 spin_and_switch (spin_count, (obj != rwp_object));
1256 goto retry;
1257 }
1258 else
1259 {
1260 int cookie = find_free_index();
1261
1262 if (cookie != -1)
1263 {
1264 alloc_objects[cookie] = obj;
1265 needs_checking = 0;
1266 //if (cookie >= 4)
1267 //{
1268 // GCToOSInterface::DebugBreak();
1269 //}
1270
1271 dprintf (3, ("loh alloc: set %Ix at %d", obj, cookie));
1272 return cookie;
1273 }
1274 else
1275 {
1276 needs_checking = 0;
1277 dprintf (3, ("loh alloc: setting %Ix will spin to acquire a free index", obj));
1278 spin_and_switch (spin_count, (find_free_index () != -1));
1279 goto retry;
1280 }
1281 }
1282 }
1283 else
1284 {
1285 dprintf (3, ("loh alloc: will spin on checking %Ix", obj));
1286 spin_and_switch (spin_count, (needs_checking == 0));
1287 goto retry;
1288 }
1289 }
1290
1291 void bgc_mark_done ()
1292 {
1293 dprintf (3, ("cm: release lock on %Ix", (uint8_t *)rwp_object));
1294 rwp_object = 0;
1295 }
1296
1297 void loh_alloc_done_with_index (int index)
1298 {
1299 dprintf (3, ("loh alloc: release lock on %Ix based on %d", (uint8_t *)alloc_objects[index], index));
1300 assert ((index >= 0) && (index < max_pending_allocs));
1301 alloc_objects[index] = (uint8_t*)0;
1302 }
1303
1304 void loh_alloc_done (uint8_t* obj)
1305 {
1306#ifdef BACKGROUND_GC
1307 if (!gc_heap::cm_in_progress)
1308 {
1309 return;
1310 }
1311
1312 for (int i = 0; i < max_pending_allocs; i++)
1313 {
1314 if (alloc_objects [i] == obj)
1315 {
1316 dprintf (3, ("loh alloc: release lock on %Ix at %d", (uint8_t *)alloc_objects[i], i));
1317 alloc_objects[i] = (uint8_t*)0;
1318 return;
1319 }
1320 }
1321#endif //BACKGROUND_GC
1322 }
1323};
1324
1325// Note that this class was written assuming just synchronization between
1326// one background GC thread and multiple user threads that might request
1327// an FGC - it does not take into account what kind of locks the multiple
1328// user threads might be holding at the time (eg, there could only be one
1329// user thread requesting an FGC because it needs to take gc_lock first)
1330// so you'll see checks that may not be necessary if you take those conditions
1331// into consideration.
1332//
1333// With the introduction of Server Background GC we no longer use this
1334// class to do synchronization between FGCs and BGC.
1335class recursive_gc_sync
1336{
1337 static VOLATILE(int32_t) foreground_request_count;//initial state 0
1338 static VOLATILE(BOOL) gc_background_running; //initial state FALSE
1339 static VOLATILE(int32_t) foreground_count; // initial state 0;
1340 static VOLATILE(uint32_t) foreground_gate; // initial state FALSE;
1341 static GCEvent foreground_complete;//Auto Reset
1342 static GCEvent foreground_allowed;//Auto Reset
1343public:
1344 static void begin_background();
1345 static void end_background();
1346 static void begin_foreground();
1347 static void end_foreground();
1348 BOOL allow_foreground ();
1349 static BOOL init();
1350 static void shutdown();
1351 static BOOL background_running_p() {return gc_background_running;}
1352};
1353
1354VOLATILE(int32_t) recursive_gc_sync::foreground_request_count = 0;//initial state 0
1355VOLATILE(int32_t) recursive_gc_sync::foreground_count = 0; // initial state 0;
1356VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE
1357VOLATILE(uint32_t) recursive_gc_sync::foreground_gate = 0;
1358GCEvent recursive_gc_sync::foreground_complete;//Auto Reset
1359GCEvent recursive_gc_sync::foreground_allowed;//Manual Reset
1360
1361BOOL recursive_gc_sync::init ()
1362{
1363 foreground_request_count = 0;
1364 foreground_count = 0;
1365 gc_background_running = FALSE;
1366 foreground_gate = 0;
1367
1368 if (!foreground_complete.CreateOSAutoEventNoThrow(FALSE))
1369 {
1370 goto error;
1371 }
1372 if (!foreground_allowed.CreateManualEventNoThrow(FALSE))
1373 {
1374 goto error;
1375 }
1376 return TRUE;
1377
1378error:
1379 shutdown();
1380 return FALSE;
1381
1382}
1383
1384void recursive_gc_sync::shutdown()
1385{
1386 if (foreground_complete.IsValid())
1387 foreground_complete.CloseEvent();
1388 if (foreground_allowed.IsValid())
1389 foreground_allowed.CloseEvent();
1390}
1391
1392void recursive_gc_sync::begin_background()
1393{
1394 dprintf (2, ("begin background"));
1395 foreground_request_count = 1;
1396 foreground_count = 1;
1397 foreground_allowed.Reset();
1398 gc_background_running = TRUE;
1399}
1400void recursive_gc_sync::end_background()
1401{
1402 dprintf (2, ("end background"));
1403 gc_background_running = FALSE;
1404 foreground_gate = 1;
1405 foreground_allowed.Set();
1406}
1407
1408void recursive_gc_sync::begin_foreground()
1409{
1410 dprintf (2, ("begin_foreground"));
1411
1412 bool cooperative_mode = false;
1413 if (gc_background_running)
1414 {
1415 gc_heap::fire_alloc_wait_event_begin (awr_fgc_wait_for_bgc);
1416 gc_heap::alloc_wait_event_p = TRUE;
1417
1418try_again_top:
1419
1420 Interlocked::Increment (&foreground_request_count);
1421
1422try_again_no_inc:
1423 dprintf(2, ("Waiting sync gc point"));
1424 assert (foreground_allowed.IsValid());
1425 assert (foreground_complete.IsValid());
1426
1427 cooperative_mode = gc_heap::enable_preemptive ();
1428
1429 foreground_allowed.Wait(INFINITE, FALSE);
1430
1431 dprintf(2, ("Waiting sync gc point is done"));
1432
1433 gc_heap::disable_preemptive (cooperative_mode);
1434
1435 if (foreground_gate)
1436 {
1437 Interlocked::Increment (&foreground_count);
1438 dprintf (2, ("foreground_count: %d", (int32_t)foreground_count));
1439 if (foreground_gate)
1440 {
1441 gc_heap::settings.concurrent = FALSE;
1442 return;
1443 }
1444 else
1445 {
1446 end_foreground();
1447 goto try_again_top;
1448 }
1449 }
1450 else
1451 {
1452 goto try_again_no_inc;
1453 }
1454 }
1455}
1456
1457void recursive_gc_sync::end_foreground()
1458{
1459 dprintf (2, ("end_foreground"));
1460 if (gc_background_running)
1461 {
1462 Interlocked::Decrement (&foreground_request_count);
1463 dprintf (2, ("foreground_count before decrement: %d", (int32_t)foreground_count));
1464 if (Interlocked::Decrement (&foreground_count) == 0)
1465 {
1466 //c_write ((BOOL*)&foreground_gate, 0);
1467 // TODO - couldn't make the syntax work with Volatile<T>
1468 foreground_gate = 0;
1469 if (foreground_count == 0)
1470 {
1471 foreground_allowed.Reset ();
1472 dprintf(2, ("setting foreground complete event"));
1473 foreground_complete.Set();
1474 }
1475 }
1476 }
1477}
1478
1479inline
1480BOOL recursive_gc_sync::allow_foreground()
1481{
1482 assert (gc_heap::settings.concurrent);
1483 dprintf (100, ("enter allow_foreground, f_req_count: %d, f_count: %d",
1484 (int32_t)foreground_request_count, (int32_t)foreground_count));
1485
1486 BOOL did_fgc = FALSE;
1487
1488 //if we have suspended the EE, just return because
1489 //some thread could be waiting on this to proceed.
1490 if (!GCHeap::GcInProgress)
1491 {
1492 //TODO BACKGROUND_GC This is to stress the concurrency between
1493 //background and foreground
1494// gc_heap::disallow_new_allocation (0);
1495
1496 //GCToOSInterface::YieldThread(0);
1497
1498 //END of TODO
1499 if (foreground_request_count != 0)
1500 {
1501 //foreground wants to run
1502 //save the important settings
1503 //TODO BACKGROUND_GC be more selective about the important settings.
1504 gc_mechanisms saved_settings = gc_heap::settings;
1505 do
1506 {
1507 did_fgc = TRUE;
1508 //c_write ((BOOL*)&foreground_gate, 1);
1509 // TODO - couldn't make the syntax work with Volatile<T>
1510 foreground_gate = 1;
1511 foreground_allowed.Set ();
1512 foreground_complete.Wait (INFINITE, FALSE);
1513 }while (/*foreground_request_count ||*/ foreground_gate);
1514
1515 assert (!foreground_gate);
1516
1517 //restore the important settings
1518 gc_heap::settings = saved_settings;
1519 GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
1520 //the background GC shouldn't be using gc_high and gc_low
1521 //gc_low = lowest_address;
1522 //gc_high = highest_address;
1523 }
1524
1525 //TODO BACKGROUND_GC This is to stress the concurrency between
1526 //background and foreground
1527// gc_heap::allow_new_allocation (0);
1528 //END of TODO
1529 }
1530
1531 dprintf (100, ("leave allow_foreground"));
1532 assert (gc_heap::settings.concurrent);
1533 return did_fgc;
1534}
1535
1536#endif //BACKGROUND_GC
1537#endif //DACCESS_COMPILE
1538
1539
1540#if defined(COUNT_CYCLES)
1541#ifdef _MSC_VER
1542#pragma warning(disable:4035)
1543#endif //_MSC_VER
1544
1545static
1546unsigned GetCycleCount32() // enough for about 40 seconds
1547{
1548__asm push EDX
1549__asm _emit 0x0F
1550__asm _emit 0x31
1551__asm pop EDX
1552};
1553
1554#pragma warning(default:4035)
1555
1556#endif //COUNT_CYCLES
1557
1558#ifdef TIME_GC
1559int mark_time, plan_time, sweep_time, reloc_time, compact_time;
1560#endif //TIME_GC
1561
1562#ifndef MULTIPLE_HEAPS
1563
1564#endif // MULTIPLE_HEAPS
1565
1566void reset_memory (uint8_t* o, size_t sizeo);
1567
1568#ifdef WRITE_WATCH
1569
1570#ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1571static bool virtual_alloc_hardware_write_watch = false;
1572#endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1573
1574static bool hardware_write_watch_capability = false;
1575
1576#ifndef DACCESS_COMPILE
1577
1578//check if the write watch APIs are supported.
1579
1580void hardware_write_watch_api_supported()
1581{
1582 if (GCToOSInterface::SupportsWriteWatch())
1583 {
1584 hardware_write_watch_capability = true;
1585 dprintf (2, ("WriteWatch supported"));
1586 }
1587 else
1588 {
1589 dprintf (2,("WriteWatch not supported"));
1590 }
1591}
1592
1593#endif //!DACCESS_COMPILE
1594
1595inline bool can_use_hardware_write_watch()
1596{
1597 return hardware_write_watch_capability;
1598}
1599
1600inline bool can_use_write_watch_for_gc_heap()
1601{
1602#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1603 return true;
1604#else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1605 return can_use_hardware_write_watch();
1606#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1607}
1608
1609inline bool can_use_write_watch_for_card_table()
1610{
1611#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
1612 return true;
1613#else
1614 return can_use_hardware_write_watch();
1615#endif
1616}
1617
1618#else
1619#define mem_reserve (MEM_RESERVE)
1620#endif //WRITE_WATCH
1621
1622//check if the low memory notification is supported
1623
1624#ifndef DACCESS_COMPILE
1625
1626void WaitLongerNoInstru (int i)
1627{
1628 // every 8th attempt:
1629 bool bToggleGC = GCToEEInterface::EnablePreemptiveGC();
1630
1631 // if we're waiting for gc to finish, we should block immediately
1632 if (g_fSuspensionPending == 0)
1633 {
1634 if (g_num_processors > 1)
1635 {
1636 YieldProcessor(); // indicate to the processor that we are spining
1637 if (i & 0x01f)
1638 GCToOSInterface::YieldThread (0);
1639 else
1640 GCToOSInterface::Sleep (5);
1641 }
1642 else
1643 GCToOSInterface::Sleep (5);
1644 }
1645
1646 // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1647 // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1648 // It is important that the thread is going to wait for GC. Otherwise the thread
1649 // is in a tight loop. If the thread has high priority, the perf is going to be very BAD.
1650 if (bToggleGC)
1651 {
1652#ifdef _DEBUG
1653 // In debug builds, all enter_spin_lock operations go through this code. If a GC has
1654 // started, it is important to block until the GC thread calls set_gc_done (since it is
1655 // guaranteed to have cleared g_TrapReturningThreads by this point). This avoids livelock
1656 // conditions which can otherwise occur if threads are allowed to spin in this function
1657 // (and therefore starve the GC thread) between the point when the GC thread sets the
1658 // WaitForGC event and the point when the GC thread clears g_TrapReturningThreads.
1659 if (gc_heap::gc_started)
1660 {
1661 gc_heap::wait_for_gc_done();
1662 }
1663#endif // _DEBUG
1664 GCToEEInterface::DisablePreemptiveGC();
1665 }
1666 else if (g_fSuspensionPending > 0)
1667 {
1668 g_theGCHeap->WaitUntilGCComplete();
1669 }
1670}
1671
1672inline
1673static void safe_switch_to_thread()
1674{
1675 bool cooperative_mode = gc_heap::enable_preemptive();
1676
1677 GCToOSInterface::YieldThread(0);
1678
1679 gc_heap::disable_preemptive(cooperative_mode);
1680}
1681
1682//
1683// We need the following methods to have volatile arguments, so that they can accept
1684// raw pointers in addition to the results of the & operator on Volatile<T>.
1685//
1686inline
1687static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1688{
1689retry:
1690
1691 if (Interlocked::CompareExchange(lock, 0, -1) >= 0)
1692 {
1693 unsigned int i = 0;
1694 while (VolatileLoad(lock) >= 0)
1695 {
1696 if ((++i & 7) && !IsGCInProgress())
1697 {
1698 if (g_num_processors > 1)
1699 {
1700#ifndef MULTIPLE_HEAPS
1701 int spin_count = 32 * yp_spin_count_unit;
1702#else //!MULTIPLE_HEAPS
1703 int spin_count = yp_spin_count_unit;
1704#endif //!MULTIPLE_HEAPS
1705 for (int j = 0; j < spin_count; j++)
1706 {
1707 if (VolatileLoad(lock) < 0 || IsGCInProgress())
1708 break;
1709 YieldProcessor(); // indicate to the processor that we are spining
1710 }
1711 if (VolatileLoad(lock) >= 0 && !IsGCInProgress())
1712 {
1713 safe_switch_to_thread();
1714 }
1715 }
1716 else
1717 {
1718 safe_switch_to_thread();
1719 }
1720 }
1721 else
1722 {
1723 WaitLongerNoInstru(i);
1724 }
1725 }
1726 goto retry;
1727 }
1728}
1729
1730inline
1731static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) int32_t* lock)
1732{
1733 return (Interlocked::CompareExchange(&*lock, 0, -1) < 0);
1734}
1735
1736inline
1737static void leave_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1738{
1739 VolatileStore<int32_t>((int32_t*)lock, -1);
1740}
1741
1742#ifdef _DEBUG
1743
1744inline
1745static void enter_spin_lock(GCSpinLock *pSpinLock)
1746{
1747 enter_spin_lock_noinstru(&pSpinLock->lock);
1748 assert (pSpinLock->holding_thread == (Thread*)-1);
1749 pSpinLock->holding_thread = GCToEEInterface::GetThread();
1750}
1751
1752inline
1753static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock)
1754{
1755 BOOL ret = try_enter_spin_lock_noinstru(&pSpinLock->lock);
1756 if (ret)
1757 pSpinLock->holding_thread = GCToEEInterface::GetThread();
1758 return ret;
1759}
1760
1761inline
1762static void leave_spin_lock(GCSpinLock *pSpinLock)
1763{
1764 bool gc_thread_p = GCToEEInterface::WasCurrentThreadCreatedByGC();
1765// _ASSERTE((pSpinLock->holding_thread == GCToEEInterface::GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p);
1766 pSpinLock->released_by_gc_p = gc_thread_p;
1767 pSpinLock->holding_thread = (Thread*) -1;
1768 if (pSpinLock->lock != -1)
1769 leave_spin_lock_noinstru(&pSpinLock->lock);
1770}
1771
1772#define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) \
1773 _ASSERTE((pSpinLock)->holding_thread == GCToEEInterface::GetThread());
1774
1775#define ASSERT_NOT_HOLDING_SPIN_LOCK(pSpinLock) \
1776 _ASSERTE((pSpinLock)->holding_thread != GCToEEInterface::GetThread());
1777
1778#else //_DEBUG
1779
1780//In the concurrent version, the Enable/DisablePreemptiveGC is optional because
1781//the gc thread call WaitLonger.
1782void WaitLonger (int i
1783#ifdef SYNCHRONIZATION_STATS
1784 , GCSpinLock* spin_lock
1785#endif //SYNCHRONIZATION_STATS
1786 )
1787{
1788#ifdef SYNCHRONIZATION_STATS
1789 (spin_lock->num_wait_longer)++;
1790#endif //SYNCHRONIZATION_STATS
1791
1792 // every 8th attempt:
1793 bool bToggleGC = GCToEEInterface::EnablePreemptiveGC();
1794 assert (bToggleGC);
1795
1796 // if we're waiting for gc to finish, we should block immediately
1797 if (!gc_heap::gc_started)
1798 {
1799#ifdef SYNCHRONIZATION_STATS
1800 (spin_lock->num_switch_thread_w)++;
1801#endif //SYNCHRONIZATION_STATS
1802 if (g_num_processors > 1)
1803 {
1804 YieldProcessor(); // indicate to the processor that we are spining
1805 if (i & 0x01f)
1806 GCToOSInterface::YieldThread (0);
1807 else
1808 GCToOSInterface::Sleep (5);
1809 }
1810 else
1811 GCToOSInterface::Sleep (5);
1812 }
1813
1814 // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1815 // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1816 // It is important that the thread is going to wait for GC. Otherwise the thread
1817 // is in a tight loop. If the thread has high priority, the perf is going to be very BAD.
1818 if (gc_heap::gc_started)
1819 {
1820 gc_heap::wait_for_gc_done();
1821 }
1822
1823 if (bToggleGC)
1824 {
1825#ifdef SYNCHRONIZATION_STATS
1826 (spin_lock->num_disable_preemptive_w)++;
1827#endif //SYNCHRONIZATION_STATS
1828 GCToEEInterface::DisablePreemptiveGC();
1829 }
1830}
1831
1832inline
1833static void enter_spin_lock (GCSpinLock* spin_lock)
1834{
1835retry:
1836
1837 if (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) >= 0)
1838 {
1839 unsigned int i = 0;
1840 while (spin_lock->lock >= 0)
1841 {
1842 if ((++i & 7) && !gc_heap::gc_started)
1843 {
1844 if (g_num_processors > 1)
1845 {
1846#ifndef MULTIPLE_HEAPS
1847 int spin_count = 32 * yp_spin_count_unit;
1848#else //!MULTIPLE_HEAPS
1849 int spin_count = yp_spin_count_unit;
1850#endif //!MULTIPLE_HEAPS
1851 for (int j = 0; j < spin_count; j++)
1852 {
1853 if (spin_lock->lock < 0 || gc_heap::gc_started)
1854 break;
1855 YieldProcessor(); // indicate to the processor that we are spining
1856 }
1857 if (spin_lock->lock >= 0 && !gc_heap::gc_started)
1858 {
1859#ifdef SYNCHRONIZATION_STATS
1860 (spin_lock->num_switch_thread)++;
1861#endif //SYNCHRONIZATION_STATS
1862 bool cooperative_mode = gc_heap::enable_preemptive ();
1863
1864 GCToOSInterface::YieldThread(0);
1865
1866 gc_heap::disable_preemptive (cooperative_mode);
1867 }
1868 }
1869 else
1870 GCToOSInterface::YieldThread(0);
1871 }
1872 else
1873 {
1874 WaitLonger(i
1875#ifdef SYNCHRONIZATION_STATS
1876 , spin_lock
1877#endif //SYNCHRONIZATION_STATS
1878 );
1879 }
1880 }
1881 goto retry;
1882 }
1883}
1884
1885inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock)
1886{
1887 return (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) < 0);
1888}
1889
1890inline
1891static void leave_spin_lock (GCSpinLock * spin_lock)
1892{
1893 spin_lock->lock = -1;
1894}
1895
1896#define ASSERT_HOLDING_SPIN_LOCK(pSpinLock)
1897
1898#endif //_DEBUG
1899
1900bool gc_heap::enable_preemptive ()
1901{
1902 return GCToEEInterface::EnablePreemptiveGC();
1903}
1904
1905void gc_heap::disable_preemptive (bool restore_cooperative)
1906{
1907 if (restore_cooperative)
1908 {
1909 GCToEEInterface::DisablePreemptiveGC();
1910 }
1911}
1912
1913#endif // !DACCESS_COMPILE
1914
1915typedef void ** PTR_PTR;
1916//This function clears a piece of memory
1917// size has to be Dword aligned
1918
1919inline
1920void memclr ( uint8_t* mem, size_t size)
1921{
1922 dprintf (3, ("MEMCLR: %Ix, %d", mem, size));
1923 assert ((size & (sizeof(PTR_PTR)-1)) == 0);
1924 assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1925
1926#if 0
1927 // The compiler will recognize this pattern and replace it with memset call. We can as well just call
1928 // memset directly to make it obvious what's going on.
1929 PTR_PTR m = (PTR_PTR) mem;
1930 for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
1931 *(m++) = 0;
1932#endif
1933
1934 memset (mem, 0, size);
1935}
1936
1937void memcopy (uint8_t* dmem, uint8_t* smem, size_t size)
1938{
1939 const size_t sz4ptr = sizeof(PTR_PTR)*4;
1940 const size_t sz2ptr = sizeof(PTR_PTR)*2;
1941 const size_t sz1ptr = sizeof(PTR_PTR)*1;
1942
1943 // size must be a multiple of the pointer size
1944 assert ((size & (sizeof (PTR_PTR)-1)) == 0);
1945 assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1946
1947 // copy in groups of four pointer sized things at a time
1948 if (size >= sz4ptr)
1949 {
1950 do
1951 {
1952 ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1953 ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1954 ((PTR_PTR)dmem)[2] = ((PTR_PTR)smem)[2];
1955 ((PTR_PTR)dmem)[3] = ((PTR_PTR)smem)[3];
1956 dmem += sz4ptr;
1957 smem += sz4ptr;
1958 }
1959 while ((size -= sz4ptr) >= sz4ptr);
1960 }
1961
1962 // still two pointer sized things or more left to copy?
1963 if (size & sz2ptr)
1964 {
1965 ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1966 ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1967 dmem += sz2ptr;
1968 smem += sz2ptr;
1969 }
1970
1971 // still one pointer sized thing left to copy?
1972 if (size & sz1ptr)
1973 {
1974 ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1975 // dmem += sz1ptr;
1976 // smem += sz1ptr;
1977 }
1978
1979}
1980
1981inline
1982ptrdiff_t round_down (ptrdiff_t add, int pitch)
1983{
1984 return ((add / pitch) * pitch);
1985}
1986
1987#if defined(FEATURE_STRUCTALIGN) && defined(RESPECT_LARGE_ALIGNMENT)
1988// FEATURE_STRUCTALIGN allows the compiler to dictate the alignment,
1989// i.e, if a larger alignment matters or is beneficial, the compiler
1990// generated info tells us so. RESPECT_LARGE_ALIGNMENT is just the
1991// converse - it's a heuristic for the GC to use a larger alignment.
1992#error FEATURE_STRUCTALIGN should imply !RESPECT_LARGE_ALIGNMENT
1993#endif
1994
1995#if defined(FEATURE_STRUCTALIGN) && defined(FEATURE_LOH_COMPACTION)
1996#error FEATURE_STRUCTALIGN and FEATURE_LOH_COMPACTION are mutually exclusive
1997#endif
1998
1999#if defined(GROWABLE_SEG_MAPPING_TABLE) && !defined(SEG_MAPPING_TABLE)
2000#error if GROWABLE_SEG_MAPPING_TABLE is defined, SEG_MAPPING_TABLE must be defined
2001#endif
2002
2003inline
2004BOOL same_large_alignment_p (uint8_t* p1, uint8_t* p2)
2005{
2006#ifdef RESPECT_LARGE_ALIGNMENT
2007 return ((((size_t)p1 ^ (size_t)p2) & 7) == 0);
2008#else
2009 UNREFERENCED_PARAMETER(p1);
2010 UNREFERENCED_PARAMETER(p2);
2011 return TRUE;
2012#endif //RESPECT_LARGE_ALIGNMENT
2013}
2014
2015inline
2016size_t switch_alignment_size (BOOL already_padded_p)
2017{
2018 if (already_padded_p)
2019 return DATA_ALIGNMENT;
2020 else
2021 return (Align (min_obj_size) +((Align (min_obj_size)&DATA_ALIGNMENT)^DATA_ALIGNMENT));
2022}
2023
2024
2025#ifdef FEATURE_STRUCTALIGN
2026void set_node_aligninfo (uint8_t *node, int requiredAlignment, ptrdiff_t pad);
2027void clear_node_aligninfo (uint8_t *node);
2028#else // FEATURE_STRUCTALIGN
2029#define node_realigned(node) (((plug_and_reloc*)(node))[-1].reloc & 1)
2030void set_node_realigned (uint8_t* node);
2031void clear_node_realigned(uint8_t* node);
2032#endif // FEATURE_STRUCTALIGN
2033
2034inline
2035size_t AlignQword (size_t nbytes)
2036{
2037#ifdef FEATURE_STRUCTALIGN
2038 // This function is used to align everything on the large object
2039 // heap to an 8-byte boundary, to reduce the number of unaligned
2040 // accesses to (say) arrays of doubles. With FEATURE_STRUCTALIGN,
2041 // the compiler dictates the optimal alignment instead of having
2042 // a heuristic in the GC.
2043 return Align (nbytes);
2044#else // FEATURE_STRUCTALIGN
2045 return (nbytes + 7) & ~7;
2046#endif // FEATURE_STRUCTALIGN
2047}
2048
2049inline
2050BOOL Aligned (size_t n)
2051{
2052 return (n & ALIGNCONST) == 0;
2053}
2054
2055#define OBJECT_ALIGNMENT_OFFSET (sizeof(MethodTable *))
2056
2057#ifdef FEATURE_STRUCTALIGN
2058#define MAX_STRUCTALIGN OS_PAGE_SIZE
2059#else // FEATURE_STRUCTALIGN
2060#define MAX_STRUCTALIGN 0
2061#endif // FEATURE_STRUCTALIGN
2062
2063#ifdef FEATURE_STRUCTALIGN
2064inline
2065ptrdiff_t AdjustmentForMinPadSize(ptrdiff_t pad, int requiredAlignment)
2066{
2067 // The resulting alignpad must be either 0 or at least min_obj_size.
2068 // Note that by computing the following difference on unsigned types,
2069 // we can do the range check 0 < alignpad < min_obj_size with a
2070 // single conditional branch.
2071 if ((size_t)(pad - DATA_ALIGNMENT) < Align (min_obj_size) - DATA_ALIGNMENT)
2072 {
2073 return requiredAlignment;
2074 }
2075 return 0;
2076}
2077
2078inline
2079uint8_t* StructAlign (uint8_t* origPtr, int requiredAlignment, ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
2080{
2081 // required alignment must be a power of two
2082 _ASSERTE(((size_t)origPtr & ALIGNCONST) == 0);
2083 _ASSERTE(((requiredAlignment - 1) & requiredAlignment) == 0);
2084 _ASSERTE(requiredAlignment >= sizeof(void *));
2085 _ASSERTE(requiredAlignment <= MAX_STRUCTALIGN);
2086
2087 // When this method is invoked for individual objects (i.e., alignmentOffset
2088 // is just the size of the PostHeader), what needs to be aligned when
2089 // we're done is the pointer to the payload of the object (which means
2090 // the actual resulting object pointer is typically not aligned).
2091
2092 uint8_t* result = (uint8_t*)Align ((size_t)origPtr + alignmentOffset, requiredAlignment-1) - alignmentOffset;
2093 ptrdiff_t alignpad = result - origPtr;
2094
2095 return result + AdjustmentForMinPadSize (alignpad, requiredAlignment);
2096}
2097
2098inline
2099ptrdiff_t ComputeStructAlignPad (uint8_t* plug, int requiredAlignment, size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
2100{
2101 return StructAlign (plug, requiredAlignment, alignmentOffset) - plug;
2102}
2103
2104BOOL IsStructAligned (uint8_t *ptr, int requiredAlignment)
2105{
2106 return StructAlign (ptr, requiredAlignment) == ptr;
2107}
2108
2109inline
2110ptrdiff_t ComputeMaxStructAlignPad (int requiredAlignment)
2111{
2112 if (requiredAlignment == DATA_ALIGNMENT)
2113 return 0;
2114 // Since a non-zero alignment padding cannot be less than min_obj_size (so we can fit the
2115 // alignment padding object), the worst-case alignment padding is correspondingly larger
2116 // than the required alignment.
2117 return requiredAlignment + Align (min_obj_size) - DATA_ALIGNMENT;
2118}
2119
2120inline
2121ptrdiff_t ComputeMaxStructAlignPadLarge (int requiredAlignment)
2122{
2123 if (requiredAlignment <= get_alignment_constant (TRUE)+1)
2124 return 0;
2125 // This is the same as ComputeMaxStructAlignPad, except that in addition to leaving space
2126 // for padding before the actual object, it also leaves space for filling a gap after the
2127 // actual object. This is needed on the large object heap, as the outer allocation functions
2128 // don't operate on an allocation context (which would have left space for the final gap).
2129 return requiredAlignment + Align (min_obj_size) * 2 - DATA_ALIGNMENT;
2130}
2131
2132uint8_t* gc_heap::pad_for_alignment (uint8_t* newAlloc, int requiredAlignment, size_t size, alloc_context* acontext)
2133{
2134 uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2135 if (alignedPtr != newAlloc) {
2136 make_unused_array (newAlloc, alignedPtr - newAlloc);
2137 }
2138 acontext->alloc_ptr = alignedPtr + Align (size);
2139 return alignedPtr;
2140}
2141
2142uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignment, size_t size)
2143{
2144 uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2145 if (alignedPtr != newAlloc) {
2146 make_unused_array (newAlloc, alignedPtr - newAlloc);
2147 }
2148 if (alignedPtr < newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment)) {
2149 make_unused_array (alignedPtr + AlignQword (size), newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment) - alignedPtr);
2150 }
2151 return alignedPtr;
2152}
2153#else // FEATURE_STRUCTALIGN
2154#define ComputeMaxStructAlignPad(requiredAlignment) 0
2155#define ComputeMaxStructAlignPadLarge(requiredAlignment) 0
2156#endif // FEATURE_STRUCTALIGN
2157
2158//CLR_SIZE is the max amount of bytes from gen0 that is set to 0 in one chunk
2159#ifdef SERVER_GC
2160#define CLR_SIZE ((size_t)(8*1024))
2161#else //SERVER_GC
2162#define CLR_SIZE ((size_t)(8*1024))
2163#endif //SERVER_GC
2164
2165#define END_SPACE_AFTER_GC (loh_size_threshold + MAX_STRUCTALIGN)
2166
2167#ifdef BACKGROUND_GC
2168#define SEGMENT_INITIAL_COMMIT (2*OS_PAGE_SIZE)
2169#else
2170#define SEGMENT_INITIAL_COMMIT (OS_PAGE_SIZE)
2171#endif //BACKGROUND_GC
2172
2173#ifdef SERVER_GC
2174
2175#ifdef BIT64
2176
2177#define INITIAL_ALLOC ((size_t)((size_t)4*1024*1024*1024))
2178#define LHEAP_ALLOC ((size_t)(1024*1024*256))
2179
2180#else
2181
2182#define INITIAL_ALLOC ((size_t)(1024*1024*64))
2183#define LHEAP_ALLOC ((size_t)(1024*1024*32))
2184
2185#endif // BIT64
2186
2187#else //SERVER_GC
2188
2189#ifdef BIT64
2190
2191#define INITIAL_ALLOC ((size_t)(1024*1024*256))
2192#define LHEAP_ALLOC ((size_t)(1024*1024*128))
2193
2194#else
2195
2196#define INITIAL_ALLOC ((size_t)(1024*1024*16))
2197#define LHEAP_ALLOC ((size_t)(1024*1024*16))
2198
2199#endif // BIT64
2200
2201#endif //SERVER_GC
2202
2203//amount in bytes of the etw allocation tick
2204const size_t etw_allocation_tick = 100*1024;
2205
2206const size_t low_latency_alloc = 256*1024;
2207
2208const size_t fgn_check_quantum = 2*1024*1024;
2209
2210#ifdef MH_SC_MARK
2211const int max_snoop_level = 128;
2212#endif //MH_SC_MARK
2213
2214
2215#ifdef CARD_BUNDLE
2216//threshold of heap size to turn on card bundles.
2217#define SH_TH_CARD_BUNDLE (40*1024*1024)
2218#define MH_TH_CARD_BUNDLE (180*1024*1024)
2219#endif //CARD_BUNDLE
2220
2221#define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000
2222
2223inline
2224size_t align_on_page (size_t add)
2225{
2226 return ((add + OS_PAGE_SIZE - 1) & ~((size_t)OS_PAGE_SIZE - 1));
2227}
2228
2229inline
2230uint8_t* align_on_page (uint8_t* add)
2231{
2232 return (uint8_t*)align_on_page ((size_t) add);
2233}
2234
2235inline
2236size_t align_lower_page (size_t add)
2237{
2238 return (add & ~((size_t)OS_PAGE_SIZE - 1));
2239}
2240
2241inline
2242uint8_t* align_lower_page (uint8_t* add)
2243{
2244 return (uint8_t*)align_lower_page ((size_t)add);
2245}
2246
2247inline
2248size_t align_write_watch_lower_page (size_t add)
2249{
2250 return (add & ~(WRITE_WATCH_UNIT_SIZE - 1));
2251}
2252
2253inline
2254uint8_t* align_write_watch_lower_page (uint8_t* add)
2255{
2256 return (uint8_t*)align_lower_page ((size_t)add);
2257}
2258
2259
2260inline
2261BOOL power_of_two_p (size_t integer)
2262{
2263 return !(integer & (integer-1));
2264}
2265
2266inline
2267BOOL oddp (size_t integer)
2268{
2269 return (integer & 1) != 0;
2270}
2271
2272// we only ever use this for WORDs.
2273size_t logcount (size_t word)
2274{
2275 //counts the number of high bits in a 16 bit word.
2276 assert (word < 0x10000);
2277 size_t count;
2278 count = (word & 0x5555) + ( (word >> 1 ) & 0x5555);
2279 count = (count & 0x3333) + ( (count >> 2) & 0x3333);
2280 count = (count & 0x0F0F) + ( (count >> 4) & 0x0F0F);
2281 count = (count & 0x00FF) + ( (count >> 8) & 0x00FF);
2282 return count;
2283}
2284
2285#ifndef DACCESS_COMPILE
2286
2287void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_bounds_check)
2288{
2289 WriteBarrierParameters args = {};
2290 args.operation = WriteBarrierOp::StompResize;
2291 args.is_runtime_suspended = is_runtime_suspended;
2292 args.requires_upper_bounds_check = requires_upper_bounds_check;
2293
2294 args.card_table = g_gc_card_table;
2295#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
2296 args.card_bundle_table = g_gc_card_bundle_table;
2297#endif
2298
2299 args.lowest_address = g_gc_lowest_address;
2300 args.highest_address = g_gc_highest_address;
2301
2302#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2303 if (SoftwareWriteWatch::IsEnabledForGCHeap())
2304 {
2305 args.write_watch_table = g_gc_sw_ww_table;
2306 }
2307#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2308
2309 GCToEEInterface::StompWriteBarrier(&args);
2310}
2311
2312void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2313{
2314 WriteBarrierParameters args = {};
2315 args.operation = WriteBarrierOp::StompEphemeral;
2316 args.is_runtime_suspended = true;
2317 args.ephemeral_low = ephemeral_low;
2318 args.ephemeral_high = ephemeral_high;
2319 GCToEEInterface::StompWriteBarrier(&args);
2320}
2321
2322void stomp_write_barrier_initialize(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2323{
2324 WriteBarrierParameters args = {};
2325 args.operation = WriteBarrierOp::Initialize;
2326 args.is_runtime_suspended = true;
2327 args.requires_upper_bounds_check = false;
2328 args.card_table = g_gc_card_table;
2329
2330#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
2331 args.card_bundle_table = g_gc_card_bundle_table;
2332#endif
2333
2334 args.lowest_address = g_gc_lowest_address;
2335 args.highest_address = g_gc_highest_address;
2336 args.ephemeral_low = ephemeral_low;
2337 args.ephemeral_high = ephemeral_high;
2338 GCToEEInterface::StompWriteBarrier(&args);
2339}
2340
2341#endif // DACCESS_COMPILE
2342
2343//extract the low bits [0,low[ of a uint32_t
2344#define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1))
2345//extract the high bits [high, 32] of a uint32_t
2346#define highbits(wrd, bits) ((wrd) & ~((1 << (bits))-1))
2347
2348// Things we need to manually initialize:
2349// gen0 min_size - based on cache
2350// gen0/1 max_size - based on segment size
2351static static_data static_data_table[latency_level_last - latency_level_first + 1][NUMBERGENERATIONS] =
2352{
2353 // latency_level_memory_footprint
2354 {
2355 // gen0
2356 {0, 0, 40000, 0.5f, 9.0f, 20.0f, 1000, 1},
2357 // gen1
2358 {163840, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10},
2359 // gen2
2360 {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100},
2361 // gen3
2362 {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0}
2363 },
2364
2365 // latency_level_balanced
2366 {
2367 // gen0
2368 {0, 0, 40000, 0.5f,
2369#ifdef MULTIPLE_HEAPS
2370 20.0f, 40.0f,
2371#else
2372 9.0f, 20.0f,
2373#endif //MULTIPLE_HEAPS
2374 1000, 1},
2375 // gen1
2376 {9*32*1024, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10},
2377 // gen2
2378 {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100},
2379 // gen3
2380 {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0}
2381 },
2382};
2383
2384class mark;
2385class generation;
2386class heap_segment;
2387class CObjectHeader;
2388class dynamic_data;
2389class l_heap;
2390class sorted_table;
2391class c_synchronize;
2392
2393#ifdef FEATURE_PREMORTEM_FINALIZATION
2394#ifndef DACCESS_COMPILE
2395static
2396HRESULT AllocateCFinalize(CFinalize **pCFinalize);
2397#endif //!DACCESS_COMPILE
2398#endif // FEATURE_PREMORTEM_FINALIZATION
2399
2400uint8_t* tree_search (uint8_t* tree, uint8_t* old_address);
2401
2402
2403#ifdef USE_INTROSORT
2404#define _sort introsort::sort
2405#else //USE_INTROSORT
2406#define _sort qsort1
2407void qsort1(uint8_t** low, uint8_t** high, unsigned int depth);
2408#endif //USE_INTROSORT
2409
2410void* virtual_alloc (size_t size);
2411void virtual_free (void* add, size_t size);
2412
2413/* per heap static initialization */
2414#ifdef MARK_ARRAY
2415#ifndef MULTIPLE_HEAPS
2416uint32_t* gc_heap::mark_array;
2417#endif //MULTIPLE_HEAPS
2418#endif //MARK_ARRAY
2419
2420#ifdef MARK_LIST
2421uint8_t** gc_heap::g_mark_list;
2422
2423#ifdef PARALLEL_MARK_LIST_SORT
2424uint8_t** gc_heap::g_mark_list_copy;
2425#endif //PARALLEL_MARK_LIST_SORT
2426
2427size_t gc_heap::mark_list_size;
2428#endif //MARK_LIST
2429
2430#ifdef SEG_MAPPING_TABLE
2431seg_mapping* seg_mapping_table;
2432#endif //SEG_MAPPING_TABLE
2433
2434#if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2435sorted_table* gc_heap::seg_table;
2436#endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
2437
2438#ifdef MULTIPLE_HEAPS
2439GCEvent gc_heap::ee_suspend_event;
2440size_t gc_heap::min_balance_threshold = 0;
2441#endif //MULTIPLE_HEAPS
2442
2443VOLATILE(BOOL) gc_heap::gc_started;
2444
2445#ifdef MULTIPLE_HEAPS
2446
2447GCEvent gc_heap::gc_start_event;
2448bool gc_heap::gc_thread_no_affinitize_p = false;
2449uintptr_t process_mask = 0;
2450
2451int gc_heap::n_heaps;
2452
2453gc_heap** gc_heap::g_heaps;
2454
2455size_t* gc_heap::g_promoted;
2456
2457#ifdef MH_SC_MARK
2458int* gc_heap::g_mark_stack_busy;
2459#endif //MH_SC_MARK
2460
2461
2462#ifdef BACKGROUND_GC
2463size_t* gc_heap::g_bpromoted;
2464#endif //BACKGROUND_GC
2465
2466#else //MULTIPLE_HEAPS
2467
2468size_t gc_heap::g_promoted;
2469
2470#ifdef BACKGROUND_GC
2471size_t gc_heap::g_bpromoted;
2472#endif //BACKGROUND_GC
2473
2474#endif //MULTIPLE_HEAPS
2475
2476size_t gc_heap::reserved_memory = 0;
2477size_t gc_heap::reserved_memory_limit = 0;
2478BOOL gc_heap::g_low_memory_status;
2479
2480#ifndef DACCESS_COMPILE
2481static gc_reason gc_trigger_reason = reason_empty;
2482#endif //DACCESS_COMPILE
2483
2484gc_latency_level gc_heap::latency_level = latency_level_default;
2485
2486gc_mechanisms gc_heap::settings;
2487
2488gc_history_global gc_heap::gc_data_global;
2489
2490size_t gc_heap::gc_last_ephemeral_decommit_time = 0;
2491
2492size_t gc_heap::gc_gen0_desired_high;
2493
2494#ifdef SHORT_PLUGS
2495double gc_heap::short_plugs_pad_ratio = 0;
2496#endif //SHORT_PLUGS
2497
2498#if defined(BIT64)
2499#define MAX_ALLOWED_MEM_LOAD 85
2500
2501// consider putting this in dynamic data -
2502// we may want different values for workstation
2503// and server GC.
2504#define MIN_YOUNGEST_GEN_DESIRED (16*1024*1024)
2505
2506size_t gc_heap::youngest_gen_desired_th;
2507#endif //BIT64
2508
2509uint32_t gc_heap::last_gc_memory_load = 0;
2510
2511size_t gc_heap::last_gc_heap_size = 0;
2512
2513size_t gc_heap::last_gc_fragmentation = 0;
2514
2515uint64_t gc_heap::mem_one_percent = 0;
2516
2517uint32_t gc_heap::high_memory_load_th = 0;
2518
2519uint32_t gc_heap::m_high_memory_load_th;
2520
2521uint32_t gc_heap::v_high_memory_load_th;
2522
2523uint64_t gc_heap::total_physical_mem = 0;
2524
2525uint64_t gc_heap::entry_available_physical_mem = 0;
2526
2527#ifdef BACKGROUND_GC
2528GCEvent gc_heap::bgc_start_event;
2529
2530gc_mechanisms gc_heap::saved_bgc_settings;
2531
2532GCEvent gc_heap::background_gc_done_event;
2533
2534GCEvent gc_heap::ee_proceed_event;
2535
2536bool gc_heap::gc_can_use_concurrent = false;
2537
2538bool gc_heap::temp_disable_concurrent_p = false;
2539
2540uint32_t gc_heap::cm_in_progress = FALSE;
2541
2542BOOL gc_heap::dont_restart_ee_p = FALSE;
2543
2544BOOL gc_heap::keep_bgc_threads_p = FALSE;
2545
2546GCEvent gc_heap::bgc_threads_sync_event;
2547
2548BOOL gc_heap::do_ephemeral_gc_p = FALSE;
2549
2550BOOL gc_heap::do_concurrent_p = FALSE;
2551
2552size_t gc_heap::ephemeral_fgc_counts[max_generation];
2553
2554BOOL gc_heap::alloc_wait_event_p = FALSE;
2555
2556VOLATILE(c_gc_state) gc_heap::current_c_gc_state = c_gc_state_free;
2557
2558#endif //BACKGROUND_GC
2559
2560#ifndef MULTIPLE_HEAPS
2561#ifdef SPINLOCK_HISTORY
2562int gc_heap::spinlock_info_index = 0;
2563spinlock_info gc_heap::last_spinlock_info[max_saved_spinlock_info + 8];
2564#endif //SPINLOCK_HISTORY
2565
2566size_t gc_heap::fgn_last_alloc = 0;
2567
2568int gc_heap::generation_skip_ratio = 100;
2569
2570uint64_t gc_heap::loh_alloc_since_cg = 0;
2571
2572BOOL gc_heap::elevation_requested = FALSE;
2573
2574BOOL gc_heap::last_gc_before_oom = FALSE;
2575
2576BOOL gc_heap::sufficient_gen0_space_p = FALSE;
2577
2578#ifdef BACKGROUND_GC
2579uint8_t* gc_heap::background_saved_lowest_address = 0;
2580uint8_t* gc_heap::background_saved_highest_address = 0;
2581uint8_t* gc_heap::next_sweep_obj = 0;
2582uint8_t* gc_heap::current_sweep_pos = 0;
2583exclusive_sync* gc_heap::bgc_alloc_lock;
2584#endif //BACKGROUND_GC
2585
2586oom_history gc_heap::oom_info;
2587
2588fgm_history gc_heap::fgm_result;
2589
2590BOOL gc_heap::ro_segments_in_range;
2591
2592size_t gc_heap::gen0_big_free_spaces = 0;
2593
2594uint8_t* gc_heap::ephemeral_low;
2595
2596uint8_t* gc_heap::ephemeral_high;
2597
2598uint8_t* gc_heap::lowest_address;
2599
2600uint8_t* gc_heap::highest_address;
2601
2602BOOL gc_heap::ephemeral_promotion;
2603
2604uint8_t* gc_heap::saved_ephemeral_plan_start[NUMBERGENERATIONS-1];
2605size_t gc_heap::saved_ephemeral_plan_start_size[NUMBERGENERATIONS-1];
2606
2607short* gc_heap::brick_table;
2608
2609uint32_t* gc_heap::card_table;
2610
2611#ifdef CARD_BUNDLE
2612uint32_t* gc_heap::card_bundle_table;
2613#endif //CARD_BUNDLE
2614
2615uint8_t* gc_heap::gc_low;
2616
2617uint8_t* gc_heap::gc_high;
2618
2619uint8_t* gc_heap::demotion_low;
2620
2621uint8_t* gc_heap::demotion_high;
2622
2623BOOL gc_heap::demote_gen1_p = TRUE;
2624
2625uint8_t* gc_heap::last_gen1_pin_end;
2626
2627gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons;
2628
2629size_t gc_heap::etw_allocation_running_amount[2];
2630
2631int gc_heap::gc_policy = 0;
2632
2633size_t gc_heap::allocation_running_time;
2634
2635size_t gc_heap::allocation_running_amount;
2636
2637heap_segment* gc_heap::ephemeral_heap_segment = 0;
2638
2639BOOL gc_heap::blocking_collection = FALSE;
2640
2641heap_segment* gc_heap::freeable_large_heap_segment = 0;
2642
2643size_t gc_heap::time_bgc_last = 0;
2644
2645size_t gc_heap::mark_stack_tos = 0;
2646
2647size_t gc_heap::mark_stack_bos = 0;
2648
2649size_t gc_heap::mark_stack_array_length = 0;
2650
2651mark* gc_heap::mark_stack_array = 0;
2652
2653#if defined (_DEBUG) && defined (VERIFY_HEAP)
2654BOOL gc_heap::verify_pinned_queue_p = FALSE;
2655#endif // defined (_DEBUG) && defined (VERIFY_HEAP)
2656
2657uint8_t* gc_heap::oldest_pinned_plug = 0;
2658
2659#if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
2660size_t gc_heap::num_pinned_objects = 0;
2661#endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
2662
2663#ifdef FEATURE_LOH_COMPACTION
2664size_t gc_heap::loh_pinned_queue_tos = 0;
2665
2666size_t gc_heap::loh_pinned_queue_bos = 0;
2667
2668size_t gc_heap::loh_pinned_queue_length = 0;
2669
2670mark* gc_heap::loh_pinned_queue = 0;
2671
2672BOOL gc_heap::loh_compacted_p = FALSE;
2673#endif //FEATURE_LOH_COMPACTION
2674
2675#ifdef BACKGROUND_GC
2676
2677EEThreadId gc_heap::bgc_thread_id;
2678
2679uint8_t* gc_heap::background_written_addresses [array_size+2];
2680
2681heap_segment* gc_heap::freeable_small_heap_segment = 0;
2682
2683size_t gc_heap::bgc_overflow_count = 0;
2684
2685size_t gc_heap::bgc_begin_loh_size = 0;
2686size_t gc_heap::end_loh_size = 0;
2687
2688uint32_t gc_heap::bgc_alloc_spin_loh = 0;
2689
2690size_t gc_heap::bgc_loh_size_increased = 0;
2691
2692size_t gc_heap::bgc_loh_allocated_in_free = 0;
2693
2694size_t gc_heap::background_soh_alloc_count = 0;
2695
2696size_t gc_heap::background_loh_alloc_count = 0;
2697
2698uint8_t** gc_heap::background_mark_stack_tos = 0;
2699
2700uint8_t** gc_heap::background_mark_stack_array = 0;
2701
2702size_t gc_heap::background_mark_stack_array_length = 0;
2703
2704uint8_t* gc_heap::background_min_overflow_address =0;
2705
2706uint8_t* gc_heap::background_max_overflow_address =0;
2707
2708BOOL gc_heap::processed_soh_overflow_p = FALSE;
2709
2710uint8_t* gc_heap::background_min_soh_overflow_address =0;
2711
2712uint8_t* gc_heap::background_max_soh_overflow_address =0;
2713
2714heap_segment* gc_heap::saved_sweep_ephemeral_seg = 0;
2715
2716uint8_t* gc_heap::saved_sweep_ephemeral_start = 0;
2717
2718heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0;
2719
2720Thread* gc_heap::bgc_thread = 0;
2721
2722BOOL gc_heap::expanded_in_fgc = FALSE;
2723
2724uint8_t** gc_heap::c_mark_list = 0;
2725
2726size_t gc_heap::c_mark_list_length = 0;
2727
2728size_t gc_heap::c_mark_list_index = 0;
2729
2730gc_history_per_heap gc_heap::bgc_data_per_heap;
2731
2732BOOL gc_heap::bgc_thread_running;
2733
2734CLRCriticalSection gc_heap::bgc_threads_timeout_cs;
2735
2736GCEvent gc_heap::gc_lh_block_event;
2737
2738#endif //BACKGROUND_GC
2739
2740#ifdef MARK_LIST
2741uint8_t** gc_heap::mark_list;
2742uint8_t** gc_heap::mark_list_index;
2743uint8_t** gc_heap::mark_list_end;
2744#endif //MARK_LIST
2745
2746#ifdef SNOOP_STATS
2747snoop_stats_data gc_heap::snoop_stat;
2748#endif //SNOOP_STATS
2749
2750uint8_t* gc_heap::min_overflow_address = MAX_PTR;
2751
2752uint8_t* gc_heap::max_overflow_address = 0;
2753
2754uint8_t* gc_heap::shigh = 0;
2755
2756uint8_t* gc_heap::slow = MAX_PTR;
2757
2758size_t gc_heap::ordered_free_space_indices[MAX_NUM_BUCKETS];
2759
2760size_t gc_heap::saved_ordered_free_space_indices[MAX_NUM_BUCKETS];
2761
2762size_t gc_heap::ordered_plug_indices[MAX_NUM_BUCKETS];
2763
2764size_t gc_heap::saved_ordered_plug_indices[MAX_NUM_BUCKETS];
2765
2766BOOL gc_heap::ordered_plug_indices_init = FALSE;
2767
2768BOOL gc_heap::use_bestfit = FALSE;
2769
2770uint8_t* gc_heap::bestfit_first_pin = 0;
2771
2772BOOL gc_heap::commit_end_of_seg = FALSE;
2773
2774size_t gc_heap::max_free_space_items = 0;
2775
2776size_t gc_heap::free_space_buckets = 0;
2777
2778size_t gc_heap::free_space_items = 0;
2779
2780int gc_heap::trimmed_free_space_index = 0;
2781
2782size_t gc_heap::total_ephemeral_plugs = 0;
2783
2784seg_free_spaces* gc_heap::bestfit_seg = 0;
2785
2786size_t gc_heap::total_ephemeral_size = 0;
2787
2788#ifdef HEAP_ANALYZE
2789
2790size_t gc_heap::internal_root_array_length = initial_internal_roots;
2791
2792uint8_t** gc_heap::internal_root_array = 0;
2793
2794size_t gc_heap::internal_root_array_index = 0;
2795
2796BOOL gc_heap::heap_analyze_success = TRUE;
2797
2798uint8_t* gc_heap::current_obj = 0;
2799size_t gc_heap::current_obj_size = 0;
2800
2801#endif //HEAP_ANALYZE
2802
2803#ifdef GC_CONFIG_DRIVEN
2804size_t gc_heap::interesting_data_per_gc[max_idp_count];
2805//size_t gc_heap::interesting_data_per_heap[max_idp_count];
2806//size_t gc_heap::interesting_mechanisms_per_heap[max_im_count];
2807#endif //GC_CONFIG_DRIVEN
2808#endif //MULTIPLE_HEAPS
2809
2810no_gc_region_info gc_heap::current_no_gc_region_info;
2811BOOL gc_heap::proceed_with_gc_p = FALSE;
2812GCSpinLock gc_heap::gc_lock;
2813
2814size_t gc_heap::eph_gen_starts_size = 0;
2815heap_segment* gc_heap::segment_standby_list;
2816size_t gc_heap::last_gc_index = 0;
2817#ifdef SEG_MAPPING_TABLE
2818size_t gc_heap::min_segment_size = 0;
2819size_t gc_heap::min_segment_size_shr = 0;
2820#endif //SEG_MAPPING_TABLE
2821size_t gc_heap::soh_segment_size = 0;
2822size_t gc_heap::min_loh_segment_size = 0;
2823size_t gc_heap::segment_info_size = 0;
2824
2825#ifdef GC_CONFIG_DRIVEN
2826size_t gc_heap::time_init = 0;
2827size_t gc_heap::time_since_init = 0;
2828size_t gc_heap::compact_or_sweep_gcs[2];
2829#endif //GC_CONFIG_DRIVEN
2830
2831#ifdef FEATURE_LOH_COMPACTION
2832BOOL gc_heap::loh_compaction_always_p = FALSE;
2833gc_loh_compaction_mode gc_heap::loh_compaction_mode = loh_compaction_default;
2834int gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY;
2835
2836#endif //FEATURE_LOH_COMPACTION
2837
2838GCEvent gc_heap::full_gc_approach_event;
2839
2840GCEvent gc_heap::full_gc_end_event;
2841
2842uint32_t gc_heap::fgn_maxgen_percent = 0;
2843
2844uint32_t gc_heap::fgn_loh_percent = 0;
2845
2846#ifdef BACKGROUND_GC
2847BOOL gc_heap::fgn_last_gc_was_concurrent = FALSE;
2848#endif //BACKGROUND_GC
2849
2850VOLATILE(bool) gc_heap::full_gc_approach_event_set;
2851
2852size_t gc_heap::full_gc_counts[gc_type_max];
2853
2854bool gc_heap::maxgen_size_inc_p = false;
2855
2856BOOL gc_heap::should_expand_in_full_gc = FALSE;
2857
2858// Provisional mode related stuff.
2859bool gc_heap::provisional_mode_triggered = false;
2860bool gc_heap::pm_trigger_full_gc = false;
2861size_t gc_heap::provisional_triggered_gc_count = 0;
2862size_t gc_heap::provisional_off_gc_count = 0;
2863size_t gc_heap::num_provisional_triggered = 0;
2864bool gc_heap::pm_stress_on = false;
2865
2866#ifdef HEAP_ANALYZE
2867BOOL gc_heap::heap_analyze_enabled = FALSE;
2868#endif //HEAP_ANALYZE
2869
2870#ifndef MULTIPLE_HEAPS
2871
2872alloc_list gc_heap::loh_alloc_list [NUM_LOH_ALIST-1];
2873alloc_list gc_heap::gen2_alloc_list[NUM_GEN2_ALIST-1];
2874
2875dynamic_data gc_heap::dynamic_data_table [NUMBERGENERATIONS+1];
2876gc_history_per_heap gc_heap::gc_data_per_heap;
2877size_t gc_heap::maxgen_pinned_compact_before_advance = 0;
2878
2879uint8_t* gc_heap::alloc_allocated = 0;
2880
2881size_t gc_heap::allocation_quantum = CLR_SIZE;
2882
2883GCSpinLock gc_heap::more_space_lock_soh;
2884GCSpinLock gc_heap::more_space_lock_loh;
2885VOLATILE(int32_t) gc_heap::loh_alloc_thread_count = 0;
2886
2887#ifdef SYNCHRONIZATION_STATS
2888unsigned int gc_heap::good_suspension = 0;
2889unsigned int gc_heap::bad_suspension = 0;
2890uint64_t gc_heap::total_msl_acquire = 0;
2891unsigned int gc_heap::num_msl_acquired = 0;
2892unsigned int gc_heap::num_high_msl_acquire = 0;
2893unsigned int gc_heap::num_low_msl_acquire = 0;
2894#endif //SYNCHRONIZATION_STATS
2895
2896size_t gc_heap::alloc_contexts_used = 0;
2897size_t gc_heap::soh_allocation_no_gc = 0;
2898size_t gc_heap::loh_allocation_no_gc = 0;
2899bool gc_heap::no_gc_oom_p = false;
2900heap_segment* gc_heap::saved_loh_segment_no_gc = 0;
2901
2902#endif //MULTIPLE_HEAPS
2903
2904#ifndef MULTIPLE_HEAPS
2905
2906BOOL gc_heap::gen0_bricks_cleared = FALSE;
2907
2908#ifdef FFIND_OBJECT
2909int gc_heap::gen0_must_clear_bricks = 0;
2910#endif //FFIND_OBJECT
2911
2912#ifdef FEATURE_PREMORTEM_FINALIZATION
2913CFinalize* gc_heap::finalize_queue = 0;
2914#endif // FEATURE_PREMORTEM_FINALIZATION
2915
2916generation gc_heap::generation_table [NUMBERGENERATIONS + 1];
2917
2918size_t gc_heap::interesting_data_per_heap[max_idp_count];
2919
2920size_t gc_heap::compact_reasons_per_heap[max_compact_reasons_count];
2921
2922size_t gc_heap::expand_mechanisms_per_heap[max_expand_mechanisms_count];
2923
2924size_t gc_heap::interesting_mechanism_bits_per_heap[max_gc_mechanism_bits_count];
2925
2926#endif // MULTIPLE_HEAPS
2927
2928/* end of per heap static initialization */
2929
2930/* end of static initialization */
2931
2932#ifndef DACCESS_COMPILE
2933
2934void gen_to_condemn_tuning::print (int heap_num)
2935{
2936#ifdef DT_LOG
2937 dprintf (DT_LOG_0, ("condemned reasons (%d %d)", condemn_reasons_gen, condemn_reasons_condition));
2938 dprintf (DT_LOG_0, ("%s", record_condemn_reasons_gen_header));
2939 gc_condemn_reason_gen r_gen;
2940 for (int i = 0; i < gcrg_max; i++)
2941 {
2942 r_gen = (gc_condemn_reason_gen)(i);
2943 str_reasons_gen[i * 2] = get_gen_char (get_gen (r_gen));
2944 }
2945 dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_gen));
2946
2947 dprintf (DT_LOG_0, ("%s", record_condemn_reasons_condition_header));
2948 gc_condemn_reason_condition r_condition;
2949 for (int i = 0; i < gcrc_max; i++)
2950 {
2951 r_condition = (gc_condemn_reason_condition)(i);
2952 str_reasons_condition[i * 2] = get_condition_char (get_condition (r_condition));
2953 }
2954
2955 dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_condition));
2956#else
2957 UNREFERENCED_PARAMETER(heap_num);
2958#endif //DT_LOG
2959}
2960
2961void gc_generation_data::print (int heap_num, int gen_num)
2962{
2963#if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2964 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",
2965 heap_num, gen_num,
2966 size_before,
2967 free_list_space_before, free_obj_space_before,
2968 size_after,
2969 free_list_space_after, free_obj_space_after,
2970 in, pinned_surv, npinned_surv,
2971 new_allocation));
2972#else
2973 UNREFERENCED_PARAMETER(heap_num);
2974 UNREFERENCED_PARAMETER(gen_num);
2975#endif //SIMPLE_DPRINTF && DT_LOG
2976}
2977
2978void gc_history_per_heap::set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value)
2979{
2980 uint32_t* mechanism = &mechanisms[mechanism_per_heap];
2981 *mechanism = 0;
2982 *mechanism |= mechanism_mask;
2983 *mechanism |= (1 << value);
2984
2985#ifdef DT_LOG
2986 gc_mechanism_descr* descr = &gc_mechanisms_descr[mechanism_per_heap];
2987 dprintf (DT_LOG_0, ("setting %s: %s",
2988 descr->name,
2989 (descr->descr)[value]));
2990#endif //DT_LOG
2991}
2992
2993void gc_history_per_heap::print()
2994{
2995#if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2996 for (int i = 0; i < (sizeof (gen_data)/sizeof (gc_generation_data)); i++)
2997 {
2998 gen_data[i].print (heap_index, i);
2999 }
3000
3001 dprintf (DT_LOG_0, ("fla %Id flr %Id esa %Id ca %Id pa %Id paa %Id, rfle %d, ec %Id",
3002 maxgen_size_info.free_list_allocated,
3003 maxgen_size_info.free_list_rejected,
3004 maxgen_size_info.end_seg_allocated,
3005 maxgen_size_info.condemned_allocated,
3006 maxgen_size_info.pinned_allocated,
3007 maxgen_size_info.pinned_allocated_advance,
3008 maxgen_size_info.running_free_list_efficiency,
3009 extra_gen0_committed));
3010
3011 int mechanism = 0;
3012 gc_mechanism_descr* descr = 0;
3013
3014 for (int i = 0; i < max_mechanism_per_heap; i++)
3015 {
3016 mechanism = get_mechanism ((gc_mechanism_per_heap)i);
3017
3018 if (mechanism >= 0)
3019 {
3020 descr = &gc_mechanisms_descr[(gc_mechanism_per_heap)i];
3021 dprintf (DT_LOG_0, ("[%2d]%s%s",
3022 heap_index,
3023 descr->name,
3024 (descr->descr)[mechanism]));
3025 }
3026 }
3027#endif //SIMPLE_DPRINTF && DT_LOG
3028}
3029
3030void gc_history_global::print()
3031{
3032#ifdef DT_LOG
3033 char str_settings[64];
3034 memset (str_settings, '|', sizeof (char) * 64);
3035 str_settings[max_global_mechanisms_count*2] = 0;
3036
3037 for (int i = 0; i < max_global_mechanisms_count; i++)
3038 {
3039 str_settings[i * 2] = (get_mechanism_p ((gc_global_mechanism_p)i) ? 'Y' : 'N');
3040 }
3041
3042 dprintf (DT_LOG_0, ("[hp]|c|p|o|d|b|e|"));
3043
3044 dprintf (DT_LOG_0, ("%4d|%s", num_heaps, str_settings));
3045 dprintf (DT_LOG_0, ("Condemned gen%d(reason: %s; mode: %s), youngest budget %Id(%d), memload %d",
3046 condemned_generation,
3047 str_gc_reasons[reason],
3048 str_gc_pause_modes[pause_mode],
3049 final_youngest_desired,
3050 gen0_reduction_count,
3051 mem_pressure));
3052#endif //DT_LOG
3053}
3054
3055void gc_heap::fire_per_heap_hist_event (gc_history_per_heap* current_gc_data_per_heap, int heap_num)
3056{
3057 maxgen_size_increase* maxgen_size_info = &(current_gc_data_per_heap->maxgen_size_info);
3058 FIRE_EVENT(GCPerHeapHistory_V3,
3059 (void *)(maxgen_size_info->free_list_allocated),
3060 (void *)(maxgen_size_info->free_list_rejected),
3061 (void *)(maxgen_size_info->end_seg_allocated),
3062 (void *)(maxgen_size_info->condemned_allocated),
3063 (void *)(maxgen_size_info->pinned_allocated),
3064 (void *)(maxgen_size_info->pinned_allocated_advance),
3065 maxgen_size_info->running_free_list_efficiency,
3066 current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons0(),
3067 current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons1(),
3068 current_gc_data_per_heap->mechanisms[gc_heap_compact],
3069 current_gc_data_per_heap->mechanisms[gc_heap_expand],
3070 current_gc_data_per_heap->heap_index,
3071 (void *)(current_gc_data_per_heap->extra_gen0_committed),
3072 (max_generation + 2),
3073 (uint32_t)(sizeof (gc_generation_data)),
3074 (void *)&(current_gc_data_per_heap->gen_data[0]));
3075
3076 current_gc_data_per_heap->print();
3077 current_gc_data_per_heap->gen_to_condemn_reasons.print (heap_num);
3078}
3079
3080void gc_heap::fire_pevents()
3081{
3082 settings.record (&gc_data_global);
3083 gc_data_global.print();
3084
3085 FIRE_EVENT(GCGlobalHeapHistory_V2,
3086 gc_data_global.final_youngest_desired,
3087 gc_data_global.num_heaps,
3088 gc_data_global.condemned_generation,
3089 gc_data_global.gen0_reduction_count,
3090 gc_data_global.reason,
3091 gc_data_global.global_mechanims_p,
3092 gc_data_global.pause_mode,
3093 gc_data_global.mem_pressure);
3094
3095#ifdef MULTIPLE_HEAPS
3096 for (int i = 0; i < gc_heap::n_heaps; i++)
3097 {
3098 gc_heap* hp = gc_heap::g_heaps[i];
3099 gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
3100 fire_per_heap_hist_event (current_gc_data_per_heap, hp->heap_number);
3101 }
3102#else
3103 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
3104 fire_per_heap_hist_event (current_gc_data_per_heap, heap_number);
3105#endif
3106}
3107
3108inline BOOL
3109gc_heap::dt_low_ephemeral_space_p (gc_tuning_point tp)
3110{
3111 BOOL ret = FALSE;
3112
3113 switch (tp)
3114 {
3115 case tuning_deciding_condemned_gen:
3116 case tuning_deciding_compaction:
3117 case tuning_deciding_expansion:
3118 case tuning_deciding_full_gc:
3119 {
3120 ret = (!ephemeral_gen_fit_p (tp));
3121 break;
3122 }
3123 case tuning_deciding_promote_ephemeral:
3124 {
3125 size_t new_gen0size = approximate_new_allocation();
3126 ptrdiff_t plan_ephemeral_size = total_ephemeral_size;
3127
3128 dprintf (GTC_LOG, ("h%d: plan eph size is %Id, new gen0 is %Id",
3129 heap_number, plan_ephemeral_size, new_gen0size));
3130 // If we were in no_gc_region we could have allocated a larger than normal segment,
3131 // and the next seg we allocate will be a normal sized seg so if we can't fit the new
3132 // ephemeral generations there, do an ephemeral promotion.
3133 ret = ((soh_segment_size - segment_info_size) < (plan_ephemeral_size + new_gen0size));
3134 break;
3135 }
3136 default:
3137 break;
3138 }
3139
3140 return ret;
3141}
3142
3143BOOL
3144gc_heap::dt_high_frag_p (gc_tuning_point tp,
3145 int gen_number,
3146 BOOL elevate_p)
3147{
3148 BOOL ret = FALSE;
3149
3150 switch (tp)
3151 {
3152 case tuning_deciding_condemned_gen:
3153 {
3154 dynamic_data* dd = dynamic_data_of (gen_number);
3155 float fragmentation_burden = 0;
3156
3157 if (elevate_p)
3158 {
3159 ret = (dd_fragmentation (dynamic_data_of (max_generation)) >= dd_max_size(dd));
3160 dprintf (GTC_LOG, ("h%d: frag is %Id, max size is %Id",
3161 heap_number, dd_fragmentation (dd), dd_max_size(dd)));
3162 }
3163 else
3164 {
3165#ifndef MULTIPLE_HEAPS
3166 if (gen_number == max_generation)
3167 {
3168 float frag_ratio = (float)(dd_fragmentation (dynamic_data_of (max_generation))) / (float)generation_size (max_generation);
3169 if (frag_ratio > 0.65)
3170 {
3171 dprintf (GTC_LOG, ("g2 FR: %d%%", (int)(frag_ratio*100)));
3172 return TRUE;
3173 }
3174 }
3175#endif //!MULTIPLE_HEAPS
3176 size_t fr = generation_unusable_fragmentation (generation_of (gen_number));
3177 ret = (fr > dd_fragmentation_limit(dd));
3178 if (ret)
3179 {
3180 fragmentation_burden = (float)fr / generation_size (gen_number);
3181 ret = (fragmentation_burden > dd_v_fragmentation_burden_limit (dd));
3182 }
3183 dprintf (GTC_LOG, ("h%d: gen%d, frag is %Id, alloc effi: %d%%, unusable frag is %Id, ratio is %d",
3184 heap_number, gen_number, dd_fragmentation (dd),
3185 (int)(100*generation_allocator_efficiency (generation_of (gen_number))),
3186 fr, (int)(fragmentation_burden*100)));
3187 }
3188 break;
3189 }
3190 default:
3191 break;
3192 }
3193
3194 return ret;
3195}
3196
3197inline BOOL
3198gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number)
3199{
3200 BOOL ret = FALSE;
3201
3202 switch (tp)
3203 {
3204 case tuning_deciding_condemned_gen:
3205 {
3206 if (gen_number == max_generation)
3207 {
3208 dynamic_data* dd = dynamic_data_of (gen_number);
3209 size_t maxgen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd));
3210 size_t maxgen_total_size = maxgen_allocated + dd_current_size (dd);
3211 size_t est_maxgen_surv = (size_t)((float) (maxgen_total_size) * dd_surv (dd));
3212 size_t est_maxgen_free = maxgen_total_size - est_maxgen_surv + dd_fragmentation (dd);
3213
3214 dprintf (GTC_LOG, ("h%d: Total gen2 size: %Id, est gen2 dead space: %Id (s: %d, allocated: %Id), frag: %Id",
3215 heap_number,
3216 maxgen_total_size,
3217 est_maxgen_free,
3218 (int)(dd_surv (dd) * 100),
3219 maxgen_allocated,
3220 dd_fragmentation (dd)));
3221
3222 uint32_t num_heaps = 1;
3223
3224#ifdef MULTIPLE_HEAPS
3225 num_heaps = gc_heap::n_heaps;
3226#endif //MULTIPLE_HEAPS
3227
3228 size_t min_frag_th = min_reclaim_fragmentation_threshold (num_heaps);
3229 dprintf (GTC_LOG, ("h%d, min frag is %Id", heap_number, min_frag_th));
3230 ret = (est_maxgen_free >= min_frag_th);
3231 }
3232 else
3233 {
3234 assert (0);
3235 }
3236 break;
3237 }
3238
3239 default:
3240 break;
3241 }
3242
3243 return ret;
3244}
3245
3246// DTREVIEW: Right now we only estimate gen2 fragmentation.
3247// on 64-bit though we should consider gen1 or even gen0 fragmentatioin as
3248// well
3249inline BOOL
3250gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, uint64_t available_mem)
3251{
3252 BOOL ret = FALSE;
3253
3254 switch (tp)
3255 {
3256 case tuning_deciding_condemned_gen:
3257 {
3258 if (gen_number == max_generation)
3259 {
3260 dynamic_data* dd = dynamic_data_of (gen_number);
3261 float est_frag_ratio = 0;
3262 if (dd_current_size (dd) == 0)
3263 {
3264 est_frag_ratio = 1;
3265 }
3266 else if ((dd_fragmentation (dd) == 0) || (dd_fragmentation (dd) + dd_current_size (dd) == 0))
3267 {
3268 est_frag_ratio = 0;
3269 }
3270 else
3271 {
3272 est_frag_ratio = (float)dd_fragmentation (dd) / (float)(dd_fragmentation (dd) + dd_current_size (dd));
3273 }
3274
3275 size_t est_frag = (dd_fragmentation (dd) + (size_t)((dd_desired_allocation (dd) - dd_new_allocation (dd)) * est_frag_ratio));
3276 dprintf (GTC_LOG, ("h%d: gen%d: current_size is %Id, frag is %Id, est_frag_ratio is %d%%, estimated frag is %Id",
3277 heap_number,
3278 gen_number,
3279 dd_current_size (dd),
3280 dd_fragmentation (dd),
3281 (int)(est_frag_ratio*100),
3282 est_frag));
3283
3284 uint32_t num_heaps = 1;
3285
3286#ifdef MULTIPLE_HEAPS
3287 num_heaps = gc_heap::n_heaps;
3288#endif //MULTIPLE_HEAPS
3289 uint64_t min_frag_th = min_high_fragmentation_threshold(available_mem, num_heaps);
3290 //dprintf (GTC_LOG, ("h%d, min frag is %I64d", heap_number, min_frag_th));
3291 ret = (est_frag >= min_frag_th);
3292 }
3293 else
3294 {
3295 assert (0);
3296 }
3297 break;
3298 }
3299
3300 default:
3301 break;
3302 }
3303
3304 return ret;
3305}
3306
3307inline BOOL
3308gc_heap::dt_low_card_table_efficiency_p (gc_tuning_point tp)
3309{
3310 BOOL ret = FALSE;
3311
3312 switch (tp)
3313 {
3314 case tuning_deciding_condemned_gen:
3315 {
3316 /* promote into max-generation if the card table has too many
3317 * generation faults besides the n -> 0
3318 */
3319 ret = (generation_skip_ratio < 30);
3320 break;
3321 }
3322
3323 default:
3324 break;
3325 }
3326
3327 return ret;
3328}
3329
3330inline BOOL
3331in_range_for_segment(uint8_t* add, heap_segment* seg)
3332{
3333 return ((add >= heap_segment_mem (seg)) && (add < heap_segment_reserved (seg)));
3334}
3335
3336#if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
3337// The array we allocate is organized as follows:
3338// 0th element is the address of the last array we allocated.
3339// starting from the 1st element are the segment addresses, that's
3340// what buckets() returns.
3341struct bk
3342{
3343 uint8_t* add;
3344 size_t val;
3345};
3346
3347class sorted_table
3348{
3349private:
3350 ptrdiff_t size;
3351 ptrdiff_t count;
3352 bk* slots;
3353 bk* buckets() { return (slots + 1); }
3354 uint8_t*& last_slot (bk* arr) { return arr[0].add; }
3355 bk* old_slots;
3356public:
3357 static sorted_table* make_sorted_table ();
3358 BOOL insert (uint8_t* add, size_t val);;
3359 size_t lookup (uint8_t*& add);
3360 void remove (uint8_t* add);
3361 void clear ();
3362 void delete_sorted_table();
3363 void delete_old_slots();
3364 void enqueue_old_slot(bk* sl);
3365 BOOL ensure_space_for_insert();
3366};
3367
3368sorted_table*
3369sorted_table::make_sorted_table ()
3370{
3371 size_t size = 400;
3372
3373 // allocate one more bk to store the older slot address.
3374 sorted_table* res = (sorted_table*)new char [sizeof (sorted_table) + (size + 1) * sizeof (bk)];
3375 if (!res)
3376 return 0;
3377 res->size = size;
3378 res->slots = (bk*)(res + 1);
3379 res->old_slots = 0;
3380 res->clear();
3381 return res;
3382}
3383
3384void
3385sorted_table::delete_sorted_table()
3386{
3387 if (slots != (bk*)(this+1))
3388 {
3389 delete slots;
3390 }
3391 delete_old_slots();
3392 delete this;
3393}
3394void
3395sorted_table::delete_old_slots()
3396{
3397 uint8_t* sl = (uint8_t*)old_slots;
3398 while (sl)
3399 {
3400 uint8_t* dsl = sl;
3401 sl = last_slot ((bk*)sl);
3402 delete dsl;
3403 }
3404 old_slots = 0;
3405}
3406void
3407sorted_table::enqueue_old_slot(bk* sl)
3408{
3409 last_slot (sl) = (uint8_t*)old_slots;
3410 old_slots = sl;
3411}
3412
3413inline
3414size_t
3415sorted_table::lookup (uint8_t*& add)
3416{
3417 ptrdiff_t high = (count-1);
3418 ptrdiff_t low = 0;
3419 ptrdiff_t ti;
3420 ptrdiff_t mid;
3421 bk* buck = buckets();
3422 while (low <= high)
3423 {
3424 mid = ((low + high)/2);
3425 ti = mid;
3426 if (buck[ti].add > add)
3427 {
3428 if ((ti > 0) && (buck[ti-1].add <= add))
3429 {
3430 add = buck[ti-1].add;
3431 return buck[ti - 1].val;
3432 }
3433 high = mid - 1;
3434 }
3435 else
3436 {
3437 if (buck[ti+1].add > add)
3438 {
3439 add = buck[ti].add;
3440 return buck[ti].val;
3441 }
3442 low = mid + 1;
3443 }
3444 }
3445 add = 0;
3446 return 0;
3447}
3448
3449BOOL
3450sorted_table::ensure_space_for_insert()
3451{
3452 if (count == size)
3453 {
3454 size = (size * 3)/2;
3455 assert((size * sizeof (bk)) > 0);
3456 bk* res = (bk*)new (nothrow) char [(size + 1) * sizeof (bk)];
3457 assert (res);
3458 if (!res)
3459 return FALSE;
3460
3461 last_slot (res) = 0;
3462 memcpy (((bk*)res + 1), buckets(), count * sizeof (bk));
3463 bk* last_old_slots = slots;
3464 slots = res;
3465 if (last_old_slots != (bk*)(this + 1))
3466 enqueue_old_slot (last_old_slots);
3467 }
3468 return TRUE;
3469}
3470
3471BOOL
3472sorted_table::insert (uint8_t* add, size_t val)
3473{
3474 //grow if no more room
3475 assert (count < size);
3476
3477 //insert sorted
3478 ptrdiff_t high = (count-1);
3479 ptrdiff_t low = 0;
3480 ptrdiff_t ti;
3481 ptrdiff_t mid;
3482 bk* buck = buckets();
3483 while (low <= high)
3484 {
3485 mid = ((low + high)/2);
3486 ti = mid;
3487 if (buck[ti].add > add)
3488 {
3489 if ((ti == 0) || (buck[ti-1].add <= add))
3490 {
3491 // found insertion point
3492 for (ptrdiff_t k = count; k > ti;k--)
3493 {
3494 buck [k] = buck [k-1];
3495 }
3496 buck[ti].add = add;
3497 buck[ti].val = val;
3498 count++;
3499 return TRUE;
3500 }
3501 high = mid - 1;
3502 }
3503 else
3504 {
3505 if (buck[ti+1].add > add)
3506 {
3507 //found the insertion point
3508 for (ptrdiff_t k = count; k > ti+1;k--)
3509 {
3510 buck [k] = buck [k-1];
3511 }
3512 buck[ti+1].add = add;
3513 buck[ti+1].val = val;
3514 count++;
3515 return TRUE;
3516 }
3517 low = mid + 1;
3518 }
3519 }
3520 assert (0);
3521 return TRUE;
3522}
3523
3524void
3525sorted_table::remove (uint8_t* add)
3526{
3527 ptrdiff_t high = (count-1);
3528 ptrdiff_t low = 0;
3529 ptrdiff_t ti;
3530 ptrdiff_t mid;
3531 bk* buck = buckets();
3532 while (low <= high)
3533 {
3534 mid = ((low + high)/2);
3535 ti = mid;
3536 if (buck[ti].add > add)
3537 {
3538 if (buck[ti-1].add <= add)
3539 {
3540 // found the guy to remove
3541 for (ptrdiff_t k = ti; k < count; k++)
3542 buck[k-1] = buck[k];
3543 count--;
3544 return;
3545 }
3546 high = mid - 1;
3547 }
3548 else
3549 {
3550 if (buck[ti+1].add > add)
3551 {
3552 // found the guy to remove
3553 for (ptrdiff_t k = ti+1; k < count; k++)
3554 buck[k-1] = buck[k];
3555 count--;
3556 return;
3557 }
3558 low = mid + 1;
3559 }
3560 }
3561 assert (0);
3562}
3563
3564void
3565sorted_table::clear()
3566{
3567 count = 1;
3568 buckets()[0].add = MAX_PTR;
3569}
3570#endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
3571
3572#ifdef SEG_MAPPING_TABLE
3573#ifdef GROWABLE_SEG_MAPPING_TABLE
3574inline
3575uint8_t* align_on_segment (uint8_t* add)
3576{
3577 return (uint8_t*)((size_t)(add + (gc_heap::min_segment_size - 1)) & ~(gc_heap::min_segment_size - 1));
3578}
3579
3580inline
3581uint8_t* align_lower_segment (uint8_t* add)
3582{
3583 return (uint8_t*)((size_t)(add) & ~(gc_heap::min_segment_size - 1));
3584}
3585
3586size_t size_seg_mapping_table_of (uint8_t* from, uint8_t* end)
3587{
3588 from = align_lower_segment (from);
3589 end = align_on_segment (end);
3590 dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((size_t)(end - from) >> gc_heap::min_segment_size_shr))));
3591 return sizeof (seg_mapping)*((size_t)(end - from) >> gc_heap::min_segment_size_shr);
3592}
3593
3594// for seg_mapping_table we want it to start from a pointer sized address.
3595inline
3596size_t align_for_seg_mapping_table (size_t size)
3597{
3598 return ((size + (sizeof (uint8_t*) - 1)) &~ (sizeof (uint8_t*) - 1));
3599}
3600
3601inline
3602size_t seg_mapping_word_of (uint8_t* add)
3603{
3604 return (size_t)add >> gc_heap::min_segment_size_shr;
3605}
3606#else //GROWABLE_SEG_MAPPING_TABLE
3607BOOL seg_mapping_table_init()
3608{
3609#ifdef BIT64
3610 uint64_t total_address_space = (uint64_t)8*1024*1024*1024*1024;
3611#else
3612 uint64_t total_address_space = (uint64_t)4*1024*1024*1024;
3613#endif // BIT64
3614
3615 size_t num_entries = (size_t)(total_address_space >> gc_heap::min_segment_size_shr);
3616 seg_mapping_table = new seg_mapping[num_entries];
3617
3618 if (seg_mapping_table)
3619 {
3620 memset (seg_mapping_table, 0, num_entries * sizeof (seg_mapping));
3621 dprintf (1, ("created %d entries for heap mapping (%Id bytes)",
3622 num_entries, (num_entries * sizeof (seg_mapping))));
3623 return TRUE;
3624 }
3625 else
3626 {
3627 dprintf (1, ("failed to create %d entries for heap mapping (%Id bytes)",
3628 num_entries, (num_entries * sizeof (seg_mapping))));
3629 return FALSE;
3630 }
3631}
3632#endif //GROWABLE_SEG_MAPPING_TABLE
3633
3634#ifdef FEATURE_BASICFREEZE
3635inline
3636size_t ro_seg_begin_index (heap_segment* seg)
3637{
3638 size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3639 begin_index = max (begin_index, (size_t)g_gc_lowest_address >> gc_heap::min_segment_size_shr);
3640 return begin_index;
3641}
3642
3643inline
3644size_t ro_seg_end_index (heap_segment* seg)
3645{
3646 size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) >> gc_heap::min_segment_size_shr;
3647 end_index = min (end_index, (size_t)g_gc_highest_address >> gc_heap::min_segment_size_shr);
3648 return end_index;
3649}
3650
3651void seg_mapping_table_add_ro_segment (heap_segment* seg)
3652{
3653#ifdef GROWABLE_SEG_MAPPING_TABLE
3654 if ((heap_segment_reserved (seg) <= g_gc_lowest_address) || (heap_segment_mem (seg) >= g_gc_highest_address))
3655 return;
3656#endif //GROWABLE_SEG_MAPPING_TABLE
3657
3658 for (size_t entry_index = ro_seg_begin_index (seg); entry_index <= ro_seg_end_index (seg); entry_index++)
3659 seg_mapping_table[entry_index].seg1 = (heap_segment*)((size_t)seg_mapping_table[entry_index].seg1 | ro_in_entry);
3660}
3661
3662void seg_mapping_table_remove_ro_segment (heap_segment* seg)
3663{
3664 UNREFERENCED_PARAMETER(seg);
3665#if 0
3666// POSSIBLE PERF TODO: right now we are not doing anything because we can't simply remove the flag. If it proves
3667// to be a perf problem, we can search in the current ro segs and see if any lands in this range and only
3668// remove the flag if none lands in this range.
3669#endif //0
3670}
3671
3672heap_segment* ro_segment_lookup (uint8_t* o)
3673{
3674 uint8_t* ro_seg_start = o;
3675 heap_segment* seg = (heap_segment*)gc_heap::seg_table->lookup (ro_seg_start);
3676
3677 if (ro_seg_start && in_range_for_segment (o, seg))
3678 return seg;
3679 else
3680 return 0;
3681}
3682
3683#endif //FEATURE_BASICFREEZE
3684
3685void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
3686{
3687 size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3688 size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3689 seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3690 size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
3691 seg_mapping* end_entry = &seg_mapping_table[end_index];
3692
3693 dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)",
3694 seg, begin_index, heap_segment_reserved (seg), end_index));
3695
3696 dprintf (1, ("before add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3697 begin_index, (seg_mapping_table[begin_index].boundary + 1),
3698 end_index, (seg_mapping_table[end_index].boundary + 1)));
3699
3700#ifdef MULTIPLE_HEAPS
3701#ifdef SIMPLE_DPRINTF
3702 dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end %d: h0: %Ix(%d), h1: %Ix(%d)",
3703 begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3704 (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3705 end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3706 (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3707#endif //SIMPLE_DPRINTF
3708 assert (end_entry->boundary == 0);
3709 assert (end_entry->h0 == 0);
3710 end_entry->h0 = hp;
3711 assert (begin_entry->h1 == 0);
3712 begin_entry->h1 = hp;
3713#else
3714 UNREFERENCED_PARAMETER(hp);
3715#endif //MULTIPLE_HEAPS
3716
3717 end_entry->boundary = (uint8_t*)seg_end;
3718
3719 dprintf (1, ("set entry %d seg1 and %d seg0 to %Ix", begin_index, end_index, seg));
3720 assert ((begin_entry->seg1 == 0) || ((size_t)(begin_entry->seg1) == ro_in_entry));
3721 begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) | (size_t)seg);
3722 end_entry->seg0 = seg;
3723
3724 // for every entry inbetween we need to set its heap too.
3725 for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3726 {
3727 assert (seg_mapping_table[entry_index].boundary == 0);
3728#ifdef MULTIPLE_HEAPS
3729 assert (seg_mapping_table[entry_index].h0 == 0);
3730 seg_mapping_table[entry_index].h1 = hp;
3731#endif //MULTIPLE_HEAPS
3732 seg_mapping_table[entry_index].seg1 = seg;
3733 }
3734
3735 dprintf (1, ("after add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3736 begin_index, (seg_mapping_table[begin_index].boundary + 1),
3737 end_index, (seg_mapping_table[end_index].boundary + 1)));
3738#if defined(MULTIPLE_HEAPS) && defined(SIMPLE_DPRINTF)
3739 dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end: %d h0: %Ix(%d), h1: %Ix(%d)",
3740 begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3741 (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3742 end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3743 (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3744#endif //MULTIPLE_HEAPS && SIMPLE_DPRINTF
3745}
3746
3747void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
3748{
3749 size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3750 size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3751 seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3752 size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
3753 seg_mapping* end_entry = &seg_mapping_table[end_index];
3754 dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)",
3755 seg, begin_index, heap_segment_reserved (seg), end_index));
3756
3757 assert (end_entry->boundary == (uint8_t*)seg_end);
3758 end_entry->boundary = 0;
3759
3760#ifdef MULTIPLE_HEAPS
3761 gc_heap* hp = heap_segment_heap (seg);
3762 assert (end_entry->h0 == hp);
3763 end_entry->h0 = 0;
3764 assert (begin_entry->h1 == hp);
3765 begin_entry->h1 = 0;
3766#endif //MULTIPLE_HEAPS
3767
3768 assert (begin_entry->seg1 != 0);
3769 begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) & ro_in_entry);
3770 end_entry->seg0 = 0;
3771
3772 // for every entry inbetween we need to reset its heap too.
3773 for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3774 {
3775 assert (seg_mapping_table[entry_index].boundary == 0);
3776#ifdef MULTIPLE_HEAPS
3777 assert (seg_mapping_table[entry_index].h0 == 0);
3778 assert (seg_mapping_table[entry_index].h1 == hp);
3779 seg_mapping_table[entry_index].h1 = 0;
3780#endif //MULTIPLE_HEAPS
3781 seg_mapping_table[entry_index].seg1 = 0;
3782 }
3783
3784 dprintf (1, ("after remove: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix",
3785 begin_index, (seg_mapping_table[begin_index].boundary + 1),
3786 end_index, (seg_mapping_table[end_index].boundary + 1)));
3787#ifdef MULTIPLE_HEAPS
3788 dprintf (1, ("begin %d: h0: %Ix, h1: %Ix; end: %d h0: %Ix, h1: %Ix",
3789 begin_index, (uint8_t*)(begin_entry->h0), (uint8_t*)(begin_entry->h1),
3790 end_index, (uint8_t*)(end_entry->h0), (uint8_t*)(end_entry->h1)));
3791#endif //MULTIPLE_HEAPS
3792}
3793
3794#ifdef MULTIPLE_HEAPS
3795inline
3796gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o)
3797{
3798 size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
3799 seg_mapping* entry = &seg_mapping_table[index];
3800
3801 gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
3802
3803 dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
3804 o, index, (entry->boundary + 1),
3805 (uint8_t*)(entry->h0), (uint8_t*)(entry->seg0),
3806 (uint8_t*)(entry->h1), (uint8_t*)(entry->seg1)));
3807
3808#ifdef _DEBUG
3809 heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3810#ifdef FEATURE_BASICFREEZE
3811 if ((size_t)seg & ro_in_entry)
3812 seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3813#endif //FEATURE_BASICFREEZE
3814
3815 if (seg)
3816 {
3817 if (in_range_for_segment (o, seg))
3818 {
3819 dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, seg, (uint8_t*)heap_segment_allocated (seg)));
3820 }
3821 else
3822 {
3823 dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg",
3824 seg, (uint8_t*)heap_segment_allocated (seg), o));
3825 }
3826 }
3827 else
3828 {
3829 dprintf (2, ("could not find obj %Ix in any existing segments", o));
3830 }
3831#endif //_DEBUG
3832
3833 return hp;
3834}
3835
3836gc_heap* seg_mapping_table_heap_of (uint8_t* o)
3837{
3838#ifdef GROWABLE_SEG_MAPPING_TABLE
3839 if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3840 return 0;
3841#endif //GROWABLE_SEG_MAPPING_TABLE
3842
3843 return seg_mapping_table_heap_of_worker (o);
3844}
3845
3846gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o)
3847{
3848#if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3849 if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3850 return 0;
3851#endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3852
3853 return seg_mapping_table_heap_of_worker (o);
3854}
3855#endif //MULTIPLE_HEAPS
3856
3857// Only returns a valid seg if we can actually find o on the seg.
3858heap_segment* seg_mapping_table_segment_of (uint8_t* o)
3859{
3860#if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3861 if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3862#ifdef FEATURE_BASICFREEZE
3863 return ro_segment_lookup (o);
3864#else
3865 return 0;
3866#endif //FEATURE_BASICFREEZE
3867#endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3868
3869 size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
3870 seg_mapping* entry = &seg_mapping_table[index];
3871
3872 dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
3873 o, index, (entry->boundary + 1),
3874 (uint8_t*)(entry->seg0), (uint8_t*)(entry->seg1)));
3875
3876 heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3877#ifdef FEATURE_BASICFREEZE
3878 if ((size_t)seg & ro_in_entry)
3879 seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3880#endif //FEATURE_BASICFREEZE
3881
3882 if (seg)
3883 {
3884 if (in_range_for_segment (o, seg))
3885 {
3886 dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg)));
3887 }
3888 else
3889 {
3890 dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg, setting it to 0",
3891 (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg), o));
3892 seg = 0;
3893 }
3894 }
3895 else
3896 {
3897 dprintf (2, ("could not find obj %Ix in any existing segments", o));
3898 }
3899
3900#ifdef FEATURE_BASICFREEZE
3901 // TODO: This was originally written assuming that the seg_mapping_table would always contain entries for ro
3902 // segments whenever the ro segment falls into the [g_gc_lowest_address,g_gc_highest_address) range. I.e., it had an
3903 // extra "&& (size_t)(entry->seg1) & ro_in_entry" expression. However, at the moment, grow_brick_card_table does
3904 // not correctly go through the ro segments and add them back to the seg_mapping_table when the [lowest,highest)
3905 // range changes. We should probably go ahead and modify grow_brick_card_table and put back the
3906 // "&& (size_t)(entry->seg1) & ro_in_entry" here.
3907 if (!seg)
3908 {
3909 seg = ro_segment_lookup (o);
3910 if (seg && !in_range_for_segment (o, seg))
3911 seg = 0;
3912 }
3913#endif //FEATURE_BASICFREEZE
3914
3915 return seg;
3916}
3917#endif //SEG_MAPPING_TABLE
3918
3919size_t gcard_of ( uint8_t*);
3920
3921#define memref(i) *(uint8_t**)(i)
3922
3923//GC Flags
3924#define GC_MARKED (size_t)0x1
3925#define slot(i, j) ((uint8_t**)(i))[j+1]
3926
3927#define free_object_base_size (plug_skew + sizeof(ArrayBase))
3928
3929class CObjectHeader : public Object
3930{
3931public:
3932
3933#if defined(FEATURE_REDHAWK) || defined(BUILD_AS_STANDALONE)
3934 // The GC expects the following methods that are provided by the Object class in the CLR but not provided
3935 // by Redhawk's version of Object.
3936 uint32_t GetNumComponents()
3937 {
3938 return ((ArrayBase *)this)->GetNumComponents();
3939 }
3940
3941 void Validate(BOOL bDeep=TRUE, BOOL bVerifyNextHeader = TRUE)
3942 {
3943 UNREFERENCED_PARAMETER(bVerifyNextHeader);
3944
3945 if (this == NULL)
3946 return;
3947
3948 MethodTable * pMT = GetMethodTable();
3949
3950 _ASSERTE(pMT->SanityCheck());
3951
3952 bool noRangeChecks =
3953 (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_RANGE_CHECKS) == GCConfig::HEAPVERIFY_NO_RANGE_CHECKS;
3954
3955 BOOL fSmallObjectHeapPtr = FALSE, fLargeObjectHeapPtr = FALSE;
3956 if (!noRangeChecks)
3957 {
3958 fSmallObjectHeapPtr = g_theGCHeap->IsHeapPointer(this, TRUE);
3959 if (!fSmallObjectHeapPtr)
3960 fLargeObjectHeapPtr = g_theGCHeap->IsHeapPointer(this);
3961
3962 _ASSERTE(fSmallObjectHeapPtr || fLargeObjectHeapPtr);
3963 }
3964
3965#ifdef FEATURE_STRUCTALIGN
3966 _ASSERTE(IsStructAligned((uint8_t *)this, GetMethodTable()->GetBaseAlignment()));
3967#endif // FEATURE_STRUCTALIGN
3968
3969#ifdef FEATURE_64BIT_ALIGNMENT
3970 if (pMT->RequiresAlign8())
3971 {
3972 _ASSERTE((((size_t)this) & 0x7) == (pMT->IsValueType() ? 4U : 0U));
3973 }
3974#endif // FEATURE_64BIT_ALIGNMENT
3975
3976#ifdef VERIFY_HEAP
3977 if (bDeep && (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC))
3978 g_theGCHeap->ValidateObjectMember(this);
3979#endif
3980 if (fSmallObjectHeapPtr)
3981 {
3982#ifdef FEATURE_BASICFREEZE
3983 _ASSERTE(!g_theGCHeap->IsLargeObject(pMT) || g_theGCHeap->IsInFrozenSegment(this));
3984#else
3985 _ASSERTE(!g_theGCHeap->IsLargeObject(pMT));
3986#endif
3987 }
3988 }
3989
3990 void ValidatePromote(ScanContext *sc, uint32_t flags)
3991 {
3992 UNREFERENCED_PARAMETER(sc);
3993 UNREFERENCED_PARAMETER(flags);
3994
3995 Validate();
3996 }
3997
3998 void ValidateHeap(Object *from, BOOL bDeep)
3999 {
4000 UNREFERENCED_PARAMETER(from);
4001
4002 Validate(bDeep, FALSE);
4003 }
4004
4005#endif //FEATURE_REDHAWK || BUILD_AS_STANDALONE
4006
4007 /////
4008 //
4009 // Header Status Information
4010 //
4011
4012 MethodTable *GetMethodTable() const
4013 {
4014 return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED))));
4015 }
4016
4017 void SetMarked()
4018 {
4019 RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED));
4020 }
4021
4022 BOOL IsMarked() const
4023 {
4024 return !!(((size_t)RawGetMethodTable()) & GC_MARKED);
4025 }
4026
4027 void SetPinned()
4028 {
4029 assert (!(gc_heap::settings.concurrent));
4030 GetHeader()->SetGCBit();
4031 }
4032
4033 BOOL IsPinned() const
4034 {
4035 return !!((((CObjectHeader*)this)->GetHeader()->GetBits()) & BIT_SBLK_GC_RESERVE);
4036 }
4037
4038 void ClearMarked()
4039 {
4040 RawSetMethodTable( GetMethodTable() );
4041 }
4042
4043 CGCDesc *GetSlotMap ()
4044 {
4045 assert (GetMethodTable()->ContainsPointers());
4046 return CGCDesc::GetCGCDescFromMT(GetMethodTable());
4047 }
4048
4049 void SetFree(size_t size)
4050 {
4051 assert (size >= free_object_base_size);
4052
4053 assert (g_gc_pFreeObjectMethodTable->GetBaseSize() == free_object_base_size);
4054 assert (g_gc_pFreeObjectMethodTable->RawGetComponentSize() == 1);
4055
4056 RawSetMethodTable( g_gc_pFreeObjectMethodTable );
4057
4058 size_t* numComponentsPtr = (size_t*) &((uint8_t*) this)[ArrayBase::GetOffsetOfNumComponents()];
4059 *numComponentsPtr = size - free_object_base_size;
4060#ifdef VERIFY_HEAP
4061 //This introduces a bug in the free list management.
4062 //((void**) this)[-1] = 0; // clear the sync block,
4063 assert (*numComponentsPtr >= 0);
4064 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
4065 memset (((uint8_t*)this)+sizeof(ArrayBase), 0xcc, *numComponentsPtr);
4066#endif //VERIFY_HEAP
4067 }
4068
4069 void UnsetFree()
4070 {
4071 size_t size = free_object_base_size - plug_skew;
4072
4073 // since we only need to clear 2 ptr size, we do it manually
4074 PTR_PTR m = (PTR_PTR) this;
4075 for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
4076 *(m++) = 0;
4077 }
4078
4079 BOOL IsFree () const
4080 {
4081 return (GetMethodTable() == g_gc_pFreeObjectMethodTable);
4082 }
4083
4084#ifdef FEATURE_STRUCTALIGN
4085 int GetRequiredAlignment () const
4086 {
4087 return GetMethodTable()->GetRequiredAlignment();
4088 }
4089#endif // FEATURE_STRUCTALIGN
4090
4091 BOOL ContainsPointers() const
4092 {
4093 return GetMethodTable()->ContainsPointers();
4094 }
4095
4096#ifdef COLLECTIBLE_CLASS
4097 BOOL Collectible() const
4098 {
4099 return GetMethodTable()->Collectible();
4100 }
4101
4102 FORCEINLINE BOOL ContainsPointersOrCollectible() const
4103 {
4104 MethodTable *pMethodTable = GetMethodTable();
4105 return (pMethodTable->ContainsPointers() || pMethodTable->Collectible());
4106 }
4107#endif //COLLECTIBLE_CLASS
4108
4109 Object* GetObjectBase() const
4110 {
4111 return (Object*) this;
4112 }
4113};
4114
4115#define header(i) ((CObjectHeader*)(i))
4116
4117#define free_list_slot(x) ((uint8_t**)(x))[2]
4118#define free_list_undo(x) ((uint8_t**)(x))[-1]
4119#define UNDO_EMPTY ((uint8_t*)1)
4120
4121#ifdef SHORT_PLUGS
4122inline
4123void set_plug_padded (uint8_t* node)
4124{
4125 header(node)->SetMarked();
4126}
4127inline
4128void clear_plug_padded (uint8_t* node)
4129{
4130 header(node)->ClearMarked();
4131}
4132inline
4133BOOL is_plug_padded (uint8_t* node)
4134{
4135 return header(node)->IsMarked();
4136}
4137#else //SHORT_PLUGS
4138inline void set_plug_padded (uint8_t* node){}
4139inline void clear_plug_padded (uint8_t* node){}
4140inline
4141BOOL is_plug_padded (uint8_t* node){return FALSE;}
4142#endif //SHORT_PLUGS
4143
4144
4145inline size_t unused_array_size(uint8_t * p)
4146{
4147 assert(((CObjectHeader*)p)->IsFree());
4148
4149 size_t* numComponentsPtr = (size_t*)(p + ArrayBase::GetOffsetOfNumComponents());
4150 return free_object_base_size + *numComponentsPtr;
4151}
4152
4153heap_segment* heap_segment_rw (heap_segment* ns)
4154{
4155 if ((ns == 0) || !heap_segment_read_only_p (ns))
4156 {
4157 return ns;
4158 }
4159 else
4160 {
4161 do
4162 {
4163 ns = heap_segment_next (ns);
4164 } while ((ns != 0) && heap_segment_read_only_p (ns));
4165 return ns;
4166 }
4167}
4168
4169//returns the next non ro segment.
4170heap_segment* heap_segment_next_rw (heap_segment* seg)
4171{
4172 heap_segment* ns = heap_segment_next (seg);
4173 return heap_segment_rw (ns);
4174}
4175
4176// returns the segment before seg.
4177heap_segment* heap_segment_prev_rw (heap_segment* begin, heap_segment* seg)
4178{
4179 assert (begin != 0);
4180 heap_segment* prev = begin;
4181 heap_segment* current = heap_segment_next_rw (begin);
4182
4183 while (current && current != seg)
4184 {
4185 prev = current;
4186 current = heap_segment_next_rw (current);
4187 }
4188
4189 if (current == seg)
4190 {
4191 return prev;
4192 }
4193 else
4194 {
4195 return 0;
4196 }
4197}
4198
4199// returns the segment before seg.
4200heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg)
4201{
4202 assert (begin != 0);
4203 heap_segment* prev = begin;
4204 heap_segment* current = heap_segment_next (begin);
4205
4206 while (current && current != seg)
4207 {
4208 prev = current;
4209 current = heap_segment_next (current);
4210 }
4211
4212 if (current == seg)
4213 {
4214 return prev;
4215 }
4216 else
4217 {
4218 return 0;
4219 }
4220}
4221
4222heap_segment* heap_segment_in_range (heap_segment* ns)
4223{
4224 if ((ns == 0) || heap_segment_in_range_p (ns))
4225 {
4226 return ns;
4227 }
4228 else
4229 {
4230 do
4231 {
4232 ns = heap_segment_next (ns);
4233 } while ((ns != 0) && !heap_segment_in_range_p (ns));
4234 return ns;
4235 }
4236}
4237
4238heap_segment* heap_segment_next_in_range (heap_segment* seg)
4239{
4240 heap_segment* ns = heap_segment_next (seg);
4241 return heap_segment_in_range (ns);
4242}
4243
4244typedef struct
4245{
4246 uint8_t* memory_base;
4247} imemory_data;
4248
4249typedef struct
4250{
4251 imemory_data *initial_memory;
4252 imemory_data *initial_normal_heap; // points into initial_memory_array
4253 imemory_data *initial_large_heap; // points into initial_memory_array
4254
4255 size_t block_size_normal;
4256 size_t block_size_large;
4257
4258 size_t block_count; // # of blocks in each
4259 size_t current_block_normal;
4260 size_t current_block_large;
4261
4262 enum
4263 {
4264 ALLATONCE = 1,
4265 TWO_STAGE,
4266 EACH_BLOCK
4267 };
4268
4269 size_t allocation_pattern;
4270} initial_memory_details;
4271
4272initial_memory_details memory_details;
4273
4274BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
4275{
4276 BOOL reserve_success = FALSE;
4277
4278 // should only be called once
4279 assert (memory_details.initial_memory == 0);
4280
4281 memory_details.initial_memory = new (nothrow) imemory_data[num_heaps*2];
4282 if (memory_details.initial_memory == 0)
4283 {
4284 dprintf (2, ("failed to reserve %Id bytes for imemory_data", num_heaps*2*sizeof(imemory_data)));
4285 return FALSE;
4286 }
4287
4288 memory_details.initial_normal_heap = memory_details.initial_memory;
4289 memory_details.initial_large_heap = memory_details.initial_memory + num_heaps;
4290 memory_details.block_size_normal = normal_size;
4291 memory_details.block_size_large = large_size;
4292 memory_details.block_count = num_heaps;
4293
4294 memory_details.current_block_normal = 0;
4295 memory_details.current_block_large = 0;
4296
4297 g_gc_lowest_address = MAX_PTR;
4298 g_gc_highest_address = 0;
4299
4300 if (((size_t)MAX_PTR - large_size) < normal_size)
4301 {
4302 // we are already overflowing with just one heap.
4303 dprintf (2, ("0x%Ix + 0x%Ix already overflow", normal_size, large_size));
4304 return FALSE;
4305 }
4306
4307 if (((size_t)MAX_PTR / memory_details.block_count) < (normal_size + large_size))
4308 {
4309 dprintf (2, ("(0x%Ix + 0x%Ix)*0x%Ix overflow", normal_size, large_size, memory_details.block_count));
4310 return FALSE;
4311 }
4312
4313 size_t requestedMemory = memory_details.block_count * (normal_size + large_size);
4314
4315 uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory);
4316 if (allatonce_block)
4317 {
4318 g_gc_lowest_address = allatonce_block;
4319 g_gc_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size));
4320 memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
4321
4322 for(size_t i = 0; i < memory_details.block_count; i++)
4323 {
4324 memory_details.initial_normal_heap[i].memory_base = allatonce_block + (i*normal_size);
4325 memory_details.initial_large_heap[i].memory_base = allatonce_block +
4326 (memory_details.block_count*normal_size) + (i*large_size);
4327 reserve_success = TRUE;
4328 }
4329 }
4330 else
4331 {
4332 // try to allocate 2 blocks
4333 uint8_t* b1 = 0;
4334 uint8_t* b2 = 0;
4335 b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size);
4336 if (b1)
4337 {
4338 b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size);
4339 if (b2)
4340 {
4341 memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
4342 g_gc_lowest_address = min(b1,b2);
4343 g_gc_highest_address = max(b1 + memory_details.block_count*normal_size,
4344 b2 + memory_details.block_count*large_size);
4345 for(size_t i = 0; i < memory_details.block_count; i++)
4346 {
4347 memory_details.initial_normal_heap[i].memory_base = b1 + (i*normal_size);
4348 memory_details.initial_large_heap[i].memory_base = b2 + (i*large_size);
4349 reserve_success = TRUE;
4350 }
4351 }
4352 else
4353 {
4354 // b2 allocation failed, we'll go on to try allocating each block.
4355 // We could preserve the b1 alloc, but code complexity increases
4356 virtual_free (b1, memory_details.block_count * normal_size);
4357 }
4358 }
4359
4360 if ((b2==NULL) && ( memory_details.block_count > 1))
4361 {
4362 memory_details.allocation_pattern = initial_memory_details::EACH_BLOCK;
4363
4364 imemory_data *current_block = memory_details.initial_memory;
4365 for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4366 {
4367 size_t block_size = ((i < memory_details.block_count) ?
4368 memory_details.block_size_normal :
4369 memory_details.block_size_large);
4370 current_block->memory_base =
4371 (uint8_t*)virtual_alloc (block_size);
4372 if (current_block->memory_base == 0)
4373 {
4374 // Free the blocks that we've allocated so far
4375 current_block = memory_details.initial_memory;
4376 for(size_t j = 0; j < i; j++, current_block++){
4377 if (current_block->memory_base != 0){
4378 block_size = ((j < memory_details.block_count) ?
4379 memory_details.block_size_normal :
4380 memory_details.block_size_large);
4381 virtual_free (current_block->memory_base , block_size);
4382 }
4383 }
4384 reserve_success = FALSE;
4385 break;
4386 }
4387 else
4388 {
4389 if (current_block->memory_base < g_gc_lowest_address)
4390 g_gc_lowest_address = current_block->memory_base;
4391 if (((uint8_t *) current_block->memory_base + block_size) > g_gc_highest_address)
4392 g_gc_highest_address = (current_block->memory_base + block_size);
4393 }
4394 reserve_success = TRUE;
4395 }
4396 }
4397 }
4398
4399 return reserve_success;
4400}
4401
4402void destroy_initial_memory()
4403{
4404 if (memory_details.initial_memory != NULL)
4405 {
4406 if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE)
4407 {
4408 virtual_free(memory_details.initial_memory[0].memory_base,
4409 memory_details.block_count*(memory_details.block_size_normal +
4410 memory_details.block_size_large));
4411 }
4412 else if (memory_details.allocation_pattern == initial_memory_details::TWO_STAGE)
4413 {
4414 virtual_free (memory_details.initial_normal_heap[0].memory_base,
4415 memory_details.block_count*memory_details.block_size_normal);
4416
4417 virtual_free (memory_details.initial_large_heap[0].memory_base,
4418 memory_details.block_count*memory_details.block_size_large);
4419 }
4420 else
4421 {
4422 assert (memory_details.allocation_pattern == initial_memory_details::EACH_BLOCK);
4423 imemory_data *current_block = memory_details.initial_memory;
4424 for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4425 {
4426 size_t block_size = (i < memory_details.block_count) ? memory_details.block_size_normal :
4427 memory_details.block_size_large;
4428 if (current_block->memory_base != NULL)
4429 {
4430 virtual_free (current_block->memory_base, block_size);
4431 }
4432 }
4433 }
4434
4435 delete [] memory_details.initial_memory;
4436 memory_details.initial_memory = NULL;
4437 memory_details.initial_normal_heap = NULL;
4438 memory_details.initial_large_heap = NULL;
4439 }
4440}
4441
4442void* next_initial_memory (size_t size)
4443{
4444 assert ((size == memory_details.block_size_normal) || (size == memory_details.block_size_large));
4445 void *res = NULL;
4446
4447 if ((size != memory_details.block_size_normal) ||
4448 ((memory_details.current_block_normal == memory_details.block_count) &&
4449 (memory_details.block_size_normal == memory_details.block_size_large)))
4450 {
4451 // If the block sizes are the same, flow block requests from normal to large
4452 assert (memory_details.current_block_large < memory_details.block_count);
4453 assert (memory_details.initial_large_heap != 0);
4454
4455 res = memory_details.initial_large_heap[memory_details.current_block_large].memory_base;
4456 memory_details.current_block_large++;
4457 }
4458 else
4459 {
4460 assert (memory_details.current_block_normal < memory_details.block_count);
4461 assert (memory_details.initial_normal_heap != NULL);
4462
4463 res = memory_details.initial_normal_heap[memory_details.current_block_normal].memory_base;
4464 memory_details.current_block_normal++;
4465 }
4466
4467 return res;
4468}
4469
4470heap_segment* get_initial_segment (size_t size, int h_number)
4471{
4472 void* mem = next_initial_memory (size);
4473 heap_segment* res = gc_heap::make_heap_segment ((uint8_t*)mem, size , h_number);
4474
4475 return res;
4476}
4477
4478void* virtual_alloc (size_t size)
4479{
4480 size_t requested_size = size;
4481
4482 if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4483 {
4484 gc_heap::reserved_memory_limit =
4485 GCScan::AskForMoreReservedMemory (gc_heap::reserved_memory_limit, requested_size);
4486 if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4487 {
4488 return 0;
4489 }
4490 }
4491
4492 uint32_t flags = VirtualReserveFlags::None;
4493#ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4494 if (virtual_alloc_hardware_write_watch)
4495 {
4496 flags = VirtualReserveFlags::WriteWatch;
4497 }
4498#endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4499 void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags);
4500 void *aligned_mem = prgmem;
4501
4502 // We don't want (prgmem + size) to be right at the end of the address space
4503 // because we'd have to worry about that everytime we do (address + size).
4504 // We also want to make sure that we leave loh_size_threshold at the end
4505 // so we allocate a small object we don't need to worry about overflow there
4506 // when we do alloc_ptr+size.
4507 if (prgmem)
4508 {
4509 uint8_t* end_mem = (uint8_t*)prgmem + requested_size;
4510
4511 if ((end_mem == 0) || ((size_t)(MAX_PTR - end_mem) <= END_SPACE_AFTER_GC))
4512 {
4513 GCToOSInterface::VirtualRelease (prgmem, requested_size);
4514 dprintf (2, ("Virtual Alloc size %Id returned memory right against 4GB [%Ix, %Ix[ - discarding",
4515 requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4516 prgmem = 0;
4517 aligned_mem = 0;
4518 }
4519 }
4520
4521 if (prgmem)
4522 {
4523 gc_heap::reserved_memory += requested_size;
4524 }
4525
4526 dprintf (2, ("Virtual Alloc size %Id: [%Ix, %Ix[",
4527 requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4528
4529 return aligned_mem;
4530}
4531
4532void virtual_free (void* add, size_t size)
4533{
4534 GCToOSInterface::VirtualRelease (add, size);
4535 gc_heap::reserved_memory -= size;
4536 dprintf (2, ("Virtual Free size %Id: [%Ix, %Ix[",
4537 size, (size_t)add, (size_t)((uint8_t*)add+size)));
4538}
4539
4540static size_t get_valid_segment_size (BOOL large_seg=FALSE)
4541{
4542 size_t seg_size, initial_seg_size;
4543
4544 if (!large_seg)
4545 {
4546 initial_seg_size = INITIAL_ALLOC;
4547 seg_size = static_cast<size_t>(GCConfig::GetSegmentSize());
4548 }
4549 else
4550 {
4551 initial_seg_size = LHEAP_ALLOC;
4552 seg_size = static_cast<size_t>(GCConfig::GetSegmentSize()) / 2;
4553 }
4554
4555#ifdef MULTIPLE_HEAPS
4556#ifdef BIT64
4557 if (!large_seg)
4558#endif // BIT64
4559 {
4560 if (g_num_processors > 4)
4561 initial_seg_size /= 2;
4562 if (g_num_processors > 8)
4563 initial_seg_size /= 2;
4564 }
4565#endif //MULTIPLE_HEAPS
4566
4567 // if seg_size is small but not 0 (0 is default if config not set)
4568 // then set the segment to the minimum size
4569 if (!g_theGCHeap->IsValidSegmentSize(seg_size))
4570 {
4571 // if requested size is between 1 byte and 4MB, use min
4572 if ((seg_size >> 1) && !(seg_size >> 22))
4573 seg_size = 1024*1024*4;
4574 else
4575 seg_size = initial_seg_size;
4576 }
4577
4578#ifdef SEG_MAPPING_TABLE
4579#ifdef BIT64
4580 seg_size = round_up_power2 (seg_size);
4581#else
4582 seg_size = round_down_power2 (seg_size);
4583#endif // BIT64
4584#endif //SEG_MAPPING_TABLE
4585
4586 return (seg_size);
4587}
4588
4589void
4590gc_heap::compute_new_ephemeral_size()
4591{
4592 int eph_gen_max = max_generation - 1 - (settings.promotion ? 1 : 0);
4593 size_t padding_size = 0;
4594
4595 for (int i = 0; i <= eph_gen_max; i++)
4596 {
4597 dynamic_data* dd = dynamic_data_of (i);
4598 total_ephemeral_size += (dd_survived_size (dd) - dd_pinned_survived_size (dd));
4599#ifdef RESPECT_LARGE_ALIGNMENT
4600 total_ephemeral_size += dd_num_npinned_plugs (dd) * switch_alignment_size (FALSE);
4601#endif //RESPECT_LARGE_ALIGNMENT
4602#ifdef FEATURE_STRUCTALIGN
4603 total_ephemeral_size += dd_num_npinned_plugs (dd) * MAX_STRUCTALIGN;
4604#endif //FEATURE_STRUCTALIGN
4605
4606#ifdef SHORT_PLUGS
4607 padding_size += dd_padding_size (dd);
4608#endif //SHORT_PLUGS
4609 }
4610
4611 total_ephemeral_size += eph_gen_starts_size;
4612
4613#ifdef RESPECT_LARGE_ALIGNMENT
4614 size_t planned_ephemeral_size = heap_segment_plan_allocated (ephemeral_heap_segment) -
4615 generation_plan_allocation_start (generation_of (max_generation-1));
4616 total_ephemeral_size = min (total_ephemeral_size, planned_ephemeral_size);
4617#endif //RESPECT_LARGE_ALIGNMENT
4618
4619#ifdef SHORT_PLUGS
4620 total_ephemeral_size = Align ((size_t)((double)total_ephemeral_size * short_plugs_pad_ratio) + 1);
4621 total_ephemeral_size += Align (DESIRED_PLUG_LENGTH);
4622#endif //SHORT_PLUGS
4623
4624 dprintf (3, ("total ephemeral size is %Ix, padding %Ix(%Ix)",
4625 total_ephemeral_size,
4626 padding_size, (total_ephemeral_size - padding_size)));
4627}
4628
4629#ifdef _MSC_VER
4630#pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
4631#endif // _MSC_VER
4632
4633heap_segment*
4634gc_heap::soh_get_segment_to_expand()
4635{
4636 size_t size = soh_segment_size;
4637
4638 ordered_plug_indices_init = FALSE;
4639 use_bestfit = FALSE;
4640
4641 //compute the size of the new ephemeral heap segment.
4642 compute_new_ephemeral_size();
4643
4644 if ((settings.pause_mode != pause_low_latency) &&
4645 (settings.pause_mode != pause_no_gc)
4646#ifdef BACKGROUND_GC
4647 && (!recursive_gc_sync::background_running_p())
4648#endif //BACKGROUND_GC
4649 )
4650 {
4651 allocator* gen_alloc = ((settings.condemned_generation == max_generation) ? 0 :
4652 generation_allocator (generation_of (max_generation)));
4653 dprintf (2, ("(gen%d)soh_get_segment_to_expand", settings.condemned_generation));
4654
4655 // try to find one in the gen 2 segment list, search backwards because the first segments
4656 // tend to be more compact than the later ones.
4657 heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
4658
4659 PREFIX_ASSUME(fseg != NULL);
4660
4661#ifdef SEG_REUSE_STATS
4662 int try_reuse = 0;
4663#endif //SEG_REUSE_STATS
4664
4665 heap_segment* seg = ephemeral_heap_segment;
4666 while ((seg = heap_segment_prev_rw (fseg, seg)) && (seg != fseg))
4667 {
4668#ifdef SEG_REUSE_STATS
4669 try_reuse++;
4670#endif //SEG_REUSE_STATS
4671
4672 if (can_expand_into_p (seg, size/3, total_ephemeral_size, gen_alloc))
4673 {
4674 get_gc_data_per_heap()->set_mechanism (gc_heap_expand,
4675 (use_bestfit ? expand_reuse_bestfit : expand_reuse_normal));
4676 if (settings.condemned_generation == max_generation)
4677 {
4678 if (use_bestfit)
4679 {
4680 build_ordered_free_spaces (seg);
4681 dprintf (GTC_LOG, ("can use best fit"));
4682 }
4683
4684#ifdef SEG_REUSE_STATS
4685 dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse",
4686 settings.condemned_generation, try_reuse));
4687#endif //SEG_REUSE_STATS
4688 dprintf (GTC_LOG, ("max_gen: Found existing segment to expand into %Ix", (size_t)seg));
4689 return seg;
4690 }
4691 else
4692 {
4693#ifdef SEG_REUSE_STATS
4694 dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse - returning",
4695 settings.condemned_generation, try_reuse));
4696#endif //SEG_REUSE_STATS
4697 dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix", (size_t)seg));
4698
4699 // If we return 0 here, the allocator will think since we are short on end
4700 // of seg we neeed to trigger a full compacting GC. So if sustained low latency
4701 // is set we should acquire a new seg instead, that way we wouldn't be short.
4702 // The real solution, of course, is to actually implement seg reuse in gen1.
4703 if (settings.pause_mode != pause_sustained_low_latency)
4704 {
4705 dprintf (GTC_LOG, ("max_gen-1: SustainedLowLatency is set, acquire a new seg"));
4706 get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_next_full_gc);
4707 return 0;
4708 }
4709 }
4710 }
4711 }
4712 }
4713
4714 heap_segment* result = get_segment (size, FALSE);
4715
4716 if(result)
4717 {
4718#ifdef BACKGROUND_GC
4719 if (current_c_gc_state == c_gc_state_planning)
4720 {
4721 // When we expand heap during bgc sweep, we set the seg to be swept so
4722 // we'll always look at cards for objects on the new segment.
4723 result->flags |= heap_segment_flags_swept;
4724 }
4725#endif //BACKGROUND_GC
4726
4727 FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(result),
4728 (size_t)(heap_segment_reserved (result) - heap_segment_mem(result)),
4729 gc_etw_segment_small_object_heap);
4730 }
4731
4732 get_gc_data_per_heap()->set_mechanism (gc_heap_expand, (result ? expand_new_seg : expand_no_memory));
4733
4734 if (result == 0)
4735 {
4736 dprintf (2, ("h%d: failed to allocate a new segment!", heap_number));
4737 }
4738 else
4739 {
4740#ifdef MULTIPLE_HEAPS
4741 heap_segment_heap (result) = this;
4742#endif //MULTIPLE_HEAPS
4743 }
4744
4745 dprintf (GTC_LOG, ("(gen%d)creating new segment %Ix", settings.condemned_generation, result));
4746 return result;
4747}
4748
4749#ifdef _MSC_VER
4750#pragma warning(default:4706)
4751#endif // _MSC_VER
4752
4753//returns 0 in case of allocation failure
4754heap_segment*
4755gc_heap::get_segment (size_t size, BOOL loh_p)
4756{
4757 heap_segment* result = 0;
4758
4759 if (segment_standby_list != 0)
4760 {
4761 result = segment_standby_list;
4762 heap_segment* last = 0;
4763 while (result)
4764 {
4765 size_t hs = (size_t)(heap_segment_reserved (result) - (uint8_t*)result);
4766 if ((hs >= size) && ((hs / 2) < size))
4767 {
4768 dprintf (2, ("Hoarded segment %Ix found", (size_t) result));
4769 if (last)
4770 {
4771 heap_segment_next (last) = heap_segment_next (result);
4772 }
4773 else
4774 {
4775 segment_standby_list = heap_segment_next (result);
4776 }
4777 break;
4778 }
4779 else
4780 {
4781 last = result;
4782 result = heap_segment_next (result);
4783 }
4784 }
4785 }
4786
4787 if (result)
4788 {
4789 init_heap_segment (result);
4790#ifdef BACKGROUND_GC
4791 if (should_commit_mark_array())
4792 {
4793 dprintf (GC_TABLE_LOG, ("hoarded seg %Ix, mark_array is %Ix", result, mark_array));
4794 if (!commit_mark_array_new_seg (__this, result))
4795 {
4796 dprintf (GC_TABLE_LOG, ("failed to commit mark array for hoarded seg"));
4797 // If we can't use it we need to thread it back.
4798 if (segment_standby_list != 0)
4799 {
4800 heap_segment_next (result) = segment_standby_list;
4801 segment_standby_list = result;
4802 }
4803 else
4804 {
4805 segment_standby_list = result;
4806 }
4807
4808 result = 0;
4809 }
4810 }
4811#endif //BACKGROUND_GC
4812
4813#ifdef SEG_MAPPING_TABLE
4814 if (result)
4815 seg_mapping_table_add_segment (result, __this);
4816#endif //SEG_MAPPING_TABLE
4817 }
4818
4819 if (!result)
4820 {
4821#ifndef SEG_MAPPING_TABLE
4822 if (!seg_table->ensure_space_for_insert ())
4823 return 0;
4824#endif //SEG_MAPPING_TABLE
4825 void* mem = virtual_alloc (size);
4826 if (!mem)
4827 {
4828 fgm_result.set_fgm (fgm_reserve_segment, size, loh_p);
4829 return 0;
4830 }
4831
4832 result = gc_heap::make_heap_segment ((uint8_t*)mem, size, heap_number);
4833
4834 if (result)
4835 {
4836 uint8_t* start;
4837 uint8_t* end;
4838 if (mem < g_gc_lowest_address)
4839 {
4840 start = (uint8_t*)mem;
4841 }
4842 else
4843 {
4844 start = (uint8_t*)g_gc_lowest_address;
4845 }
4846
4847 if (((uint8_t*)mem + size) > g_gc_highest_address)
4848 {
4849 end = (uint8_t*)mem + size;
4850 }
4851 else
4852 {
4853 end = (uint8_t*)g_gc_highest_address;
4854 }
4855
4856 if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0)
4857 {
4858 virtual_free (mem, size);
4859 return 0;
4860 }
4861 }
4862 else
4863 {
4864 fgm_result.set_fgm (fgm_commit_segment_beg, SEGMENT_INITIAL_COMMIT, loh_p);
4865 virtual_free (mem, size);
4866 }
4867
4868 if (result)
4869 {
4870#ifdef SEG_MAPPING_TABLE
4871 seg_mapping_table_add_segment (result, __this);
4872#else //SEG_MAPPING_TABLE
4873 gc_heap::seg_table->insert ((uint8_t*)result, delta);
4874#endif //SEG_MAPPING_TABLE
4875 }
4876 }
4877
4878#ifdef BACKGROUND_GC
4879 if (result)
4880 {
4881 ::record_changed_seg ((uint8_t*)result, heap_segment_reserved (result),
4882 settings.gc_index, current_bgc_state,
4883 seg_added);
4884 bgc_verify_mark_array_cleared (result);
4885 }
4886#endif //BACKGROUND_GC
4887
4888 dprintf (GC_TABLE_LOG, ("h%d: new seg: %Ix-%Ix (%Id)", heap_number, result, ((uint8_t*)result + size), size));
4889 return result;
4890}
4891
4892void release_segment (heap_segment* sg)
4893{
4894 ptrdiff_t delta = 0;
4895 FIRE_EVENT(GCFreeSegment_V1, heap_segment_mem(sg));
4896 virtual_free (sg, (uint8_t*)heap_segment_reserved (sg)-(uint8_t*)sg);
4897}
4898
4899heap_segment* gc_heap::get_segment_for_loh (size_t size
4900#ifdef MULTIPLE_HEAPS
4901 , gc_heap* hp
4902#endif //MULTIPLE_HEAPS
4903 )
4904{
4905#ifndef MULTIPLE_HEAPS
4906 gc_heap* hp = 0;
4907#endif //MULTIPLE_HEAPS
4908 heap_segment* res = hp->get_segment (size, TRUE);
4909 if (res != 0)
4910 {
4911#ifdef MULTIPLE_HEAPS
4912 heap_segment_heap (res) = hp;
4913#endif //MULTIPLE_HEAPS
4914 res->flags |= heap_segment_flags_loh;
4915
4916 FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(res), (size_t)(heap_segment_reserved (res) - heap_segment_mem(res)), gc_etw_segment_large_object_heap);
4917
4918 GCToEEInterface::DiagUpdateGenerationBounds();
4919
4920#ifdef MULTIPLE_HEAPS
4921 hp->thread_loh_segment (res);
4922#else
4923 thread_loh_segment (res);
4924#endif //MULTIPLE_HEAPS
4925 }
4926
4927 return res;
4928}
4929
4930void gc_heap::thread_loh_segment (heap_segment* new_seg)
4931{
4932 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
4933
4934 while (heap_segment_next_rw (seg))
4935 seg = heap_segment_next_rw (seg);
4936 heap_segment_next (seg) = new_seg;
4937}
4938
4939heap_segment*
4940gc_heap::get_large_segment (size_t size, BOOL* did_full_compact_gc)
4941{
4942 *did_full_compact_gc = FALSE;
4943 size_t last_full_compact_gc_count = get_full_compact_gc_count();
4944
4945 //access to get_segment needs to be serialized
4946 add_saved_spinlock_info (true, me_release, mt_get_large_seg);
4947 leave_spin_lock (&more_space_lock_loh);
4948 enter_spin_lock (&gc_heap::gc_lock);
4949 dprintf (SPINLOCK_LOG, ("[%d]Seg: Egc", heap_number));
4950 // if a GC happened between here and before we ask for a segment in
4951 // get_large_segment, we need to count that GC.
4952 size_t current_full_compact_gc_count = get_full_compact_gc_count();
4953
4954 if (current_full_compact_gc_count > last_full_compact_gc_count)
4955 {
4956 *did_full_compact_gc = TRUE;
4957 }
4958
4959 heap_segment* res = get_segment_for_loh (size
4960#ifdef MULTIPLE_HEAPS
4961 , this
4962#endif //MULTIPLE_HEAPS
4963 );
4964
4965 dprintf (SPINLOCK_LOG, ("[%d]Seg: A Lgc", heap_number));
4966 leave_spin_lock (&gc_heap::gc_lock);
4967 enter_spin_lock (&more_space_lock_loh);
4968 add_saved_spinlock_info (true, me_acquire, mt_get_large_seg);
4969
4970 return res;
4971}
4972
4973#if 0
4974BOOL gc_heap::unprotect_segment (heap_segment* seg)
4975{
4976 uint8_t* start = align_lower_page (heap_segment_mem (seg));
4977 ptrdiff_t region_size = heap_segment_allocated (seg) - start;
4978
4979 if (region_size != 0 )
4980 {
4981 dprintf (3, ("unprotecting segment %Ix:", (size_t)seg));
4982
4983 BOOL status = GCToOSInterface::VirtualUnprotect (start, region_size);
4984 assert (status);
4985 return status;
4986 }
4987 return FALSE;
4988}
4989#endif
4990
4991#ifdef MULTIPLE_HEAPS
4992#ifdef _X86_
4993#ifdef _MSC_VER
4994#pragma warning(disable:4035)
4995 static ptrdiff_t get_cycle_count()
4996 {
4997 __asm rdtsc
4998 }
4999#pragma warning(default:4035)
5000#elif defined(__GNUC__)
5001 static ptrdiff_t get_cycle_count()
5002 {
5003 ptrdiff_t cycles;
5004 ptrdiff_t cyclesHi;
5005 __asm__ __volatile__
5006 ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
5007 return cycles;
5008 }
5009#else //_MSC_VER
5010#error Unknown compiler
5011#endif //_MSC_VER
5012#elif defined(_TARGET_AMD64_)
5013#ifdef _MSC_VER
5014extern "C" uint64_t __rdtsc();
5015#pragma intrinsic(__rdtsc)
5016 static ptrdiff_t get_cycle_count()
5017 {
5018 return (ptrdiff_t)__rdtsc();
5019 }
5020#elif defined(__clang__)
5021 static ptrdiff_t get_cycle_count()
5022 {
5023 ptrdiff_t cycles;
5024 ptrdiff_t cyclesHi;
5025 __asm__ __volatile__
5026 ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
5027 return (cyclesHi << 32) | cycles;
5028 }
5029#else // _MSC_VER
5030 extern "C" ptrdiff_t get_cycle_count(void);
5031#endif // _MSC_VER
5032#elif defined(_TARGET_ARM_)
5033 static ptrdiff_t get_cycle_count()
5034 {
5035 // @ARMTODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
5036 // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
5037 // all buffer access times being reported as equal in access_time().
5038 return 0;
5039 }
5040#elif defined(_TARGET_ARM64_)
5041 static ptrdiff_t get_cycle_count()
5042 {
5043 // @ARM64TODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
5044 // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
5045 // all buffer access times being reported as equal in access_time().
5046 return 0;
5047 }
5048#else
5049#error NYI platform: get_cycle_count
5050#endif //_TARGET_X86_
5051
5052class heap_select
5053{
5054 heap_select() {}
5055 static uint8_t* sniff_buffer;
5056 static unsigned n_sniff_buffers;
5057 static unsigned cur_sniff_index;
5058
5059 static uint16_t proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5060 static uint16_t heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5061 static uint16_t heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5062 static uint16_t heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5063 static uint16_t heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5064 static uint16_t numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5065
5066 static int access_time(uint8_t *sniff_buffer, int heap_number, unsigned sniff_index, unsigned n_sniff_buffers)
5067 {
5068 ptrdiff_t start_cycles = get_cycle_count();
5069 uint8_t sniff = sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE];
5070 assert (sniff == 0);
5071 ptrdiff_t elapsed_cycles = get_cycle_count() - start_cycles;
5072 // add sniff here just to defeat the optimizer
5073 elapsed_cycles += sniff;
5074 return (int) elapsed_cycles;
5075 }
5076
5077public:
5078 static BOOL init(int n_heaps)
5079 {
5080 assert (sniff_buffer == NULL && n_sniff_buffers == 0);
5081 if (!GCToOSInterface::CanGetCurrentProcessorNumber())
5082 {
5083 n_sniff_buffers = n_heaps*2+1;
5084 size_t n_cache_lines = 1 + n_heaps * n_sniff_buffers + 1;
5085 size_t sniff_buf_size = n_cache_lines * HS_CACHE_LINE_SIZE;
5086 if (sniff_buf_size / HS_CACHE_LINE_SIZE != n_cache_lines) // check for overlow
5087 {
5088 return FALSE;
5089 }
5090
5091 sniff_buffer = new (nothrow) uint8_t[sniff_buf_size];
5092 if (sniff_buffer == 0)
5093 return FALSE;
5094 memset(sniff_buffer, 0, sniff_buf_size*sizeof(uint8_t));
5095 }
5096
5097 //can not enable gc numa aware, force all heaps to be in
5098 //one numa node by filling the array with all 0s
5099 if (!GCToOSInterface::CanEnableGCNumaAware())
5100 memset(heap_no_to_numa_node, 0, sizeof (heap_no_to_numa_node));
5101
5102 return TRUE;
5103 }
5104
5105 static void init_cpu_mapping(gc_heap * /*heap*/, int heap_number)
5106 {
5107 if (GCToOSInterface::CanGetCurrentProcessorNumber())
5108 {
5109 uint32_t proc_no = GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps;
5110 // We can safely cast heap_number to a uint16_t 'cause GetCurrentProcessCpuCount
5111 // only returns up to MAX_SUPPORTED_CPUS procs right now. We only ever create at most
5112 // MAX_SUPPORTED_CPUS GC threads.
5113 proc_no_to_heap_no[proc_no] = (uint16_t)heap_number;
5114 }
5115 }
5116
5117 static void mark_heap(int heap_number)
5118 {
5119 if (GCToOSInterface::CanGetCurrentProcessorNumber())
5120 return;
5121
5122 for (unsigned sniff_index = 0; sniff_index < n_sniff_buffers; sniff_index++)
5123 sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5124 }
5125
5126 static int select_heap(alloc_context* acontext, int /*hint*/)
5127 {
5128 UNREFERENCED_PARAMETER(acontext); // only referenced by dprintf
5129
5130 if (GCToOSInterface::CanGetCurrentProcessorNumber())
5131 return proc_no_to_heap_no[GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps];
5132
5133 unsigned sniff_index = Interlocked::Increment(&cur_sniff_index);
5134 sniff_index %= n_sniff_buffers;
5135
5136 int best_heap = 0;
5137 int best_access_time = 1000*1000*1000;
5138 int second_best_access_time = best_access_time;
5139
5140 uint8_t *l_sniff_buffer = sniff_buffer;
5141 unsigned l_n_sniff_buffers = n_sniff_buffers;
5142 for (int heap_number = 0; heap_number < gc_heap::n_heaps; heap_number++)
5143 {
5144 int this_access_time = access_time(l_sniff_buffer, heap_number, sniff_index, l_n_sniff_buffers);
5145 if (this_access_time < best_access_time)
5146 {
5147 second_best_access_time = best_access_time;
5148 best_access_time = this_access_time;
5149 best_heap = heap_number;
5150 }
5151 else if (this_access_time < second_best_access_time)
5152 {
5153 second_best_access_time = this_access_time;
5154 }
5155 }
5156
5157 if (best_access_time*2 < second_best_access_time)
5158 {
5159 sniff_buffer[(1 + best_heap*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5160
5161 dprintf (3, ("select_heap yields crisp %d for context %p\n", best_heap, (void *)acontext));
5162 }
5163 else
5164 {
5165 dprintf (3, ("select_heap yields vague %d for context %p\n", best_heap, (void *)acontext ));
5166 }
5167
5168 return best_heap;
5169 }
5170
5171 static bool can_find_heap_fast()
5172 {
5173 return GCToOSInterface::CanGetCurrentProcessorNumber();
5174 }
5175
5176 static uint16_t find_proc_no_from_heap_no(int heap_number)
5177 {
5178 return heap_no_to_proc_no[heap_number];
5179 }
5180
5181 static void set_proc_no_for_heap(int heap_number, uint16_t proc_no)
5182 {
5183 heap_no_to_proc_no[heap_number] = proc_no;
5184 }
5185
5186 static uint16_t find_numa_node_from_heap_no(int heap_number)
5187 {
5188 return heap_no_to_numa_node[heap_number];
5189 }
5190
5191 static void set_numa_node_for_heap(int heap_number, uint16_t numa_node)
5192 {
5193 heap_no_to_numa_node[heap_number] = numa_node;
5194 }
5195
5196 static uint16_t find_cpu_group_from_heap_no(int heap_number)
5197 {
5198 return heap_no_to_cpu_group[heap_number];
5199 }
5200
5201 static void set_cpu_group_for_heap(int heap_number, uint16_t group_number)
5202 {
5203 heap_no_to_cpu_group[heap_number] = group_number;
5204 }
5205
5206 static uint16_t find_group_proc_from_heap_no(int heap_number)
5207 {
5208 return heap_no_to_group_proc[heap_number];
5209 }
5210
5211 static void set_group_proc_for_heap(int heap_number, uint16_t group_proc)
5212 {
5213 heap_no_to_group_proc[heap_number] = group_proc;
5214 }
5215
5216 static void init_numa_node_to_heap_map(int nheaps)
5217 { // called right after GCHeap::Init() for each heap is finished
5218 // when numa is not enabled, heap_no_to_numa_node[] are all filled
5219 // with 0s during initialization, and will be treated as one node
5220 numa_node_to_heap_map[0] = 0;
5221 int node_index = 1;
5222
5223 for (int i=1; i < nheaps; i++)
5224 {
5225 if (heap_no_to_numa_node[i] != heap_no_to_numa_node[i-1])
5226 numa_node_to_heap_map[node_index++] = (uint16_t)i;
5227 }
5228 numa_node_to_heap_map[node_index] = (uint16_t)nheaps; //mark the end with nheaps
5229 }
5230
5231 static void get_heap_range_for_heap(int hn, int* start, int* end)
5232 { // 1-tier/no numa case: heap_no_to_numa_node[] all zeros,
5233 // and treated as in one node. thus: start=0, end=n_heaps
5234 uint16_t numa_node = heap_no_to_numa_node[hn];
5235 *start = (int)numa_node_to_heap_map[numa_node];
5236 *end = (int)(numa_node_to_heap_map[numa_node+1]);
5237 }
5238};
5239uint8_t* heap_select::sniff_buffer;
5240unsigned heap_select::n_sniff_buffers;
5241unsigned heap_select::cur_sniff_index;
5242uint16_t heap_select::proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5243uint16_t heap_select::heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5244uint16_t heap_select::heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5245uint16_t heap_select::heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5246uint16_t heap_select::heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5247uint16_t heap_select::numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5248
5249BOOL gc_heap::create_thread_support (unsigned number_of_heaps)
5250{
5251 BOOL ret = FALSE;
5252 if (!gc_start_event.CreateOSManualEventNoThrow (FALSE))
5253 {
5254 goto cleanup;
5255 }
5256 if (!ee_suspend_event.CreateOSAutoEventNoThrow (FALSE))
5257 {
5258 goto cleanup;
5259 }
5260 if (!gc_t_join.init (number_of_heaps, join_flavor_server_gc))
5261 {
5262 goto cleanup;
5263 }
5264
5265 ret = TRUE;
5266
5267cleanup:
5268
5269 if (!ret)
5270 {
5271 destroy_thread_support();
5272 }
5273
5274 return ret;
5275}
5276
5277void gc_heap::destroy_thread_support ()
5278{
5279 if (ee_suspend_event.IsValid())
5280 {
5281 ee_suspend_event.CloseEvent();
5282 }
5283 if (gc_start_event.IsValid())
5284 {
5285 gc_start_event.CloseEvent();
5286 }
5287}
5288
5289void set_thread_group_affinity_for_heap(int heap_number, GCThreadAffinity* affinity)
5290{
5291 affinity->Group = GCThreadAffinity::None;
5292 affinity->Processor = GCThreadAffinity::None;
5293
5294 uint16_t gn, gpn;
5295 GCToOSInterface::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
5296
5297 int bit_number = 0;
5298 for (uintptr_t mask = 1; mask !=0; mask <<=1)
5299 {
5300 if (bit_number == gpn)
5301 {
5302 dprintf(3, ("using processor group %d, mask %Ix for heap %d\n", gn, mask, heap_number));
5303 affinity->Processor = gpn;
5304 affinity->Group = gn;
5305 heap_select::set_cpu_group_for_heap(heap_number, gn);
5306 heap_select::set_group_proc_for_heap(heap_number, gpn);
5307 if (GCToOSInterface::CanEnableGCNumaAware())
5308 {
5309 PROCESSOR_NUMBER proc_no;
5310 proc_no.Group = gn;
5311 proc_no.Number = (uint8_t)gpn;
5312 proc_no.Reserved = 0;
5313
5314 uint16_t node_no = 0;
5315 if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no))
5316 heap_select::set_numa_node_for_heap(heap_number, node_no);
5317 }
5318 else
5319 { // no numa setting, each cpu group is treated as a node
5320 heap_select::set_numa_node_for_heap(heap_number, gn);
5321 }
5322 return;
5323 }
5324 bit_number++;
5325 }
5326}
5327
5328void set_thread_affinity_mask_for_heap(int heap_number, GCThreadAffinity* affinity)
5329{
5330 affinity->Group = GCThreadAffinity::None;
5331 affinity->Processor = GCThreadAffinity::None;
5332
5333 uintptr_t pmask = process_mask;
5334 int bit_number = 0;
5335 uint8_t proc_number = 0;
5336 for (uintptr_t mask = 1; mask != 0; mask <<= 1)
5337 {
5338 if ((mask & pmask) != 0)
5339 {
5340 if (bit_number == heap_number)
5341 {
5342 dprintf (3, ("Using processor %d for heap %d", proc_number, heap_number));
5343 affinity->Processor = proc_number;
5344 heap_select::set_proc_no_for_heap(heap_number, proc_number);
5345 if (GCToOSInterface::CanEnableGCNumaAware())
5346 {
5347 uint16_t node_no = 0;
5348 PROCESSOR_NUMBER proc_no;
5349 proc_no.Group = 0;
5350 proc_no.Number = (uint8_t)proc_number;
5351 proc_no.Reserved = 0;
5352 if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no))
5353 {
5354 heap_select::set_numa_node_for_heap(heap_number, node_no);
5355 }
5356 }
5357 return;
5358 }
5359 bit_number++;
5360 }
5361 proc_number++;
5362 }
5363}
5364
5365bool gc_heap::create_gc_thread ()
5366{
5367 dprintf (3, ("Creating gc thread\n"));
5368 return GCToEEInterface::CreateThread(gc_thread_stub, this, false, ".NET Server GC");
5369}
5370
5371#ifdef _MSC_VER
5372#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
5373#endif //_MSC_VER
5374void gc_heap::gc_thread_function ()
5375{
5376 assert (gc_done_event.IsValid());
5377 assert (gc_start_event.IsValid());
5378 dprintf (3, ("gc thread started"));
5379
5380 heap_select::init_cpu_mapping(this, heap_number);
5381
5382 while (1)
5383 {
5384 assert (!gc_t_join.joined());
5385
5386 if (heap_number == 0)
5387 {
5388 gc_heap::ee_suspend_event.Wait(INFINITE, FALSE);
5389
5390 BEGIN_TIMING(suspend_ee_during_log);
5391 GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
5392 END_TIMING(suspend_ee_during_log);
5393
5394 proceed_with_gc_p = TRUE;
5395
5396 if (!should_proceed_with_gc())
5397 {
5398 update_collection_counts_for_no_gc();
5399 proceed_with_gc_p = FALSE;
5400 }
5401 else
5402 {
5403 settings.init_mechanisms();
5404 gc_start_event.Set();
5405 }
5406 dprintf (3, ("%d gc thread waiting...", heap_number));
5407 }
5408 else
5409 {
5410 gc_start_event.Wait(INFINITE, FALSE);
5411 dprintf (3, ("%d gc thread waiting... Done", heap_number));
5412 }
5413
5414 assert ((heap_number == 0) || proceed_with_gc_p);
5415
5416 if (proceed_with_gc_p)
5417 {
5418 garbage_collect (GCHeap::GcCondemnedGeneration);
5419
5420 if (pm_trigger_full_gc)
5421 {
5422 garbage_collect_pm_full_gc();
5423 }
5424 }
5425
5426 if (heap_number == 0)
5427 {
5428 if (proceed_with_gc_p && (!settings.concurrent))
5429 {
5430 do_post_gc();
5431 }
5432
5433#ifdef BACKGROUND_GC
5434 recover_bgc_settings();
5435#endif //BACKGROUND_GC
5436
5437#ifdef MULTIPLE_HEAPS
5438 for (int i = 0; i < gc_heap::n_heaps; i++)
5439 {
5440 gc_heap* hp = gc_heap::g_heaps[i];
5441 hp->add_saved_spinlock_info (false, me_release, mt_block_gc);
5442 leave_spin_lock(&hp->more_space_lock_soh);
5443 }
5444#endif //MULTIPLE_HEAPS
5445
5446 gc_heap::gc_started = FALSE;
5447
5448 BEGIN_TIMING(restart_ee_during_log);
5449 GCToEEInterface::RestartEE(TRUE);
5450 END_TIMING(restart_ee_during_log);
5451 process_sync_log_stats();
5452
5453 dprintf (SPINLOCK_LOG, ("GC Lgc"));
5454 leave_spin_lock (&gc_heap::gc_lock);
5455
5456 gc_heap::internal_gc_done = true;
5457
5458 if (proceed_with_gc_p)
5459 set_gc_done();
5460 else
5461 {
5462 // If we didn't actually do a GC, it means we didn't wait up the other threads,
5463 // we still need to set the gc_done_event for those threads.
5464 for (int i = 0; i < gc_heap::n_heaps; i++)
5465 {
5466 gc_heap* hp = gc_heap::g_heaps[i];
5467 hp->set_gc_done();
5468 }
5469 }
5470 }
5471 else
5472 {
5473 int spin_count = 32 * (gc_heap::n_heaps - 1);
5474
5475 // wait until RestartEE has progressed to a stage where we can restart user threads
5476 while (!gc_heap::internal_gc_done && !GCHeap::SafeToRestartManagedThreads())
5477 {
5478 spin_and_switch (spin_count, (gc_heap::internal_gc_done || GCHeap::SafeToRestartManagedThreads()));
5479 }
5480 set_gc_done();
5481 }
5482 }
5483}
5484#ifdef _MSC_VER
5485#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
5486#endif //_MSC_VER
5487
5488#endif //MULTIPLE_HEAPS
5489
5490bool virtual_alloc_commit_for_heap(void* addr, size_t size, int h_number)
5491{
5492#if defined(MULTIPLE_HEAPS) && !defined(FEATURE_REDHAWK)
5493 // Currently there is no way for us to specific the numa node to allocate on via hosting interfaces to
5494 // a host. This will need to be added later.
5495#if !defined(FEATURE_CORECLR) && !defined(BUILD_AS_STANDALONE)
5496 if (!CLRMemoryHosted())
5497#endif
5498 {
5499 if (GCToOSInterface::CanEnableGCNumaAware())
5500 {
5501 uint32_t numa_node = heap_select::find_numa_node_from_heap_no(h_number);
5502 if (GCToOSInterface::VirtualCommit(addr, size, numa_node))
5503 return true;
5504 }
5505 }
5506#else
5507 UNREFERENCED_PARAMETER(h_number);
5508#endif
5509
5510 //numa aware not enabled, or call failed --> fallback to VirtualCommit()
5511 return GCToOSInterface::VirtualCommit(addr, size);
5512}
5513
5514#ifndef SEG_MAPPING_TABLE
5515inline
5516heap_segment* gc_heap::segment_of (uint8_t* add, ptrdiff_t& delta, BOOL verify_p)
5517{
5518 uint8_t* sadd = add;
5519 heap_segment* hs = 0;
5520 heap_segment* hs1 = 0;
5521 if (!((add >= g_gc_lowest_address) && (add < g_gc_highest_address)))
5522 {
5523 delta = 0;
5524 return 0;
5525 }
5526 //repeat in case there is a concurrent insertion in the table.
5527 do
5528 {
5529 hs = hs1;
5530 sadd = add;
5531 seg_table->lookup (sadd);
5532 hs1 = (heap_segment*)sadd;
5533 } while (hs1 && !in_range_for_segment (add, hs1) && (hs != hs1));
5534
5535 hs = hs1;
5536
5537 if ((hs == 0) ||
5538 (verify_p && (add > heap_segment_reserved ((heap_segment*)(sadd + delta)))))
5539 delta = 0;
5540 return hs;
5541}
5542#endif //SEG_MAPPING_TABLE
5543
5544class mark
5545{
5546public:
5547 uint8_t* first;
5548 size_t len;
5549
5550 // If we want to save space we can have a pool of plug_and_gap's instead of
5551 // always having 2 allocated for each pinned plug.
5552 gap_reloc_pair saved_pre_plug;
5553 // If we decide to not compact, we need to restore the original values.
5554 gap_reloc_pair saved_pre_plug_reloc;
5555
5556 gap_reloc_pair saved_post_plug;
5557
5558 // Supposedly Pinned objects cannot have references but we are seeing some from pinvoke
5559 // frames. Also if it's an artificially pinned plug created by us, it can certainly
5560 // have references.
5561 // We know these cases will be rare so we can optimize this to be only allocated on decommand.
5562 gap_reloc_pair saved_post_plug_reloc;
5563
5564 // We need to calculate this after we are done with plan phase and before compact
5565 // phase because compact phase will change the bricks so relocate_address will no
5566 // longer work.
5567 uint8_t* saved_pre_plug_info_reloc_start;
5568
5569 // We need to save this because we will have no way to calculate it, unlike the
5570 // pre plug info start which is right before this plug.
5571 uint8_t* saved_post_plug_info_start;
5572
5573#ifdef SHORT_PLUGS
5574 uint8_t* allocation_context_start_region;
5575#endif //SHORT_PLUGS
5576
5577 // How the bits in these bytes are organized:
5578 // MSB --> LSB
5579 // 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
5580 // 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.
5581 BOOL saved_pre_p;
5582 BOOL saved_post_p;
5583
5584#ifdef _DEBUG
5585 // We are seeing this is getting corrupted for a PP with a NP after.
5586 // Save it when we first set it and make sure it doesn't change.
5587 gap_reloc_pair saved_post_plug_debug;
5588#endif //_DEBUG
5589
5590 size_t get_max_short_bits()
5591 {
5592 return (sizeof (gap_reloc_pair) / sizeof (uint8_t*));
5593 }
5594
5595 // pre bits
5596 size_t get_pre_short_start_bit ()
5597 {
5598 return (sizeof (saved_pre_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5599 }
5600
5601 BOOL pre_short_p()
5602 {
5603 return (saved_pre_p & (1 << (sizeof (saved_pre_p) * 8 - 1)));
5604 }
5605
5606 void set_pre_short()
5607 {
5608 saved_pre_p |= (1 << (sizeof (saved_pre_p) * 8 - 1));
5609 }
5610
5611 void set_pre_short_bit (size_t bit)
5612 {
5613 saved_pre_p |= 1 << (get_pre_short_start_bit() + bit);
5614 }
5615
5616 BOOL pre_short_bit_p (size_t bit)
5617 {
5618 return (saved_pre_p & (1 << (get_pre_short_start_bit() + bit)));
5619 }
5620
5621#ifdef COLLECTIBLE_CLASS
5622 void set_pre_short_collectible()
5623 {
5624 saved_pre_p |= 2;
5625 }
5626
5627 BOOL pre_short_collectible_p()
5628 {
5629 return (saved_pre_p & 2);
5630 }
5631#endif //COLLECTIBLE_CLASS
5632
5633 // post bits
5634 size_t get_post_short_start_bit ()
5635 {
5636 return (sizeof (saved_post_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5637 }
5638
5639 BOOL post_short_p()
5640 {
5641 return (saved_post_p & (1 << (sizeof (saved_post_p) * 8 - 1)));
5642 }
5643
5644 void set_post_short()
5645 {
5646 saved_post_p |= (1 << (sizeof (saved_post_p) * 8 - 1));
5647 }
5648
5649 void set_post_short_bit (size_t bit)
5650 {
5651 saved_post_p |= 1 << (get_post_short_start_bit() + bit);
5652 }
5653
5654 BOOL post_short_bit_p (size_t bit)
5655 {
5656 return (saved_post_p & (1 << (get_post_short_start_bit() + bit)));
5657 }
5658
5659#ifdef COLLECTIBLE_CLASS
5660 void set_post_short_collectible()
5661 {
5662 saved_post_p |= 2;
5663 }
5664
5665 BOOL post_short_collectible_p()
5666 {
5667 return (saved_post_p & 2);
5668 }
5669#endif //COLLECTIBLE_CLASS
5670
5671 uint8_t* get_plug_address() { return first; }
5672
5673 BOOL has_pre_plug_info() { return saved_pre_p; }
5674 BOOL has_post_plug_info() { return saved_post_p; }
5675
5676 gap_reloc_pair* get_pre_plug_reloc_info() { return &saved_pre_plug_reloc; }
5677 gap_reloc_pair* get_post_plug_reloc_info() { return &saved_post_plug_reloc; }
5678 void set_pre_plug_info_reloc_start (uint8_t* reloc) { saved_pre_plug_info_reloc_start = reloc; }
5679 uint8_t* get_post_plug_info_start() { return saved_post_plug_info_start; }
5680
5681 // We need to temporarily recover the shortened plugs for compact phase so we can
5682 // copy over the whole plug and their related info (mark bits/cards). But we will
5683 // need to set the artificial gap back so compact phase can keep reading the plug info.
5684 // We also need to recover the saved info because we'll need to recover it later.
5685 //
5686 // So we would call swap_p*_plug_and_saved once to recover the object info; then call
5687 // it again to recover the artificial gap.
5688 void swap_pre_plug_and_saved()
5689 {
5690 gap_reloc_pair temp;
5691 memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5692 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5693 saved_pre_plug_reloc = temp;
5694 }
5695
5696 void swap_post_plug_and_saved()
5697 {
5698 gap_reloc_pair temp;
5699 memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5700 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5701 saved_post_plug_reloc = temp;
5702 }
5703
5704 void swap_pre_plug_and_saved_for_profiler()
5705 {
5706 gap_reloc_pair temp;
5707 memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5708 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5709 saved_pre_plug = temp;
5710 }
5711
5712 void swap_post_plug_and_saved_for_profiler()
5713 {
5714 gap_reloc_pair temp;
5715 memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5716 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5717 saved_post_plug = temp;
5718 }
5719
5720 // We should think about whether it's really necessary to have to copy back the pre plug
5721 // info since it was already copied during compacting plugs. But if a plug doesn't move
5722 // by >= 3 ptr size (the size of gap_reloc_pair), it means we'd have to recover pre plug info.
5723 void recover_plug_info()
5724 {
5725 if (saved_pre_p)
5726 {
5727 if (gc_heap::settings.compaction)
5728 {
5729 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix",
5730 first,
5731 &saved_pre_plug_reloc,
5732 saved_pre_plug_info_reloc_start));
5733 memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5734 }
5735 else
5736 {
5737 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix",
5738 first,
5739 &saved_pre_plug,
5740 (first - sizeof (plug_and_gap))));
5741 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5742 }
5743 }
5744
5745 if (saved_post_p)
5746 {
5747 if (gc_heap::settings.compaction)
5748 {
5749 dprintf (3, ("%Ix: REC Post: %Ix-%Ix",
5750 first,
5751 &saved_post_plug_reloc,
5752 saved_post_plug_info_start));
5753 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5754 }
5755 else
5756 {
5757 dprintf (3, ("%Ix: REC Post: %Ix-%Ix",
5758 first,
5759 &saved_post_plug,
5760 saved_post_plug_info_start));
5761 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5762 }
5763 }
5764 }
5765};
5766
5767
5768void gc_mechanisms::init_mechanisms()
5769{
5770 condemned_generation = 0;
5771 promotion = FALSE;//TRUE;
5772 compaction = TRUE;
5773#ifdef FEATURE_LOH_COMPACTION
5774 loh_compaction = gc_heap::should_compact_loh();
5775#else
5776 loh_compaction = FALSE;
5777#endif //FEATURE_LOH_COMPACTION
5778 heap_expansion = FALSE;
5779 concurrent = FALSE;
5780 demotion = FALSE;
5781 elevation_reduced = FALSE;
5782 found_finalizers = FALSE;
5783#ifdef BACKGROUND_GC
5784 background_p = recursive_gc_sync::background_running_p() != FALSE;
5785 allocations_allowed = TRUE;
5786#endif //BACKGROUND_GC
5787
5788 entry_memory_load = 0;
5789 exit_memory_load = 0;
5790
5791#ifdef STRESS_HEAP
5792 stress_induced = FALSE;
5793#endif // STRESS_HEAP
5794}
5795
5796void gc_mechanisms::first_init()
5797{
5798 gc_index = 0;
5799 gen0_reduction_count = 0;
5800 should_lock_elevation = FALSE;
5801 elevation_locked_count = 0;
5802 reason = reason_empty;
5803#ifdef BACKGROUND_GC
5804 pause_mode = gc_heap::gc_can_use_concurrent ? pause_interactive : pause_batch;
5805#ifdef _DEBUG
5806 int debug_pause_mode = static_cast<int>(GCConfig::GetLatencyMode());
5807 if (debug_pause_mode >= 0)
5808 {
5809 assert (debug_pause_mode <= pause_sustained_low_latency);
5810 pause_mode = (gc_pause_mode)debug_pause_mode;
5811 }
5812#endif //_DEBUG
5813#else //BACKGROUND_GC
5814 pause_mode = pause_batch;
5815#endif //BACKGROUND_GC
5816
5817 init_mechanisms();
5818}
5819
5820void gc_mechanisms::record (gc_history_global* history)
5821{
5822#ifdef MULTIPLE_HEAPS
5823 history->num_heaps = gc_heap::n_heaps;
5824#else
5825 history->num_heaps = 1;
5826#endif //MULTIPLE_HEAPS
5827
5828 history->condemned_generation = condemned_generation;
5829 history->gen0_reduction_count = gen0_reduction_count;
5830 history->reason = reason;
5831 history->pause_mode = (int)pause_mode;
5832 history->mem_pressure = entry_memory_load;
5833 history->global_mechanims_p = 0;
5834
5835 // start setting the boolean values.
5836 if (concurrent)
5837 history->set_mechanism_p (global_concurrent);
5838
5839 if (compaction)
5840 history->set_mechanism_p (global_compaction);
5841
5842 if (promotion)
5843 history->set_mechanism_p (global_promotion);
5844
5845 if (demotion)
5846 history->set_mechanism_p (global_demotion);
5847
5848 if (card_bundles)
5849 history->set_mechanism_p (global_card_bundles);
5850
5851 if (elevation_reduced)
5852 history->set_mechanism_p (global_elevation);
5853}
5854
5855/**********************************
5856 called at the beginning of GC to fix the allocated size to
5857 what is really allocated, or to turn the free area into an unused object
5858 It needs to be called after all of the other allocation contexts have been
5859 fixed since it relies on alloc_allocated.
5860 ********************************/
5861
5862//for_gc_p indicates that the work is being done for GC,
5863//as opposed to concurrent heap verification
5864void gc_heap::fix_youngest_allocation_area (BOOL for_gc_p)
5865{
5866 UNREFERENCED_PARAMETER(for_gc_p);
5867
5868 // The gen 0 alloc context is never used for allocation in the allocator path. It's
5869 // still used in the allocation path during GCs.
5870 assert (generation_allocation_pointer (youngest_generation) == nullptr);
5871 assert (generation_allocation_limit (youngest_generation) == nullptr);
5872 heap_segment_allocated (ephemeral_heap_segment) = alloc_allocated;
5873}
5874
5875void gc_heap::fix_large_allocation_area (BOOL for_gc_p)
5876{
5877 UNREFERENCED_PARAMETER(for_gc_p);
5878
5879#ifdef _DEBUG
5880 alloc_context* acontext =
5881#endif // _DEBUG
5882 generation_alloc_context (large_object_generation);
5883 assert (acontext->alloc_ptr == 0);
5884 assert (acontext->alloc_limit == 0);
5885#if 0
5886 dprintf (3, ("Large object alloc context: ptr: %Ix, limit %Ix",
5887 (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5888 fix_allocation_context (acontext, FALSE, get_alignment_constant (FALSE));
5889 if (for_gc_p)
5890 {
5891 acontext->alloc_ptr = 0;
5892 acontext->alloc_limit = acontext->alloc_ptr;
5893 }
5894#endif //0
5895}
5896
5897//for_gc_p indicates that the work is being done for GC,
5898//as opposed to concurrent heap verification
5899void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
5900 int align_const)
5901{
5902 dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
5903 (size_t)acontext,
5904 (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5905
5906 if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) ||
5907 !for_gc_p)
5908 {
5909 uint8_t* point = acontext->alloc_ptr;
5910 if (point != 0)
5911 {
5912 size_t size = (acontext->alloc_limit - acontext->alloc_ptr);
5913 // the allocation area was from the free list
5914 // it was shortened by Align (min_obj_size) to make room for
5915 // at least the shortest unused object
5916 size += Align (min_obj_size, align_const);
5917 assert ((size >= Align (min_obj_size)));
5918
5919 dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point,
5920 (size_t)point + size ));
5921 make_unused_array (point, size);
5922
5923 if (for_gc_p)
5924 {
5925 generation_free_obj_space (generation_of (0)) += size;
5926 alloc_contexts_used ++;
5927 }
5928 }
5929 }
5930 else if (for_gc_p)
5931 {
5932 alloc_allocated = acontext->alloc_ptr;
5933 assert (heap_segment_allocated (ephemeral_heap_segment) <=
5934 heap_segment_committed (ephemeral_heap_segment));
5935 alloc_contexts_used ++;
5936 }
5937
5938 if (for_gc_p)
5939 {
5940 // We need to update the alloc_bytes to reflect the portion that we have not used
5941 acontext->alloc_bytes -= (acontext->alloc_limit - acontext->alloc_ptr);
5942 acontext->alloc_ptr = 0;
5943 acontext->alloc_limit = acontext->alloc_ptr;
5944 }
5945}
5946
5947//used by the heap verification for concurrent gc.
5948//it nulls out the words set by fix_allocation_context for heap_verification
5949void repair_allocation (gc_alloc_context* acontext, void*)
5950{
5951 uint8_t* point = acontext->alloc_ptr;
5952
5953 if (point != 0)
5954 {
5955 dprintf (3, ("Clearing [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5956 (size_t)acontext->alloc_limit+Align(min_obj_size)));
5957 memclr (acontext->alloc_ptr - plug_skew,
5958 (acontext->alloc_limit - acontext->alloc_ptr)+Align (min_obj_size));
5959 }
5960}
5961
5962void void_allocation (gc_alloc_context* acontext, void*)
5963{
5964 uint8_t* point = acontext->alloc_ptr;
5965
5966 if (point != 0)
5967 {
5968 dprintf (3, ("Void [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
5969 (size_t)acontext->alloc_limit+Align(min_obj_size)));
5970 acontext->alloc_ptr = 0;
5971 acontext->alloc_limit = acontext->alloc_ptr;
5972 }
5973}
5974
5975void gc_heap::repair_allocation_contexts (BOOL repair_p)
5976{
5977 GCToEEInterface::GcEnumAllocContexts (repair_p ? repair_allocation : void_allocation, NULL);
5978}
5979
5980struct fix_alloc_context_args
5981{
5982 BOOL for_gc_p;
5983 void* heap;
5984};
5985
5986void fix_alloc_context (gc_alloc_context* acontext, void* param)
5987{
5988 fix_alloc_context_args* args = (fix_alloc_context_args*)param;
5989 g_theGCHeap->FixAllocContext(acontext, (void*)(size_t)(args->for_gc_p), args->heap);
5990}
5991
5992void gc_heap::fix_allocation_contexts (BOOL for_gc_p)
5993{
5994 fix_alloc_context_args args;
5995 args.for_gc_p = for_gc_p;
5996 args.heap = __this;
5997
5998 GCToEEInterface::GcEnumAllocContexts(fix_alloc_context, &args);
5999 fix_youngest_allocation_area(for_gc_p);
6000 fix_large_allocation_area(for_gc_p);
6001}
6002
6003void gc_heap::fix_older_allocation_area (generation* older_gen)
6004{
6005 heap_segment* older_gen_seg = generation_allocation_segment (older_gen);
6006 if (generation_allocation_limit (older_gen) !=
6007 heap_segment_plan_allocated (older_gen_seg))
6008 {
6009 uint8_t* point = generation_allocation_pointer (older_gen);
6010
6011 size_t size = (generation_allocation_limit (older_gen) -
6012 generation_allocation_pointer (older_gen));
6013 if (size != 0)
6014 {
6015 assert ((size >= Align (min_obj_size)));
6016 dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, (size_t)point+size));
6017 make_unused_array (point, size);
6018 if (size >= min_free_list)
6019 {
6020 generation_allocator (older_gen)->thread_item_front (point, size);
6021 add_gen_free (older_gen->gen_num, size);
6022 generation_free_list_space (older_gen) += size;
6023 }
6024 else
6025 {
6026 generation_free_obj_space (older_gen) += size;
6027 }
6028 }
6029 }
6030 else
6031 {
6032 assert (older_gen_seg != ephemeral_heap_segment);
6033 heap_segment_plan_allocated (older_gen_seg) =
6034 generation_allocation_pointer (older_gen);
6035 generation_allocation_limit (older_gen) =
6036 generation_allocation_pointer (older_gen);
6037 }
6038
6039 generation_allocation_pointer (older_gen) = 0;
6040 generation_allocation_limit (older_gen) = 0;
6041}
6042
6043void gc_heap::set_allocation_heap_segment (generation* gen)
6044{
6045 uint8_t* p = generation_allocation_start (gen);
6046 assert (p);
6047 heap_segment* seg = generation_allocation_segment (gen);
6048 if (in_range_for_segment (p, seg))
6049 return;
6050
6051 // try ephemeral heap segment in case of heap expansion
6052 seg = ephemeral_heap_segment;
6053 if (!in_range_for_segment (p, seg))
6054 {
6055 seg = heap_segment_rw (generation_start_segment (gen));
6056
6057 PREFIX_ASSUME(seg != NULL);
6058
6059 while (!in_range_for_segment (p, seg))
6060 {
6061 seg = heap_segment_next_rw (seg);
6062 PREFIX_ASSUME(seg != NULL);
6063 }
6064 }
6065
6066 generation_allocation_segment (gen) = seg;
6067}
6068
6069void gc_heap::reset_allocation_pointers (generation* gen, uint8_t* start)
6070{
6071 assert (start);
6072 assert (Align ((size_t)start) == (size_t)start);
6073 generation_allocation_start (gen) = start;
6074 generation_allocation_pointer (gen) = 0;//start + Align (min_obj_size);
6075 generation_allocation_limit (gen) = 0;//generation_allocation_pointer (gen);
6076 set_allocation_heap_segment (gen);
6077}
6078
6079#ifdef BACKGROUND_GC
6080//TODO BACKGROUND_GC this is for test only
6081void
6082gc_heap::disallow_new_allocation (int gen_number)
6083{
6084 UNREFERENCED_PARAMETER(gen_number);
6085 settings.allocations_allowed = FALSE;
6086}
6087void
6088gc_heap::allow_new_allocation (int gen_number)
6089{
6090 UNREFERENCED_PARAMETER(gen_number);
6091 settings.allocations_allowed = TRUE;
6092}
6093
6094#endif //BACKGROUND_GC
6095
6096bool gc_heap::new_allocation_allowed (int gen_number)
6097{
6098#ifdef BACKGROUND_GC
6099 //TODO BACKGROUND_GC this is for test only
6100 if (!settings.allocations_allowed)
6101 {
6102 dprintf (2, ("new allocation not allowed"));
6103 return FALSE;
6104 }
6105#endif //BACKGROUND_GC
6106
6107 if (dd_new_allocation (dynamic_data_of (gen_number)) < 0)
6108 {
6109 if (gen_number != 0)
6110 {
6111 // For LOH we will give it more budget before we try a GC.
6112 if (settings.concurrent)
6113 {
6114 dynamic_data* dd2 = dynamic_data_of (max_generation + 1 );
6115
6116 if (dd_new_allocation (dd2) <= (ptrdiff_t)(-2 * dd_desired_allocation (dd2)))
6117 {
6118 return TRUE;
6119 }
6120 }
6121 }
6122 return FALSE;
6123 }
6124#ifndef MULTIPLE_HEAPS
6125 else if ((settings.pause_mode != pause_no_gc) && (gen_number == 0))
6126 {
6127 dprintf (3, ("evaluating allocation rate"));
6128 dynamic_data* dd0 = dynamic_data_of (0);
6129 if ((allocation_running_amount - dd_new_allocation (dd0)) >
6130 dd_min_size (dd0))
6131 {
6132 uint32_t ctime = GCToOSInterface::GetLowPrecisionTimeStamp();
6133 if ((ctime - allocation_running_time) > 1000)
6134 {
6135 dprintf (2, (">1s since last gen0 gc"));
6136 return FALSE;
6137 }
6138 else
6139 {
6140 allocation_running_amount = dd_new_allocation (dd0);
6141 }
6142 }
6143 }
6144#endif //MULTIPLE_HEAPS
6145 return TRUE;
6146}
6147
6148inline
6149ptrdiff_t gc_heap::get_desired_allocation (int gen_number)
6150{
6151 return dd_desired_allocation (dynamic_data_of (gen_number));
6152}
6153
6154inline
6155ptrdiff_t gc_heap::get_new_allocation (int gen_number)
6156{
6157 return dd_new_allocation (dynamic_data_of (gen_number));
6158}
6159
6160//return the amount allocated so far in gen_number
6161inline
6162ptrdiff_t gc_heap::get_allocation (int gen_number)
6163{
6164 dynamic_data* dd = dynamic_data_of (gen_number);
6165
6166 return dd_desired_allocation (dd) - dd_new_allocation (dd);
6167}
6168
6169inline
6170BOOL grow_mark_stack (mark*& m, size_t& len, size_t init_len)
6171{
6172 size_t new_size = max (init_len, 2*len);
6173 mark* tmp = new (nothrow) mark [new_size];
6174 if (tmp)
6175 {
6176 memcpy (tmp, m, len * sizeof (mark));
6177 delete m;
6178 m = tmp;
6179 len = new_size;
6180 return TRUE;
6181 }
6182 else
6183 {
6184 dprintf (1, ("Failed to allocate %Id bytes for mark stack", (len * sizeof (mark))));
6185 return FALSE;
6186 }
6187}
6188
6189inline
6190uint8_t* pinned_plug (mark* m)
6191{
6192 return m->first;
6193}
6194
6195inline
6196size_t& pinned_len (mark* m)
6197{
6198 return m->len;
6199}
6200
6201inline
6202void set_new_pin_info (mark* m, uint8_t* pin_free_space_start)
6203{
6204 m->len = pinned_plug (m) - pin_free_space_start;
6205#ifdef SHORT_PLUGS
6206 m->allocation_context_start_region = pin_free_space_start;
6207#endif //SHORT_PLUGS
6208}
6209
6210#ifdef SHORT_PLUGS
6211inline
6212uint8_t*& pin_allocation_context_start_region (mark* m)
6213{
6214 return m->allocation_context_start_region;
6215}
6216
6217uint8_t* get_plug_start_in_saved (uint8_t* old_loc, mark* pinned_plug_entry)
6218{
6219 uint8_t* saved_pre_plug_info = (uint8_t*)(pinned_plug_entry->get_pre_plug_reloc_info());
6220 uint8_t* plug_start_in_saved = saved_pre_plug_info + (old_loc - (pinned_plug (pinned_plug_entry) - sizeof (plug_and_gap)));
6221 //dprintf (1, ("detected a very short plug: %Ix before PP %Ix, pad %Ix",
6222 // old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6223 dprintf (1, ("EP: %Ix(%Ix), %Ix", old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6224 return plug_start_in_saved;
6225}
6226
6227inline
6228void set_padding_in_expand (uint8_t* old_loc,
6229 BOOL set_padding_on_saved_p,
6230 mark* pinned_plug_entry)
6231{
6232 if (set_padding_on_saved_p)
6233 {
6234 set_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6235 }
6236 else
6237 {
6238 set_plug_padded (old_loc);
6239 }
6240}
6241
6242inline
6243void clear_padding_in_expand (uint8_t* old_loc,
6244 BOOL set_padding_on_saved_p,
6245 mark* pinned_plug_entry)
6246{
6247 if (set_padding_on_saved_p)
6248 {
6249 clear_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6250 }
6251 else
6252 {
6253 clear_plug_padded (old_loc);
6254 }
6255}
6256#endif //SHORT_PLUGS
6257
6258void gc_heap::reset_pinned_queue()
6259{
6260 mark_stack_tos = 0;
6261 mark_stack_bos = 0;
6262}
6263
6264void gc_heap::reset_pinned_queue_bos()
6265{
6266 mark_stack_bos = 0;
6267}
6268
6269// last_pinned_plug is only for asserting purpose.
6270void gc_heap::merge_with_last_pinned_plug (uint8_t* last_pinned_plug, size_t plug_size)
6271{
6272 if (last_pinned_plug)
6273 {
6274 mark& last_m = mark_stack_array[mark_stack_tos - 1];
6275 assert (last_pinned_plug == last_m.first);
6276 if (last_m.saved_post_p)
6277 {
6278 last_m.saved_post_p = FALSE;
6279 dprintf (3, ("setting last plug %Ix post to false", last_m.first));
6280 // We need to recover what the gap has overwritten.
6281 memcpy ((last_m.first + last_m.len - sizeof (plug_and_gap)), &(last_m.saved_post_plug), sizeof (gap_reloc_pair));
6282 }
6283 last_m.len += plug_size;
6284 dprintf (3, ("recovered the last part of plug %Ix, setting its plug size to %Ix", last_m.first, last_m.len));
6285 }
6286}
6287
6288void gc_heap::set_allocator_next_pin (uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6289{
6290 dprintf (3, ("sanp: ptr: %Ix, limit: %Ix", alloc_pointer, alloc_limit));
6291 dprintf (3, ("oldest %Id: %Ix", mark_stack_bos, pinned_plug (oldest_pin())));
6292 if (!(pinned_plug_que_empty_p()))
6293 {
6294 mark* oldest_entry = oldest_pin();
6295 uint8_t* plug = pinned_plug (oldest_entry);
6296 if ((plug >= alloc_pointer) && (plug < alloc_limit))
6297 {
6298 alloc_limit = pinned_plug (oldest_entry);
6299 dprintf (3, ("now setting alloc context: %Ix->%Ix(%Id)",
6300 alloc_pointer, alloc_limit, (alloc_limit - alloc_pointer)));
6301 }
6302 }
6303}
6304
6305void gc_heap::set_allocator_next_pin (generation* gen)
6306{
6307 dprintf (3, ("SANP: gen%d, ptr; %Ix, limit: %Ix", gen->gen_num, generation_allocation_pointer (gen), generation_allocation_limit (gen)));
6308 if (!(pinned_plug_que_empty_p()))
6309 {
6310 mark* oldest_entry = oldest_pin();
6311 uint8_t* plug = pinned_plug (oldest_entry);
6312 if ((plug >= generation_allocation_pointer (gen)) &&
6313 (plug < generation_allocation_limit (gen)))
6314 {
6315 generation_allocation_limit (gen) = pinned_plug (oldest_entry);
6316 dprintf (3, ("SANP: get next pin free space in gen%d for alloc: %Ix->%Ix(%Id)",
6317 gen->gen_num,
6318 generation_allocation_pointer (gen), generation_allocation_limit (gen),
6319 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
6320 }
6321 else
6322 assert (!((plug < generation_allocation_pointer (gen)) &&
6323 (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
6324 }
6325}
6326
6327// After we set the info, we increase tos.
6328void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6329{
6330 UNREFERENCED_PARAMETER(last_pinned_plug);
6331
6332 mark& m = mark_stack_array[mark_stack_tos];
6333 assert (m.first == last_pinned_plug);
6334
6335 m.len = plug_len;
6336 mark_stack_tos++;
6337 set_allocator_next_pin (alloc_pointer, alloc_limit);
6338}
6339
6340// After we set the info, we increase tos.
6341void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, generation* gen)
6342{
6343 UNREFERENCED_PARAMETER(last_pinned_plug);
6344
6345 mark& m = mark_stack_array[mark_stack_tos];
6346 assert (m.first == last_pinned_plug);
6347
6348 m.len = plug_len;
6349 mark_stack_tos++;
6350 assert (gen != 0);
6351 // Why are we checking here? gen is never 0.
6352 if (gen != 0)
6353 {
6354 set_allocator_next_pin (gen);
6355 }
6356}
6357
6358size_t gc_heap::deque_pinned_plug ()
6359{
6360 dprintf (3, ("dequed: %Id", mark_stack_bos));
6361 size_t m = mark_stack_bos;
6362 mark_stack_bos++;
6363 return m;
6364}
6365
6366inline
6367mark* gc_heap::pinned_plug_of (size_t bos)
6368{
6369 return &mark_stack_array [ bos ];
6370}
6371
6372inline
6373mark* gc_heap::oldest_pin ()
6374{
6375 return pinned_plug_of (mark_stack_bos);
6376}
6377
6378inline
6379BOOL gc_heap::pinned_plug_que_empty_p ()
6380{
6381 return (mark_stack_bos == mark_stack_tos);
6382}
6383
6384inline
6385mark* gc_heap::before_oldest_pin()
6386{
6387 if (mark_stack_bos >= 1)
6388 return pinned_plug_of (mark_stack_bos-1);
6389 else
6390 return 0;
6391}
6392
6393inline
6394BOOL gc_heap::ephemeral_pointer_p (uint8_t* o)
6395{
6396 return ((o >= ephemeral_low) && (o < ephemeral_high));
6397}
6398
6399#ifdef MH_SC_MARK
6400inline
6401int& gc_heap::mark_stack_busy()
6402{
6403 return g_mark_stack_busy [(heap_number+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
6404}
6405#endif //MH_SC_MARK
6406
6407void gc_heap::make_mark_stack (mark* arr)
6408{
6409 reset_pinned_queue();
6410 mark_stack_array = arr;
6411 mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6412#ifdef MH_SC_MARK
6413 mark_stack_busy() = 0;
6414#endif //MH_SC_MARK
6415}
6416
6417#ifdef BACKGROUND_GC
6418inline
6419size_t& gc_heap::bpromoted_bytes(int thread)
6420{
6421#ifdef MULTIPLE_HEAPS
6422 return g_bpromoted [thread*16];
6423#else //MULTIPLE_HEAPS
6424 UNREFERENCED_PARAMETER(thread);
6425 return g_bpromoted;
6426#endif //MULTIPLE_HEAPS
6427}
6428
6429void gc_heap::make_background_mark_stack (uint8_t** arr)
6430{
6431 background_mark_stack_array = arr;
6432 background_mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6433 background_mark_stack_tos = arr;
6434}
6435
6436void gc_heap::make_c_mark_list (uint8_t** arr)
6437{
6438 c_mark_list = arr;
6439 c_mark_list_index = 0;
6440 c_mark_list_length = 1 + (OS_PAGE_SIZE / MIN_OBJECT_SIZE);
6441}
6442#endif //BACKGROUND_GC
6443
6444
6445#ifdef CARD_BUNDLE
6446
6447// The card bundle keeps track of groups of card words.
6448static const size_t card_bundle_word_width = 32;
6449
6450// How do we express the fact that 32 bits (card_word_width) is one uint32_t?
6451static const size_t card_bundle_size = (size_t)(GC_PAGE_SIZE / (sizeof(uint32_t)*card_bundle_word_width));
6452
6453inline
6454size_t card_bundle_word (size_t cardb)
6455{
6456 return cardb / card_bundle_word_width;
6457}
6458
6459inline
6460uint32_t card_bundle_bit (size_t cardb)
6461{
6462 return (uint32_t)(cardb % card_bundle_word_width);
6463}
6464
6465size_t align_cardw_on_bundle (size_t cardw)
6466{
6467 return ((size_t)(cardw + card_bundle_size - 1) & ~(card_bundle_size - 1 ));
6468}
6469
6470// Get the card bundle representing a card word
6471size_t cardw_card_bundle (size_t cardw)
6472{
6473 return cardw / card_bundle_size;
6474}
6475
6476// Get the first card word in a card bundle
6477size_t card_bundle_cardw (size_t cardb)
6478{
6479 return cardb * card_bundle_size;
6480}
6481
6482// Clear the specified card bundle
6483void gc_heap::card_bundle_clear (size_t cardb)
6484{
6485 card_bundle_table [card_bundle_word (cardb)] &= ~(1 << card_bundle_bit (cardb));
6486 dprintf (1,("Cleared card bundle %Ix [%Ix, %Ix[", cardb, (size_t)card_bundle_cardw (cardb),
6487 (size_t)card_bundle_cardw (cardb+1)));
6488}
6489
6490void gc_heap::card_bundle_set (size_t cardb)
6491{
6492 if (!card_bundle_set_p (cardb))
6493 {
6494 card_bundle_table [card_bundle_word (cardb)] |= (1 << card_bundle_bit (cardb));
6495 }
6496}
6497
6498// Set the card bundle bits between start_cardb and end_cardb
6499void gc_heap::card_bundles_set (size_t start_cardb, size_t end_cardb)
6500{
6501 if (start_cardb == end_cardb)
6502 {
6503 card_bundle_set(start_cardb);
6504 return;
6505 }
6506
6507 size_t start_word = card_bundle_word (start_cardb);
6508 size_t end_word = card_bundle_word (end_cardb);
6509
6510 if (start_word < end_word)
6511 {
6512 // Set the partial words
6513 card_bundle_table [start_word] |= highbits (~0u, card_bundle_bit (start_cardb));
6514
6515 if (card_bundle_bit (end_cardb))
6516 card_bundle_table [end_word] |= lowbits (~0u, card_bundle_bit (end_cardb));
6517
6518 // Set the full words
6519 for (size_t i = start_word + 1; i < end_word; i++)
6520 card_bundle_table [i] = ~0u;
6521 }
6522 else
6523 {
6524 card_bundle_table [start_word] |= (highbits (~0u, card_bundle_bit (start_cardb)) &
6525 lowbits (~0u, card_bundle_bit (end_cardb)));
6526 }
6527}
6528
6529// Indicates whether the specified bundle is set.
6530BOOL 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
6535// Returns the size (in bytes) of a card bundle representing the region from 'from' to 'end'
6536size_t size_card_bundle_of (uint8_t* from, uint8_t* end)
6537{
6538 // Number of heap bytes represented by a card bundle word
6539 size_t cbw_span = card_size * card_word_width * card_bundle_size * card_bundle_word_width;
6540
6541 // Align the start of the region down
6542 from = (uint8_t*)((size_t)from & ~(cbw_span - 1));
6543
6544 // Align the end of the region up
6545 end = (uint8_t*)((size_t)(end + (cbw_span - 1)) & ~(cbw_span - 1));
6546
6547 // Make sure they're really aligned
6548 assert (((size_t)from & (cbw_span - 1)) == 0);
6549 assert (((size_t)end & (cbw_span - 1)) == 0);
6550
6551 return ((end - from) / cbw_span) * sizeof (uint32_t);
6552}
6553
6554// Takes a pointer to a card bundle table and an address, and returns a pointer that represents
6555// where a theoretical card bundle table that represents every address (starting from 0) would
6556// start if the bundle word representing the address were to be located at the pointer passed in.
6557// The returned 'translated' pointer makes it convenient/fast to calculate where the card bundle
6558// for a given address is using a simple shift operation on the address.
6559uint32_t* translate_card_bundle_table (uint32_t* cb, uint8_t* lowest_address)
6560{
6561 // The number of bytes of heap memory represented by a card bundle word
6562 const size_t heap_bytes_for_bundle_word = card_size * card_word_width * card_bundle_size * card_bundle_word_width;
6563
6564 // Each card bundle word is 32 bits
6565 return (uint32_t*)((uint8_t*)cb - (((size_t)lowest_address / heap_bytes_for_bundle_word) * sizeof (uint32_t)));
6566}
6567
6568void gc_heap::enable_card_bundles ()
6569{
6570 if (can_use_write_watch_for_card_table() && (!card_bundles_enabled()))
6571 {
6572 dprintf (1, ("Enabling card bundles"));
6573
6574 // We initially set all of the card bundles
6575 card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
6576 cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
6577 settings.card_bundles = TRUE;
6578 }
6579}
6580
6581BOOL gc_heap::card_bundles_enabled ()
6582{
6583 return settings.card_bundles;
6584}
6585
6586#endif // CARD_BUNDLE
6587
6588#if defined (_TARGET_AMD64_)
6589#define brick_size ((size_t)4096)
6590#else
6591#define brick_size ((size_t)2048)
6592#endif //_TARGET_AMD64_
6593
6594inline
6595size_t gc_heap::brick_of (uint8_t* add)
6596{
6597 return (size_t)(add - lowest_address) / brick_size;
6598}
6599
6600inline
6601uint8_t* gc_heap::brick_address (size_t brick)
6602{
6603 return lowest_address + (brick_size * brick);
6604}
6605
6606
6607void gc_heap::clear_brick_table (uint8_t* from, uint8_t* end)
6608{
6609 for (size_t i = brick_of (from);i < brick_of (end); i++)
6610 brick_table[i] = 0;
6611}
6612
6613//codes for the brick entries:
6614//entry == 0 -> not assigned
6615//entry >0 offset is entry-1
6616//entry <0 jump back entry bricks
6617
6618
6619inline
6620void gc_heap::set_brick (size_t index, ptrdiff_t val)
6621{
6622 if (val < -32767)
6623 {
6624 val = -32767;
6625 }
6626 assert (val < 32767);
6627 if (val >= 0)
6628 brick_table [index] = (short)val+1;
6629 else
6630 brick_table [index] = (short)val;
6631}
6632
6633inline
6634int gc_heap::get_brick_entry (size_t index)
6635{
6636#ifdef MULTIPLE_HEAPS
6637 return VolatileLoadWithoutBarrier(&brick_table [index]);
6638#else
6639 return brick_table[index];
6640#endif
6641}
6642
6643
6644inline
6645uint8_t* align_on_brick (uint8_t* add)
6646{
6647 return (uint8_t*)((size_t)(add + brick_size - 1) & ~(brick_size - 1));
6648}
6649
6650inline
6651uint8_t* align_lower_brick (uint8_t* add)
6652{
6653 return (uint8_t*)(((size_t)add) & ~(brick_size - 1));
6654}
6655
6656size_t size_brick_of (uint8_t* from, uint8_t* end)
6657{
6658 assert (((size_t)from & (brick_size-1)) == 0);
6659 assert (((size_t)end & (brick_size-1)) == 0);
6660
6661 return ((end - from) / brick_size) * sizeof (short);
6662}
6663
6664inline
6665uint8_t* gc_heap::card_address (size_t card)
6666{
6667 return (uint8_t*) (card_size * card);
6668}
6669
6670inline
6671size_t gc_heap::card_of ( uint8_t* object)
6672{
6673 return (size_t)(object) / card_size;
6674}
6675
6676inline
6677size_t gc_heap::card_to_brick (size_t card)
6678{
6679 return brick_of (card_address (card));
6680}
6681
6682inline
6683uint8_t* align_on_card (uint8_t* add)
6684{
6685 return (uint8_t*)((size_t)(add + card_size - 1) & ~(card_size - 1 ));
6686}
6687inline
6688uint8_t* align_on_card_word (uint8_t* add)
6689{
6690 return (uint8_t*) ((size_t)(add + (card_size*card_word_width)-1) & ~(card_size*card_word_width - 1));
6691}
6692
6693inline
6694uint8_t* align_lower_card (uint8_t* add)
6695{
6696 return (uint8_t*)((size_t)add & ~(card_size-1));
6697}
6698
6699inline
6700void gc_heap::clear_card (size_t card)
6701{
6702 card_table [card_word (card)] =
6703 (card_table [card_word (card)] & ~(1 << card_bit (card)));
6704 dprintf (3,("Cleared card %Ix [%Ix, %Ix[", card, (size_t)card_address (card),
6705 (size_t)card_address (card+1)));
6706}
6707
6708inline
6709void gc_heap::set_card (size_t card)
6710{
6711 size_t word = card_word (card);
6712 card_table[word] = (card_table [word] | (1 << card_bit (card)));
6713
6714#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
6715 // Also set the card bundle that corresponds to the card
6716 size_t bundle_to_set = cardw_card_bundle(word);
6717
6718 card_bundle_set(bundle_to_set);
6719
6720 dprintf (3,("Set card %Ix [%Ix, %Ix[ and bundle %Ix", card, (size_t)card_address (card), (size_t)card_address (card+1), bundle_to_set));
6721 assert(card_bundle_set_p(bundle_to_set) != 0);
6722#endif
6723}
6724
6725inline
6726BOOL gc_heap::card_set_p (size_t card)
6727{
6728 return ( card_table [ card_word (card) ] & (1 << card_bit (card)));
6729}
6730
6731// Returns the number of DWORDs in the card table that cover the
6732// range of addresses [from, end[.
6733size_t count_card_of (uint8_t* from, uint8_t* end)
6734{
6735 return card_word (gcard_of (end - 1)) - card_word (gcard_of (from)) + 1;
6736}
6737
6738// Returns the number of bytes to allocate for a card table
6739// that covers the range of addresses [from, end[.
6740size_t size_card_of (uint8_t* from, uint8_t* end)
6741{
6742 return count_card_of (from, end) * sizeof(uint32_t);
6743}
6744
6745// We don't store seg_mapping_table in card_table_info because there's only always one view.
6746class card_table_info
6747{
6748public:
6749 unsigned recount;
6750 uint8_t* lowest_address;
6751 uint8_t* highest_address;
6752 short* brick_table;
6753
6754#ifdef CARD_BUNDLE
6755 uint32_t* card_bundle_table;
6756#endif //CARD_BUNDLE
6757
6758 // mark_array is always at the end of the data structure because we
6759 // want to be able to make one commit call for everything before it.
6760#ifdef MARK_ARRAY
6761 uint32_t* mark_array;
6762#endif //MARK_ARRAY
6763
6764 size_t size;
6765 uint32_t* next_card_table;
6766};
6767
6768//These are accessors on untranslated cardtable
6769inline
6770unsigned& card_table_refcount (uint32_t* c_table)
6771{
6772 return *(unsigned*)((char*)c_table - sizeof (card_table_info));
6773}
6774
6775inline
6776uint8_t*& card_table_lowest_address (uint32_t* c_table)
6777{
6778 return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->lowest_address;
6779}
6780
6781uint32_t* translate_card_table (uint32_t* ct)
6782{
6783 return (uint32_t*)((uint8_t*)ct - card_word (gcard_of (card_table_lowest_address (ct))) * sizeof(uint32_t));
6784}
6785
6786inline
6787uint8_t*& card_table_highest_address (uint32_t* c_table)
6788{
6789 return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->highest_address;
6790}
6791
6792inline
6793short*& card_table_brick_table (uint32_t* c_table)
6794{
6795 return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->brick_table;
6796}
6797
6798#ifdef CARD_BUNDLE
6799inline
6800uint32_t*& card_table_card_bundle_table (uint32_t* c_table)
6801{
6802 return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->card_bundle_table;
6803}
6804#endif //CARD_BUNDLE
6805
6806#ifdef MARK_ARRAY
6807/* Support for mark_array */
6808
6809inline
6810uint32_t*& card_table_mark_array (uint32_t* c_table)
6811{
6812 return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->mark_array;
6813}
6814
6815#ifdef BIT64
6816#define mark_bit_pitch ((size_t)16)
6817#else
6818#define mark_bit_pitch ((size_t)8)
6819#endif // BIT64
6820#define mark_word_width ((size_t)32)
6821#define mark_word_size (mark_word_width * mark_bit_pitch)
6822
6823inline
6824uint8_t* align_on_mark_bit (uint8_t* add)
6825{
6826 return (uint8_t*)((size_t)(add + (mark_bit_pitch - 1)) & ~(mark_bit_pitch - 1));
6827}
6828
6829inline
6830uint8_t* align_lower_mark_bit (uint8_t* add)
6831{
6832 return (uint8_t*)((size_t)(add) & ~(mark_bit_pitch - 1));
6833}
6834
6835inline
6836BOOL is_aligned_on_mark_word (uint8_t* add)
6837{
6838 return ((size_t)add == ((size_t)(add) & ~(mark_word_size - 1)));
6839}
6840
6841inline
6842uint8_t* align_on_mark_word (uint8_t* add)
6843{
6844 return (uint8_t*)((size_t)(add + mark_word_size - 1) & ~(mark_word_size - 1));
6845}
6846
6847inline
6848uint8_t* align_lower_mark_word (uint8_t* add)
6849{
6850 return (uint8_t*)((size_t)(add) & ~(mark_word_size - 1));
6851}
6852
6853inline
6854size_t mark_bit_of (uint8_t* add)
6855{
6856 return ((size_t)add / mark_bit_pitch);
6857}
6858
6859inline
6860unsigned int mark_bit_bit (size_t mark_bit)
6861{
6862 return (unsigned int)(mark_bit % mark_word_width);
6863}
6864
6865inline
6866size_t mark_bit_word (size_t mark_bit)
6867{
6868 return (mark_bit / mark_word_width);
6869}
6870
6871inline
6872size_t mark_word_of (uint8_t* add)
6873{
6874 return ((size_t)add) / mark_word_size;
6875}
6876
6877uint8_t* mark_word_address (size_t wd)
6878{
6879 return (uint8_t*)(wd*mark_word_size);
6880}
6881
6882uint8_t* mark_bit_address (size_t mark_bit)
6883{
6884 return (uint8_t*)(mark_bit*mark_bit_pitch);
6885}
6886
6887inline
6888size_t mark_bit_bit_of (uint8_t* add)
6889{
6890 return (((size_t)add / mark_bit_pitch) % mark_word_width);
6891}
6892
6893inline
6894unsigned int gc_heap::mark_array_marked(uint8_t* add)
6895{
6896 return mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add));
6897}
6898
6899inline
6900BOOL gc_heap::is_mark_bit_set (uint8_t* add)
6901{
6902 return (mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add)));
6903}
6904
6905inline
6906void gc_heap::mark_array_set_marked (uint8_t* add)
6907{
6908 size_t index = mark_word_of (add);
6909 uint32_t val = (1 << mark_bit_bit_of (add));
6910#ifdef MULTIPLE_HEAPS
6911 Interlocked::Or (&(mark_array [index]), val);
6912#else
6913 mark_array [index] |= val;
6914#endif
6915}
6916
6917inline
6918void gc_heap::mark_array_clear_marked (uint8_t* add)
6919{
6920 mark_array [mark_word_of (add)] &= ~(1 << mark_bit_bit_of (add));
6921}
6922
6923size_t size_mark_array_of (uint8_t* from, uint8_t* end)
6924{
6925 assert (((size_t)from & ((mark_word_size)-1)) == 0);
6926 assert (((size_t)end & ((mark_word_size)-1)) == 0);
6927 return sizeof (uint32_t)*(((end - from) / mark_word_size));
6928}
6929
6930//In order to eliminate the lowest_address in the mark array
6931//computations (mark_word_of, etc) mark_array is offset
6932// according to the lowest_address.
6933uint32_t* translate_mark_array (uint32_t* ma)
6934{
6935 return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_gc_lowest_address));
6936}
6937
6938// from and end must be page aligned addresses.
6939void gc_heap::clear_mark_array (uint8_t* from, uint8_t* end, BOOL check_only/*=TRUE*/
6940#ifdef FEATURE_BASICFREEZE
6941 , BOOL read_only/*=FALSE*/
6942#endif // FEATURE_BASICFREEZE
6943 )
6944{
6945 if(!gc_can_use_concurrent)
6946 return;
6947
6948#ifdef FEATURE_BASICFREEZE
6949 if (!read_only)
6950#endif // FEATURE_BASICFREEZE
6951 {
6952 assert (from == align_on_mark_word (from));
6953 }
6954 assert (end == align_on_mark_word (end));
6955
6956#ifdef BACKGROUND_GC
6957 uint8_t* current_lowest_address = background_saved_lowest_address;
6958 uint8_t* current_highest_address = background_saved_highest_address;
6959#else
6960 uint8_t* current_lowest_address = lowest_address;
6961 uint8_t* current_highest_address = highest_address;
6962#endif //BACKGROUND_GC
6963
6964 //there is a possibility of the addresses to be
6965 //outside of the covered range because of a newly allocated
6966 //large object segment
6967 if ((end <= current_highest_address) && (from >= current_lowest_address))
6968 {
6969 size_t beg_word = mark_word_of (align_on_mark_word (from));
6970 MAYBE_UNUSED_VAR(beg_word);
6971 //align end word to make sure to cover the address
6972 size_t end_word = mark_word_of (align_on_mark_word (end));
6973 MAYBE_UNUSED_VAR(end_word);
6974 dprintf (3, ("Calling clearing mark array [%Ix, %Ix[ for addresses [%Ix, %Ix[(%s)",
6975 (size_t)mark_word_address (beg_word),
6976 (size_t)mark_word_address (end_word),
6977 (size_t)from, (size_t)end,
6978 (check_only ? "check_only" : "clear")));
6979 if (!check_only)
6980 {
6981 uint8_t* op = from;
6982 while (op < mark_word_address (beg_word))
6983 {
6984 mark_array_clear_marked (op);
6985 op += mark_bit_pitch;
6986 }
6987
6988 memset (&mark_array[beg_word], 0, (end_word - beg_word)*sizeof (uint32_t));
6989 }
6990#ifdef _DEBUG
6991 else
6992 {
6993 //Beware, it is assumed that the mark array word straddling
6994 //start has been cleared before
6995 //verify that the array is empty.
6996 size_t markw = mark_word_of (align_on_mark_word (from));
6997 size_t markw_end = mark_word_of (align_on_mark_word (end));
6998 while (markw < markw_end)
6999 {
7000 assert (!(mark_array [markw]));
7001 markw++;
7002 }
7003 uint8_t* p = mark_word_address (markw_end);
7004 while (p < end)
7005 {
7006 assert (!(mark_array_marked (p)));
7007 p++;
7008 }
7009 }
7010#endif //_DEBUG
7011 }
7012}
7013#endif //MARK_ARRAY
7014
7015//These work on untranslated card tables
7016inline
7017uint32_t*& card_table_next (uint32_t* c_table)
7018{
7019 return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->next_card_table;
7020}
7021
7022inline
7023size_t& card_table_size (uint32_t* c_table)
7024{
7025 return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->size;
7026}
7027
7028void own_card_table (uint32_t* c_table)
7029{
7030 card_table_refcount (c_table) += 1;
7031}
7032
7033void destroy_card_table (uint32_t* c_table);
7034
7035void delete_next_card_table (uint32_t* c_table)
7036{
7037 uint32_t* n_table = card_table_next (c_table);
7038 if (n_table)
7039 {
7040 if (card_table_next (n_table))
7041 {
7042 delete_next_card_table (n_table);
7043 }
7044 if (card_table_refcount (n_table) == 0)
7045 {
7046 destroy_card_table (n_table);
7047 card_table_next (c_table) = 0;
7048 }
7049 }
7050}
7051
7052void release_card_table (uint32_t* c_table)
7053{
7054 assert (card_table_refcount (c_table) >0);
7055 card_table_refcount (c_table) -= 1;
7056 if (card_table_refcount (c_table) == 0)
7057 {
7058 delete_next_card_table (c_table);
7059 if (card_table_next (c_table) == 0)
7060 {
7061 destroy_card_table (c_table);
7062 // sever the link from the parent
7063 if (&g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))] == c_table)
7064 {
7065 g_gc_card_table = 0;
7066
7067#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7068 g_gc_card_bundle_table = 0;
7069#endif
7070#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7071 SoftwareWriteWatch::StaticClose();
7072#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7073 }
7074 else
7075 {
7076 uint32_t* p_table = &g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))];
7077 if (p_table)
7078 {
7079 while (p_table && (card_table_next (p_table) != c_table))
7080 p_table = card_table_next (p_table);
7081 card_table_next (p_table) = 0;
7082 }
7083 }
7084 }
7085 }
7086}
7087
7088void destroy_card_table (uint32_t* c_table)
7089{
7090// delete (uint32_t*)&card_table_refcount(c_table);
7091
7092 GCToOSInterface::VirtualRelease (&card_table_refcount(c_table), card_table_size(c_table));
7093 dprintf (2, ("Table Virtual Free : %Ix", (size_t)&card_table_refcount(c_table)));
7094}
7095
7096uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end)
7097{
7098 assert (g_gc_lowest_address == start);
7099 assert (g_gc_highest_address == end);
7100
7101 uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7102
7103 size_t bs = size_brick_of (start, end);
7104 size_t cs = size_card_of (start, end);
7105#ifdef MARK_ARRAY
7106 size_t ms = (gc_can_use_concurrent ?
7107 size_mark_array_of (start, end) :
7108 0);
7109#else
7110 size_t ms = 0;
7111#endif //MARK_ARRAY
7112
7113 size_t cb = 0;
7114
7115#ifdef CARD_BUNDLE
7116 if (can_use_write_watch_for_card_table())
7117 {
7118 cb = size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address);
7119#ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7120 // If we're not manually managing the card bundles, we will need to use OS write
7121 // watch APIs over this region to track changes.
7122 virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
7123#endif
7124 }
7125#endif //CARD_BUNDLE
7126
7127 size_t wws = 0;
7128#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7129 size_t sw_ww_table_offset = 0;
7130 if (gc_can_use_concurrent)
7131 {
7132 size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7133 sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7134 wws = sw_ww_table_offset - sw_ww_size_before_table + SoftwareWriteWatch::GetTableByteSize(start, end);
7135 }
7136#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7137
7138#ifdef GROWABLE_SEG_MAPPING_TABLE
7139 size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7140 size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7141 size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7142
7143 st += (st_table_offset_aligned - st_table_offset);
7144#else //GROWABLE_SEG_MAPPING_TABLE
7145 size_t st = 0;
7146#endif //GROWABLE_SEG_MAPPING_TABLE
7147
7148 // it is impossible for alloc_size to overflow due bounds on each of
7149 // its components.
7150 size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7151 uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags);
7152
7153 if (!mem)
7154 return 0;
7155
7156 dprintf (2, ("Init - Card table alloc for %Id bytes: [%Ix, %Ix[",
7157 alloc_size, (size_t)mem, (size_t)(mem+alloc_size)));
7158
7159 // mark array will be committed separately (per segment).
7160 size_t commit_size = alloc_size - ms;
7161
7162 if (!GCToOSInterface::VirtualCommit (mem, commit_size))
7163 {
7164 dprintf (2, ("Card table commit failed"));
7165 GCToOSInterface::VirtualRelease (mem, alloc_size);
7166 return 0;
7167 }
7168
7169 // initialize the ref count
7170 uint32_t* ct = (uint32_t*)(mem+sizeof (card_table_info));
7171 card_table_refcount (ct) = 0;
7172 card_table_lowest_address (ct) = start;
7173 card_table_highest_address (ct) = end;
7174 card_table_brick_table (ct) = (short*)((uint8_t*)ct + cs);
7175 card_table_size (ct) = alloc_size;
7176 card_table_next (ct) = 0;
7177
7178#ifdef CARD_BUNDLE
7179 card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7180
7181#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7182 g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), g_gc_lowest_address);
7183#endif
7184
7185#endif //CARD_BUNDLE
7186
7187#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7188 if (gc_can_use_concurrent)
7189 {
7190 SoftwareWriteWatch::InitializeUntranslatedTable(mem + sw_ww_table_offset, start);
7191 }
7192#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7193
7194#ifdef GROWABLE_SEG_MAPPING_TABLE
7195 seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7196 seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table -
7197 size_seg_mapping_table_of (0, (align_lower_segment (g_gc_lowest_address))));
7198#endif //GROWABLE_SEG_MAPPING_TABLE
7199
7200#ifdef MARK_ARRAY
7201 if (gc_can_use_concurrent)
7202 card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7203 else
7204 card_table_mark_array (ct) = NULL;
7205#endif //MARK_ARRAY
7206
7207 return translate_card_table(ct);
7208}
7209
7210void gc_heap::set_fgm_result (failure_get_memory f, size_t s, BOOL loh_p)
7211{
7212#ifdef MULTIPLE_HEAPS
7213 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
7214 {
7215 gc_heap* hp = gc_heap::g_heaps [hn];
7216 hp->fgm_result.set_fgm (f, s, loh_p);
7217 }
7218#else //MULTIPLE_HEAPS
7219 fgm_result.set_fgm (f, s, loh_p);
7220#endif //MULTIPLE_HEAPS
7221}
7222
7223//returns 0 for success, -1 otherwise
7224// We are doing all the decommitting here because we want to make sure we have
7225// enough memory to do so - if we do this during copy_brick_card_table and
7226// and fail to decommit it would make the failure case very complicated to
7227// handle. This way we can waste some decommit if we call this multiple
7228// times before the next FGC but it's easier to handle the failure case.
7229int gc_heap::grow_brick_card_tables (uint8_t* start,
7230 uint8_t* end,
7231 size_t size,
7232 heap_segment* new_seg,
7233 gc_heap* hp,
7234 BOOL loh_p)
7235{
7236 uint8_t* la = g_gc_lowest_address;
7237 uint8_t* ha = g_gc_highest_address;
7238 uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address);
7239 uint8_t* saved_g_highest_address = max (end, g_gc_highest_address);
7240 seg_mapping* new_seg_mapping_table = nullptr;
7241#ifdef BACKGROUND_GC
7242 // This value is only for logging purpose - it's not necessarily exactly what we
7243 // would commit for mark array but close enough for diagnostics purpose.
7244 size_t logging_ma_commit_size = size_mark_array_of (0, (uint8_t*)size);
7245#endif //BACKGROUND_GC
7246
7247 // See if the address is already covered
7248 if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address))
7249 {
7250 {
7251 //modify the higest address so the span covered
7252 //is twice the previous one.
7253 uint8_t* top = (uint8_t*)0 + Align (GCToOSInterface::GetVirtualMemoryLimit());
7254 // On non-Windows systems, we get only an approximate value that can possibly be
7255 // slightly lower than the saved_g_highest_address.
7256 // In such case, we set the top to the saved_g_highest_address so that the
7257 // card and brick tables always cover the whole new range.
7258 if (top < saved_g_highest_address)
7259 {
7260 top = saved_g_highest_address;
7261 }
7262 size_t ps = ha-la;
7263#ifdef BIT64
7264 if (ps > (uint64_t)200*1024*1024*1024)
7265 ps += (uint64_t)100*1024*1024*1024;
7266 else
7267#endif // BIT64
7268 ps *= 2;
7269
7270 if (saved_g_lowest_address < g_gc_lowest_address)
7271 {
7272 if (ps > (size_t)g_gc_lowest_address)
7273 saved_g_lowest_address = (uint8_t*)(size_t)OS_PAGE_SIZE;
7274 else
7275 {
7276 assert (((size_t)g_gc_lowest_address - ps) >= OS_PAGE_SIZE);
7277 saved_g_lowest_address = min (saved_g_lowest_address, (g_gc_lowest_address - ps));
7278 }
7279 }
7280
7281 if (saved_g_highest_address > g_gc_highest_address)
7282 {
7283 saved_g_highest_address = max ((saved_g_lowest_address + ps), saved_g_highest_address);
7284 if (saved_g_highest_address > top)
7285 saved_g_highest_address = top;
7286 }
7287 }
7288 dprintf (GC_TABLE_LOG, ("Growing card table [%Ix, %Ix[",
7289 (size_t)saved_g_lowest_address,
7290 (size_t)saved_g_highest_address));
7291
7292 bool write_barrier_updated = false;
7293 uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7294 uint32_t* saved_g_card_table = g_gc_card_table;
7295
7296#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7297 uint32_t* saved_g_card_bundle_table = g_gc_card_bundle_table;
7298#endif
7299
7300 uint32_t* ct = 0;
7301 uint32_t* translated_ct = 0;
7302 short* bt = 0;
7303
7304 size_t cs = size_card_of (saved_g_lowest_address, saved_g_highest_address);
7305 size_t bs = size_brick_of (saved_g_lowest_address, saved_g_highest_address);
7306
7307#ifdef MARK_ARRAY
7308 size_t ms = (gc_heap::gc_can_use_concurrent ?
7309 size_mark_array_of (saved_g_lowest_address, saved_g_highest_address) :
7310 0);
7311#else
7312 size_t ms = 0;
7313#endif //MARK_ARRAY
7314
7315 size_t cb = 0;
7316
7317#ifdef CARD_BUNDLE
7318 if (can_use_write_watch_for_card_table())
7319 {
7320 cb = size_card_bundle_of (saved_g_lowest_address, saved_g_highest_address);
7321
7322#ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7323 // If we're not manually managing the card bundles, we will need to use OS write
7324 // watch APIs over this region to track changes.
7325 virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
7326#endif
7327 }
7328#endif //CARD_BUNDLE
7329
7330 size_t wws = 0;
7331#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7332 size_t sw_ww_table_offset = 0;
7333 if (gc_can_use_concurrent)
7334 {
7335 size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7336 sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7337 wws =
7338 sw_ww_table_offset -
7339 sw_ww_size_before_table +
7340 SoftwareWriteWatch::GetTableByteSize(saved_g_lowest_address, saved_g_highest_address);
7341 }
7342#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7343
7344#ifdef GROWABLE_SEG_MAPPING_TABLE
7345 size_t st = size_seg_mapping_table_of (saved_g_lowest_address, saved_g_highest_address);
7346 size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7347 size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7348 st += (st_table_offset_aligned - st_table_offset);
7349#else //GROWABLE_SEG_MAPPING_TABLE
7350 size_t st = 0;
7351#endif //GROWABLE_SEG_MAPPING_TABLE
7352
7353 // it is impossible for alloc_size to overflow due bounds on each of
7354 // its components.
7355 size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7356 dprintf (GC_TABLE_LOG, ("card table: %Id; brick table: %Id; card bundle: %Id; sw ww table: %Id; seg table: %Id; mark array: %Id",
7357 cs, bs, cb, wws, st, ms));
7358
7359 uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags);
7360
7361 if (!mem)
7362 {
7363 set_fgm_result (fgm_grow_table, alloc_size, loh_p);
7364 goto fail;
7365 }
7366
7367 dprintf (GC_TABLE_LOG, ("Table alloc for %Id bytes: [%Ix, %Ix[",
7368 alloc_size, (size_t)mem, (size_t)((uint8_t*)mem+alloc_size)));
7369
7370 {
7371 // mark array will be committed separately (per segment).
7372 size_t commit_size = alloc_size - ms;
7373
7374 if (!GCToOSInterface::VirtualCommit (mem, commit_size))
7375 {
7376 dprintf (GC_TABLE_LOG, ("Table commit failed"));
7377 set_fgm_result (fgm_commit_table, commit_size, loh_p);
7378 goto fail;
7379 }
7380 }
7381
7382 ct = (uint32_t*)(mem + sizeof (card_table_info));
7383 card_table_refcount (ct) = 0;
7384 card_table_lowest_address (ct) = saved_g_lowest_address;
7385 card_table_highest_address (ct) = saved_g_highest_address;
7386 card_table_next (ct) = &g_gc_card_table[card_word (gcard_of (la))];
7387
7388 //clear the card table
7389/*
7390 memclr ((uint8_t*)ct,
7391 (((saved_g_highest_address - saved_g_lowest_address)*sizeof (uint32_t) /
7392 (card_size * card_word_width))
7393 + sizeof (uint32_t)));
7394*/
7395
7396 bt = (short*)((uint8_t*)ct + cs);
7397
7398 // No initialization needed, will be done in copy_brick_card
7399
7400 card_table_brick_table (ct) = bt;
7401
7402#ifdef CARD_BUNDLE
7403 card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7404 //set all bundle to look at all of the cards
7405 memset(card_table_card_bundle_table (ct), 0xFF, cb);
7406#endif //CARD_BUNDLE
7407
7408#ifdef GROWABLE_SEG_MAPPING_TABLE
7409 {
7410 new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7411 new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table -
7412 size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address))));
7413 memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7414 &seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7415 size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address));
7416
7417 // new_seg_mapping_table gets assigned to seg_mapping_table at the bottom of this function,
7418 // not here. The reason for this is that, if we fail at mark array committing (OOM) and we've
7419 // already switched seg_mapping_table to point to the new mapping table, we'll decommit it and
7420 // run into trouble. By not assigning here, we're making sure that we will not change seg_mapping_table
7421 // if an OOM occurs.
7422 }
7423#endif //GROWABLE_SEG_MAPPING_TABLE
7424
7425#ifdef MARK_ARRAY
7426 if(gc_can_use_concurrent)
7427 card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7428 else
7429 card_table_mark_array (ct) = NULL;
7430#endif //MARK_ARRAY
7431
7432 translated_ct = translate_card_table (ct);
7433
7434 dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix",
7435 (size_t)ct, (size_t)translated_ct, (size_t)new_seg_mapping_table, (size_t)card_table_mark_array (ct)));
7436
7437#ifdef BACKGROUND_GC
7438 if (hp->should_commit_mark_array())
7439 {
7440 dprintf (GC_TABLE_LOG, ("new low: %Ix, new high: %Ix, latest mark array is %Ix(translate: %Ix)",
7441 saved_g_lowest_address, saved_g_highest_address,
7442 card_table_mark_array (ct),
7443 translate_mark_array (card_table_mark_array (ct))));
7444 uint32_t* new_mark_array = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, saved_g_lowest_address));
7445 if (!commit_new_mark_array_global (new_mark_array))
7446 {
7447 dprintf (GC_TABLE_LOG, ("failed to commit portions in the mark array for existing segments"));
7448 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7449 goto fail;
7450 }
7451
7452 if (!commit_mark_array_new_seg (hp, new_seg, translated_ct, saved_g_lowest_address))
7453 {
7454 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg"));
7455 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7456 goto fail;
7457 }
7458 }
7459 else
7460 {
7461 clear_commit_flag_global();
7462 }
7463#endif //BACKGROUND_GC
7464
7465#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7466 if (gc_can_use_concurrent)
7467 {
7468 // The current design of software write watch requires that the runtime is suspended during resize. Suspending
7469 // on resize is preferred because it is a far less frequent operation than GetWriteWatch() / ResetWriteWatch().
7470 // Suspending here allows copying dirty state from the old table into the new table, and not have to merge old
7471 // table info lazily as done for card tables.
7472
7473 // Either this thread was the thread that did the suspension which means we are suspended; or this is called
7474 // from a GC thread which means we are in a blocking GC and also suspended.
7475 bool is_runtime_suspended = GCToEEInterface::IsGCThread();
7476 if (!is_runtime_suspended)
7477 {
7478 // Note on points where the runtime is suspended anywhere in this function. Upon an attempt to suspend the
7479 // runtime, a different thread may suspend first, causing this thread to block at the point of the suspend call.
7480 // So, at any suspend point, externally visible state needs to be consistent, as code that depends on that state
7481 // may run while this thread is blocked. This includes updates to g_gc_card_table, g_gc_lowest_address, and
7482 // g_gc_highest_address.
7483 suspend_EE();
7484 }
7485
7486 g_gc_card_table = translated_ct;
7487
7488#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7489 g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address);
7490#endif
7491
7492 SoftwareWriteWatch::SetResizedUntranslatedTable(
7493 mem + sw_ww_table_offset,
7494 saved_g_lowest_address,
7495 saved_g_highest_address);
7496
7497 seg_mapping_table = new_seg_mapping_table;
7498
7499 // Since the runtime is already suspended, update the write barrier here as well.
7500 // This passes a bool telling whether we need to switch to the post
7501 // grow version of the write barrier. This test tells us if the new
7502 // segment was allocated at a lower address than the old, requiring
7503 // that we start doing an upper bounds check in the write barrier.
7504 g_gc_lowest_address = saved_g_lowest_address;
7505 g_gc_highest_address = saved_g_highest_address;
7506 stomp_write_barrier_resize(true, la != saved_g_lowest_address);
7507 write_barrier_updated = true;
7508
7509 if (!is_runtime_suspended)
7510 {
7511 restart_EE();
7512 }
7513 }
7514 else
7515#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7516 {
7517 g_gc_card_table = translated_ct;
7518
7519#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7520 g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address);
7521#endif
7522 }
7523
7524 if (!write_barrier_updated)
7525 {
7526 seg_mapping_table = new_seg_mapping_table;
7527 GCToOSInterface::FlushProcessWriteBuffers();
7528 g_gc_lowest_address = saved_g_lowest_address;
7529 g_gc_highest_address = saved_g_highest_address;
7530
7531 // This passes a bool telling whether we need to switch to the post
7532 // grow version of the write barrier. This test tells us if the new
7533 // segment was allocated at a lower address than the old, requiring
7534 // that we start doing an upper bounds check in the write barrier.
7535 // This will also suspend the runtime if the write barrier type needs
7536 // to be changed, so we are doing this after all global state has
7537 // been updated. See the comment above suspend_EE() above for more
7538 // info.
7539 stomp_write_barrier_resize(GCToEEInterface::IsGCThread(), la != saved_g_lowest_address);
7540 }
7541
7542 return 0;
7543
7544fail:
7545 //cleanup mess and return -1;
7546
7547 if (mem)
7548 {
7549 assert(g_gc_card_table == saved_g_card_table);
7550
7551#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7552 assert(g_gc_card_bundle_table == saved_g_card_bundle_table);
7553#endif
7554
7555 //delete (uint32_t*)((uint8_t*)ct - sizeof(card_table_info));
7556 if (!GCToOSInterface::VirtualRelease (mem, alloc_size))
7557 {
7558 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualRelease failed"));
7559 assert (!"release failed");
7560 }
7561 }
7562
7563 return -1;
7564 }
7565 else
7566 {
7567#ifdef BACKGROUND_GC
7568 if (hp->should_commit_mark_array())
7569 {
7570 dprintf (GC_TABLE_LOG, ("in range new seg %Ix, mark_array is %Ix", new_seg, hp->mark_array));
7571 if (!commit_mark_array_new_seg (hp, new_seg))
7572 {
7573 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg in range"));
7574 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7575 return -1;
7576 }
7577 }
7578#endif //BACKGROUND_GC
7579 }
7580
7581 return 0;
7582}
7583
7584//copy all of the arrays managed by the card table for a page aligned range
7585void gc_heap::copy_brick_card_range (uint8_t* la, uint32_t* old_card_table,
7586 short* old_brick_table,
7587 heap_segment* seg,
7588 uint8_t* start, uint8_t* end)
7589{
7590 ptrdiff_t brick_offset = brick_of (start) - brick_of (la);
7591
7592
7593 dprintf (2, ("copying tables for range [%Ix %Ix[", (size_t)start, (size_t)end));
7594
7595 // copy brick table
7596 short* brick_start = &brick_table [brick_of (start)];
7597 if (old_brick_table)
7598 {
7599 // segments are always on page boundaries
7600 memcpy (brick_start, &old_brick_table[brick_offset],
7601 size_brick_of (start, end));
7602
7603 }
7604 else
7605 {
7606 // This is a large heap, just clear the brick table
7607 }
7608
7609 uint32_t* old_ct = &old_card_table[card_word (card_of (la))];
7610#ifdef MARK_ARRAY
7611#ifdef BACKGROUND_GC
7612 UNREFERENCED_PARAMETER(seg);
7613 if (recursive_gc_sync::background_running_p())
7614 {
7615 uint32_t* old_mark_array = card_table_mark_array (old_ct);
7616
7617 // We don't need to go through all the card tables here because
7618 // we only need to copy from the GC version of the mark array - when we
7619 // mark (even in allocate_large_object) we always use that mark array.
7620 if ((card_table_highest_address (old_ct) >= start) &&
7621 (card_table_lowest_address (old_ct) <= end))
7622 {
7623 if ((background_saved_highest_address >= start) &&
7624 (background_saved_lowest_address <= end))
7625 {
7626 //copy the mark bits
7627 // segments are always on page boundaries
7628 uint8_t* m_start = max (background_saved_lowest_address, start);
7629 uint8_t* m_end = min (background_saved_highest_address, end);
7630 memcpy (&mark_array[mark_word_of (m_start)],
7631 &old_mark_array[mark_word_of (m_start) - mark_word_of (la)],
7632 size_mark_array_of (m_start, m_end));
7633 }
7634 }
7635 else
7636 {
7637 //only large segments can be out of range
7638 assert (old_brick_table == 0);
7639 }
7640 }
7641#else //BACKGROUND_GC
7642 assert (seg != 0);
7643 clear_mark_array (start, heap_segment_committed(seg));
7644#endif //BACKGROUND_GC
7645#endif //MARK_ARRAY
7646
7647 // n way merge with all of the card table ever used in between
7648 uint32_t* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]);
7649
7650 assert (ct);
7651 while (card_table_next (old_ct) != ct)
7652 {
7653 //copy if old card table contained [start, end[
7654 if ((card_table_highest_address (ct) >= end) &&
7655 (card_table_lowest_address (ct) <= start))
7656 {
7657 // or the card_tables
7658
7659 size_t start_word = card_word (card_of (start));
7660
7661 uint32_t* dest = &card_table[start_word];
7662 uint32_t* src = &((translate_card_table (ct))[start_word]);
7663 ptrdiff_t count = count_card_of (start, end);
7664 for (int x = 0; x < count; x++)
7665 {
7666 *dest |= *src;
7667
7668#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7669 if (*src != 0)
7670 {
7671 card_bundle_set(cardw_card_bundle(start_word+x));
7672 }
7673#endif
7674
7675 dest++;
7676 src++;
7677 }
7678 }
7679 ct = card_table_next (ct);
7680 }
7681}
7682
7683//initialize all of the arrays managed by the card table for a page aligned range when an existing ro segment becomes in range
7684void gc_heap::init_brick_card_range (heap_segment* seg)
7685{
7686 dprintf (2, ("initialising tables for range [%Ix %Ix[",
7687 (size_t)heap_segment_mem (seg),
7688 (size_t)heap_segment_allocated (seg)));
7689
7690 // initialize the brick table
7691 for (size_t b = brick_of (heap_segment_mem (seg));
7692 b < brick_of (align_on_brick (heap_segment_allocated (seg)));
7693 b++)
7694 {
7695 set_brick (b, -1);
7696 }
7697
7698#ifdef MARK_ARRAY
7699 if (recursive_gc_sync::background_running_p() && (seg->flags & heap_segment_flags_ma_committed))
7700 {
7701 assert (seg != 0);
7702 clear_mark_array (heap_segment_mem (seg), heap_segment_committed(seg));
7703 }
7704#endif //MARK_ARRAY
7705
7706 clear_card_for_addresses (heap_segment_mem (seg),
7707 heap_segment_allocated (seg));
7708}
7709
7710void gc_heap::copy_brick_card_table()
7711{
7712 uint8_t* la = lowest_address;
7713 uint8_t* ha = highest_address;
7714 MAYBE_UNUSED_VAR(ha);
7715 uint32_t* old_card_table = card_table;
7716 short* old_brick_table = brick_table;
7717
7718 assert (la == card_table_lowest_address (&old_card_table[card_word (card_of (la))]));
7719 assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))]));
7720
7721 /* todo: Need a global lock for this */
7722 uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
7723 own_card_table (ct);
7724 card_table = translate_card_table (ct);
7725 /* End of global lock */
7726 highest_address = card_table_highest_address (ct);
7727 lowest_address = card_table_lowest_address (ct);
7728
7729 brick_table = card_table_brick_table (ct);
7730
7731#ifdef MARK_ARRAY
7732 if (gc_can_use_concurrent)
7733 {
7734 mark_array = translate_mark_array (card_table_mark_array (ct));
7735 assert (mark_word_of (g_gc_highest_address) ==
7736 mark_word_of (align_on_mark_word (g_gc_highest_address)));
7737 }
7738 else
7739 mark_array = NULL;
7740#endif //MARK_ARRAY
7741
7742#ifdef CARD_BUNDLE
7743#if defined(MARK_ARRAY) && defined(_DEBUG)
7744 size_t cb_end = (size_t)((uint8_t*)card_table_card_bundle_table (ct) + size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address));
7745#ifdef GROWABLE_SEG_MAPPING_TABLE
7746 size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7747 size_t cb_end_aligned = align_for_seg_mapping_table (cb_end);
7748 st += (cb_end_aligned - cb_end);
7749#else //GROWABLE_SEG_MAPPING_TABLE
7750 size_t st = 0;
7751#endif //GROWABLE_SEG_MAPPING_TABLE
7752#endif //MARK_ARRAY && _DEBUG
7753 card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
7754
7755 // Ensure that the word that represents g_gc_lowest_address in the translated table is located at the
7756 // start of the untranslated table.
7757 assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
7758 card_table_card_bundle_table (ct));
7759
7760 //set the card table if we are in a heap growth scenario
7761 if (card_bundles_enabled())
7762 {
7763 card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
7764 cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
7765 }
7766 //check if we need to turn on card_bundles.
7767#ifdef MULTIPLE_HEAPS
7768 // use INT64 arithmetic here because of possible overflow on 32p
7769 uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*gc_heap::n_heaps;
7770#else
7771 // use INT64 arithmetic here because of possible overflow on 32p
7772 uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
7773#endif //MULTIPLE_HEAPS
7774 if (reserved_memory >= th)
7775 {
7776 enable_card_bundles();
7777 }
7778
7779#endif //CARD_BUNDLE
7780
7781 // for each of the segments and heaps, copy the brick table and
7782 // or the card table
7783 heap_segment* seg = generation_start_segment (generation_of (max_generation));
7784 while (seg)
7785 {
7786 if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7787 {
7788 //check if it became in range
7789 if ((heap_segment_reserved (seg) > lowest_address) &&
7790 (heap_segment_mem (seg) < highest_address))
7791 {
7792 set_ro_segment_in_range (seg);
7793 }
7794 }
7795 else
7796 {
7797
7798 uint8_t* end = align_on_page (heap_segment_allocated (seg));
7799 copy_brick_card_range (la, old_card_table,
7800 old_brick_table,
7801 seg,
7802 align_lower_page (heap_segment_mem (seg)),
7803 end);
7804 }
7805 seg = heap_segment_next (seg);
7806 }
7807
7808 seg = generation_start_segment (large_object_generation);
7809 while (seg)
7810 {
7811 if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7812 {
7813 //check if it became in range
7814 if ((heap_segment_reserved (seg) > lowest_address) &&
7815 (heap_segment_mem (seg) < highest_address))
7816 {
7817 set_ro_segment_in_range (seg);
7818 }
7819 }
7820 else
7821 {
7822 uint8_t* end = align_on_page (heap_segment_allocated (seg));
7823 copy_brick_card_range (la, old_card_table,
7824 0,
7825 seg,
7826 align_lower_page (heap_segment_mem (seg)),
7827 end);
7828 }
7829 seg = heap_segment_next (seg);
7830 }
7831
7832 release_card_table (&old_card_table[card_word (card_of(la))]);
7833}
7834
7835#ifdef FEATURE_BASICFREEZE
7836BOOL gc_heap::insert_ro_segment (heap_segment* seg)
7837{
7838 enter_spin_lock (&gc_heap::gc_lock);
7839
7840 if (!gc_heap::seg_table->ensure_space_for_insert ()
7841 || (should_commit_mark_array() && !commit_mark_array_new_seg(__this, seg)))
7842 {
7843 leave_spin_lock(&gc_heap::gc_lock);
7844 return FALSE;
7845 }
7846
7847 //insert at the head of the segment list
7848 generation* gen2 = generation_of (max_generation);
7849 heap_segment* oldhead = generation_start_segment (gen2);
7850 heap_segment_next (seg) = oldhead;
7851 generation_start_segment (gen2) = seg;
7852
7853 seg_table->insert (heap_segment_mem(seg), (size_t)seg);
7854
7855#ifdef SEG_MAPPING_TABLE
7856 seg_mapping_table_add_ro_segment (seg);
7857#endif //SEG_MAPPING_TABLE
7858
7859 //test if in range
7860 if ((heap_segment_reserved (seg) > lowest_address) &&
7861 (heap_segment_mem (seg) < highest_address))
7862 {
7863 set_ro_segment_in_range (seg);
7864 }
7865
7866 FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg), (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), gc_etw_segment_read_only_heap);
7867
7868 leave_spin_lock (&gc_heap::gc_lock);
7869 return TRUE;
7870}
7871
7872// No one is calling this function right now. If this is getting called we need
7873// to take care of decommitting the mark array for it - we will need to remember
7874// which portion of the mark array was committed and only decommit that.
7875void gc_heap::remove_ro_segment (heap_segment* seg)
7876{
7877//clear the mark bits so a new segment allocated in its place will have a clear mark bits
7878#ifdef MARK_ARRAY
7879 if (gc_can_use_concurrent)
7880 {
7881 clear_mark_array (align_lower_mark_word (max (heap_segment_mem (seg), lowest_address)),
7882 align_on_card_word (min (heap_segment_allocated (seg), highest_address)),
7883 false); // read_only segments need the mark clear
7884 }
7885#endif //MARK_ARRAY
7886
7887 enter_spin_lock (&gc_heap::gc_lock);
7888
7889 seg_table->remove ((uint8_t*)seg);
7890
7891#ifdef SEG_MAPPING_TABLE
7892 seg_mapping_table_remove_ro_segment (seg);
7893#endif //SEG_MAPPING_TABLE
7894
7895 // Locate segment (and previous segment) in the list.
7896 generation* gen2 = generation_of (max_generation);
7897 heap_segment* curr_seg = generation_start_segment (gen2);
7898 heap_segment* prev_seg = NULL;
7899
7900 while (curr_seg && curr_seg != seg)
7901 {
7902 prev_seg = curr_seg;
7903 curr_seg = heap_segment_next (curr_seg);
7904 }
7905 assert (curr_seg == seg);
7906
7907 // Patch previous segment (or list head if there is none) to skip the removed segment.
7908 if (prev_seg)
7909 heap_segment_next (prev_seg) = heap_segment_next (curr_seg);
7910 else
7911 generation_start_segment (gen2) = heap_segment_next (curr_seg);
7912
7913 leave_spin_lock (&gc_heap::gc_lock);
7914}
7915#endif //FEATURE_BASICFREEZE
7916
7917BOOL gc_heap::set_ro_segment_in_range (heap_segment* seg)
7918{
7919 //set it in range
7920 seg->flags |= heap_segment_flags_inrange;
7921// init_brick_card_range (seg);
7922 ro_segments_in_range = TRUE;
7923 //right now, segments aren't protected
7924 //unprotect_segment (seg);
7925 return TRUE;
7926}
7927
7928#ifdef MARK_LIST
7929
7930uint8_t** make_mark_list (size_t size)
7931{
7932 uint8_t** mark_list = new (nothrow) uint8_t* [size];
7933 return mark_list;
7934}
7935
7936#define swap(a,b){uint8_t* t; t = a; a = b; b = t;}
7937
7938void verify_qsort_array (uint8_t* *low, uint8_t* *high)
7939{
7940 uint8_t **i = 0;
7941
7942 for (i = low+1; i <= high; i++)
7943 {
7944 if (*i < *(i-1))
7945 {
7946 FATAL_GC_ERROR();
7947 }
7948 }
7949}
7950
7951#ifndef USE_INTROSORT
7952void qsort1( uint8_t* *low, uint8_t* *high, unsigned int depth)
7953{
7954 if (((low + 16) >= high) || (depth > 100))
7955 {
7956 //insertion sort
7957 uint8_t **i, **j;
7958 for (i = low+1; i <= high; i++)
7959 {
7960 uint8_t* val = *i;
7961 for (j=i;j >low && val<*(j-1);j--)
7962 {
7963 *j=*(j-1);
7964 }
7965 *j=val;
7966 }
7967 }
7968 else
7969 {
7970 uint8_t *pivot, **left, **right;
7971
7972 //sort low middle and high
7973 if (*(low+((high-low)/2)) < *low)
7974 swap (*(low+((high-low)/2)), *low);
7975 if (*high < *low)
7976 swap (*low, *high);
7977 if (*high < *(low+((high-low)/2)))
7978 swap (*(low+((high-low)/2)), *high);
7979
7980 swap (*(low+((high-low)/2)), *(high-1));
7981 pivot = *(high-1);
7982 left = low; right = high-1;
7983 while (1) {
7984 while (*(--right) > pivot);
7985 while (*(++left) < pivot);
7986 if (left < right)
7987 {
7988 swap(*left, *right);
7989 }
7990 else
7991 break;
7992 }
7993 swap (*left, *(high-1));
7994 qsort1(low, left-1, depth+1);
7995 qsort1(left+1, high, depth+1);
7996 }
7997}
7998#endif //USE_INTROSORT
7999void rqsort1( uint8_t* *low, uint8_t* *high)
8000{
8001 if ((low + 16) >= high)
8002 {
8003 //insertion sort
8004 uint8_t **i, **j;
8005 for (i = low+1; i <= high; i++)
8006 {
8007 uint8_t* val = *i;
8008 for (j=i;j >low && val>*(j-1);j--)
8009 {
8010 *j=*(j-1);
8011 }
8012 *j=val;
8013 }
8014 }
8015 else
8016 {
8017 uint8_t *pivot, **left, **right;
8018
8019 //sort low middle and high
8020 if (*(low+((high-low)/2)) > *low)
8021 swap (*(low+((high-low)/2)), *low);
8022 if (*high > *low)
8023 swap (*low, *high);
8024 if (*high > *(low+((high-low)/2)))
8025 swap (*(low+((high-low)/2)), *high);
8026
8027 swap (*(low+((high-low)/2)), *(high-1));
8028 pivot = *(high-1);
8029 left = low; right = high-1;
8030 while (1) {
8031 while (*(--right) < pivot);
8032 while (*(++left) > pivot);
8033 if (left < right)
8034 {
8035 swap(*left, *right);
8036 }
8037 else
8038 break;
8039 }
8040 swap (*left, *(high-1));
8041 rqsort1(low, left-1);
8042 rqsort1(left+1, high);
8043 }
8044}
8045
8046#ifdef USE_INTROSORT
8047class introsort
8048{
8049
8050private:
8051 static const int size_threshold = 64;
8052 static const int max_depth = 100;
8053
8054
8055inline static void swap_elements(uint8_t** i,uint8_t** j)
8056 {
8057 uint8_t* t=*i;
8058 *i=*j;
8059 *j=t;
8060 }
8061
8062public:
8063 static void sort (uint8_t** begin, uint8_t** end, int ignored)
8064 {
8065 ignored = 0;
8066 introsort_loop (begin, end, max_depth);
8067 insertionsort (begin, end);
8068 }
8069
8070private:
8071
8072 static void introsort_loop (uint8_t** lo, uint8_t** hi, int depth_limit)
8073 {
8074 while (hi-lo >= size_threshold)
8075 {
8076 if (depth_limit == 0)
8077 {
8078 heapsort (lo, hi);
8079 return;
8080 }
8081 uint8_t** p=median_partition (lo, hi);
8082 depth_limit=depth_limit-1;
8083 introsort_loop (p, hi, depth_limit);
8084 hi=p-1;
8085 }
8086 }
8087
8088 static uint8_t** median_partition (uint8_t** low, uint8_t** high)
8089 {
8090 uint8_t *pivot, **left, **right;
8091
8092 //sort low middle and high
8093 if (*(low+((high-low)/2)) < *low)
8094 swap_elements ((low+((high-low)/2)), low);
8095 if (*high < *low)
8096 swap_elements (low, high);
8097 if (*high < *(low+((high-low)/2)))
8098 swap_elements ((low+((high-low)/2)), high);
8099
8100 swap_elements ((low+((high-low)/2)), (high-1));
8101 pivot = *(high-1);
8102 left = low; right = high-1;
8103 while (1) {
8104 while (*(--right) > pivot);
8105 while (*(++left) < pivot);
8106 if (left < right)
8107 {
8108 swap_elements(left, right);
8109 }
8110 else
8111 break;
8112 }
8113 swap_elements (left, (high-1));
8114 return left;
8115 }
8116
8117
8118 static void insertionsort (uint8_t** lo, uint8_t** hi)
8119 {
8120 for (uint8_t** i=lo+1; i <= hi; i++)
8121 {
8122 uint8_t** j = i;
8123 uint8_t* t = *i;
8124 while((j > lo) && (t <*(j-1)))
8125 {
8126 *j = *(j-1);
8127 j--;
8128 }
8129 *j = t;
8130 }
8131 }
8132
8133 static void heapsort (uint8_t** lo, uint8_t** hi)
8134 {
8135 size_t n = hi - lo + 1;
8136 for (size_t i=n / 2; i >= 1; i--)
8137 {
8138 downheap (i,n,lo);
8139 }
8140 for (size_t i = n; i > 1; i--)
8141 {
8142 swap_elements (lo, lo + i - 1);
8143 downheap(1, i - 1, lo);
8144 }
8145 }
8146
8147 static void downheap (size_t i, size_t n, uint8_t** lo)
8148 {
8149 uint8_t* d = *(lo + i - 1);
8150 size_t child;
8151 while (i <= n / 2)
8152 {
8153 child = 2*i;
8154 if (child < n && *(lo + child - 1)<(*(lo + child)))
8155 {
8156 child++;
8157 }
8158 if (!(d<*(lo + child - 1)))
8159 {
8160 break;
8161 }
8162 *(lo + i - 1) = *(lo + child - 1);
8163 i = child;
8164 }
8165 *(lo + i - 1) = d;
8166 }
8167
8168};
8169
8170#endif //USE_INTROSORT
8171
8172#ifdef MULTIPLE_HEAPS
8173#ifdef PARALLEL_MARK_LIST_SORT
8174void gc_heap::sort_mark_list()
8175{
8176 // if this heap had a mark list overflow, we don't do anything
8177 if (mark_list_index > mark_list_end)
8178 {
8179// printf("sort_mark_list: overflow on heap %d\n", heap_number);
8180 return;
8181 }
8182
8183 // if any other heap had a mark list overflow, we fake one too,
8184 // so we don't use an incomplete mark list by mistake
8185 for (int i = 0; i < n_heaps; i++)
8186 {
8187 if (g_heaps[i]->mark_list_index > g_heaps[i]->mark_list_end)
8188 {
8189 mark_list_index = mark_list_end + 1;
8190// printf("sort_mark_list: overflow on heap %d\n", i);
8191 return;
8192 }
8193 }
8194
8195// unsigned long start = GetCycleCount32();
8196
8197 dprintf (3, ("Sorting mark lists"));
8198 if (mark_list_index > mark_list)
8199 _sort (mark_list, mark_list_index - 1, 0);
8200
8201// 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);
8202// start = GetCycleCount32();
8203
8204 // first set the pieces for all heaps to empty
8205 int heap_num;
8206 for (heap_num = 0; heap_num < n_heaps; heap_num++)
8207 {
8208 mark_list_piece_start[heap_num] = NULL;
8209 mark_list_piece_end[heap_num] = NULL;
8210 }
8211
8212 uint8_t** x = mark_list;
8213
8214// predicate means: x is still within the mark list, and within the bounds of this heap
8215#define predicate(x) (((x) < mark_list_index) && (*(x) < heap->ephemeral_high))
8216
8217 heap_num = -1;
8218 while (x < mark_list_index)
8219 {
8220 gc_heap* heap;
8221 // find the heap x points into - searching cyclically from the last heap,
8222 // because in many cases the right heap is the next one or comes soon after
8223 int last_heap_num = heap_num;
8224 MAYBE_UNUSED_VAR(last_heap_num);
8225 do
8226 {
8227 heap_num++;
8228 if (heap_num >= n_heaps)
8229 heap_num = 0;
8230 assert(heap_num != last_heap_num); // we should always find the heap - infinite loop if not!
8231 heap = g_heaps[heap_num];
8232 }
8233 while (!(*x >= heap->ephemeral_low && *x < heap->ephemeral_high));
8234
8235 // x is the start of the mark list piece for this heap
8236 mark_list_piece_start[heap_num] = x;
8237
8238 // to find the end of the mark list piece for this heap, find the first x
8239 // that has !predicate(x), i.e. that is either not in this heap, or beyond the end of the list
8240 if (predicate(x))
8241 {
8242 // let's see if we get lucky and the whole rest belongs to this piece
8243 if (predicate(mark_list_index-1))
8244 {
8245 x = mark_list_index;
8246 mark_list_piece_end[heap_num] = x;
8247 break;
8248 }
8249
8250 // we play a variant of binary search to find the point sooner.
8251 // the first loop advances by increasing steps until the predicate turns false.
8252 // then we retreat the last step, and the second loop advances by decreasing steps, keeping the predicate true.
8253 unsigned inc = 1;
8254 do
8255 {
8256 inc *= 2;
8257 uint8_t** temp_x = x;
8258 x += inc;
8259 if (temp_x > x)
8260 {
8261 break;
8262 }
8263 }
8264 while (predicate(x));
8265 // we know that only the last step was wrong, so we undo it
8266 x -= inc;
8267 do
8268 {
8269 // loop invariant - predicate holds at x, but not x + inc
8270 assert (predicate(x) && !(((x + inc) > x) && predicate(x + inc)));
8271 inc /= 2;
8272 if (((x + inc) > x) && predicate(x + inc))
8273 {
8274 x += inc;
8275 }
8276 }
8277 while (inc > 1);
8278 // the termination condition and the loop invariant together imply this:
8279 assert(predicate(x) && !predicate(x + inc) && (inc == 1));
8280 // so the spot we're looking for is one further
8281 x += 1;
8282 }
8283 mark_list_piece_end[heap_num] = x;
8284 }
8285
8286#undef predicate
8287
8288// printf("second phase of sort_mark_list for heap %d took %u cycles\n", this->heap_number, GetCycleCount32() - start);
8289}
8290
8291void gc_heap::append_to_mark_list(uint8_t **start, uint8_t **end)
8292{
8293 size_t slots_needed = end - start;
8294 size_t slots_available = mark_list_end + 1 - mark_list_index;
8295 size_t slots_to_copy = min(slots_needed, slots_available);
8296 memcpy(mark_list_index, start, slots_to_copy*sizeof(*start));
8297 mark_list_index += slots_to_copy;
8298// printf("heap %d: appended %Id slots to mark_list\n", heap_number, slots_to_copy);
8299}
8300
8301void gc_heap::merge_mark_lists()
8302{
8303 uint8_t** source[MAX_SUPPORTED_CPUS];
8304 uint8_t** source_end[MAX_SUPPORTED_CPUS];
8305 int source_heap[MAX_SUPPORTED_CPUS];
8306 int source_count = 0;
8307
8308 // in case of mark list overflow, don't bother
8309 if (mark_list_index > mark_list_end)
8310 {
8311// printf("merge_mark_lists: overflow\n");
8312 return;
8313 }
8314
8315 dprintf(3, ("merge_mark_lists: heap_number = %d starts out with %Id entries", heap_number, mark_list_index - mark_list));
8316// unsigned long start = GetCycleCount32();
8317 for (int i = 0; i < n_heaps; i++)
8318 {
8319 gc_heap* heap = g_heaps[i];
8320 if (heap->mark_list_piece_start[heap_number] < heap->mark_list_piece_end[heap_number])
8321 {
8322 source[source_count] = heap->mark_list_piece_start[heap_number];
8323 source_end[source_count] = heap->mark_list_piece_end[heap_number];
8324 source_heap[source_count] = i;
8325 if (source_count < MAX_SUPPORTED_CPUS)
8326 source_count++;
8327 }
8328 }
8329// printf("first phase of merge_mark_lists for heap %d took %u cycles\n", heap_number, GetCycleCount32() - start);
8330
8331 dprintf(3, ("heap_number = %d has %d sources\n", heap_number, source_count));
8332#if defined(_DEBUG) || defined(TRACE_GC)
8333 for (int j = 0; j < source_count; j++)
8334 {
8335 dprintf(3, ("heap_number = %d ", heap_number));
8336 dprintf(3, (" source from heap %d = %Ix .. %Ix (%Id entries)",
8337 (size_t)(source_heap[j]), (size_t)(source[j][0]), (size_t)(source_end[j][-1]), (size_t)(source_end[j] - source[j])));
8338 // the sources should all be sorted
8339 for (uint8_t **x = source[j]; x < source_end[j] - 1; x++)
8340 {
8341 if (x[0] > x[1])
8342 {
8343 dprintf(3, ("oops, mark_list from source %d for heap %d isn't sorted\n", j, heap_number));
8344 assert (0);
8345 }
8346 }
8347 }
8348#endif //_DEBUG || TRACE_GC
8349
8350// start = GetCycleCount32();
8351
8352 mark_list = &g_mark_list_copy [heap_number*mark_list_size];
8353 mark_list_index = mark_list;
8354 mark_list_end = &mark_list [mark_list_size-1];
8355 int piece_count = 0;
8356 if (source_count == 0)
8357 {
8358 ; // nothing to do
8359 }
8360 else if (source_count == 1)
8361 {
8362 mark_list = source[0];
8363 mark_list_index = source_end[0];
8364 mark_list_end = mark_list_index;
8365 piece_count++;
8366 }
8367 else
8368 {
8369 while (source_count > 1)
8370 {
8371 // find the lowest and second lowest value in the sources we're merging from
8372 int lowest_source = 0;
8373 uint8_t *lowest = *source[0];
8374 uint8_t *second_lowest = *source[1];
8375 for (int i = 1; i < source_count; i++)
8376 {
8377 if (lowest > *source[i])
8378 {
8379 second_lowest = lowest;
8380 lowest = *source[i];
8381 lowest_source = i;
8382 }
8383 else if (second_lowest > *source[i])
8384 {
8385 second_lowest = *source[i];
8386 }
8387 }
8388
8389 // find the point in the lowest source where it either runs out or is not <= second_lowest anymore
8390
8391 // let's first try to get lucky and see if the whole source is <= second_lowest -- this is actually quite common
8392 uint8_t **x;
8393 if (source_end[lowest_source][-1] <= second_lowest)
8394 x = source_end[lowest_source];
8395 else
8396 {
8397 // use linear search to find the end -- could also use binary search as in sort_mark_list,
8398 // but saw no improvement doing that
8399 for (x = source[lowest_source]; x < source_end[lowest_source] && *x <= second_lowest; x++)
8400 ;
8401 }
8402
8403 // blast this piece to the mark list
8404 append_to_mark_list(source[lowest_source], x);
8405 piece_count++;
8406
8407 source[lowest_source] = x;
8408
8409 // check whether this source is now exhausted
8410 if (x >= source_end[lowest_source])
8411 {
8412 // if it's not the source with the highest index, copy the source with the highest index
8413 // over it so the non-empty sources are always at the beginning
8414 if (lowest_source < source_count-1)
8415 {
8416 source[lowest_source] = source[source_count-1];
8417 source_end[lowest_source] = source_end[source_count-1];
8418 }
8419 source_count--;
8420 }
8421 }
8422 // we're left with just one source that we copy
8423 append_to_mark_list(source[0], source_end[0]);
8424 piece_count++;
8425 }
8426
8427// printf("second phase of merge_mark_lists for heap %d took %u cycles to merge %d pieces\n", heap_number, GetCycleCount32() - start, piece_count);
8428
8429#if defined(_DEBUG) || defined(TRACE_GC)
8430 // the final mark list must be sorted
8431 for (uint8_t **x = mark_list; x < mark_list_index - 1; x++)
8432 {
8433 if (x[0] > x[1])
8434 {
8435 dprintf(3, ("oops, mark_list for heap %d isn't sorted at the end of merge_mark_lists", heap_number));
8436 assert (0);
8437 }
8438 }
8439#endif //defined(_DEBUG) || defined(TRACE_GC)
8440}
8441#else //PARALLEL_MARK_LIST_SORT
8442void gc_heap::combine_mark_lists()
8443{
8444 dprintf (3, ("Combining mark lists"));
8445 //verify if a heap has overflowed its mark list
8446 BOOL use_mark_list = TRUE;
8447 for (int i = 0; i < n_heaps; i++)
8448 {
8449 if (g_heaps [i]->mark_list_index > g_heaps [i]->mark_list_end)
8450 {
8451 use_mark_list = FALSE;
8452 break;
8453 }
8454 }
8455
8456 if (use_mark_list)
8457 {
8458 dprintf (3, ("Using mark list"));
8459 //compact the gaps out of the mark list
8460 int gn = 0;
8461 uint8_t** current_gap = g_heaps [gn]->mark_list_index;
8462 uint8_t** current_gap_end = g_heaps[gn]->mark_list_end + 1;
8463 uint8_t** dst_last = current_gap-1;
8464
8465 int srcn = n_heaps-1;
8466 gc_heap* srch = g_heaps [srcn];
8467 uint8_t** src = srch->mark_list_index - 1;
8468 uint8_t** src_beg = srch->mark_list;
8469
8470 while (current_gap <= src)
8471 {
8472 while ((gn < n_heaps-1) && (current_gap >= current_gap_end))
8473 {
8474 //go to the next gap
8475 gn++;
8476 dprintf (3, ("Going to the next gap %d", gn));
8477 assert (gn < n_heaps);
8478 current_gap = g_heaps [gn]->mark_list_index;
8479 current_gap_end = g_heaps[gn]->mark_list_end + 1;
8480 assert ((gn == (n_heaps-1)) || (current_gap_end == g_heaps[gn+1]->mark_list));
8481 }
8482 while ((srcn > 0) && (src < src_beg))
8483 {
8484 //go to the previous source
8485 srcn--;
8486 dprintf (3, ("going to the previous source %d", srcn));
8487 assert (srcn>=0);
8488 gc_heap* srch = g_heaps [srcn];
8489 src = srch->mark_list_index - 1;
8490 src_beg = srch->mark_list;
8491 }
8492 if (current_gap < src)
8493 {
8494 dst_last = current_gap;
8495 *current_gap++ = *src--;
8496 }
8497 }
8498 dprintf (3, ("src: %Ix dst_last: %Ix", (size_t)src, (size_t)dst_last));
8499
8500 uint8_t** end_of_list = max (src, dst_last);
8501
8502 //sort the resulting compacted list
8503 assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]);
8504 if (end_of_list > &g_mark_list[0])
8505 _sort (&g_mark_list[0], end_of_list, 0);
8506 //adjust the mark_list to the begining of the resulting mark list.
8507 for (int i = 0; i < n_heaps; i++)
8508 {
8509 g_heaps [i]->mark_list = g_mark_list;
8510 g_heaps [i]->mark_list_index = end_of_list + 1;
8511 g_heaps [i]->mark_list_end = end_of_list + 1;
8512 }
8513 }
8514 else
8515 {
8516 uint8_t** end_of_list = g_mark_list;
8517 //adjust the mark_list to the begining of the resulting mark list.
8518 //put the index beyond the end to turn off mark list processing
8519 for (int i = 0; i < n_heaps; i++)
8520 {
8521 g_heaps [i]->mark_list = g_mark_list;
8522 g_heaps [i]->mark_list_index = end_of_list + 1;
8523 g_heaps [i]->mark_list_end = end_of_list;
8524 }
8525 }
8526}
8527#endif // PARALLEL_MARK_LIST_SORT
8528#endif //MULTIPLE_HEAPS
8529#endif //MARK_LIST
8530
8531class seg_free_spaces
8532{
8533 struct seg_free_space
8534 {
8535 BOOL is_plug;
8536 void* start;
8537 };
8538
8539 struct free_space_bucket
8540 {
8541 seg_free_space* free_space;
8542 ptrdiff_t count_add; // Assigned when we first contruct the array.
8543 ptrdiff_t count_fit; // How many items left when we are fitting plugs.
8544 };
8545
8546 void move_bucket (int old_power2, int new_power2)
8547 {
8548 // PREFAST warning 22015: old_power2 could be negative
8549 assert (old_power2 >= 0);
8550 assert (old_power2 >= new_power2);
8551
8552 if (old_power2 == new_power2)
8553 {
8554 return;
8555 }
8556
8557 seg_free_space* src_index = free_space_buckets[old_power2].free_space;
8558 for (int i = old_power2; i > new_power2; i--)
8559 {
8560 seg_free_space** dest = &(free_space_buckets[i].free_space);
8561 (*dest)++;
8562
8563 seg_free_space* dest_index = free_space_buckets[i - 1].free_space;
8564 if (i > (new_power2 + 1))
8565 {
8566 seg_free_space temp = *src_index;
8567 *src_index = *dest_index;
8568 *dest_index = temp;
8569 }
8570 src_index = dest_index;
8571 }
8572
8573 free_space_buckets[old_power2].count_fit--;
8574 free_space_buckets[new_power2].count_fit++;
8575 }
8576
8577#ifdef _DEBUG
8578
8579 void dump_free_space (seg_free_space* item)
8580 {
8581 uint8_t* addr = 0;
8582 size_t len = 0;
8583
8584 if (item->is_plug)
8585 {
8586 mark* m = (mark*)(item->start);
8587 len = pinned_len (m);
8588 addr = pinned_plug (m) - len;
8589 }
8590 else
8591 {
8592 heap_segment* seg = (heap_segment*)(item->start);
8593 addr = heap_segment_plan_allocated (seg);
8594 len = heap_segment_committed (seg) - addr;
8595 }
8596
8597 dprintf (SEG_REUSE_LOG_1, ("[%d]0x%Ix %Id", heap_num, addr, len));
8598 }
8599
8600 void dump()
8601 {
8602 seg_free_space* item = NULL;
8603 int i = 0;
8604
8605 dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------\nnow the free spaces look like:", heap_num));
8606 for (i = 0; i < (free_space_bucket_count - 1); i++)
8607 {
8608 dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8609 dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8610 item = free_space_buckets[i].free_space;
8611 while (item < free_space_buckets[i + 1].free_space)
8612 {
8613 dump_free_space (item);
8614 item++;
8615 }
8616 dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8617 }
8618
8619 dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8620 dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8621 item = free_space_buckets[i].free_space;
8622
8623 while (item <= &seg_free_space_array[free_space_item_count - 1])
8624 {
8625 dump_free_space (item);
8626 item++;
8627 }
8628 dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8629 }
8630
8631#endif //_DEBUG
8632
8633 free_space_bucket* free_space_buckets;
8634 seg_free_space* seg_free_space_array;
8635 ptrdiff_t free_space_bucket_count;
8636 ptrdiff_t free_space_item_count;
8637 int base_power2;
8638 int heap_num;
8639#ifdef _DEBUG
8640 BOOL has_end_of_seg;
8641#endif //_DEBUG
8642
8643public:
8644
8645 seg_free_spaces (int h_number)
8646 {
8647 heap_num = h_number;
8648 }
8649
8650 BOOL alloc ()
8651 {
8652 size_t total_prealloc_size =
8653 MAX_NUM_BUCKETS * sizeof (free_space_bucket) +
8654 MAX_NUM_FREE_SPACES * sizeof (seg_free_space);
8655
8656 free_space_buckets = (free_space_bucket*) new (nothrow) uint8_t[total_prealloc_size];
8657
8658 return (!!free_space_buckets);
8659 }
8660
8661 // We take the ordered free space array we got from the 1st pass,
8662 // and feed the portion that we decided to use to this method, ie,
8663 // the largest item_count free spaces.
8664 void add_buckets (int base, size_t* ordered_free_spaces, int bucket_count, size_t item_count)
8665 {
8666 assert (free_space_buckets);
8667 assert (item_count <= (size_t)MAX_PTR);
8668
8669 free_space_bucket_count = bucket_count;
8670 free_space_item_count = item_count;
8671 base_power2 = base;
8672#ifdef _DEBUG
8673 has_end_of_seg = FALSE;
8674#endif //_DEBUG
8675
8676 ptrdiff_t total_item_count = 0;
8677 ptrdiff_t i = 0;
8678
8679 seg_free_space_array = (seg_free_space*)(free_space_buckets + free_space_bucket_count);
8680
8681 for (i = 0; i < (ptrdiff_t)item_count; i++)
8682 {
8683 seg_free_space_array[i].start = 0;
8684 seg_free_space_array[i].is_plug = FALSE;
8685 }
8686
8687 for (i = 0; i < bucket_count; i++)
8688 {
8689 free_space_buckets[i].count_add = ordered_free_spaces[i];
8690 free_space_buckets[i].count_fit = ordered_free_spaces[i];
8691 free_space_buckets[i].free_space = &seg_free_space_array[total_item_count];
8692 total_item_count += free_space_buckets[i].count_add;
8693 }
8694
8695 assert (total_item_count == (ptrdiff_t)item_count);
8696 }
8697
8698 // If we are adding a free space before a plug we pass the
8699 // mark stack position so we can update the length; we could
8700 // also be adding the free space after the last plug in which
8701 // case start is the segment which we'll need to update the
8702 // heap_segment_plan_allocated.
8703 void add (void* start, BOOL plug_p, BOOL first_p)
8704 {
8705 size_t size = (plug_p ?
8706 pinned_len ((mark*)start) :
8707 (heap_segment_committed ((heap_segment*)start) -
8708 heap_segment_plan_allocated ((heap_segment*)start)));
8709
8710 if (plug_p)
8711 {
8712 dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space before plug: %Id", heap_num, size));
8713 }
8714 else
8715 {
8716 dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space at end of seg: %Id", heap_num, size));
8717#ifdef _DEBUG
8718 has_end_of_seg = TRUE;
8719#endif //_DEBUG
8720 }
8721
8722 if (first_p)
8723 {
8724 size_t eph_gen_starts = gc_heap::eph_gen_starts_size;
8725 size -= eph_gen_starts;
8726 if (plug_p)
8727 {
8728 mark* m = (mark*)(start);
8729 pinned_len (m) -= eph_gen_starts;
8730 }
8731 else
8732 {
8733 heap_segment* seg = (heap_segment*)start;
8734 heap_segment_plan_allocated (seg) += eph_gen_starts;
8735 }
8736 }
8737
8738 int bucket_power2 = index_of_highest_set_bit (size);
8739 if (bucket_power2 < base_power2)
8740 {
8741 return;
8742 }
8743
8744 free_space_bucket* bucket = &free_space_buckets[bucket_power2 - base_power2];
8745
8746 seg_free_space* bucket_free_space = bucket->free_space;
8747 assert (plug_p || (!plug_p && bucket->count_add));
8748
8749 if (bucket->count_add == 0)
8750 {
8751 dprintf (SEG_REUSE_LOG_1, ("[%d]Already have enough of 2^%d", heap_num, bucket_power2));
8752 return;
8753 }
8754
8755 ptrdiff_t index = bucket->count_add - 1;
8756
8757 dprintf (SEG_REUSE_LOG_1, ("[%d]Building free spaces: adding %Ix; len: %Id (2^%d)",
8758 heap_num,
8759 (plug_p ?
8760 (pinned_plug ((mark*)start) - pinned_len ((mark*)start)) :
8761 heap_segment_plan_allocated ((heap_segment*)start)),
8762 size,
8763 bucket_power2));
8764
8765 if (plug_p)
8766 {
8767 bucket_free_space[index].is_plug = TRUE;
8768 }
8769
8770 bucket_free_space[index].start = start;
8771 bucket->count_add--;
8772 }
8773
8774#ifdef _DEBUG
8775
8776 // Do a consistency check after all free spaces are added.
8777 void check()
8778 {
8779 ptrdiff_t i = 0;
8780 int end_of_seg_count = 0;
8781
8782 for (i = 0; i < free_space_item_count; i++)
8783 {
8784 assert (seg_free_space_array[i].start);
8785 if (!(seg_free_space_array[i].is_plug))
8786 {
8787 end_of_seg_count++;
8788 }
8789 }
8790
8791 if (has_end_of_seg)
8792 {
8793 assert (end_of_seg_count == 1);
8794 }
8795 else
8796 {
8797 assert (end_of_seg_count == 0);
8798 }
8799
8800 for (i = 0; i < free_space_bucket_count; i++)
8801 {
8802 assert (free_space_buckets[i].count_add == 0);
8803 }
8804 }
8805
8806#endif //_DEBUG
8807
8808 uint8_t* fit (uint8_t* old_loc,
8809#ifdef SHORT_PLUGS
8810 BOOL set_padding_on_saved_p,
8811 mark* pinned_plug_entry,
8812#endif //SHORT_PLUGS
8813 size_t plug_size
8814 REQD_ALIGN_AND_OFFSET_DCL)
8815 {
8816 if (old_loc)
8817 {
8818#ifdef SHORT_PLUGS
8819 assert (!is_plug_padded (old_loc));
8820#endif //SHORT_PLUGS
8821 assert (!node_realigned (old_loc));
8822 }
8823
8824 size_t saved_plug_size = plug_size;
8825
8826#ifdef FEATURE_STRUCTALIGN
8827 // BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account
8828 _ASSERTE(requiredAlignment == DATA_ALIGNMENT && false);
8829#endif // FEATURE_STRUCTALIGN
8830 // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the
8831 // the bucket.
8832
8833 size_t plug_size_to_fit = plug_size;
8834
8835 // best fit is only done for gen1 to gen2 and we do not pad in gen2.
8836 int pad_in_front = 0;
8837
8838#ifdef SHORT_PLUGS
8839 plug_size_to_fit += (pad_in_front ? Align(min_obj_size) : 0);
8840#endif //SHORT_PLUGS
8841
8842 int plug_power2 = index_of_highest_set_bit (round_up_power2 (plug_size_to_fit + Align(min_obj_size)));
8843 ptrdiff_t i;
8844 uint8_t* new_address = 0;
8845
8846 if (plug_power2 < base_power2)
8847 {
8848 plug_power2 = base_power2;
8849 }
8850
8851 int chosen_power2 = plug_power2 - base_power2;
8852retry:
8853 for (i = chosen_power2; i < free_space_bucket_count; i++)
8854 {
8855 if (free_space_buckets[i].count_fit != 0)
8856 {
8857 break;
8858 }
8859 chosen_power2++;
8860 }
8861
8862 dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting plug len %Id (2^%d) using 2^%d free space",
8863 heap_num,
8864 plug_size,
8865 plug_power2,
8866 (chosen_power2 + base_power2)));
8867
8868 assert (i < free_space_bucket_count);
8869
8870 seg_free_space* bucket_free_space = free_space_buckets[chosen_power2].free_space;
8871 ptrdiff_t free_space_count = free_space_buckets[chosen_power2].count_fit;
8872 size_t new_free_space_size = 0;
8873 BOOL can_fit = FALSE;
8874 size_t pad = 0;
8875
8876 for (i = 0; i < free_space_count; i++)
8877 {
8878 size_t free_space_size = 0;
8879 pad = 0;
8880#ifdef SHORT_PLUGS
8881 BOOL short_plugs_padding_p = FALSE;
8882#endif //SHORT_PLUGS
8883 BOOL realign_padding_p = FALSE;
8884
8885 if (bucket_free_space[i].is_plug)
8886 {
8887 mark* m = (mark*)(bucket_free_space[i].start);
8888 uint8_t* plug_free_space_start = pinned_plug (m) - pinned_len (m);
8889
8890#ifdef SHORT_PLUGS
8891 if ((pad_in_front & USE_PADDING_FRONT) &&
8892 (((plug_free_space_start - pin_allocation_context_start_region (m))==0) ||
8893 ((plug_free_space_start - pin_allocation_context_start_region (m))>=DESIRED_PLUG_LENGTH)))
8894 {
8895 pad = Align (min_obj_size);
8896 short_plugs_padding_p = TRUE;
8897 }
8898#endif //SHORT_PLUGS
8899
8900 if (!((old_loc == 0) || same_large_alignment_p (old_loc, plug_free_space_start+pad)))
8901 {
8902 pad += switch_alignment_size (pad != 0);
8903 realign_padding_p = TRUE;
8904 }
8905
8906 plug_size = saved_plug_size + pad;
8907
8908 free_space_size = pinned_len (m);
8909 new_address = pinned_plug (m) - pinned_len (m);
8910
8911 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8912 free_space_size == plug_size)
8913 {
8914 new_free_space_size = free_space_size - plug_size;
8915 pinned_len (m) = new_free_space_size;
8916#ifdef SIMPLE_DPRINTF
8917 dprintf (SEG_REUSE_LOG_0, ("[%d]FP: 0x%Ix->0x%Ix(%Ix)(%Ix), [0x%Ix (2^%d) -> [0x%Ix (2^%d)",
8918 heap_num,
8919 old_loc,
8920 new_address,
8921 (plug_size - pad),
8922 pad,
8923 pinned_plug (m),
8924 index_of_highest_set_bit (free_space_size),
8925 (pinned_plug (m) - pinned_len (m)),
8926 index_of_highest_set_bit (new_free_space_size)));
8927#endif //SIMPLE_DPRINTF
8928
8929#ifdef SHORT_PLUGS
8930 if (short_plugs_padding_p)
8931 {
8932 pin_allocation_context_start_region (m) = plug_free_space_start;
8933 set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
8934 }
8935#endif //SHORT_PLUGS
8936
8937 if (realign_padding_p)
8938 {
8939 set_node_realigned (old_loc);
8940 }
8941
8942 can_fit = TRUE;
8943 }
8944 }
8945 else
8946 {
8947 heap_segment* seg = (heap_segment*)(bucket_free_space[i].start);
8948 free_space_size = heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
8949
8950 if (!((old_loc == 0) || same_large_alignment_p (old_loc, heap_segment_plan_allocated (seg))))
8951 {
8952 pad = switch_alignment_size (FALSE);
8953 realign_padding_p = TRUE;
8954 }
8955
8956 plug_size = saved_plug_size + pad;
8957
8958 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8959 free_space_size == plug_size)
8960 {
8961 new_address = heap_segment_plan_allocated (seg);
8962 new_free_space_size = free_space_size - plug_size;
8963 heap_segment_plan_allocated (seg) = new_address + plug_size;
8964#ifdef SIMPLE_DPRINTF
8965 dprintf (SEG_REUSE_LOG_0, ("[%d]FS: 0x%Ix-> 0x%Ix(%Ix) (2^%d) -> 0x%Ix (2^%d)",
8966 heap_num,
8967 old_loc,
8968 new_address,
8969 (plug_size - pad),
8970 index_of_highest_set_bit (free_space_size),
8971 heap_segment_plan_allocated (seg),
8972 index_of_highest_set_bit (new_free_space_size)));
8973#endif //SIMPLE_DPRINTF
8974
8975 if (realign_padding_p)
8976 set_node_realigned (old_loc);
8977
8978 can_fit = TRUE;
8979 }
8980 }
8981
8982 if (can_fit)
8983 {
8984 break;
8985 }
8986 }
8987
8988 if (!can_fit)
8989 {
8990 assert (chosen_power2 == 0);
8991 chosen_power2 = 1;
8992 goto retry;
8993 }
8994 else
8995 {
8996 if (pad)
8997 {
8998 new_address += pad;
8999 }
9000 assert ((chosen_power2 && (i == 0)) ||
9001 (!chosen_power2) && (i < free_space_count));
9002 }
9003
9004 int new_bucket_power2 = index_of_highest_set_bit (new_free_space_size);
9005
9006 if (new_bucket_power2 < base_power2)
9007 {
9008 new_bucket_power2 = base_power2;
9009 }
9010
9011 move_bucket (chosen_power2, new_bucket_power2 - base_power2);
9012
9013 //dump();
9014
9015 return new_address;
9016 }
9017
9018 void cleanup ()
9019 {
9020 if (free_space_buckets)
9021 {
9022 delete [] free_space_buckets;
9023 }
9024 if (seg_free_space_array)
9025 {
9026 delete [] seg_free_space_array;
9027 }
9028 }
9029};
9030
9031
9032#define marked(i) header(i)->IsMarked()
9033#define set_marked(i) header(i)->SetMarked()
9034#define clear_marked(i) header(i)->ClearMarked()
9035#define pinned(i) header(i)->IsPinned()
9036#define set_pinned(i) header(i)->SetPinned()
9037#define clear_pinned(i) header(i)->GetHeader()->ClrGCBit();
9038
9039inline size_t my_get_size (Object* ob)
9040{
9041 MethodTable* mT = header(ob)->GetMethodTable();
9042 return (mT->GetBaseSize() +
9043 (mT->HasComponentSize() ?
9044 ((size_t)((CObjectHeader*)ob)->GetNumComponents() * mT->RawGetComponentSize()) : 0));
9045}
9046
9047//#define size(i) header(i)->GetSize()
9048#define size(i) my_get_size (header(i))
9049
9050#define contain_pointers(i) header(i)->ContainsPointers()
9051#ifdef COLLECTIBLE_CLASS
9052#define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible()
9053
9054#define get_class_object(i) GCToEEInterface::GetLoaderAllocatorObjectForGC((Object *)i)
9055#define is_collectible(i) method_table(i)->Collectible()
9056#else //COLLECTIBLE_CLASS
9057#define contain_pointers_or_collectible(i) header(i)->ContainsPointers()
9058#endif //COLLECTIBLE_CLASS
9059
9060#if defined (MARK_ARRAY) && defined (BACKGROUND_GC)
9061inline
9062void gc_heap::seg_clear_mark_array_bits_soh (heap_segment* seg)
9063{
9064 uint8_t* range_beg = 0;
9065 uint8_t* range_end = 0;
9066 if (bgc_mark_array_range (seg, FALSE, &range_beg, &range_end))
9067 {
9068 clear_mark_array (range_beg, align_on_mark_word (range_end), FALSE
9069#ifdef FEATURE_BASICFREEZE
9070 , TRUE
9071#endif // FEATURE_BASICFREEZE
9072 );
9073 }
9074}
9075
9076void gc_heap::clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
9077{
9078 if ((start < background_saved_highest_address) &&
9079 (end > background_saved_lowest_address))
9080 {
9081 start = max (start, background_saved_lowest_address);
9082 end = min (end, background_saved_highest_address);
9083
9084 size_t start_mark_bit = mark_bit_of (start);
9085 size_t end_mark_bit = mark_bit_of (end);
9086 unsigned int startbit = mark_bit_bit (start_mark_bit);
9087 unsigned int endbit = mark_bit_bit (end_mark_bit);
9088 size_t startwrd = mark_bit_word (start_mark_bit);
9089 size_t endwrd = mark_bit_word (end_mark_bit);
9090
9091 dprintf (3, ("Clearing all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
9092 (size_t)start, (size_t)start_mark_bit,
9093 (size_t)end, (size_t)end_mark_bit));
9094
9095 unsigned int firstwrd = lowbits (~0, startbit);
9096 unsigned int lastwrd = highbits (~0, endbit);
9097
9098 if (startwrd == endwrd)
9099 {
9100 unsigned int wrd = firstwrd | lastwrd;
9101 mark_array[startwrd] &= wrd;
9102 return;
9103 }
9104
9105 // clear the first mark word.
9106 if (startbit)
9107 {
9108 mark_array[startwrd] &= firstwrd;
9109 startwrd++;
9110 }
9111
9112 for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
9113 {
9114 mark_array[wrdtmp] = 0;
9115 }
9116
9117 // clear the last mark word.
9118 if (endbit)
9119 {
9120 mark_array[endwrd] &= lastwrd;
9121 }
9122 }
9123}
9124
9125void gc_heap::bgc_clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
9126{
9127 if ((start < background_saved_highest_address) &&
9128 (end > background_saved_lowest_address))
9129 {
9130 start = max (start, background_saved_lowest_address);
9131 end = min (end, background_saved_highest_address);
9132
9133 clear_batch_mark_array_bits (start, end);
9134 }
9135}
9136
9137void gc_heap::clear_mark_array_by_objects (uint8_t* from, uint8_t* end, BOOL loh_p)
9138{
9139 dprintf (3, ("clearing mark array bits by objects for addr [%Ix,[%Ix",
9140 from, end));
9141 int align_const = get_alignment_constant (!loh_p);
9142
9143 uint8_t* o = from;
9144
9145 while (o < end)
9146 {
9147 uint8_t* next_o = o + Align (size (o), align_const);
9148
9149 if (background_object_marked (o, TRUE))
9150 {
9151 dprintf (3, ("%Ix was marked by bgc, is now cleared", o));
9152 }
9153
9154 o = next_o;
9155 }
9156}
9157#endif //MARK_ARRAY && BACKGROUND_GC
9158
9159inline
9160BOOL gc_heap::is_mark_set (uint8_t* o)
9161{
9162 return marked (o);
9163}
9164
9165#if defined (_MSC_VER) && defined (_TARGET_X86_)
9166#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
9167#endif //_MSC_VER && _TARGET_X86_
9168
9169// return the generation number of an object.
9170// It is assumed that the object is valid.
9171//Note that this will return max_generation for a LOH object
9172int gc_heap::object_gennum (uint8_t* o)
9173{
9174 if (in_range_for_segment (o, ephemeral_heap_segment) &&
9175 (o >= generation_allocation_start (generation_of (max_generation-1))))
9176 {
9177 // in an ephemeral generation.
9178 for ( int i = 0; i < max_generation-1; i++)
9179 {
9180 if ((o >= generation_allocation_start (generation_of (i))))
9181 return i;
9182 }
9183 return max_generation-1;
9184 }
9185 else
9186 {
9187 return max_generation;
9188 }
9189}
9190
9191int gc_heap::object_gennum_plan (uint8_t* o)
9192{
9193 if (in_range_for_segment (o, ephemeral_heap_segment))
9194 {
9195 for (int i = 0; i <= max_generation-1; i++)
9196 {
9197 uint8_t* plan_start = generation_plan_allocation_start (generation_of (i));
9198 if (plan_start && (o >= plan_start))
9199 {
9200 return i;
9201 }
9202 }
9203 }
9204 return max_generation;
9205}
9206
9207#if defined(_MSC_VER) && defined(_TARGET_X86_)
9208#pragma optimize("", on) // Go back to command line default optimizations
9209#endif //_MSC_VER && _TARGET_X86_
9210
9211heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h_number)
9212{
9213 size_t initial_commit = SEGMENT_INITIAL_COMMIT;
9214
9215 //Commit the first page
9216 if (!virtual_alloc_commit_for_heap (new_pages, initial_commit, h_number))
9217 {
9218 return 0;
9219 }
9220
9221 //overlay the heap_segment
9222 heap_segment* new_segment = (heap_segment*)new_pages;
9223
9224 uint8_t* start = new_pages + segment_info_size;
9225 heap_segment_mem (new_segment) = start;
9226 heap_segment_used (new_segment) = start;
9227 heap_segment_reserved (new_segment) = new_pages + size;
9228 heap_segment_committed (new_segment) = new_pages + initial_commit;
9229 init_heap_segment (new_segment);
9230 dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
9231 return new_segment;
9232}
9233
9234void gc_heap::init_heap_segment (heap_segment* seg)
9235{
9236 seg->flags = 0;
9237 heap_segment_next (seg) = 0;
9238 heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
9239 heap_segment_allocated (seg) = heap_segment_mem (seg);
9240#ifdef BACKGROUND_GC
9241 heap_segment_background_allocated (seg) = 0;
9242 heap_segment_saved_bg_allocated (seg) = 0;
9243#endif //BACKGROUND_GC
9244}
9245
9246//Releases the segment to the OS.
9247// this is always called on one thread only so calling seg_table->remove is fine.
9248void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding)
9249{
9250 if (!heap_segment_loh_p (seg))
9251 {
9252 //cleanup the brick table back to the empty value
9253 clear_brick_table (heap_segment_mem (seg), heap_segment_reserved (seg));
9254 }
9255
9256 if (consider_hoarding)
9257 {
9258 assert ((heap_segment_mem (seg) - (uint8_t*)seg) <= ptrdiff_t(2*OS_PAGE_SIZE));
9259 size_t ss = (size_t) (heap_segment_reserved (seg) - (uint8_t*)seg);
9260 //Don't keep the big ones.
9261 if (ss <= INITIAL_ALLOC)
9262 {
9263 dprintf (2, ("Hoarding segment %Ix", (size_t)seg));
9264#ifdef BACKGROUND_GC
9265 // We don't need to clear the decommitted flag because when this segment is used
9266 // for a new segment the flags will be cleared.
9267 if (!heap_segment_decommitted_p (seg))
9268#endif //BACKGROUND_GC
9269 {
9270 decommit_heap_segment (seg);
9271 }
9272
9273#ifdef SEG_MAPPING_TABLE
9274 seg_mapping_table_remove_segment (seg);
9275#endif //SEG_MAPPING_TABLE
9276
9277 heap_segment_next (seg) = segment_standby_list;
9278 segment_standby_list = seg;
9279 seg = 0;
9280 }
9281 }
9282
9283 if (seg != 0)
9284 {
9285 dprintf (2, ("h%d: del seg: [%Ix, %Ix[",
9286 heap_number, (size_t)seg,
9287 (size_t)(heap_segment_reserved (seg))));
9288
9289#ifdef BACKGROUND_GC
9290 ::record_changed_seg ((uint8_t*)seg, heap_segment_reserved (seg),
9291 settings.gc_index, current_bgc_state,
9292 seg_deleted);
9293 decommit_mark_array_by_seg (seg);
9294#endif //BACKGROUND_GC
9295
9296#ifdef SEG_MAPPING_TABLE
9297 seg_mapping_table_remove_segment (seg);
9298#else //SEG_MAPPING_TABLE
9299 seg_table->remove ((uint8_t*)seg);
9300#endif //SEG_MAPPING_TABLE
9301
9302 release_segment (seg);
9303 }
9304}
9305
9306//resets the pages beyond allocates size so they won't be swapped out and back in
9307
9308void gc_heap::reset_heap_segment_pages (heap_segment* seg)
9309{
9310 size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg));
9311 size_t size = (size_t)heap_segment_committed (seg) - page_start;
9312 if (size != 0)
9313 GCToOSInterface::VirtualReset((void*)page_start, size, false /* unlock */);
9314}
9315
9316void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
9317 size_t extra_space)
9318{
9319 uint8_t* page_start = align_on_page (heap_segment_allocated(seg));
9320 size_t size = heap_segment_committed (seg) - page_start;
9321 extra_space = align_on_page (extra_space);
9322 if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE))
9323 {
9324 page_start += max(extra_space, 32*OS_PAGE_SIZE);
9325 size -= max (extra_space, 32*OS_PAGE_SIZE);
9326
9327 GCToOSInterface::VirtualDecommit (page_start, size);
9328 dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)",
9329 (size_t)page_start,
9330 (size_t)(page_start + size),
9331 size));
9332 heap_segment_committed (seg) = page_start;
9333 if (heap_segment_used (seg) > heap_segment_committed (seg))
9334 {
9335 heap_segment_used (seg) = heap_segment_committed (seg);
9336 }
9337 }
9338}
9339
9340//decommit all pages except one or 2
9341void gc_heap::decommit_heap_segment (heap_segment* seg)
9342{
9343 uint8_t* page_start = align_on_page (heap_segment_mem (seg));
9344
9345 dprintf (3, ("Decommitting heap segment %Ix", (size_t)seg));
9346
9347#ifdef BACKGROUND_GC
9348 page_start += OS_PAGE_SIZE;
9349#endif //BACKGROUND_GC
9350
9351 size_t size = heap_segment_committed (seg) - page_start;
9352 GCToOSInterface::VirtualDecommit (page_start, size);
9353
9354 //re-init the segment object
9355 heap_segment_committed (seg) = page_start;
9356 if (heap_segment_used (seg) > heap_segment_committed (seg))
9357 {
9358 heap_segment_used (seg) = heap_segment_committed (seg);
9359 }
9360}
9361
9362void gc_heap::clear_gen0_bricks()
9363{
9364 if (!gen0_bricks_cleared)
9365 {
9366 gen0_bricks_cleared = TRUE;
9367 //initialize brick table for gen 0
9368 for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
9369 b < brick_of (align_on_brick
9370 (heap_segment_allocated (ephemeral_heap_segment)));
9371 b++)
9372 {
9373 set_brick (b, -1);
9374 }
9375 }
9376}
9377
9378#ifdef BACKGROUND_GC
9379void gc_heap::rearrange_small_heap_segments()
9380{
9381 heap_segment* seg = freeable_small_heap_segment;
9382 while (seg)
9383 {
9384 heap_segment* next_seg = heap_segment_next (seg);
9385 // TODO: we need to consider hoarding here.
9386 delete_heap_segment (seg, FALSE);
9387 seg = next_seg;
9388 }
9389 freeable_small_heap_segment = 0;
9390}
9391#endif //BACKGROUND_GC
9392
9393void gc_heap::rearrange_large_heap_segments()
9394{
9395 dprintf (2, ("deleting empty large segments"));
9396 heap_segment* seg = freeable_large_heap_segment;
9397 while (seg)
9398 {
9399 heap_segment* next_seg = heap_segment_next (seg);
9400 delete_heap_segment (seg, GCConfig::GetRetainVM());
9401 seg = next_seg;
9402 }
9403 freeable_large_heap_segment = 0;
9404}
9405
9406void gc_heap::rearrange_heap_segments(BOOL compacting)
9407{
9408 heap_segment* seg =
9409 generation_start_segment (generation_of (max_generation));
9410
9411 heap_segment* prev_seg = 0;
9412 heap_segment* next_seg = 0;
9413 while (seg)
9414 {
9415 next_seg = heap_segment_next (seg);
9416
9417 //link ephemeral segment when expanding
9418 if ((next_seg == 0) && (seg != ephemeral_heap_segment))
9419 {
9420 seg->next = ephemeral_heap_segment;
9421 next_seg = heap_segment_next (seg);
9422 }
9423
9424 //re-used expanded heap segment
9425 if ((seg == ephemeral_heap_segment) && next_seg)
9426 {
9427 heap_segment_next (prev_seg) = next_seg;
9428 heap_segment_next (seg) = 0;
9429 }
9430 else
9431 {
9432 uint8_t* end_segment = (compacting ?
9433 heap_segment_plan_allocated (seg) :
9434 heap_segment_allocated (seg));
9435 // check if the segment was reached by allocation
9436 if ((end_segment == heap_segment_mem (seg))&&
9437 !heap_segment_read_only_p (seg))
9438 {
9439 //if not, unthread and delete
9440 assert (prev_seg);
9441 assert (seg != ephemeral_heap_segment);
9442 heap_segment_next (prev_seg) = next_seg;
9443 delete_heap_segment (seg, GCConfig::GetRetainVM());
9444
9445 dprintf (2, ("Deleting heap segment %Ix", (size_t)seg));
9446 }
9447 else
9448 {
9449 if (!heap_segment_read_only_p (seg))
9450 {
9451 if (compacting)
9452 {
9453 heap_segment_allocated (seg) =
9454 heap_segment_plan_allocated (seg);
9455 }
9456
9457 // reset the pages between allocated and committed.
9458 if (seg != ephemeral_heap_segment)
9459 {
9460 decommit_heap_segment_pages (seg, 0);
9461 }
9462 }
9463 prev_seg = seg;
9464 }
9465 }
9466
9467 seg = next_seg;
9468 }
9469}
9470
9471
9472#ifdef WRITE_WATCH
9473
9474uint8_t* g_addresses [array_size+2]; // to get around the bug in GetWriteWatch
9475
9476#ifdef TIME_WRITE_WATCH
9477static unsigned int tot_cycles = 0;
9478#endif //TIME_WRITE_WATCH
9479
9480#ifdef CARD_BUNDLE
9481
9482inline void gc_heap::verify_card_bundle_bits_set(size_t first_card_word, size_t last_card_word)
9483{
9484#ifdef _DEBUG
9485 for (size_t x = cardw_card_bundle (first_card_word); x < cardw_card_bundle (last_card_word); x++)
9486 {
9487 if (!card_bundle_set_p (x))
9488 {
9489 assert (!"Card bundle not set");
9490 dprintf (3, ("Card bundle %Ix not set", x));
9491 }
9492 }
9493#endif
9494}
9495
9496// Verifies that any bundles that are not set represent only cards that are not set.
9497inline void gc_heap::verify_card_bundles()
9498{
9499#ifdef _DEBUG
9500 size_t lowest_card = card_word (card_of (lowest_address));
9501 size_t highest_card = card_word (card_of (highest_address));
9502 size_t cardb = cardw_card_bundle (lowest_card);
9503 size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (highest_card));
9504
9505 while (cardb < end_cardb)
9506 {
9507 uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb), lowest_card)];
9508 uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1), highest_card)];
9509
9510 if (card_bundle_set_p (cardb) == 0)
9511 {
9512 // Verify that no card is set
9513 while (card_word < card_word_end)
9514 {
9515 if (*card_word != 0)
9516 {
9517 dprintf (3, ("gc: %d, Card word %Ix for address %Ix set, card_bundle %Ix clear",
9518 dd_collection_count (dynamic_data_of (0)),
9519 (size_t)(card_word-&card_table[0]),
9520 (size_t)(card_address ((size_t)(card_word-&card_table[0]) * card_word_width)), cardb));
9521 }
9522
9523 assert((*card_word)==0);
9524 card_word++;
9525 }
9526 }
9527
9528 cardb++;
9529 }
9530#endif
9531}
9532
9533// If card bundles are enabled, use write watch to find pages in the card table that have
9534// been dirtied, and set the corresponding card bundle bits.
9535void gc_heap::update_card_table_bundle()
9536{
9537 if (card_bundles_enabled())
9538 {
9539 // The address of the card word containing the card representing the lowest heap address
9540 uint8_t* base_address = (uint8_t*)(&card_table[card_word (card_of (lowest_address))]);
9541
9542 // The address of the card word containing the card representing the highest heap address
9543 uint8_t* high_address = (uint8_t*)(&card_table[card_word (card_of (highest_address))]);
9544
9545 uint8_t* saved_base_address = base_address;
9546 uintptr_t bcount = array_size;
9547 size_t saved_region_size = align_on_page (high_address) - saved_base_address;
9548
9549 do
9550 {
9551 size_t region_size = align_on_page (high_address) - base_address;
9552
9553 dprintf (3,("Probing card table pages [%Ix, %Ix[", (size_t)base_address, (size_t)base_address+region_size));
9554 bool success = GCToOSInterface::GetWriteWatch(false /* resetState */,
9555 base_address,
9556 region_size,
9557 (void**)g_addresses,
9558 &bcount);
9559 assert (success && "GetWriteWatch failed!");
9560
9561 dprintf (3,("Found %d pages written", bcount));
9562 for (unsigned i = 0; i < bcount; i++)
9563 {
9564 // Offset of the dirty page from the start of the card table (clamped to base_address)
9565 size_t bcardw = (uint32_t*)(max(g_addresses[i],base_address)) - &card_table[0];
9566
9567 // Offset of the end of the page from the start of the card table (clamped to high addr)
9568 size_t ecardw = (uint32_t*)(min(g_addresses[i]+OS_PAGE_SIZE, high_address)) - &card_table[0];
9569 assert (bcardw >= card_word (card_of (g_gc_lowest_address)));
9570
9571 // Set the card bundle bits representing the dirty card table page
9572 card_bundles_set (cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw)));
9573 dprintf (3,("Set Card bundle [%Ix, %Ix[", cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))));
9574
9575 verify_card_bundle_bits_set(bcardw, ecardw);
9576 }
9577
9578 if (bcount >= array_size)
9579 {
9580 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
9581 bcount = array_size;
9582 }
9583
9584 } while ((bcount >= array_size) && (base_address < high_address));
9585
9586 // Now that we've updated the card bundle bits, reset the write-tracking state.
9587 GCToOSInterface::ResetWriteWatch (saved_base_address, saved_region_size);
9588 }
9589}
9590#endif //CARD_BUNDLE
9591
9592// static
9593void gc_heap::reset_write_watch_for_gc_heap(void* base_address, size_t region_size)
9594{
9595#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9596 SoftwareWriteWatch::ClearDirty(base_address, region_size);
9597#else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9598 GCToOSInterface::ResetWriteWatch(base_address, region_size);
9599#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9600}
9601
9602// static
9603void 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)
9604{
9605#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9606 SoftwareWriteWatch::GetDirty(base_address, region_size, dirty_pages, dirty_page_count_ref, reset, is_runtime_suspended);
9607#else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9608 UNREFERENCED_PARAMETER(is_runtime_suspended);
9609 bool success = GCToOSInterface::GetWriteWatch(reset, base_address, region_size, dirty_pages, dirty_page_count_ref);
9610 assert(success);
9611#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9612}
9613
9614const size_t ww_reset_quantum = 128*1024*1024;
9615
9616inline
9617void gc_heap::switch_one_quantum()
9618{
9619 enable_preemptive ();
9620 GCToOSInterface::Sleep (1);
9621 disable_preemptive (true);
9622}
9623
9624void gc_heap::reset_ww_by_chunk (uint8_t* start_address, size_t total_reset_size)
9625{
9626 size_t reset_size = 0;
9627 size_t remaining_reset_size = 0;
9628 size_t next_reset_size = 0;
9629
9630 while (reset_size != total_reset_size)
9631 {
9632 remaining_reset_size = total_reset_size - reset_size;
9633 next_reset_size = ((remaining_reset_size >= ww_reset_quantum) ? ww_reset_quantum : remaining_reset_size);
9634 if (next_reset_size)
9635 {
9636 reset_write_watch_for_gc_heap(start_address, next_reset_size);
9637 reset_size += next_reset_size;
9638
9639 switch_one_quantum();
9640 }
9641 }
9642
9643 assert (reset_size == total_reset_size);
9644}
9645
9646// This does a Sleep(1) for every reset ww_reset_quantum bytes of reset
9647// we do concurrently.
9648void gc_heap::switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size)
9649{
9650 if (concurrent_p)
9651 {
9652 *current_total_reset_size += last_reset_size;
9653
9654 dprintf (2, ("reset %Id bytes so far", *current_total_reset_size));
9655
9656 if (*current_total_reset_size > ww_reset_quantum)
9657 {
9658 switch_one_quantum();
9659
9660 *current_total_reset_size = 0;
9661 }
9662 }
9663}
9664
9665void gc_heap::reset_write_watch (BOOL concurrent_p)
9666{
9667#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9668 // Software write watch currently requires the runtime to be suspended during reset. See SoftwareWriteWatch::ClearDirty().
9669 assert(!concurrent_p);
9670#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9671
9672 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
9673
9674 PREFIX_ASSUME(seg != NULL);
9675
9676 size_t reset_size = 0;
9677 size_t region_size = 0;
9678
9679 dprintf (2, ("bgc lowest: %Ix, bgc highest: %Ix", background_saved_lowest_address, background_saved_highest_address));
9680
9681 while (seg)
9682 {
9683 uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9684 base_address = max (base_address, background_saved_lowest_address);
9685
9686 uint8_t* high_address = 0;
9687 high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
9688 high_address = min (high_address, background_saved_highest_address);
9689
9690 if (base_address < high_address)
9691 {
9692 region_size = high_address - base_address;
9693
9694#ifdef TIME_WRITE_WATCH
9695 unsigned int time_start = GetCycleCount32();
9696#endif //TIME_WRITE_WATCH
9697 dprintf (3, ("h%d: soh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9698 //reset_ww_by_chunk (base_address, region_size);
9699 reset_write_watch_for_gc_heap(base_address, region_size);
9700
9701#ifdef TIME_WRITE_WATCH
9702 unsigned int time_stop = GetCycleCount32();
9703 tot_cycles += time_stop - time_start;
9704 printf ("ResetWriteWatch Duration: %d, total: %d\n",
9705 time_stop - time_start, tot_cycles);
9706#endif //TIME_WRITE_WATCH
9707
9708 switch_on_reset (concurrent_p, &reset_size, region_size);
9709 }
9710
9711 seg = heap_segment_next_rw (seg);
9712
9713 concurrent_print_time_delta ("CRWW soh");
9714 }
9715
9716 //concurrent_print_time_delta ("CRW soh");
9717
9718 seg = heap_segment_rw (generation_start_segment (large_object_generation));
9719
9720 PREFIX_ASSUME(seg != NULL);
9721
9722 while (seg)
9723 {
9724 uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9725 uint8_t* high_address = heap_segment_allocated (seg);
9726
9727 base_address = max (base_address, background_saved_lowest_address);
9728 high_address = min (high_address, background_saved_highest_address);
9729
9730 if (base_address < high_address)
9731 {
9732 region_size = high_address - base_address;
9733
9734#ifdef TIME_WRITE_WATCH
9735 unsigned int time_start = GetCycleCount32();
9736#endif //TIME_WRITE_WATCH
9737 dprintf (3, ("h%d: loh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9738 //reset_ww_by_chunk (base_address, region_size);
9739 reset_write_watch_for_gc_heap(base_address, region_size);
9740
9741#ifdef TIME_WRITE_WATCH
9742 unsigned int time_stop = GetCycleCount32();
9743 tot_cycles += time_stop - time_start;
9744 printf ("ResetWriteWatch Duration: %d, total: %d\n",
9745 time_stop - time_start, tot_cycles);
9746#endif //TIME_WRITE_WATCH
9747
9748 switch_on_reset (concurrent_p, &reset_size, region_size);
9749 }
9750
9751 seg = heap_segment_next_rw (seg);
9752
9753 concurrent_print_time_delta ("CRWW loh");
9754 }
9755
9756#ifdef DEBUG_WRITE_WATCH
9757 debug_write_watch = (uint8_t**)~0;
9758#endif //DEBUG_WRITE_WATCH
9759}
9760
9761#endif //WRITE_WATCH
9762
9763#ifdef BACKGROUND_GC
9764void gc_heap::restart_vm()
9765{
9766 //assert (generation_allocation_pointer (youngest_generation) == 0);
9767 dprintf (3, ("Restarting EE"));
9768 STRESS_LOG0(LF_GC, LL_INFO10000, "Concurrent GC: Retarting EE\n");
9769 ee_proceed_event.Set();
9770}
9771
9772inline
9773void fire_alloc_wait_event (alloc_wait_reason awr, BOOL begin_p)
9774{
9775 if (awr != awr_ignored)
9776 {
9777 if (begin_p)
9778 {
9779 FIRE_EVENT(BGCAllocWaitBegin, awr);
9780 }
9781 else
9782 {
9783 FIRE_EVENT(BGCAllocWaitEnd, awr);
9784 }
9785 }
9786}
9787
9788
9789void gc_heap::fire_alloc_wait_event_begin (alloc_wait_reason awr)
9790{
9791 fire_alloc_wait_event (awr, TRUE);
9792}
9793
9794
9795void gc_heap::fire_alloc_wait_event_end (alloc_wait_reason awr)
9796{
9797 fire_alloc_wait_event (awr, FALSE);
9798}
9799#endif //BACKGROUND_GC
9800void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* start, uint8_t* pointer)
9801{
9802 gen.allocation_start = start;
9803 gen.allocation_context.alloc_ptr = pointer;
9804 gen.allocation_context.alloc_limit = pointer;
9805 gen.allocation_context.alloc_bytes = 0;
9806 gen.allocation_context.alloc_bytes_loh = 0;
9807 gen.allocation_context_start_region = pointer;
9808 gen.start_segment = seg;
9809 gen.allocation_segment = seg;
9810 gen.plan_allocation_start = 0;
9811 gen.free_list_space = 0;
9812 gen.pinned_allocated = 0;
9813 gen.free_list_allocated = 0;
9814 gen.end_seg_allocated = 0;
9815 gen.condemned_allocated = 0;
9816 gen.free_obj_space = 0;
9817 gen.allocation_size = 0;
9818 gen.pinned_allocation_sweep_size = 0;
9819 gen.pinned_allocation_compact_size = 0;
9820 gen.allocate_end_seg_p = FALSE;
9821 gen.free_list_allocator.clear();
9822
9823#ifdef FREE_USAGE_STATS
9824 memset (gen.gen_free_spaces, 0, sizeof (gen.gen_free_spaces));
9825 memset (gen.gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces));
9826 memset (gen.gen_plugs, 0, sizeof (gen.gen_plugs));
9827#endif //FREE_USAGE_STATS
9828}
9829
9830void gc_heap::adjust_ephemeral_limits ()
9831{
9832 ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
9833 ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);
9834
9835 dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
9836 (size_t)ephemeral_low, (size_t)ephemeral_high))
9837
9838#ifndef MULTIPLE_HEAPS
9839 // This updates the write barrier helpers with the new info.
9840 stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high);
9841#endif // MULTIPLE_HEAPS
9842}
9843
9844#if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN)
9845FILE* CreateLogFile(const GCConfigStringHolder& temp_logfile_name, bool is_config)
9846{
9847 FILE* logFile;
9848
9849 if (!temp_logfile_name.Get())
9850 {
9851 return nullptr;
9852 }
9853
9854 char logfile_name[MAX_LONGPATH+1];
9855 uint32_t pid = GCToOSInterface::GetCurrentProcessId();
9856 const char* suffix = is_config ? ".config.log" : ".log";
9857 _snprintf_s(logfile_name, MAX_LONGPATH+1, _TRUNCATE, "%s.%d%s", temp_logfile_name.Get(), pid, suffix);
9858 logFile = fopen(logfile_name, "wb");
9859 return logFile;
9860}
9861#endif //TRACE_GC || GC_CONFIG_DRIVEN
9862
9863HRESULT gc_heap::initialize_gc (size_t segment_size,
9864 size_t heap_size
9865#ifdef MULTIPLE_HEAPS
9866 ,unsigned number_of_heaps
9867#endif //MULTIPLE_HEAPS
9868)
9869{
9870#ifdef TRACE_GC
9871 if (GCConfig::GetLogEnabled())
9872 {
9873 gc_log = CreateLogFile(GCConfig::GetLogFile(), false);
9874
9875 if (gc_log == NULL)
9876 return E_FAIL;
9877
9878 // GCLogFileSize in MBs.
9879 gc_log_file_size = static_cast<size_t>(GCConfig::GetLogFileSize());
9880
9881 if (gc_log_file_size <= 0 || gc_log_file_size > 500)
9882 {
9883 fclose (gc_log);
9884 return E_FAIL;
9885 }
9886
9887 gc_log_lock.Initialize();
9888 gc_log_buffer = new (nothrow) uint8_t [gc_log_buffer_size];
9889 if (!gc_log_buffer)
9890 {
9891 fclose(gc_log);
9892 return E_FAIL;
9893 }
9894
9895 memset (gc_log_buffer, '*', gc_log_buffer_size);
9896
9897 max_gc_buffers = gc_log_file_size * 1024 * 1024 / gc_log_buffer_size;
9898 }
9899#endif // TRACE_GC
9900
9901#ifdef GC_CONFIG_DRIVEN
9902 if (GCConfig::GetConfigLogEnabled())
9903 {
9904 gc_config_log = CreateLogFile(GCConfig::GetConfigLogFile(), true);
9905
9906 if (gc_config_log == NULL)
9907 return E_FAIL;
9908
9909 gc_config_log_buffer = new (nothrow) uint8_t [gc_config_log_buffer_size];
9910 if (!gc_config_log_buffer)
9911 {
9912 fclose(gc_config_log);
9913 return E_FAIL;
9914 }
9915
9916 compact_ratio = static_cast<int>(GCConfig::GetCompactRatio());
9917
9918 // h# | GC | gen | C | EX | NF | BF | ML | DM || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
9919 cprintf (("%2s | %6s | %1s | %1s | %2s | %2s | %2s | %2s | %2s || %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s |",
9920 "h#", // heap index
9921 "GC", // GC index
9922 "g", // generation
9923 "C", // compaction (empty means sweeping), 'M' means it was mandatory, 'W' means it was not
9924 "EX", // heap expansion
9925 "NF", // normal fit
9926 "BF", // best fit (if it indicates neither NF nor BF it means it had to acquire a new seg.
9927 "ML", // mark list
9928 "DM", // demotion
9929 "PreS", // short object before pinned plug
9930 "PostS", // short object after pinned plug
9931 "Merge", // merged pinned plugs
9932 "Conv", // converted to pinned plug
9933 "Pre", // plug before pinned plug but not after
9934 "Post", // plug after pinned plug but not before
9935 "PrPo", // plug both before and after pinned plug
9936 "PreP", // pre short object padded
9937 "PostP" // post short object padded
9938 ));
9939 }
9940#endif //GC_CONFIG_DRIVEN
9941
9942#ifdef GC_STATS
9943 GCConfigStringHolder logFileName = GCConfig::GetMixLogFile();
9944 if (logFileName.Get() != nullptr)
9945 {
9946 GCStatistics::logFileName = _strdup(logFileName.Get());
9947 GCStatistics::logFile = fopen(GCStatistics::logFileName, "a");
9948 if (!GCStatistics::logFile)
9949 {
9950 return E_FAIL;
9951 }
9952 }
9953#endif // GC_STATS
9954
9955 HRESULT hres = S_OK;
9956
9957#ifdef WRITE_WATCH
9958 hardware_write_watch_api_supported();
9959#ifdef BACKGROUND_GC
9960 if (can_use_write_watch_for_gc_heap() && GCConfig::GetConcurrentGC())
9961 {
9962 gc_can_use_concurrent = true;
9963#ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9964 virtual_alloc_hardware_write_watch = true;
9965#endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9966 }
9967 else
9968 {
9969 gc_can_use_concurrent = false;
9970 }
9971#endif //BACKGROUND_GC
9972#endif //WRITE_WATCH
9973
9974#ifdef BACKGROUND_GC
9975 // leave the first page to contain only segment info
9976 // because otherwise we could need to revisit the first page frequently in
9977 // background GC.
9978 segment_info_size = OS_PAGE_SIZE;
9979#else
9980 segment_info_size = Align (sizeof (heap_segment), get_alignment_constant (FALSE));
9981#endif //BACKGROUND_GC
9982
9983 reserved_memory = 0;
9984 unsigned block_count;
9985#ifdef MULTIPLE_HEAPS
9986 reserved_memory_limit = (segment_size + heap_size) * number_of_heaps;
9987 block_count = number_of_heaps;
9988 n_heaps = number_of_heaps;
9989#else //MULTIPLE_HEAPS
9990 reserved_memory_limit = segment_size + heap_size;
9991 block_count = 1;
9992#endif //MULTIPLE_HEAPS
9993
9994 if (!reserve_initial_memory(segment_size,heap_size,block_count))
9995 return E_OUTOFMEMORY;
9996
9997#ifdef CARD_BUNDLE
9998 //check if we need to turn on card_bundles.
9999#ifdef MULTIPLE_HEAPS
10000 // use INT64 arithmetic here because of possible overflow on 32p
10001 uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*number_of_heaps;
10002#else
10003 // use INT64 arithmetic here because of possible overflow on 32p
10004 uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
10005#endif //MULTIPLE_HEAPS
10006
10007 if (can_use_write_watch_for_card_table() && reserved_memory >= th)
10008 {
10009 settings.card_bundles = TRUE;
10010 }
10011 else
10012 {
10013 settings.card_bundles = FALSE;
10014 }
10015#endif //CARD_BUNDLE
10016
10017 settings.first_init();
10018
10019 int latency_level_from_config = static_cast<int>(GCConfig::GetLatencyLevel());
10020 if (latency_level_from_config >= latency_level_first && latency_level_from_config <= latency_level_last)
10021 {
10022 gc_heap::latency_level = static_cast<gc_latency_level>(latency_level_from_config);
10023 }
10024
10025 init_static_data();
10026
10027 g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address);
10028
10029 if (!g_gc_card_table)
10030 return E_OUTOFMEMORY;
10031
10032 gc_started = FALSE;
10033
10034#ifdef MULTIPLE_HEAPS
10035 g_heaps = new (nothrow) gc_heap* [number_of_heaps];
10036 if (!g_heaps)
10037 return E_OUTOFMEMORY;
10038
10039#ifdef _PREFAST_
10040#pragma warning(push)
10041#pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10042#endif // _PREFAST_
10043 g_promoted = new (nothrow) size_t [number_of_heaps*16];
10044 g_bpromoted = new (nothrow) size_t [number_of_heaps*16];
10045#ifdef MH_SC_MARK
10046 g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
10047#endif //MH_SC_MARK
10048#ifdef _PREFAST_
10049#pragma warning(pop)
10050#endif // _PREFAST_
10051 if (!g_promoted || !g_bpromoted)
10052 return E_OUTOFMEMORY;
10053
10054#ifdef MH_SC_MARK
10055 if (!g_mark_stack_busy)
10056 return E_OUTOFMEMORY;
10057#endif //MH_SC_MARK
10058
10059 if (!create_thread_support (number_of_heaps))
10060 return E_OUTOFMEMORY;
10061
10062 if (!heap_select::init (number_of_heaps))
10063 return E_OUTOFMEMORY;
10064
10065#endif //MULTIPLE_HEAPS
10066
10067#ifdef MULTIPLE_HEAPS
10068 yp_spin_count_unit = 32 * number_of_heaps;
10069#else
10070 yp_spin_count_unit = 32 * g_num_processors;
10071#endif //MULTIPLE_HEAPS
10072
10073 if (!init_semi_shared())
10074 {
10075 hres = E_FAIL;
10076 }
10077
10078 return hres;
10079}
10080
10081//Initializes PER_HEAP_ISOLATED data members.
10082int
10083gc_heap::init_semi_shared()
10084{
10085 int ret = 0;
10086
10087 // This is used for heap expansion - it's to fix exactly the start for gen 0
10088 // through (max_generation-1). When we expand the heap we allocate all these
10089 // gen starts at the beginning of the new ephemeral seg.
10090 eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
10091
10092#ifdef MARK_LIST
10093#ifdef MULTIPLE_HEAPS
10094 mark_list_size = min (150*1024, max (8192, soh_segment_size/(2*10*32)));
10095 g_mark_list = make_mark_list (mark_list_size*n_heaps);
10096
10097 min_balance_threshold = alloc_quantum_balance_units * CLR_SIZE * 2;
10098#ifdef PARALLEL_MARK_LIST_SORT
10099 g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
10100 if (!g_mark_list_copy)
10101 {
10102 goto cleanup;
10103 }
10104#endif //PARALLEL_MARK_LIST_SORT
10105
10106#else //MULTIPLE_HEAPS
10107
10108 mark_list_size = max (8192, soh_segment_size/(64*32));
10109 g_mark_list = make_mark_list (mark_list_size);
10110
10111#endif //MULTIPLE_HEAPS
10112
10113 dprintf (3, ("mark_list_size: %d", mark_list_size));
10114
10115 if (!g_mark_list)
10116 {
10117 goto cleanup;
10118 }
10119#endif //MARK_LIST
10120
10121#if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10122 if (!seg_mapping_table_init())
10123 goto cleanup;
10124#endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10125
10126#if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10127 seg_table = sorted_table::make_sorted_table();
10128
10129 if (!seg_table)
10130 goto cleanup;
10131#endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10132
10133 segment_standby_list = 0;
10134
10135 if (!full_gc_approach_event.CreateManualEventNoThrow(FALSE))
10136 {
10137 goto cleanup;
10138 }
10139 if (!full_gc_end_event.CreateManualEventNoThrow(FALSE))
10140 {
10141 goto cleanup;
10142 }
10143
10144 fgn_maxgen_percent = 0;
10145 fgn_loh_percent = 0;
10146 full_gc_approach_event_set = false;
10147
10148 memset (full_gc_counts, 0, sizeof (full_gc_counts));
10149
10150 last_gc_index = 0;
10151 should_expand_in_full_gc = FALSE;
10152
10153#ifdef FEATURE_LOH_COMPACTION
10154 loh_compaction_always_p = GCConfig::GetLOHCompactionMode() != 0;
10155 loh_compaction_mode = loh_compaction_default;
10156#endif //FEATURE_LOH_COMPACTION
10157
10158 loh_size_threshold = (size_t)GCConfig::GetLOHThreshold();
10159 assert (loh_size_threshold >= LARGE_OBJECT_SIZE);
10160
10161#ifdef BACKGROUND_GC
10162 memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
10163 bgc_alloc_spin_count = static_cast<uint32_t>(GCConfig::GetBGCSpinCount());
10164 bgc_alloc_spin = static_cast<uint32_t>(GCConfig::GetBGCSpin());
10165
10166 {
10167 int number_bgc_threads = 1;
10168#ifdef MULTIPLE_HEAPS
10169 number_bgc_threads = n_heaps;
10170#endif //MULTIPLE_HEAPS
10171 if (!create_bgc_threads_support (number_bgc_threads))
10172 {
10173 goto cleanup;
10174 }
10175 }
10176#endif //BACKGROUND_GC
10177
10178 memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
10179
10180#ifdef GC_CONFIG_DRIVEN
10181 compact_or_sweep_gcs[0] = 0;
10182 compact_or_sweep_gcs[1] = 0;
10183#endif //GC_CONFIG_DRIVEN
10184
10185#ifdef SHORT_PLUGS
10186 short_plugs_pad_ratio = (double)DESIRED_PLUG_LENGTH / (double)(DESIRED_PLUG_LENGTH - Align (min_obj_size));
10187#endif //SHORT_PLUGS
10188
10189 ret = 1;
10190
10191cleanup:
10192
10193 if (!ret)
10194 {
10195 if (full_gc_approach_event.IsValid())
10196 {
10197 full_gc_approach_event.CloseEvent();
10198 }
10199 if (full_gc_end_event.IsValid())
10200 {
10201 full_gc_end_event.CloseEvent();
10202 }
10203 }
10204
10205 return ret;
10206}
10207
10208gc_heap* gc_heap::make_gc_heap (
10209#ifdef MULTIPLE_HEAPS
10210 GCHeap* vm_hp,
10211 int heap_number
10212#endif //MULTIPLE_HEAPS
10213 )
10214{
10215 gc_heap* res = 0;
10216
10217#ifdef MULTIPLE_HEAPS
10218 res = new (nothrow) gc_heap;
10219 if (!res)
10220 return 0;
10221
10222 res->vm_heap = vm_hp;
10223 res->alloc_context_count = 0;
10224
10225#ifdef MARK_LIST
10226#ifdef PARALLEL_MARK_LIST_SORT
10227 res->mark_list_piece_start = new (nothrow) uint8_t**[n_heaps];
10228 if (!res->mark_list_piece_start)
10229 return 0;
10230
10231#ifdef _PREFAST_
10232#pragma warning(push)
10233#pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10234#endif // _PREFAST_
10235 res->mark_list_piece_end = new (nothrow) uint8_t**[n_heaps + 32]; // +32 is padding to reduce false sharing
10236#ifdef _PREFAST_
10237#pragma warning(pop)
10238#endif // _PREFAST_
10239
10240 if (!res->mark_list_piece_end)
10241 return 0;
10242#endif //PARALLEL_MARK_LIST_SORT
10243#endif //MARK_LIST
10244
10245
10246#endif //MULTIPLE_HEAPS
10247
10248 if (res->init_gc_heap (
10249#ifdef MULTIPLE_HEAPS
10250 heap_number
10251#else //MULTIPLE_HEAPS
10252 0
10253#endif //MULTIPLE_HEAPS
10254 )==0)
10255 {
10256 return 0;
10257 }
10258
10259#ifdef MULTIPLE_HEAPS
10260 return res;
10261#else
10262 return (gc_heap*)1;
10263#endif //MULTIPLE_HEAPS
10264}
10265
10266uint32_t
10267gc_heap::wait_for_gc_done(int32_t timeOut)
10268{
10269 bool cooperative_mode = enable_preemptive ();
10270
10271 uint32_t dwWaitResult = NOERROR;
10272
10273 gc_heap* wait_heap = NULL;
10274 while (gc_heap::gc_started)
10275 {
10276#ifdef MULTIPLE_HEAPS
10277 wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
10278 dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
10279#endif // MULTIPLE_HEAPS
10280
10281#ifdef _PREFAST_
10282 PREFIX_ASSUME(wait_heap != NULL);
10283#endif // _PREFAST_
10284
10285 dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE);
10286 }
10287 disable_preemptive (cooperative_mode);
10288
10289 return dwWaitResult;
10290}
10291
10292void
10293gc_heap::set_gc_done()
10294{
10295 enter_gc_done_event_lock();
10296 if (!gc_done_event_set)
10297 {
10298 gc_done_event_set = true;
10299 dprintf (2, ("heap %d: setting gc_done_event", heap_number));
10300 gc_done_event.Set();
10301 }
10302 exit_gc_done_event_lock();
10303}
10304
10305void
10306gc_heap::reset_gc_done()
10307{
10308 enter_gc_done_event_lock();
10309 if (gc_done_event_set)
10310 {
10311 gc_done_event_set = false;
10312 dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
10313 gc_done_event.Reset();
10314 }
10315 exit_gc_done_event_lock();
10316}
10317
10318void
10319gc_heap::enter_gc_done_event_lock()
10320{
10321 uint32_t dwSwitchCount = 0;
10322retry:
10323
10324 if (Interlocked::CompareExchange(&gc_done_event_lock, 0, -1) >= 0)
10325 {
10326 while (gc_done_event_lock >= 0)
10327 {
10328 if (g_num_processors > 1)
10329 {
10330 int spin_count = yp_spin_count_unit;
10331 for (int j = 0; j < spin_count; j++)
10332 {
10333 if (gc_done_event_lock < 0)
10334 break;
10335 YieldProcessor(); // indicate to the processor that we are spining
10336 }
10337 if (gc_done_event_lock >= 0)
10338 GCToOSInterface::YieldThread(++dwSwitchCount);
10339 }
10340 else
10341 GCToOSInterface::YieldThread(++dwSwitchCount);
10342 }
10343 goto retry;
10344 }
10345}
10346
10347void
10348gc_heap::exit_gc_done_event_lock()
10349{
10350 gc_done_event_lock = -1;
10351}
10352
10353#ifndef MULTIPLE_HEAPS
10354
10355#ifdef RECORD_LOH_STATE
10356int gc_heap::loh_state_index = 0;
10357gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
10358#endif //RECORD_LOH_STATE
10359
10360VOLATILE(int32_t) gc_heap::gc_done_event_lock;
10361VOLATILE(bool) gc_heap::gc_done_event_set;
10362GCEvent gc_heap::gc_done_event;
10363#endif //!MULTIPLE_HEAPS
10364VOLATILE(bool) gc_heap::internal_gc_done;
10365
10366void gc_heap::add_saved_spinlock_info (
10367 bool loh_p,
10368 msl_enter_state enter_state,
10369 msl_take_state take_state)
10370
10371{
10372#ifdef SPINLOCK_HISTORY
10373 spinlock_info* current = &last_spinlock_info[spinlock_info_index];
10374
10375 current->enter_state = enter_state;
10376 current->take_state = take_state;
10377 current->thread_id.SetToCurrentThread();
10378 current->loh_p = loh_p;
10379 dprintf (SPINLOCK_LOG, ("[%d]%s %s %s",
10380 heap_number,
10381 (loh_p ? "loh" : "soh"),
10382 ((enter_state == me_acquire) ? "E" : "L"),
10383 msl_take_state_str[take_state]));
10384
10385 spinlock_info_index++;
10386
10387 assert (spinlock_info_index <= max_saved_spinlock_info);
10388
10389 if (spinlock_info_index >= max_saved_spinlock_info)
10390 {
10391 spinlock_info_index = 0;
10392 }
10393#else
10394 MAYBE_UNUSED_VAR(enter_state);
10395 MAYBE_UNUSED_VAR(take_state);
10396#endif //SPINLOCK_HISTORY
10397}
10398
10399int
10400gc_heap::init_gc_heap (int h_number)
10401{
10402#ifdef MULTIPLE_HEAPS
10403
10404 time_bgc_last = 0;
10405
10406#ifdef SPINLOCK_HISTORY
10407 spinlock_info_index = 0;
10408 memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
10409#endif //SPINLOCK_HISTORY
10410
10411 // initialize per heap members.
10412 ephemeral_low = (uint8_t*)1;
10413
10414 ephemeral_high = MAX_PTR;
10415
10416 ephemeral_heap_segment = 0;
10417
10418 freeable_large_heap_segment = 0;
10419
10420 condemned_generation_num = 0;
10421
10422 blocking_collection = FALSE;
10423
10424 generation_skip_ratio = 100;
10425
10426 mark_stack_tos = 0;
10427
10428 mark_stack_bos = 0;
10429
10430 mark_stack_array_length = 0;
10431
10432 mark_stack_array = 0;
10433
10434#if defined (_DEBUG) && defined (VERIFY_HEAP)
10435 verify_pinned_queue_p = FALSE;
10436#endif // _DEBUG && VERIFY_HEAP
10437
10438 loh_pinned_queue_tos = 0;
10439
10440 loh_pinned_queue_bos = 0;
10441
10442 loh_pinned_queue_length = 0;
10443
10444 loh_pinned_queue_decay = LOH_PIN_DECAY;
10445
10446 loh_pinned_queue = 0;
10447
10448 min_overflow_address = MAX_PTR;
10449
10450 max_overflow_address = 0;
10451
10452 gen0_bricks_cleared = FALSE;
10453
10454 gen0_must_clear_bricks = 0;
10455
10456 allocation_quantum = CLR_SIZE;
10457
10458 more_space_lock_soh = gc_lock;
10459
10460 more_space_lock_loh = gc_lock;
10461
10462 ro_segments_in_range = FALSE;
10463
10464 loh_alloc_since_cg = 0;
10465
10466 new_heap_segment = NULL;
10467
10468 gen0_allocated_after_gc_p = false;
10469
10470#ifdef RECORD_LOH_STATE
10471 loh_state_index = 0;
10472#endif //RECORD_LOH_STATE
10473#endif //MULTIPLE_HEAPS
10474
10475#ifdef MULTIPLE_HEAPS
10476 if (h_number > n_heaps)
10477 {
10478 assert (!"Number of heaps exceeded");
10479 return 0;
10480 }
10481
10482 heap_number = h_number;
10483#endif //MULTIPLE_HEAPS
10484
10485 memset (&oom_info, 0, sizeof (oom_info));
10486 memset (&fgm_result, 0, sizeof (fgm_result));
10487 if (!gc_done_event.CreateManualEventNoThrow(FALSE))
10488 {
10489 return 0;
10490 }
10491 gc_done_event_lock = -1;
10492 gc_done_event_set = false;
10493
10494#ifndef SEG_MAPPING_TABLE
10495 if (!gc_heap::seg_table->ensure_space_for_insert ())
10496 {
10497 return 0;
10498 }
10499#endif //!SEG_MAPPING_TABLE
10500
10501 heap_segment* seg = get_initial_segment (soh_segment_size, h_number);
10502 if (!seg)
10503 return 0;
10504
10505 FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg),
10506 (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)),
10507 gc_etw_segment_small_object_heap);
10508
10509#ifdef SEG_MAPPING_TABLE
10510 seg_mapping_table_add_segment (seg, __this);
10511#else //SEG_MAPPING_TABLE
10512 seg_table->insert ((uint8_t*)seg, sdelta);
10513#endif //SEG_MAPPING_TABLE
10514
10515#ifdef MULTIPLE_HEAPS
10516 heap_segment_heap (seg) = this;
10517#endif //MULTIPLE_HEAPS
10518
10519 /* todo: Need a global lock for this */
10520 uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))];
10521 own_card_table (ct);
10522 card_table = translate_card_table (ct);
10523 /* End of global lock */
10524
10525 brick_table = card_table_brick_table (ct);
10526 highest_address = card_table_highest_address (ct);
10527 lowest_address = card_table_lowest_address (ct);
10528
10529#ifdef CARD_BUNDLE
10530 card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
10531 assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
10532 card_table_card_bundle_table (ct));
10533#endif //CARD_BUNDLE
10534
10535#ifdef MARK_ARRAY
10536 if (gc_can_use_concurrent)
10537 mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))]));
10538 else
10539 mark_array = NULL;
10540#endif //MARK_ARRAY
10541
10542 uint8_t* start = heap_segment_mem (seg);
10543
10544 for (int i = 0; i < 1 + max_generation; i++)
10545 {
10546 make_generation (generation_table [ (max_generation - i) ],
10547 seg, start, 0);
10548 generation_table [(max_generation - i)].gen_num = max_generation - i;
10549 start += Align (min_obj_size);
10550 }
10551
10552 heap_segment_allocated (seg) = start;
10553 alloc_allocated = start;
10554 heap_segment_used (seg) = start - plug_skew;
10555
10556 ephemeral_heap_segment = seg;
10557
10558#ifndef SEG_MAPPING_TABLE
10559 if (!gc_heap::seg_table->ensure_space_for_insert ())
10560 {
10561 return 0;
10562 }
10563#endif //!SEG_MAPPING_TABLE
10564 //Create the large segment generation
10565 heap_segment* lseg = get_initial_segment(min_loh_segment_size, h_number);
10566 if (!lseg)
10567 return 0;
10568 lseg->flags |= heap_segment_flags_loh;
10569
10570 FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(lseg),
10571 (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)),
10572 gc_etw_segment_large_object_heap);
10573
10574#ifdef SEG_MAPPING_TABLE
10575 seg_mapping_table_add_segment (lseg, __this);
10576#else //SEG_MAPPING_TABLE
10577 seg_table->insert ((uint8_t*)lseg, sdelta);
10578#endif //SEG_MAPPING_TABLE
10579
10580 generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
10581 //assign the alloc_list for the large generation
10582 generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
10583 generation_table [max_generation+1].gen_num = max_generation+1;
10584 make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
10585 heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
10586 heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
10587
10588 for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
10589 {
10590 generation* gen = generation_of (gen_num);
10591 make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
10592 }
10593
10594#ifdef MULTIPLE_HEAPS
10595 heap_segment_heap (lseg) = this;
10596
10597 //initialize the alloc context heap
10598 generation_alloc_context (generation_of (0))->set_alloc_heap(vm_heap);
10599
10600 //initialize the alloc context heap
10601 generation_alloc_context (generation_of (max_generation+1))->set_alloc_heap(vm_heap);
10602
10603#endif //MULTIPLE_HEAPS
10604
10605 //Do this only once
10606#ifdef MULTIPLE_HEAPS
10607 if (h_number == 0)
10608#endif //MULTIPLE_HEAPS
10609 {
10610#ifndef INTERIOR_POINTERS
10611 //set the brick_table for large objects
10612 //but default value is clearded
10613 //clear_brick_table ((uint8_t*)heap_segment_mem (lseg),
10614 // (uint8_t*)heap_segment_reserved (lseg));
10615
10616#else //INTERIOR_POINTERS
10617
10618 //Because of the interior pointer business, we have to clear
10619 //the whole brick table
10620 //but the default value is cleared
10621 // clear_brick_table (lowest_address, highest_address);
10622#endif //INTERIOR_POINTERS
10623 }
10624
10625 if (!init_dynamic_data())
10626 {
10627 return 0;
10628 }
10629
10630 etw_allocation_running_amount[0] = 0;
10631 etw_allocation_running_amount[1] = 0;
10632
10633 //needs to be done after the dynamic data has been initialized
10634#ifndef MULTIPLE_HEAPS
10635 allocation_running_amount = dd_min_size (dynamic_data_of (0));
10636#endif //!MULTIPLE_HEAPS
10637
10638 fgn_last_alloc = dd_min_size (dynamic_data_of (0));
10639
10640 mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
10641 if (!arr)
10642 return 0;
10643
10644 make_mark_stack(arr);
10645
10646#ifdef BACKGROUND_GC
10647 freeable_small_heap_segment = 0;
10648 gchist_index_per_heap = 0;
10649 uint8_t** b_arr = new (nothrow) (uint8_t* [MARK_STACK_INITIAL_LENGTH]);
10650 if (!b_arr)
10651 return 0;
10652
10653 make_background_mark_stack (b_arr);
10654#endif //BACKGROUND_GC
10655
10656 ephemeral_low = generation_allocation_start(generation_of(max_generation - 1));
10657 ephemeral_high = heap_segment_reserved(ephemeral_heap_segment);
10658 if (heap_number == 0)
10659 {
10660 stomp_write_barrier_initialize(
10661#ifdef MULTIPLE_HEAPS
10662 reinterpret_cast<uint8_t*>(1), reinterpret_cast<uint8_t*>(~0)
10663#else
10664 ephemeral_low, ephemeral_high
10665#endif //!MULTIPLE_HEAPS
10666 );
10667 }
10668
10669#ifdef MARK_ARRAY
10670 // why would we clear the mark array for this page? it should be cleared..
10671 // clear the first committed page
10672 //if(gc_can_use_concurrent)
10673 //{
10674 // clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10675 //}
10676#endif //MARK_ARRAY
10677
10678#ifdef MULTIPLE_HEAPS
10679 //register the heap in the heaps array
10680
10681 if (!create_gc_thread ())
10682 return 0;
10683
10684 g_heaps [heap_number] = this;
10685
10686#endif //MULTIPLE_HEAPS
10687
10688#ifdef FEATURE_PREMORTEM_FINALIZATION
10689 HRESULT hr = AllocateCFinalize(&finalize_queue);
10690 if (FAILED(hr))
10691 return 0;
10692#endif // FEATURE_PREMORTEM_FINALIZATION
10693
10694 max_free_space_items = MAX_NUM_FREE_SPACES;
10695
10696 bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10697
10698 if (!bestfit_seg)
10699 {
10700 return 0;
10701 }
10702
10703 if (!bestfit_seg->alloc())
10704 {
10705 return 0;
10706 }
10707
10708 last_gc_before_oom = FALSE;
10709
10710 sufficient_gen0_space_p = FALSE;
10711
10712#ifdef MULTIPLE_HEAPS
10713
10714#ifdef HEAP_ANALYZE
10715
10716 heap_analyze_success = TRUE;
10717
10718 internal_root_array = 0;
10719
10720 internal_root_array_index = 0;
10721
10722 internal_root_array_length = initial_internal_roots;
10723
10724 current_obj = 0;
10725
10726 current_obj_size = 0;
10727
10728#endif //HEAP_ANALYZE
10729
10730#endif // MULTIPLE_HEAPS
10731
10732#ifdef BACKGROUND_GC
10733 bgc_thread_id.Clear();
10734
10735 if (!create_bgc_thread_support())
10736 {
10737 return 0;
10738 }
10739
10740 bgc_alloc_lock = new (nothrow) exclusive_sync;
10741 if (!bgc_alloc_lock)
10742 {
10743 return 0;
10744 }
10745
10746 bgc_alloc_lock->init();
10747
10748 if (h_number == 0)
10749 {
10750 if (!recursive_gc_sync::init())
10751 return 0;
10752 }
10753
10754 bgc_thread_running = 0;
10755 bgc_thread = 0;
10756 bgc_threads_timeout_cs.Initialize();
10757 expanded_in_fgc = 0;
10758 current_bgc_state = bgc_not_in_process;
10759 background_soh_alloc_count = 0;
10760 background_loh_alloc_count = 0;
10761 bgc_overflow_count = 0;
10762 end_loh_size = dd_min_size (dynamic_data_of (max_generation + 1));
10763#endif //BACKGROUND_GC
10764
10765#ifdef GC_CONFIG_DRIVEN
10766 memset (interesting_data_per_heap, 0, sizeof (interesting_data_per_heap));
10767 memset(compact_reasons_per_heap, 0, sizeof (compact_reasons_per_heap));
10768 memset(expand_mechanisms_per_heap, 0, sizeof (expand_mechanisms_per_heap));
10769 memset(interesting_mechanism_bits_per_heap, 0, sizeof (interesting_mechanism_bits_per_heap));
10770#endif //GC_CONFIG_DRIVEN
10771
10772 return 1;
10773}
10774
10775void
10776gc_heap::destroy_semi_shared()
10777{
10778//TODO: will need to move this to per heap
10779//#ifdef BACKGROUND_GC
10780// if (c_mark_list)
10781// delete c_mark_list;
10782//#endif //BACKGROUND_GC
10783
10784#ifdef MARK_LIST
10785 if (g_mark_list)
10786 delete g_mark_list;
10787#endif //MARK_LIST
10788
10789#if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10790 if (seg_mapping_table)
10791 delete seg_mapping_table;
10792#endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10793
10794#if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10795 //destroy the segment map
10796 seg_table->delete_sorted_table();
10797#endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10798}
10799
10800void
10801gc_heap::self_destroy()
10802{
10803#ifdef BACKGROUND_GC
10804 kill_gc_thread();
10805#endif //BACKGROUND_GC
10806
10807 if (gc_done_event.IsValid())
10808 {
10809 gc_done_event.CloseEvent();
10810 }
10811
10812 // destroy every segment.
10813 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10814
10815 PREFIX_ASSUME(seg != NULL);
10816
10817 heap_segment* next_seg;
10818 while (seg)
10819 {
10820 next_seg = heap_segment_next_rw (seg);
10821 delete_heap_segment (seg);
10822 seg = next_seg;
10823 }
10824
10825 seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10826
10827 PREFIX_ASSUME(seg != NULL);
10828
10829 while (seg)
10830 {
10831 next_seg = heap_segment_next_rw (seg);
10832 delete_heap_segment (seg);
10833 seg = next_seg;
10834 }
10835
10836 // get rid of the card table
10837 release_card_table (card_table);
10838
10839 // destroy the mark stack
10840 delete mark_stack_array;
10841
10842#ifdef FEATURE_PREMORTEM_FINALIZATION
10843 if (finalize_queue)
10844 delete finalize_queue;
10845#endif // FEATURE_PREMORTEM_FINALIZATION
10846}
10847
10848void
10849gc_heap::destroy_gc_heap(gc_heap* heap)
10850{
10851 heap->self_destroy();
10852 delete heap;
10853}
10854
10855// Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10856// the finalizer queue has been drained.
10857void gc_heap::shutdown_gc()
10858{
10859 destroy_semi_shared();
10860
10861#ifdef MULTIPLE_HEAPS
10862 //delete the heaps array
10863 delete g_heaps;
10864 destroy_thread_support();
10865 n_heaps = 0;
10866#endif //MULTIPLE_HEAPS
10867 //destroy seg_manager
10868
10869 destroy_initial_memory();
10870
10871 GCToOSInterface::Shutdown();
10872}
10873
10874inline
10875BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10876 uint8_t* old_loc, int use_padding)
10877{
10878 BOOL already_padded = FALSE;
10879#ifdef SHORT_PLUGS
10880 if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
10881 {
10882 alloc_pointer = alloc_pointer + Align (min_obj_size);
10883 already_padded = TRUE;
10884 }
10885#endif //SHORT_PLUGS
10886
10887 if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
10888 size = size + switch_alignment_size (already_padded);
10889
10890#ifdef FEATURE_STRUCTALIGN
10891 alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
10892#endif // FEATURE_STRUCTALIGN
10893
10894 // in allocate_in_condemned_generation we can have this when we
10895 // set the alloc_limit to plan_allocated which could be less than
10896 // alloc_ptr
10897 if (alloc_limit < alloc_pointer)
10898 {
10899 return FALSE;
10900 }
10901
10902 if (old_loc != 0)
10903 {
10904 return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0)))
10905#ifdef SHORT_PLUGS
10906 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
10907#else //SHORT_PLUGS
10908 ||((alloc_pointer + size) == alloc_limit)
10909#endif //SHORT_PLUGS
10910 );
10911 }
10912 else
10913 {
10914 assert (size == Align (min_obj_size));
10915 return ((size_t)(alloc_limit - alloc_pointer) >= size);
10916 }
10917}
10918
10919inline
10920BOOL gc_heap::a_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit,
10921 int align_const)
10922{
10923 // We could have run into cases where this is true when alloc_allocated is the
10924 // the same as the seg committed.
10925 if (alloc_limit < alloc_pointer)
10926 {
10927 return FALSE;
10928 }
10929
10930 return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
10931}
10932
10933// Grow by committing more pages
10934BOOL gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* high_address)
10935{
10936 assert (high_address <= heap_segment_reserved (seg));
10937
10938 //return 0 if we are at the end of the segment.
10939 if (align_on_page (high_address) > heap_segment_reserved (seg))
10940 return FALSE;
10941
10942 if (high_address <= heap_segment_committed (seg))
10943 return TRUE;
10944
10945 size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
10946 c_size = max (c_size, 16*OS_PAGE_SIZE);
10947 c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
10948
10949 if (c_size == 0)
10950 return FALSE;
10951
10952 STRESS_LOG2(LF_GC, LL_INFO10000,
10953 "Growing heap_segment: %Ix high address: %Ix\n",
10954 (size_t)seg, (size_t)high_address);
10955
10956 dprintf(3, ("Growing segment allocation %Ix %Ix", (size_t)heap_segment_committed(seg),c_size));
10957
10958 if (!virtual_alloc_commit_for_heap(heap_segment_committed (seg), c_size, heap_number))
10959 {
10960 dprintf(3, ("Cannot grow heap segment"));
10961 return FALSE;
10962 }
10963#ifdef MARK_ARRAY
10964#ifndef BACKGROUND_GC
10965 clear_mark_array (heap_segment_committed (seg),
10966 heap_segment_committed (seg)+c_size, TRUE);
10967#endif //BACKGROUND_GC
10968#endif //MARK_ARRAY
10969 heap_segment_committed (seg) += c_size;
10970 STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
10971 (size_t)heap_segment_committed (seg));
10972
10973 assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
10974
10975 assert (high_address <= heap_segment_committed (seg));
10976
10977 return TRUE;
10978}
10979
10980inline
10981int 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)
10982{
10983#ifdef SHORT_PLUGS
10984 if ((old_loc != 0) && pad_front_p)
10985 {
10986 allocated = allocated + Align (min_obj_size);
10987 }
10988#endif //SHORT_PLUGS
10989
10990 if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
10991 size = size + switch_alignment_size (FALSE);
10992#ifdef FEATURE_STRUCTALIGN
10993 size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
10994 return grow_heap_segment (seg, allocated + pad + size);
10995#else // FEATURE_STRUCTALIGN
10996 return grow_heap_segment (seg, allocated + size);
10997#endif // FEATURE_STRUCTALIGN
10998}
10999
11000//used only in older generation allocation (i.e during gc).
11001void gc_heap::adjust_limit (uint8_t* start, size_t limit_size, generation* gen,
11002 int gennum)
11003{
11004 UNREFERENCED_PARAMETER(gennum);
11005 dprintf (3, ("gc Expanding segment allocation"));
11006 heap_segment* seg = generation_allocation_segment (gen);
11007 if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
11008 {
11009 if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
11010 {
11011 assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
11012 assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
11013 heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
11014 }
11015 else
11016 {
11017 uint8_t* hole = generation_allocation_pointer (gen);
11018 size_t size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
11019
11020 if (size != 0)
11021 {
11022 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
11023 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
11024 if (size >= Align (min_free_list))
11025 {
11026 if (allocated_size < min_free_list)
11027 {
11028 if (size >= (Align (min_free_list) + Align (min_obj_size)))
11029 {
11030 //split hole into min obj + threadable free item
11031 make_unused_array (hole, min_obj_size);
11032 generation_free_obj_space (gen) += Align (min_obj_size);
11033 make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
11034 generation_free_list_space (gen) += size - Align (min_obj_size);
11035 generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size),
11036 size - Align (min_obj_size));
11037 add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
11038 }
11039 else
11040 {
11041 dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
11042 make_unused_array (hole, size);
11043 generation_free_obj_space (gen) += size;
11044 }
11045 }
11046 else
11047 {
11048 dprintf (3, ("threading hole in front of free list"));
11049 make_unused_array (hole, size);
11050 generation_free_list_space (gen) += size;
11051 generation_allocator(gen)->thread_item_front (hole, size);
11052 add_gen_free (gen->gen_num, size);
11053 }
11054 }
11055 else
11056 {
11057 make_unused_array (hole, size);
11058 generation_free_obj_space (gen) += size;
11059 }
11060 }
11061 }
11062 generation_allocation_pointer (gen) = start;
11063 generation_allocation_context_start_region (gen) = start;
11064 }
11065 generation_allocation_limit (gen) = (start + limit_size);
11066}
11067
11068void verify_mem_cleared (uint8_t* start, size_t size)
11069{
11070 if (!Aligned (size))
11071 {
11072 FATAL_GC_ERROR();
11073 }
11074
11075 PTR_PTR curr_ptr = (PTR_PTR) start;
11076 for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
11077 {
11078 if (*(curr_ptr++) != 0)
11079 {
11080 FATAL_GC_ERROR();
11081 }
11082 }
11083}
11084
11085#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
11086void gc_heap::set_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11087{
11088 size_t start_mark_bit = mark_bit_of (start);
11089 size_t end_mark_bit = mark_bit_of (end);
11090 unsigned int startbit = mark_bit_bit (start_mark_bit);
11091 unsigned int endbit = mark_bit_bit (end_mark_bit);
11092 size_t startwrd = mark_bit_word (start_mark_bit);
11093 size_t endwrd = mark_bit_word (end_mark_bit);
11094
11095 dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
11096 (size_t)start, (size_t)start_mark_bit,
11097 (size_t)end, (size_t)end_mark_bit));
11098
11099 unsigned int firstwrd = ~(lowbits (~0, startbit));
11100 unsigned int lastwrd = ~(highbits (~0, endbit));
11101
11102 if (startwrd == endwrd)
11103 {
11104 unsigned int wrd = firstwrd & lastwrd;
11105 mark_array[startwrd] |= wrd;
11106 return;
11107 }
11108
11109 // set the first mark word.
11110 if (startbit)
11111 {
11112 mark_array[startwrd] |= firstwrd;
11113 startwrd++;
11114 }
11115
11116 for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11117 {
11118 mark_array[wrdtmp] = ~(unsigned int)0;
11119 }
11120
11121 // set the last mark word.
11122 if (endbit)
11123 {
11124 mark_array[endwrd] |= lastwrd;
11125 }
11126}
11127
11128// makes sure that the mark array bits between start and end are 0.
11129void gc_heap::check_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11130{
11131 size_t start_mark_bit = mark_bit_of (start);
11132 size_t end_mark_bit = mark_bit_of (end);
11133 unsigned int startbit = mark_bit_bit (start_mark_bit);
11134 unsigned int endbit = mark_bit_bit (end_mark_bit);
11135 size_t startwrd = mark_bit_word (start_mark_bit);
11136 size_t endwrd = mark_bit_word (end_mark_bit);
11137
11138 //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix",
11139 // (size_t)start, (size_t)start_mark_bit,
11140 // (size_t)end, (size_t)end_mark_bit));
11141
11142 unsigned int firstwrd = ~(lowbits (~0, startbit));
11143 unsigned int lastwrd = ~(highbits (~0, endbit));
11144
11145 if (startwrd == endwrd)
11146 {
11147 unsigned int wrd = firstwrd & lastwrd;
11148 if (mark_array[startwrd] & wrd)
11149 {
11150 dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
11151 wrd, startwrd,
11152 mark_array [startwrd], mark_word_address (startwrd)));
11153 FATAL_GC_ERROR();
11154 }
11155 return;
11156 }
11157
11158 // set the first mark word.
11159 if (startbit)
11160 {
11161 if (mark_array[startwrd] & firstwrd)
11162 {
11163 dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
11164 firstwrd, startwrd,
11165 mark_array [startwrd], mark_word_address (startwrd)));
11166 FATAL_GC_ERROR();
11167 }
11168
11169 startwrd++;
11170 }
11171
11172 for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11173 {
11174 if (mark_array[wrdtmp])
11175 {
11176 dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
11177 wrdtmp,
11178 mark_array [wrdtmp], mark_word_address (wrdtmp)));
11179 FATAL_GC_ERROR();
11180 }
11181 }
11182
11183 // set the last mark word.
11184 if (endbit)
11185 {
11186 if (mark_array[endwrd] & lastwrd)
11187 {
11188 dprintf (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
11189 lastwrd, lastwrd,
11190 mark_array [lastwrd], mark_word_address (lastwrd)));
11191 FATAL_GC_ERROR();
11192 }
11193 }
11194}
11195#endif //VERIFY_HEAP && BACKGROUND_GC
11196
11197allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
11198{
11199 assert (num_b < MAX_BUCKET_COUNT);
11200 num_buckets = num_b;
11201 frst_bucket_size = fbs;
11202 buckets = b;
11203}
11204
11205alloc_list& allocator::alloc_list_of (unsigned int bn)
11206{
11207 assert (bn < num_buckets);
11208 if (bn == 0)
11209 return first_bucket;
11210 else
11211 return buckets [bn-1];
11212}
11213
11214size_t& allocator::alloc_list_damage_count_of (unsigned int bn)
11215{
11216 assert (bn < num_buckets);
11217 if (bn == 0)
11218 return first_bucket.alloc_list_damage_count();
11219 else
11220 return buckets [bn-1].alloc_list_damage_count();
11221}
11222
11223void allocator::unlink_item (unsigned int bn, uint8_t* item, uint8_t* prev_item, BOOL use_undo_p)
11224{
11225 //unlink the free_item
11226 alloc_list* al = &alloc_list_of (bn);
11227 if (prev_item)
11228 {
11229 if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
11230 {
11231 assert (item == free_list_slot (prev_item));
11232 free_list_undo (prev_item) = item;
11233 alloc_list_damage_count_of (bn)++;
11234 }
11235 free_list_slot (prev_item) = free_list_slot(item);
11236 }
11237 else
11238 {
11239 al->alloc_list_head() = (uint8_t*)free_list_slot(item);
11240 }
11241 if (al->alloc_list_tail() == item)
11242 {
11243 al->alloc_list_tail() = prev_item;
11244 }
11245}
11246
11247void allocator::clear()
11248{
11249 for (unsigned int i = 0; i < num_buckets; i++)
11250 {
11251 alloc_list_head_of (i) = 0;
11252 alloc_list_tail_of (i) = 0;
11253 }
11254}
11255
11256//always thread to the end.
11257void allocator::thread_free_item (uint8_t* item, uint8_t*& head, uint8_t*& tail)
11258{
11259 free_list_slot (item) = 0;
11260 free_list_undo (item) = UNDO_EMPTY;
11261 assert (item != head);
11262
11263 if (head == 0)
11264 {
11265 head = item;
11266 }
11267 //TODO: This shouldn't happen anymore - verify that's the case.
11268 //the following is necessary because the last free element
11269 //may have been truncated, and tail isn't updated.
11270 else if (free_list_slot (head) == 0)
11271 {
11272 free_list_slot (head) = item;
11273 }
11274 else
11275 {
11276 assert (item != tail);
11277 assert (free_list_slot(tail) == 0);
11278 free_list_slot (tail) = item;
11279 }
11280 tail = item;
11281}
11282
11283void allocator::thread_item (uint8_t* item, size_t size)
11284{
11285 size_t sz = frst_bucket_size;
11286 unsigned int a_l_number = 0;
11287
11288 for (; a_l_number < (num_buckets-1); a_l_number++)
11289 {
11290 if (size < sz)
11291 {
11292 break;
11293 }
11294 sz = sz * 2;
11295 }
11296 alloc_list* al = &alloc_list_of (a_l_number);
11297 thread_free_item (item,
11298 al->alloc_list_head(),
11299 al->alloc_list_tail());
11300}
11301
11302void allocator::thread_item_front (uint8_t* item, size_t size)
11303{
11304 //find right free list
11305 size_t sz = frst_bucket_size;
11306 unsigned int a_l_number = 0;
11307 for (; a_l_number < (num_buckets-1); a_l_number++)
11308 {
11309 if (size < sz)
11310 {
11311 break;
11312 }
11313 sz = sz * 2;
11314 }
11315 alloc_list* al = &alloc_list_of (a_l_number);
11316 free_list_slot (item) = al->alloc_list_head();
11317 free_list_undo (item) = UNDO_EMPTY;
11318
11319 if (al->alloc_list_tail() == 0)
11320 {
11321 al->alloc_list_tail() = al->alloc_list_head();
11322 }
11323 al->alloc_list_head() = item;
11324 if (al->alloc_list_tail() == 0)
11325 {
11326 al->alloc_list_tail() = item;
11327 }
11328}
11329
11330void allocator::copy_to_alloc_list (alloc_list* toalist)
11331{
11332 for (unsigned int i = 0; i < num_buckets; i++)
11333 {
11334 toalist [i] = alloc_list_of (i);
11335#ifdef FL_VERIFICATION
11336 uint8_t* free_item = alloc_list_head_of (i);
11337 size_t count = 0;
11338 while (free_item)
11339 {
11340 count++;
11341 free_item = free_list_slot (free_item);
11342 }
11343
11344 toalist[i].item_count = count;
11345#endif //FL_VERIFICATION
11346 }
11347}
11348
11349void allocator::copy_from_alloc_list (alloc_list* fromalist)
11350{
11351 BOOL repair_list = !discard_if_no_fit_p ();
11352 for (unsigned int i = 0; i < num_buckets; i++)
11353 {
11354 size_t count = alloc_list_damage_count_of (i);
11355 alloc_list_of (i) = fromalist [i];
11356 assert (alloc_list_damage_count_of (i) == 0);
11357
11358 if (repair_list)
11359 {
11360 //repair the the list
11361 //new items may have been added during the plan phase
11362 //items may have been unlinked.
11363 uint8_t* free_item = alloc_list_head_of (i);
11364 while (free_item && count)
11365 {
11366 assert (((CObjectHeader*)free_item)->IsFree());
11367 if ((free_list_undo (free_item) != UNDO_EMPTY))
11368 {
11369 count--;
11370 free_list_slot (free_item) = free_list_undo (free_item);
11371 free_list_undo (free_item) = UNDO_EMPTY;
11372 }
11373
11374 free_item = free_list_slot (free_item);
11375 }
11376
11377#ifdef FL_VERIFICATION
11378 free_item = alloc_list_head_of (i);
11379 size_t item_count = 0;
11380 while (free_item)
11381 {
11382 item_count++;
11383 free_item = free_list_slot (free_item);
11384 }
11385
11386 assert (item_count == alloc_list_of (i).item_count);
11387#endif //FL_VERIFICATION
11388 }
11389#ifdef DEBUG
11390 uint8_t* tail_item = alloc_list_tail_of (i);
11391 assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
11392#endif
11393 }
11394}
11395
11396void allocator::commit_alloc_list_changes()
11397{
11398 BOOL repair_list = !discard_if_no_fit_p ();
11399 if (repair_list)
11400 {
11401 for (unsigned int i = 0; i < num_buckets; i++)
11402 {
11403 //remove the undo info from list.
11404 uint8_t* free_item = alloc_list_head_of (i);
11405 size_t count = alloc_list_damage_count_of (i);
11406 while (free_item && count)
11407 {
11408 assert (((CObjectHeader*)free_item)->IsFree());
11409
11410 if (free_list_undo (free_item) != UNDO_EMPTY)
11411 {
11412 free_list_undo (free_item) = UNDO_EMPTY;
11413 count--;
11414 }
11415
11416 free_item = free_list_slot (free_item);
11417 }
11418
11419 alloc_list_damage_count_of (i) = 0;
11420 }
11421 }
11422}
11423
11424void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size,
11425 alloc_context* acontext, heap_segment* seg,
11426 int align_const, int gen_number)
11427{
11428 bool loh_p = (gen_number > 0);
11429 GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
11430
11431 size_t aligned_min_obj_size = Align(min_obj_size, align_const);
11432
11433 if (seg)
11434 {
11435 assert (heap_segment_used (seg) <= heap_segment_committed (seg));
11436 }
11437
11438#ifdef MULTIPLE_HEAPS
11439 if (gen_number == 0)
11440 {
11441 if (!gen0_allocated_after_gc_p)
11442 {
11443 gen0_allocated_after_gc_p = true;
11444 }
11445 }
11446#endif //MULTIPLE_HEAPS
11447
11448 dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
11449 (size_t)start + limit_size - aligned_min_obj_size));
11450
11451 if ((acontext->alloc_limit != start) &&
11452 (acontext->alloc_limit + aligned_min_obj_size)!= start)
11453 {
11454 uint8_t* hole = acontext->alloc_ptr;
11455 if (hole != 0)
11456 {
11457 size_t size = (acontext->alloc_limit - acontext->alloc_ptr);
11458 dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
11459 // when we are finishing an allocation from a free list
11460 // we know that the free area was Align(min_obj_size) larger
11461 acontext->alloc_bytes -= size;
11462 size_t free_obj_size = size + aligned_min_obj_size;
11463 make_unused_array (hole, free_obj_size);
11464 generation_free_obj_space (generation_of (gen_number)) += free_obj_size;
11465 }
11466 acontext->alloc_ptr = start;
11467 }
11468 else
11469 {
11470 if (gen_number == 0)
11471 {
11472 size_t pad_size = Align (min_obj_size, align_const);
11473 make_unused_array (acontext->alloc_ptr, pad_size);
11474 dprintf (3, ("contigous ac: making min obj gap %Ix->%Ix(%Id)",
11475 acontext->alloc_ptr, (acontext->alloc_ptr + pad_size), pad_size));
11476 acontext->alloc_ptr += pad_size;
11477 }
11478 }
11479 acontext->alloc_limit = (start + limit_size - aligned_min_obj_size);
11480 acontext->alloc_bytes += limit_size - ((gen_number < max_generation + 1) ? aligned_min_obj_size : 0);
11481
11482#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11483 if (g_fEnableAppDomainMonitoring)
11484 {
11485 GCToEEInterface::RecordAllocatedBytesForHeap(limit_size, heap_number);
11486 }
11487#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11488
11489 uint8_t* saved_used = 0;
11490
11491 if (seg)
11492 {
11493 saved_used = heap_segment_used (seg);
11494 }
11495
11496 if (seg == ephemeral_heap_segment)
11497 {
11498 //Sometimes the allocated size is advanced without clearing the
11499 //memory. Let's catch up here
11500 if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
11501 {
11502#ifdef MARK_ARRAY
11503#ifndef BACKGROUND_GC
11504 clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
11505#endif //BACKGROUND_GC
11506#endif //MARK_ARRAY
11507 heap_segment_used (seg) = alloc_allocated - plug_skew;
11508 }
11509 }
11510#ifdef BACKGROUND_GC
11511 else if (seg)
11512 {
11513 uint8_t* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
11514#ifdef FEATURE_LOH_COMPACTION
11515 old_allocated -= Align (loh_padding_obj_size, align_const);
11516#endif //FEATURE_LOH_COMPACTION
11517
11518 assert (heap_segment_used (seg) >= old_allocated);
11519 }
11520#endif //BACKGROUND_GC
11521 if ((seg == 0) ||
11522 (start - plug_skew + limit_size) <= heap_segment_used (seg))
11523 {
11524 add_saved_spinlock_info (loh_p, me_release, mt_clr_mem);
11525 leave_spin_lock (msl);
11526 dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
11527 memclr (start - plug_skew, limit_size);
11528 }
11529 else
11530 {
11531 uint8_t* used = heap_segment_used (seg);
11532 heap_segment_used (seg) = start + limit_size - plug_skew;
11533
11534 add_saved_spinlock_info (loh_p, me_release, mt_clr_mem);
11535 leave_spin_lock (msl);
11536
11537 if ((start - plug_skew) < used)
11538 {
11539 if (used != saved_used)
11540 {
11541 FATAL_GC_ERROR ();
11542 }
11543
11544 dprintf (2, ("clearing memory before used at %Ix for %Id bytes",
11545 (start - plug_skew), (plug_skew + used - start)));
11546 memclr (start - plug_skew, used - (start - plug_skew));
11547 }
11548 }
11549
11550 //this portion can be done after we release the lock
11551 if (seg == ephemeral_heap_segment)
11552 {
11553#ifdef FFIND_OBJECT
11554 if (gen0_must_clear_bricks > 0)
11555 {
11556 //set the brick table to speed up find_object
11557 size_t b = brick_of (acontext->alloc_ptr);
11558 set_brick (b, acontext->alloc_ptr - brick_address (b));
11559 b++;
11560 dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
11561 b, brick_of (align_on_brick (start + limit_size))));
11562 volatile short* x = &brick_table [b];
11563 short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
11564
11565 for (;x < end_x;x++)
11566 *x = -1;
11567 }
11568 else
11569#endif //FFIND_OBJECT
11570 {
11571 gen0_bricks_cleared = FALSE;
11572 }
11573 }
11574
11575 // verifying the memory is completely cleared.
11576 //verify_mem_cleared (start - plug_skew, limit_size);
11577}
11578
11579size_t gc_heap::new_allocation_limit (size_t size, size_t physical_limit, int gen_number)
11580{
11581 dynamic_data* dd = dynamic_data_of (gen_number);
11582 ptrdiff_t new_alloc = dd_new_allocation (dd);
11583 assert (new_alloc == (ptrdiff_t)Align (new_alloc,
11584 get_alignment_constant (!(gen_number == (max_generation+1)))));
11585
11586 ptrdiff_t logical_limit = max (new_alloc, (ptrdiff_t)size);
11587 size_t limit = min (logical_limit, (ptrdiff_t)physical_limit);
11588 assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
11589 dd_new_allocation (dd) = (new_alloc - limit);
11590 return limit;
11591}
11592
11593size_t gc_heap::limit_from_size (size_t size, size_t physical_limit, int gen_number,
11594 int align_const)
11595{
11596 size_t padded_size = size + Align (min_obj_size, align_const);
11597 // for LOH this is not true...we could select a physical_limit that's exactly the same
11598 // as size.
11599 assert ((gen_number != 0) || (physical_limit >= padded_size));
11600 size_t min_size_to_allocate = ((gen_number == 0) ? allocation_quantum : 0);
11601
11602 // For SOH if the size asked for is very small, we want to allocate more than
11603 // just what's asked for if possible.
11604 size_t desired_size_to_allocate = max (padded_size, min_size_to_allocate);
11605 size_t new_physical_limit = min (physical_limit, desired_size_to_allocate);
11606
11607 size_t new_limit = new_allocation_limit (padded_size,
11608 new_physical_limit,
11609 gen_number);
11610 assert (new_limit >= (size + Align (min_obj_size, align_const)));
11611 dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
11612 return new_limit;
11613}
11614
11615void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size,
11616 uint8_t* allocated, uint8_t* reserved)
11617{
11618 dprintf (1, ("total committed on the heap is %Id", get_total_committed_size()));
11619
11620 UNREFERENCED_PARAMETER(heap_num);
11621
11622 if (reason == oom_budget)
11623 {
11624 alloc_size = dd_min_size (dynamic_data_of (0)) / 2;
11625 }
11626
11627 if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
11628 {
11629 // This means during the last GC we needed to reserve and/or commit more memory
11630 // but we couldn't. We proceeded with the GC and ended up not having enough
11631 // memory at the end. This is a legitimate OOM situtation. Otherwise we
11632 // probably made a mistake and didn't expand the heap when we should have.
11633 reason = oom_low_mem;
11634 }
11635
11636 oom_info.reason = reason;
11637 oom_info.allocated = allocated;
11638 oom_info.reserved = reserved;
11639 oom_info.alloc_size = alloc_size;
11640 oom_info.gc_index = settings.gc_index;
11641 oom_info.fgm = fgm_result.fgm;
11642 oom_info.size = fgm_result.size;
11643 oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
11644 oom_info.loh_p = fgm_result.loh_p;
11645
11646 fgm_result.fgm = fgm_no_failure;
11647
11648 // Break early - before the more_space_lock is release so no other threads
11649 // could have allocated on the same heap when OOM happened.
11650 if (GCConfig::GetBreakOnOOM())
11651 {
11652 GCToOSInterface::DebugBreak();
11653 }
11654}
11655
11656#ifdef BACKGROUND_GC
11657BOOL gc_heap::background_allowed_p()
11658{
11659 return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
11660}
11661#endif //BACKGROUND_GC
11662
11663void gc_heap::check_for_full_gc (int gen_num, size_t size)
11664{
11665 BOOL should_notify = FALSE;
11666 // if we detect full gc because of the allocation budget specified this is TRUE;
11667 // it's FALSE if it's due to other factors.
11668 BOOL alloc_factor = TRUE;
11669 int i = 0;
11670 int n = 0;
11671 int n_initial = gen_num;
11672 BOOL local_blocking_collection = FALSE;
11673 BOOL local_elevation_requested = FALSE;
11674 int new_alloc_remain_percent = 0;
11675
11676 if (full_gc_approach_event_set)
11677 {
11678 return;
11679 }
11680
11681 if (gen_num != (max_generation + 1))
11682 {
11683 gen_num = max_generation;
11684 }
11685
11686 dynamic_data* dd_full = dynamic_data_of (gen_num);
11687 ptrdiff_t new_alloc_remain = 0;
11688 uint32_t pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
11689
11690 for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
11691 {
11692 dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)",
11693 heap_number, gen_index,
11694 dd_new_allocation (dynamic_data_of (gen_index)),
11695 dd_desired_allocation (dynamic_data_of (gen_index))));
11696 }
11697
11698 // For small object allocations we only check every fgn_check_quantum bytes.
11699 if (n_initial == 0)
11700 {
11701 dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
11702 dynamic_data* dd_0 = dynamic_data_of (n_initial);
11703 if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
11704 (dd_new_allocation (dd_0) >= 0))
11705 {
11706 return;
11707 }
11708 else
11709 {
11710 fgn_last_alloc = dd_new_allocation (dd_0);
11711 dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
11712 }
11713
11714 // We don't consider the size that came from soh 'cause it doesn't contribute to the
11715 // gen2 budget.
11716 size = 0;
11717 }
11718
11719 for (i = n+1; i <= max_generation; i++)
11720 {
11721 if (get_new_allocation (i) <= 0)
11722 {
11723 n = min (i, max_generation);
11724 }
11725 else
11726 break;
11727 }
11728
11729 dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
11730 if (gen_num == max_generation)
11731 {
11732 // If it's small object heap we should first see if we will even be looking at gen2 budget
11733 // in the next GC or not. If not we should go directly to checking other factors.
11734 if (n < (max_generation - 1))
11735 {
11736 goto check_other_factors;
11737 }
11738 }
11739
11740 new_alloc_remain = dd_new_allocation (dd_full) - size;
11741
11742 new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
11743
11744 dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%",
11745 gen_num, pct, new_alloc_remain_percent));
11746
11747 if (new_alloc_remain_percent <= (int)pct)
11748 {
11749#ifdef BACKGROUND_GC
11750 // If background GC is enabled, we still want to check whether this will
11751 // be a blocking GC or not because we only want to notify when it's a
11752 // blocking full GC.
11753 if (background_allowed_p())
11754 {
11755 goto check_other_factors;
11756 }
11757#endif //BACKGROUND_GC
11758
11759 should_notify = TRUE;
11760 goto done;
11761 }
11762
11763check_other_factors:
11764
11765 dprintf (2, ("FGC: checking other factors"));
11766 n = generation_to_condemn (n,
11767 &local_blocking_collection,
11768 &local_elevation_requested,
11769 TRUE);
11770
11771 if (local_elevation_requested && (n == max_generation))
11772 {
11773 if (settings.should_lock_elevation)
11774 {
11775 int local_elevation_locked_count = settings.elevation_locked_count + 1;
11776 if (local_elevation_locked_count != 6)
11777 {
11778 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1",
11779 local_elevation_locked_count));
11780 n = max_generation - 1;
11781 }
11782 }
11783 }
11784
11785 dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11786
11787#ifdef BACKGROUND_GC
11788 // When background GC is enabled it decreases the accuracy of our predictability -
11789 // by the time the GC happens, we may not be under BGC anymore. If we try to
11790 // predict often enough it should be ok.
11791 if ((n == max_generation) &&
11792 (recursive_gc_sync::background_running_p()))
11793 {
11794 n = max_generation - 1;
11795 dprintf (2, ("FGN: bgc - 1 instead of 2"));
11796 }
11797
11798 if ((n == max_generation) && !local_blocking_collection)
11799 {
11800 if (!background_allowed_p())
11801 {
11802 local_blocking_collection = TRUE;
11803 }
11804 }
11805#endif //BACKGROUND_GC
11806
11807 dprintf (2, ("FGN: we estimate gen%d will be collected: %s",
11808 n,
11809 (local_blocking_collection ? "blocking" : "background")));
11810
11811 if ((n == max_generation) && local_blocking_collection)
11812 {
11813 alloc_factor = FALSE;
11814 should_notify = TRUE;
11815 goto done;
11816 }
11817
11818done:
11819
11820 if (should_notify)
11821 {
11822 dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)",
11823 n_initial,
11824 (alloc_factor ? "alloc" : "other"),
11825 dd_collection_count (dynamic_data_of (0)),
11826 new_alloc_remain_percent,
11827 gen_num));
11828
11829 send_full_gc_notification (n_initial, alloc_factor);
11830 }
11831}
11832
11833void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11834{
11835 if (!full_gc_approach_event_set)
11836 {
11837 assert (full_gc_approach_event.IsValid());
11838 FIRE_EVENT(GCFullNotify_V1, gen_num, due_to_alloc_p);
11839
11840 full_gc_end_event.Reset();
11841 full_gc_approach_event.Set();
11842 full_gc_approach_event_set = true;
11843 }
11844}
11845
11846wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms)
11847{
11848 if (fgn_maxgen_percent == 0)
11849 {
11850 return wait_full_gc_na;
11851 }
11852
11853 uint32_t wait_result = user_thread_wait(event, FALSE, time_out_ms);
11854
11855 if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11856 {
11857 if (fgn_maxgen_percent == 0)
11858 {
11859 return wait_full_gc_cancelled;
11860 }
11861
11862 if (wait_result == WAIT_OBJECT_0)
11863 {
11864#ifdef BACKGROUND_GC
11865 if (fgn_last_gc_was_concurrent)
11866 {
11867 fgn_last_gc_was_concurrent = FALSE;
11868 return wait_full_gc_na;
11869 }
11870 else
11871#endif //BACKGROUND_GC
11872 {
11873 return wait_full_gc_success;
11874 }
11875 }
11876 else
11877 {
11878 return wait_full_gc_timeout;
11879 }
11880 }
11881 else
11882 {
11883 return wait_full_gc_failed;
11884 }
11885}
11886
11887size_t gc_heap::get_full_compact_gc_count()
11888{
11889 return full_gc_counts[gc_type_compacting];
11890}
11891
11892// DTREVIEW - we should check this in dt_low_ephemeral_space_p
11893// as well.
11894inline
11895BOOL gc_heap::short_on_end_of_seg (int gen_number,
11896 heap_segment* seg,
11897 int align_const)
11898{
11899 UNREFERENCED_PARAMETER(gen_number);
11900 uint8_t* allocated = heap_segment_allocated(seg);
11901
11902 BOOL sufficient_p = a_size_fit_p (end_space_after_gc(),
11903 allocated,
11904 heap_segment_reserved (seg),
11905 align_const);
11906
11907 if (!sufficient_p)
11908 {
11909 if (sufficient_gen0_space_p)
11910 {
11911 dprintf (GTC_LOG, ("gen0 has enough free space"));
11912 }
11913
11914 sufficient_p = sufficient_gen0_space_p;
11915 }
11916
11917 return !sufficient_p;
11918}
11919
11920#ifdef _MSC_VER
11921#pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
11922#endif // _MSC_VER
11923
11924inline
11925BOOL gc_heap::a_fit_free_list_p (int gen_number,
11926 size_t size,
11927 alloc_context* acontext,
11928 int align_const)
11929{
11930 BOOL can_fit = FALSE;
11931 generation* gen = generation_of (gen_number);
11932 allocator* gen_allocator = generation_allocator (gen);
11933 size_t sz_list = gen_allocator->first_bucket_size();
11934 for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
11935 {
11936 if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
11937 {
11938 uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
11939 uint8_t* prev_free_item = 0;
11940
11941 while (free_list != 0)
11942 {
11943 dprintf (3, ("considering free list %Ix", (size_t)free_list));
11944 size_t free_list_size = unused_array_size (free_list);
11945 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
11946 {
11947 dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
11948 (size_t)free_list, free_list_size));
11949
11950 gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11951 // We ask for more Align (min_obj_size)
11952 // to make sure that we can insert a free object
11953 // in adjust_limit will set the limit lower
11954 size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
11955
11956 uint8_t* remain = (free_list + limit);
11957 size_t remain_size = (free_list_size - limit);
11958 if (remain_size >= Align(min_free_list, align_const))
11959 {
11960 make_unused_array (remain, remain_size);
11961 gen_allocator->thread_item_front (remain, remain_size);
11962 assert (remain_size >= Align (min_obj_size, align_const));
11963 }
11964 else
11965 {
11966 //absorb the entire free list
11967 limit += remain_size;
11968 }
11969 generation_free_list_space (gen) -= limit;
11970
11971 adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
11972
11973 can_fit = TRUE;
11974 goto end;
11975 }
11976 else if (gen_allocator->discard_if_no_fit_p())
11977 {
11978 assert (prev_free_item == 0);
11979 dprintf (3, ("couldn't use this free area, discarding"));
11980 generation_free_obj_space (gen) += free_list_size;
11981
11982 gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
11983 generation_free_list_space (gen) -= free_list_size;
11984 }
11985 else
11986 {
11987 prev_free_item = free_list;
11988 }
11989 free_list = free_list_slot (free_list);
11990 }
11991 }
11992 sz_list = sz_list * 2;
11993 }
11994end:
11995 return can_fit;
11996}
11997
11998
11999#ifdef BACKGROUND_GC
12000void gc_heap::bgc_loh_alloc_clr (uint8_t* alloc_start,
12001 size_t size,
12002 alloc_context* acontext,
12003 int align_const,
12004 int lock_index,
12005 BOOL check_used_p,
12006 heap_segment* seg)
12007{
12008 make_unused_array (alloc_start, size);
12009
12010#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
12011 if (g_fEnableAppDomainMonitoring)
12012 {
12013 GCToEEInterface::RecordAllocatedBytesForHeap(size, heap_number);
12014 }
12015#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
12016
12017 size_t size_of_array_base = sizeof(ArrayBase);
12018
12019 bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
12020
12021 // clear memory while not holding the lock.
12022 size_t size_to_skip = size_of_array_base;
12023 size_t size_to_clear = size - size_to_skip - plug_skew;
12024 size_t saved_size_to_clear = size_to_clear;
12025 if (check_used_p)
12026 {
12027 uint8_t* end = alloc_start + size - plug_skew;
12028 uint8_t* used = heap_segment_used (seg);
12029 if (used < end)
12030 {
12031 if ((alloc_start + size_to_skip) < used)
12032 {
12033 size_to_clear = used - (alloc_start + size_to_skip);
12034 }
12035 else
12036 {
12037 size_to_clear = 0;
12038 }
12039 dprintf (2, ("bgc loh: setting used to %Ix", end));
12040 heap_segment_used (seg) = end;
12041 }
12042
12043 dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
12044 used, alloc_start, end, size_to_clear));
12045 }
12046 else
12047 {
12048 dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
12049 }
12050
12051#ifdef VERIFY_HEAP
12052 // since we filled in 0xcc for free object when we verify heap,
12053 // we need to make sure we clear those bytes.
12054 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
12055 {
12056 if (size_to_clear < saved_size_to_clear)
12057 {
12058 size_to_clear = saved_size_to_clear;
12059 }
12060 }
12061#endif //VERIFY_HEAP
12062
12063 dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
12064 add_saved_spinlock_info (true, me_release, mt_clr_large_mem);
12065 leave_spin_lock (&more_space_lock_loh);
12066 memclr (alloc_start + size_to_skip, size_to_clear);
12067
12068 bgc_alloc_lock->loh_alloc_set (alloc_start);
12069
12070 acontext->alloc_ptr = alloc_start;
12071 acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
12072
12073 // need to clear the rest of the object before we hand it out.
12074 clear_unused_array(alloc_start, size);
12075}
12076#endif //BACKGROUND_GC
12077
12078BOOL gc_heap::a_fit_free_list_large_p (size_t size,
12079 alloc_context* acontext,
12080 int align_const)
12081{
12082 BOOL can_fit = FALSE;
12083 int gen_number = max_generation + 1;
12084 generation* gen = generation_of (gen_number);
12085 allocator* loh_allocator = generation_allocator (gen);
12086
12087#ifdef FEATURE_LOH_COMPACTION
12088 size_t loh_pad = Align (loh_padding_obj_size, align_const);
12089#endif //FEATURE_LOH_COMPACTION
12090
12091#ifdef BACKGROUND_GC
12092 int cookie = -1;
12093#endif //BACKGROUND_GC
12094 size_t sz_list = loh_allocator->first_bucket_size();
12095 for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
12096 {
12097 if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
12098 {
12099 uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
12100 uint8_t* prev_free_item = 0;
12101 while (free_list != 0)
12102 {
12103 dprintf (3, ("considering free list %Ix", (size_t)free_list));
12104
12105 size_t free_list_size = unused_array_size(free_list);
12106
12107#ifdef FEATURE_LOH_COMPACTION
12108 if ((size + loh_pad) <= free_list_size)
12109#else
12110 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
12111 (size == free_list_size))
12112#endif //FEATURE_LOH_COMPACTION
12113 {
12114#ifdef BACKGROUND_GC
12115 cookie = bgc_alloc_lock->loh_alloc_set (free_list);
12116 bgc_track_loh_alloc();
12117#endif //BACKGROUND_GC
12118
12119 //unlink the free_item
12120 loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12121
12122 // Substract min obj size because limit_from_size adds it. Not needed for LOH
12123 size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size,
12124 gen_number, align_const);
12125
12126#ifdef FEATURE_LOH_COMPACTION
12127 make_unused_array (free_list, loh_pad);
12128 limit -= loh_pad;
12129 free_list += loh_pad;
12130 free_list_size -= loh_pad;
12131#endif //FEATURE_LOH_COMPACTION
12132
12133 uint8_t* remain = (free_list + limit);
12134 size_t remain_size = (free_list_size - limit);
12135 if (remain_size != 0)
12136 {
12137 assert (remain_size >= Align (min_obj_size, align_const));
12138 make_unused_array (remain, remain_size);
12139 }
12140 if (remain_size >= Align(min_free_list, align_const))
12141 {
12142 loh_thread_gap_front (remain, remain_size, gen);
12143 assert (remain_size >= Align (min_obj_size, align_const));
12144 }
12145 else
12146 {
12147 generation_free_obj_space (gen) += remain_size;
12148 }
12149 generation_free_list_space (gen) -= free_list_size;
12150 dprintf (3, ("found fit on loh at %Ix", free_list));
12151#ifdef BACKGROUND_GC
12152 if (cookie != -1)
12153 {
12154 bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
12155 }
12156 else
12157#endif //BACKGROUND_GC
12158 {
12159 adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
12160 }
12161
12162 //fix the limit to compensate for adjust_limit_clr making it too short
12163 acontext->alloc_limit += Align (min_obj_size, align_const);
12164 can_fit = TRUE;
12165 goto exit;
12166 }
12167 prev_free_item = free_list;
12168 free_list = free_list_slot (free_list);
12169 }
12170 }
12171 sz_list = sz_list * 2;
12172 }
12173exit:
12174 return can_fit;
12175}
12176
12177#ifdef _MSC_VER
12178#pragma warning(default:4706)
12179#endif // _MSC_VER
12180
12181BOOL gc_heap::a_fit_segment_end_p (int gen_number,
12182 heap_segment* seg,
12183 size_t size,
12184 alloc_context* acontext,
12185 int align_const,
12186 BOOL* commit_failed_p)
12187{
12188 *commit_failed_p = FALSE;
12189 size_t limit = 0;
12190#ifdef BACKGROUND_GC
12191 int cookie = -1;
12192#endif //BACKGROUND_GC
12193
12194 uint8_t*& allocated = ((gen_number == 0) ?
12195 alloc_allocated :
12196 heap_segment_allocated(seg));
12197
12198 size_t pad = Align (min_obj_size, align_const);
12199
12200#ifdef FEATURE_LOH_COMPACTION
12201 size_t loh_pad = Align (loh_padding_obj_size, align_const);
12202 if (gen_number == (max_generation + 1))
12203 {
12204 pad += loh_pad;
12205 }
12206#endif //FEATURE_LOH_COMPACTION
12207
12208 uint8_t* end = heap_segment_committed (seg) - pad;
12209
12210 if (a_size_fit_p (size, allocated, end, align_const))
12211 {
12212 limit = limit_from_size (size,
12213 (end - allocated),
12214 gen_number, align_const);
12215 goto found_fit;
12216 }
12217
12218 end = heap_segment_reserved (seg) - pad;
12219
12220 if (a_size_fit_p (size, allocated, end, align_const))
12221 {
12222 limit = limit_from_size (size,
12223 (end - allocated),
12224 gen_number, align_const);
12225 if (grow_heap_segment (seg, allocated + limit))
12226 {
12227 goto found_fit;
12228 }
12229 else
12230 {
12231 dprintf (2, ("can't grow segment, doing a full gc"));
12232 *commit_failed_p = TRUE;
12233 }
12234 }
12235 goto found_no_fit;
12236
12237found_fit:
12238
12239#ifdef BACKGROUND_GC
12240 if (gen_number != 0)
12241 {
12242 cookie = bgc_alloc_lock->loh_alloc_set (allocated);
12243 bgc_track_loh_alloc();
12244 }
12245#endif //BACKGROUND_GC
12246
12247 uint8_t* old_alloc;
12248 old_alloc = allocated;
12249#ifdef FEATURE_LOH_COMPACTION
12250 if (gen_number == (max_generation + 1))
12251 {
12252 make_unused_array (old_alloc, loh_pad);
12253 old_alloc += loh_pad;
12254 allocated += loh_pad;
12255 limit -= loh_pad;
12256 }
12257#endif //FEATURE_LOH_COMPACTION
12258
12259#if defined (VERIFY_HEAP) && defined (_DEBUG)
12260 ((void**) allocated)[-1] = 0; //clear the sync block
12261#endif //VERIFY_HEAP && _DEBUG
12262 allocated += limit;
12263
12264 dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
12265
12266#ifdef BACKGROUND_GC
12267 if (cookie != -1)
12268 {
12269 bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
12270 }
12271 else
12272#endif //BACKGROUND_GC
12273 {
12274 adjust_limit_clr (old_alloc, limit, acontext, seg, align_const, gen_number);
12275 }
12276
12277 return TRUE;
12278
12279found_no_fit:
12280
12281 return FALSE;
12282}
12283
12284BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
12285 size_t size,
12286 alloc_context* acontext,
12287 int align_const,
12288 BOOL* commit_failed_p,
12289 oom_reason* oom_r)
12290{
12291 *commit_failed_p = FALSE;
12292 heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
12293 BOOL can_allocate_p = FALSE;
12294
12295 while (seg)
12296 {
12297#ifdef BACKGROUND_GC
12298 if (seg->flags & heap_segment_flags_loh_delete)
12299 {
12300 dprintf (3, ("h%d skipping seg %Ix to be deleted", heap_number, (size_t)seg));
12301 }
12302 else
12303#endif //BACKGROUND_GC
12304 {
12305 if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)),
12306 acontext, align_const, commit_failed_p))
12307 {
12308 acontext->alloc_limit += Align (min_obj_size, align_const);
12309 can_allocate_p = TRUE;
12310 break;
12311 }
12312
12313 if (*commit_failed_p)
12314 {
12315 *oom_r = oom_cant_commit;
12316 break;
12317 }
12318 }
12319
12320 seg = heap_segment_next_rw (seg);
12321 }
12322
12323 return can_allocate_p;
12324}
12325
12326#ifdef BACKGROUND_GC
12327inline
12328void gc_heap::wait_for_background (alloc_wait_reason awr, bool loh_p)
12329{
12330 GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
12331
12332 dprintf (2, ("BGC is already in progress, waiting for it to finish"));
12333 add_saved_spinlock_info (loh_p, me_release, mt_wait_bgc);
12334 leave_spin_lock (msl);
12335 background_gc_wait (awr);
12336 enter_spin_lock (msl);
12337 add_saved_spinlock_info (loh_p, me_acquire, mt_wait_bgc);
12338}
12339
12340void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr, bool loh_p)
12341{
12342 if (recursive_gc_sync::background_running_p())
12343 {
12344 uint32_t memory_load;
12345 get_memory_info (&memory_load);
12346 if (memory_load >= m_high_memory_load_th)
12347 {
12348 dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
12349 wait_for_background (awr, loh_p);
12350 }
12351 }
12352}
12353
12354#endif //BACKGROUND_GC
12355
12356// We request to trigger an ephemeral GC but we may get a full compacting GC.
12357// return TRUE if that's the case.
12358BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
12359{
12360#ifdef BACKGROUND_GC
12361 wait_for_bgc_high_memory (awr_loh_oos_bgc, false);
12362#endif //BACKGROUND_GC
12363
12364 BOOL did_full_compact_gc = FALSE;
12365
12366 dprintf (2, ("triggering a gen1 GC"));
12367 size_t last_full_compact_gc_count = get_full_compact_gc_count();
12368 vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
12369
12370#ifdef MULTIPLE_HEAPS
12371 enter_spin_lock (&more_space_lock_soh);
12372 add_saved_spinlock_info (false, me_acquire, mt_t_eph_gc);
12373#endif //MULTIPLE_HEAPS
12374
12375 size_t current_full_compact_gc_count = get_full_compact_gc_count();
12376
12377 if (current_full_compact_gc_count > last_full_compact_gc_count)
12378 {
12379 dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
12380 did_full_compact_gc = TRUE;
12381 }
12382
12383 return did_full_compact_gc;
12384}
12385
12386BOOL gc_heap::soh_try_fit (int gen_number,
12387 size_t size,
12388 alloc_context* acontext,
12389 int align_const,
12390 BOOL* commit_failed_p,
12391 BOOL* short_seg_end_p)
12392{
12393 BOOL can_allocate = TRUE;
12394 if (short_seg_end_p)
12395 {
12396 *short_seg_end_p = FALSE;
12397 }
12398
12399 can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
12400 if (!can_allocate)
12401 {
12402 if (short_seg_end_p)
12403 {
12404 *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
12405 }
12406 // If the caller doesn't care, we always try to fit at the end of seg;
12407 // otherwise we would only try if we are actually not short at end of seg.
12408 if (!short_seg_end_p || !(*short_seg_end_p))
12409 {
12410 can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size,
12411 acontext, align_const, commit_failed_p);
12412 }
12413 }
12414
12415 return can_allocate;
12416}
12417
12418BOOL gc_heap::allocate_small (int gen_number,
12419 size_t size,
12420 alloc_context* acontext,
12421 int align_const)
12422{
12423#if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
12424 if (recursive_gc_sync::background_running_p())
12425 {
12426 background_soh_alloc_count++;
12427 if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
12428 {
12429 add_saved_spinlock_info (false, me_release, mt_alloc_small);
12430 leave_spin_lock (&more_space_lock_soh);
12431 bool cooperative_mode = enable_preemptive();
12432 GCToOSInterface::Sleep (bgc_alloc_spin);
12433 disable_preemptive (cooperative_mode);
12434 enter_spin_lock (&more_space_lock_soh);
12435 add_saved_spinlock_info (false, me_acquire, mt_alloc_small);
12436 }
12437 else
12438 {
12439 //GCToOSInterface::YieldThread (0);
12440 }
12441 }
12442#endif //BACKGROUND_GC && !MULTIPLE_HEAPS
12443
12444 gc_reason gr = reason_oos_soh;
12445 oom_reason oom_r = oom_no_failure;
12446
12447 // No variable values should be "carried over" from one state to the other.
12448 // That's why there are local variable for each state
12449
12450 allocation_state soh_alloc_state = a_state_start;
12451
12452 // If we can get a new seg it means allocation will succeed.
12453 while (1)
12454 {
12455 dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
12456 switch (soh_alloc_state)
12457 {
12458 case a_state_can_allocate:
12459 case a_state_cant_allocate:
12460 {
12461 goto exit;
12462 }
12463 case a_state_start:
12464 {
12465 soh_alloc_state = a_state_try_fit;
12466 break;
12467 }
12468 case a_state_try_fit:
12469 {
12470 BOOL commit_failed_p = FALSE;
12471 BOOL can_use_existing_p = FALSE;
12472
12473 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12474 align_const, &commit_failed_p,
12475 NULL);
12476 soh_alloc_state = (can_use_existing_p ?
12477 a_state_can_allocate :
12478 (commit_failed_p ?
12479 a_state_trigger_full_compact_gc :
12480 a_state_trigger_ephemeral_gc));
12481 break;
12482 }
12483 case a_state_try_fit_after_bgc:
12484 {
12485 BOOL commit_failed_p = FALSE;
12486 BOOL can_use_existing_p = FALSE;
12487 BOOL short_seg_end_p = FALSE;
12488
12489 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12490 align_const, &commit_failed_p,
12491 &short_seg_end_p);
12492 soh_alloc_state = (can_use_existing_p ?
12493 a_state_can_allocate :
12494 (short_seg_end_p ?
12495 a_state_trigger_2nd_ephemeral_gc :
12496 a_state_trigger_full_compact_gc));
12497 break;
12498 }
12499 case a_state_try_fit_after_cg:
12500 {
12501 BOOL commit_failed_p = FALSE;
12502 BOOL can_use_existing_p = FALSE;
12503 BOOL short_seg_end_p = FALSE;
12504
12505 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12506 align_const, &commit_failed_p,
12507 &short_seg_end_p);
12508
12509 if (can_use_existing_p)
12510 {
12511 soh_alloc_state = a_state_can_allocate;
12512 }
12513#ifdef MULTIPLE_HEAPS
12514 else if (gen0_allocated_after_gc_p)
12515 {
12516 // some other threads already grabbed the more space lock and allocated
12517 // so we should attempt an ephemeral GC again.
12518 soh_alloc_state = a_state_trigger_ephemeral_gc;
12519 }
12520#endif //MULTIPLE_HEAPS
12521 else if (short_seg_end_p)
12522 {
12523 soh_alloc_state = a_state_cant_allocate;
12524 oom_r = oom_budget;
12525 }
12526 else
12527 {
12528 assert (commit_failed_p);
12529 soh_alloc_state = a_state_cant_allocate;
12530 oom_r = oom_cant_commit;
12531 }
12532 break;
12533 }
12534 case a_state_check_and_wait_for_bgc:
12535 {
12536 BOOL bgc_in_progress_p = FALSE;
12537 BOOL did_full_compacting_gc = FALSE;
12538
12539 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc, false);
12540 soh_alloc_state = (did_full_compacting_gc ?
12541 a_state_try_fit_after_cg :
12542 a_state_try_fit_after_bgc);
12543 break;
12544 }
12545 case a_state_trigger_ephemeral_gc:
12546 {
12547 BOOL commit_failed_p = FALSE;
12548 BOOL can_use_existing_p = FALSE;
12549 BOOL short_seg_end_p = FALSE;
12550 BOOL bgc_in_progress_p = FALSE;
12551 BOOL did_full_compacting_gc = FALSE;
12552
12553 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12554 if (did_full_compacting_gc)
12555 {
12556 soh_alloc_state = a_state_try_fit_after_cg;
12557 }
12558 else
12559 {
12560 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12561 align_const, &commit_failed_p,
12562 &short_seg_end_p);
12563#ifdef BACKGROUND_GC
12564 bgc_in_progress_p = recursive_gc_sync::background_running_p();
12565#endif //BACKGROUND_GC
12566
12567 if (can_use_existing_p)
12568 {
12569 soh_alloc_state = a_state_can_allocate;
12570 }
12571 else
12572 {
12573 if (short_seg_end_p)
12574 {
12575 if (should_expand_in_full_gc)
12576 {
12577 dprintf (2, ("gen1 GC wanted to expand!"));
12578 soh_alloc_state = a_state_trigger_full_compact_gc;
12579 }
12580 else
12581 {
12582 soh_alloc_state = (bgc_in_progress_p ?
12583 a_state_check_and_wait_for_bgc :
12584 a_state_trigger_full_compact_gc);
12585 }
12586 }
12587 else if (commit_failed_p)
12588 {
12589 soh_alloc_state = a_state_trigger_full_compact_gc;
12590 }
12591 else
12592 {
12593#ifdef MULTIPLE_HEAPS
12594 // some other threads already grabbed the more space lock and allocated
12595 // so we should attemp an ephemeral GC again.
12596 assert (gen0_allocated_after_gc_p);
12597 soh_alloc_state = a_state_trigger_ephemeral_gc;
12598#else //MULTIPLE_HEAPS
12599 assert (!"shouldn't get here");
12600#endif //MULTIPLE_HEAPS
12601 }
12602 }
12603 }
12604 break;
12605 }
12606 case a_state_trigger_2nd_ephemeral_gc:
12607 {
12608 BOOL commit_failed_p = FALSE;
12609 BOOL can_use_existing_p = FALSE;
12610 BOOL short_seg_end_p = FALSE;
12611 BOOL did_full_compacting_gc = FALSE;
12612
12613
12614 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12615
12616 if (did_full_compacting_gc)
12617 {
12618 soh_alloc_state = a_state_try_fit_after_cg;
12619 }
12620 else
12621 {
12622 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12623 align_const, &commit_failed_p,
12624 &short_seg_end_p);
12625 if (short_seg_end_p || commit_failed_p)
12626 {
12627 soh_alloc_state = a_state_trigger_full_compact_gc;
12628 }
12629 else
12630 {
12631 assert (can_use_existing_p);
12632 soh_alloc_state = a_state_can_allocate;
12633 }
12634 }
12635 break;
12636 }
12637 case a_state_trigger_full_compact_gc:
12638 {
12639 if (fgn_maxgen_percent)
12640 {
12641 dprintf (2, ("FGN: SOH doing last GC before we throw OOM"));
12642 send_full_gc_notification (max_generation, FALSE);
12643 }
12644
12645 BOOL got_full_compacting_gc = FALSE;
12646
12647 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r, false);
12648 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12649 break;
12650 }
12651 default:
12652 {
12653 assert (!"Invalid state!");
12654 break;
12655 }
12656 }
12657 }
12658
12659exit:
12660 if (soh_alloc_state == a_state_cant_allocate)
12661 {
12662 assert (oom_r != oom_no_failure);
12663 handle_oom (heap_number,
12664 oom_r,
12665 size,
12666 heap_segment_allocated (ephemeral_heap_segment),
12667 heap_segment_reserved (ephemeral_heap_segment));
12668
12669 add_saved_spinlock_info (false, me_release, mt_alloc_small_cant);
12670 leave_spin_lock (&more_space_lock_soh);
12671 }
12672
12673 return (soh_alloc_state == a_state_can_allocate);
12674}
12675
12676#ifdef BACKGROUND_GC
12677inline
12678void gc_heap::bgc_track_loh_alloc()
12679{
12680 if (current_c_gc_state == c_gc_state_planning)
12681 {
12682 Interlocked::Increment (&loh_alloc_thread_count);
12683 dprintf (3, ("h%d: inc lc: %d", heap_number, loh_alloc_thread_count));
12684 }
12685}
12686
12687inline
12688void gc_heap::bgc_untrack_loh_alloc()
12689{
12690 if (current_c_gc_state == c_gc_state_planning)
12691 {
12692 Interlocked::Decrement (&loh_alloc_thread_count);
12693 dprintf (3, ("h%d: dec lc: %d", heap_number, loh_alloc_thread_count));
12694 }
12695}
12696
12697BOOL gc_heap::bgc_loh_should_allocate()
12698{
12699 size_t min_gc_size = dd_min_size (dynamic_data_of (max_generation + 1));
12700
12701 if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
12702 {
12703 return TRUE;
12704 }
12705
12706 if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
12707 {
12708 if ((bgc_begin_loh_size / end_loh_size) > 2)
12709 {
12710 dprintf (3, ("alloc-ed too much before bgc started"));
12711 }
12712 else
12713 {
12714 dprintf (3, ("alloc-ed too much after bgc started"));
12715 }
12716 return FALSE;
12717 }
12718 else
12719 {
12720 bgc_alloc_spin_loh = (uint32_t)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
12721 return TRUE;
12722 }
12723}
12724#endif //BACKGROUND_GC
12725
12726size_t gc_heap::get_large_seg_size (size_t size)
12727{
12728 size_t default_seg_size = min_loh_segment_size;
12729#ifdef SEG_MAPPING_TABLE
12730 size_t align_size = default_seg_size;
12731#else //SEG_MAPPING_TABLE
12732 size_t align_size = default_seg_size / 2;
12733#endif //SEG_MAPPING_TABLE
12734 int align_const = get_alignment_constant (FALSE);
12735 size_t large_seg_size = align_on_page (
12736 max (default_seg_size,
12737 ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE +
12738 align_size) / align_size * align_size)));
12739 return large_seg_size;
12740}
12741
12742BOOL gc_heap::loh_get_new_seg (generation* gen,
12743 size_t size,
12744 int align_const,
12745 BOOL* did_full_compact_gc,
12746 oom_reason* oom_r)
12747{
12748 UNREFERENCED_PARAMETER(gen);
12749 UNREFERENCED_PARAMETER(align_const);
12750
12751 *did_full_compact_gc = FALSE;
12752
12753 size_t seg_size = get_large_seg_size (size);
12754
12755 heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
12756
12757 if (new_seg)
12758 {
12759 loh_alloc_since_cg += seg_size;
12760 }
12761 else
12762 {
12763 *oom_r = oom_loh;
12764 }
12765
12766 return (new_seg != 0);
12767}
12768
12769BOOL gc_heap::retry_full_compact_gc (size_t size)
12770{
12771 size_t seg_size = get_large_seg_size (size);
12772
12773 if (loh_alloc_since_cg >= (2 * (uint64_t)seg_size))
12774 {
12775 return TRUE;
12776 }
12777
12778#ifdef MULTIPLE_HEAPS
12779 uint64_t total_alloc_size = 0;
12780 for (int i = 0; i < n_heaps; i++)
12781 {
12782 total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12783 }
12784
12785 if (total_alloc_size >= (2 * (uint64_t)seg_size))
12786 {
12787 return TRUE;
12788 }
12789#endif //MULTIPLE_HEAPS
12790
12791 return FALSE;
12792}
12793
12794BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12795 BOOL* did_full_compact_gc,
12796 bool loh_p)
12797{
12798 BOOL bgc_in_progress = FALSE;
12799 *did_full_compact_gc = FALSE;
12800#ifdef BACKGROUND_GC
12801 if (recursive_gc_sync::background_running_p())
12802 {
12803 bgc_in_progress = TRUE;
12804 size_t last_full_compact_gc_count = get_full_compact_gc_count();
12805 wait_for_background (awr, loh_p);
12806 size_t current_full_compact_gc_count = get_full_compact_gc_count();
12807 if (current_full_compact_gc_count > last_full_compact_gc_count)
12808 {
12809 *did_full_compact_gc = TRUE;
12810 }
12811 }
12812#endif //BACKGROUND_GC
12813
12814 return bgc_in_progress;
12815}
12816
12817BOOL gc_heap::loh_try_fit (int gen_number,
12818 size_t size,
12819 alloc_context* acontext,
12820 int align_const,
12821 BOOL* commit_failed_p,
12822 oom_reason* oom_r)
12823{
12824 BOOL can_allocate = TRUE;
12825
12826 if (!a_fit_free_list_large_p (size, acontext, align_const))
12827 {
12828 can_allocate = loh_a_fit_segment_end_p (gen_number, size,
12829 acontext, align_const,
12830 commit_failed_p, oom_r);
12831
12832#ifdef BACKGROUND_GC
12833 if (can_allocate && recursive_gc_sync::background_running_p())
12834 {
12835 bgc_loh_size_increased += size;
12836 }
12837#endif //BACKGROUND_GC
12838 }
12839#ifdef BACKGROUND_GC
12840 else
12841 {
12842 if (recursive_gc_sync::background_running_p())
12843 {
12844 bgc_loh_allocated_in_free += size;
12845 }
12846 }
12847#endif //BACKGROUND_GC
12848
12849 return can_allocate;
12850}
12851
12852BOOL gc_heap::trigger_full_compact_gc (gc_reason gr,
12853 oom_reason* oom_r,
12854 bool loh_p)
12855{
12856 BOOL did_full_compact_gc = FALSE;
12857
12858 size_t last_full_compact_gc_count = get_full_compact_gc_count();
12859
12860 // Set this so the next GC will be a full compacting GC.
12861 if (!last_gc_before_oom)
12862 {
12863 last_gc_before_oom = TRUE;
12864 }
12865
12866#ifdef BACKGROUND_GC
12867 if (recursive_gc_sync::background_running_p())
12868 {
12869 wait_for_background (((gr == reason_oos_soh) ? awr_gen0_oos_bgc : awr_loh_oos_bgc), loh_p);
12870 dprintf (2, ("waited for BGC - done"));
12871 }
12872#endif //BACKGROUND_GC
12873
12874 GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
12875 size_t current_full_compact_gc_count = get_full_compact_gc_count();
12876 if (current_full_compact_gc_count > last_full_compact_gc_count)
12877 {
12878 dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
12879 assert (current_full_compact_gc_count > last_full_compact_gc_count);
12880 did_full_compact_gc = TRUE;
12881 goto exit;
12882 }
12883
12884 dprintf (3, ("h%d full GC", heap_number));
12885
12886 trigger_gc_for_alloc (max_generation, gr, msl, loh_p, mt_t_full_gc);
12887
12888 current_full_compact_gc_count = get_full_compact_gc_count();
12889
12890 if (current_full_compact_gc_count == last_full_compact_gc_count)
12891 {
12892 dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
12893 // We requested a full GC but didn't get because of the elevation logic
12894 // which means we should fail.
12895 *oom_r = oom_unproductive_full_gc;
12896 }
12897 else
12898 {
12899 dprintf (3, ("h%d: T full compacting GC (%d->%d)",
12900 heap_number,
12901 last_full_compact_gc_count,
12902 current_full_compact_gc_count));
12903
12904 assert (current_full_compact_gc_count > last_full_compact_gc_count);
12905 did_full_compact_gc = TRUE;
12906 }
12907
12908exit:
12909 return did_full_compact_gc;
12910}
12911
12912#ifdef RECORD_LOH_STATE
12913void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, EEThreadId thread_id)
12914{
12915 // When the state is can_allocate we already have released the more
12916 // space lock. So we are not logging states here since this code
12917 // is not thread safe.
12918 if (loh_state_to_save != a_state_can_allocate)
12919 {
12920 last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
12921 last_loh_states[loh_state_index].thread_id = thread_id;
12922 loh_state_index++;
12923
12924 if (loh_state_index == max_saved_loh_states)
12925 {
12926 loh_state_index = 0;
12927 }
12928
12929 assert (loh_state_index < max_saved_loh_states);
12930 }
12931}
12932#endif //RECORD_LOH_STATE
12933
12934BOOL gc_heap::allocate_large (int gen_number,
12935 size_t size,
12936 alloc_context* acontext,
12937 int align_const)
12938{
12939#ifdef BACKGROUND_GC
12940 if (recursive_gc_sync::background_running_p())
12941 {
12942 background_loh_alloc_count++;
12943 //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
12944 {
12945 if (bgc_loh_should_allocate())
12946 {
12947 if (!bgc_alloc_spin_loh)
12948 {
12949 add_saved_spinlock_info (true, me_release, mt_alloc_large);
12950 leave_spin_lock (&more_space_lock_loh);
12951 bool cooperative_mode = enable_preemptive();
12952 GCToOSInterface::YieldThread (bgc_alloc_spin_loh);
12953 disable_preemptive (cooperative_mode);
12954 enter_spin_lock (&more_space_lock_loh);
12955 add_saved_spinlock_info (true, me_acquire, mt_alloc_large);
12956 dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
12957 }
12958 }
12959 else
12960 {
12961 wait_for_background (awr_loh_alloc_during_bgc, true);
12962 }
12963 }
12964 }
12965#endif //BACKGROUND_GC
12966
12967 gc_reason gr = reason_oos_loh;
12968 generation* gen = generation_of (gen_number);
12969 oom_reason oom_r = oom_no_failure;
12970 size_t current_full_compact_gc_count = 0;
12971
12972 // No variable values should be "carried over" from one state to the other.
12973 // That's why there are local variable for each state
12974 allocation_state loh_alloc_state = a_state_start;
12975#ifdef RECORD_LOH_STATE
12976 EEThreadId current_thread_id;
12977 current_thread_id.SetToCurrentThread();
12978#endif //RECORD_LOH_STATE
12979
12980 // If we can get a new seg it means allocation will succeed.
12981 while (1)
12982 {
12983 dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
12984
12985#ifdef RECORD_LOH_STATE
12986 add_saved_loh_state (loh_alloc_state, current_thread_id);
12987#endif //RECORD_LOH_STATE
12988 switch (loh_alloc_state)
12989 {
12990 case a_state_can_allocate:
12991 case a_state_cant_allocate:
12992 {
12993 goto exit;
12994 }
12995 case a_state_start:
12996 {
12997 loh_alloc_state = a_state_try_fit;
12998 break;
12999 }
13000 case a_state_try_fit:
13001 {
13002 BOOL commit_failed_p = FALSE;
13003 BOOL can_use_existing_p = FALSE;
13004
13005 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
13006 align_const, &commit_failed_p, &oom_r);
13007 loh_alloc_state = (can_use_existing_p ?
13008 a_state_can_allocate :
13009 (commit_failed_p ?
13010 a_state_trigger_full_compact_gc :
13011 a_state_acquire_seg));
13012 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13013 break;
13014 }
13015 case a_state_try_fit_new_seg:
13016 {
13017 BOOL commit_failed_p = FALSE;
13018 BOOL can_use_existing_p = FALSE;
13019
13020 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
13021 align_const, &commit_failed_p, &oom_r);
13022 // Even after we got a new seg it doesn't necessarily mean we can allocate,
13023 // another LOH allocating thread could have beat us to acquire the msl so
13024 // we need to try again.
13025 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
13026 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13027 break;
13028 }
13029 case a_state_try_fit_new_seg_after_cg:
13030 {
13031 BOOL commit_failed_p = FALSE;
13032 BOOL can_use_existing_p = FALSE;
13033
13034 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
13035 align_const, &commit_failed_p, &oom_r);
13036 // Even after we got a new seg it doesn't necessarily mean we can allocate,
13037 // another LOH allocating thread could have beat us to acquire the msl so
13038 // we need to try again. However, if we failed to commit, which means we
13039 // did have space on the seg, we bail right away 'cause we already did a
13040 // full compacting GC.
13041 loh_alloc_state = (can_use_existing_p ?
13042 a_state_can_allocate :
13043 (commit_failed_p ?
13044 a_state_cant_allocate :
13045 a_state_acquire_seg_after_cg));
13046 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13047 break;
13048 }
13049 case a_state_try_fit_no_seg:
13050 {
13051 BOOL commit_failed_p = FALSE;
13052 BOOL can_use_existing_p = FALSE;
13053
13054 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
13055 align_const, &commit_failed_p, &oom_r);
13056 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_cant_allocate);
13057 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13058 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13059 break;
13060 }
13061 case a_state_try_fit_after_cg:
13062 {
13063 BOOL commit_failed_p = FALSE;
13064 BOOL can_use_existing_p = FALSE;
13065
13066 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
13067 align_const, &commit_failed_p, &oom_r);
13068 loh_alloc_state = (can_use_existing_p ?
13069 a_state_can_allocate :
13070 (commit_failed_p ?
13071 a_state_cant_allocate :
13072 a_state_acquire_seg_after_cg));
13073 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13074 break;
13075 }
13076 case a_state_try_fit_after_bgc:
13077 {
13078 BOOL commit_failed_p = FALSE;
13079 BOOL can_use_existing_p = FALSE;
13080
13081 can_use_existing_p = loh_try_fit (gen_number, size, acontext,
13082 align_const, &commit_failed_p, &oom_r);
13083 loh_alloc_state = (can_use_existing_p ?
13084 a_state_can_allocate :
13085 (commit_failed_p ?
13086 a_state_trigger_full_compact_gc :
13087 a_state_acquire_seg_after_bgc));
13088 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13089 break;
13090 }
13091 case a_state_acquire_seg:
13092 {
13093 BOOL can_get_new_seg_p = FALSE;
13094 BOOL did_full_compacting_gc = FALSE;
13095
13096 current_full_compact_gc_count = get_full_compact_gc_count();
13097
13098 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
13099 loh_alloc_state = (can_get_new_seg_p ?
13100 a_state_try_fit_new_seg :
13101 (did_full_compacting_gc ?
13102 a_state_check_retry_seg :
13103 a_state_check_and_wait_for_bgc));
13104 break;
13105 }
13106 case a_state_acquire_seg_after_cg:
13107 {
13108 BOOL can_get_new_seg_p = FALSE;
13109 BOOL did_full_compacting_gc = FALSE;
13110
13111 current_full_compact_gc_count = get_full_compact_gc_count();
13112
13113 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
13114 // Since we release the msl before we try to allocate a seg, other
13115 // threads could have allocated a bunch of segments before us so
13116 // we might need to retry.
13117 loh_alloc_state = (can_get_new_seg_p ?
13118 a_state_try_fit_new_seg_after_cg :
13119 a_state_check_retry_seg);
13120 break;
13121 }
13122 case a_state_acquire_seg_after_bgc:
13123 {
13124 BOOL can_get_new_seg_p = FALSE;
13125 BOOL did_full_compacting_gc = FALSE;
13126
13127 current_full_compact_gc_count = get_full_compact_gc_count();
13128
13129 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
13130 loh_alloc_state = (can_get_new_seg_p ?
13131 a_state_try_fit_new_seg :
13132 (did_full_compacting_gc ?
13133 a_state_check_retry_seg :
13134 a_state_trigger_full_compact_gc));
13135 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13136 break;
13137 }
13138 case a_state_check_and_wait_for_bgc:
13139 {
13140 BOOL bgc_in_progress_p = FALSE;
13141 BOOL did_full_compacting_gc = FALSE;
13142
13143 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc, true);
13144 loh_alloc_state = (!bgc_in_progress_p ?
13145 a_state_trigger_full_compact_gc :
13146 (did_full_compacting_gc ?
13147 a_state_try_fit_after_cg :
13148 a_state_try_fit_after_bgc));
13149 break;
13150 }
13151 case a_state_trigger_full_compact_gc:
13152 {
13153 if (fgn_maxgen_percent)
13154 {
13155 dprintf (2, ("FGN: LOH doing last GC before we throw OOM"));
13156 send_full_gc_notification (max_generation, FALSE);
13157 }
13158
13159 BOOL got_full_compacting_gc = FALSE;
13160
13161 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r, true);
13162 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
13163 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13164 break;
13165 }
13166 case a_state_check_retry_seg:
13167 {
13168 BOOL should_retry_gc = retry_full_compact_gc (size);
13169 BOOL should_retry_get_seg = FALSE;
13170 if (!should_retry_gc)
13171 {
13172 size_t last_full_compact_gc_count = current_full_compact_gc_count;
13173 current_full_compact_gc_count = get_full_compact_gc_count();
13174
13175 if (current_full_compact_gc_count > (last_full_compact_gc_count + 1))
13176 {
13177 should_retry_get_seg = TRUE;
13178 }
13179 }
13180
13181 loh_alloc_state = (should_retry_gc ?
13182 a_state_trigger_full_compact_gc :
13183 (should_retry_get_seg ?
13184 a_state_acquire_seg_after_cg :
13185 a_state_cant_allocate));
13186 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13187 break;
13188 }
13189 default:
13190 {
13191 assert (!"Invalid state!");
13192 break;
13193 }
13194 }
13195 }
13196
13197exit:
13198 if (loh_alloc_state == a_state_cant_allocate)
13199 {
13200 assert (oom_r != oom_no_failure);
13201 handle_oom (heap_number,
13202 oom_r,
13203 size,
13204 0,
13205 0);
13206
13207 add_saved_spinlock_info (true, me_release, mt_alloc_large_cant);
13208 leave_spin_lock (&more_space_lock_loh);
13209 }
13210
13211 return (loh_alloc_state == a_state_can_allocate);
13212}
13213
13214// BGC's final mark phase will acquire the msl, so release it here and re-acquire.
13215void gc_heap::trigger_gc_for_alloc (int gen_number, gc_reason gr,
13216 GCSpinLock* msl, bool loh_p,
13217 msl_take_state take_state)
13218{
13219#ifdef BACKGROUND_GC
13220 if (loh_p)
13221 {
13222 add_saved_spinlock_info (loh_p, me_release, take_state);
13223 leave_spin_lock (msl);
13224 }
13225#endif //BACKGROUND_GC
13226
13227 vm_heap->GarbageCollectGeneration (gen_number, gr);
13228
13229#ifdef MULTIPLE_HEAPS
13230 if (!loh_p)
13231 {
13232 enter_spin_lock (msl);
13233 add_saved_spinlock_info (loh_p, me_acquire, take_state);
13234 }
13235#endif //MULTIPLE_HEAPS
13236
13237#ifdef BACKGROUND_GC
13238 if (loh_p)
13239 {
13240 enter_spin_lock (msl);
13241 add_saved_spinlock_info (loh_p, me_acquire, take_state);
13242 }
13243#endif //BACKGROUND_GC
13244}
13245
13246int gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
13247 int gen_number)
13248{
13249 if (gc_heap::gc_started)
13250 {
13251 wait_for_gc_done();
13252 return -1;
13253 }
13254
13255 bool loh_p = (gen_number > 0);
13256 GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
13257
13258#ifdef SYNCHRONIZATION_STATS
13259 int64_t msl_acquire_start = GCToOSInterface::QueryPerformanceCounter();
13260#endif //SYNCHRONIZATION_STATS
13261 enter_spin_lock (msl);
13262 add_saved_spinlock_info (loh_p, me_acquire, mt_try_alloc);
13263 dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
13264#ifdef SYNCHRONIZATION_STATS
13265 int64_t msl_acquire = GCToOSInterface::QueryPerformanceCounter() - msl_acquire_start;
13266 total_msl_acquire += msl_acquire;
13267 num_msl_acquired++;
13268 if (msl_acquire > 200)
13269 {
13270 num_high_msl_acquire++;
13271 }
13272 else
13273 {
13274 num_low_msl_acquire++;
13275 }
13276#endif //SYNCHRONIZATION_STATS
13277
13278 /*
13279 // We are commenting this out 'cause we don't see the point - we already
13280 // have checked gc_started when we were acquiring the msl - no need to check
13281 // again. This complicates the logic in bgc_suspend_EE 'cause that one would
13282 // need to release msl which causes all sorts of trouble.
13283 if (gc_heap::gc_started)
13284 {
13285#ifdef SYNCHRONIZATION_STATS
13286 good_suspension++;
13287#endif //SYNCHRONIZATION_STATS
13288 BOOL fStress = (g_pConfig->GetGCStressLevel() & GCConfig::GCSTRESS_TRANSITION) != 0;
13289 if (!fStress)
13290 {
13291 //Rendez vous early (MP scaling issue)
13292 //dprintf (1, ("[%d]waiting for gc", heap_number));
13293 wait_for_gc_done();
13294#ifdef MULTIPLE_HEAPS
13295 return -1;
13296#endif //MULTIPLE_HEAPS
13297 }
13298 }
13299 */
13300
13301 dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
13302
13303 int align_const = get_alignment_constant (gen_number != (max_generation+1));
13304
13305 if (fgn_maxgen_percent)
13306 {
13307 check_for_full_gc (gen_number, size);
13308 }
13309
13310 if (!(new_allocation_allowed (gen_number)))
13311 {
13312 if (fgn_maxgen_percent && (gen_number == 0))
13313 {
13314 // We only check gen0 every so often, so take this opportunity to check again.
13315 check_for_full_gc (gen_number, size);
13316 }
13317
13318#ifdef BACKGROUND_GC
13319 wait_for_bgc_high_memory (awr_gen0_alloc, loh_p);
13320#endif //BACKGROUND_GC
13321
13322#ifdef SYNCHRONIZATION_STATS
13323 bad_suspension++;
13324#endif //SYNCHRONIZATION_STATS
13325 dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
13326
13327 if (!settings.concurrent || (gen_number == 0))
13328 {
13329 trigger_gc_for_alloc (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh),
13330 msl, loh_p, mt_try_budget);
13331 }
13332 }
13333
13334 BOOL can_allocate = ((gen_number == 0) ?
13335 allocate_small (gen_number, size, acontext, align_const) :
13336 allocate_large (gen_number, size, acontext, align_const));
13337
13338 if (can_allocate)
13339 {
13340 size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
13341 int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
13342
13343 etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
13344
13345
13346 if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
13347 {
13348#ifdef FEATURE_REDHAWK
13349 FIRE_EVENT(GCAllocationTick_V1, (uint32_t)etw_allocation_running_amount[etw_allocation_index],
13350 (gen_number == 0) ? gc_etw_alloc_soh : gc_etw_alloc_loh);
13351#else
13352 // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
13353 // The ones that do are much less efficient.
13354#if defined(FEATURE_EVENT_TRACE)
13355 if (EVENT_ENABLED(GCAllocationTick_V3))
13356 {
13357 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
13358 }
13359#endif //FEATURE_EVENT_TRACE
13360#endif //FEATURE_REDHAWK
13361 etw_allocation_running_amount[etw_allocation_index] = 0;
13362 }
13363 }
13364
13365 return (int)can_allocate;
13366}
13367
13368#ifdef MULTIPLE_HEAPS
13369void gc_heap::balance_heaps (alloc_context* acontext)
13370{
13371
13372 if (acontext->alloc_count < 4)
13373 {
13374 if (acontext->alloc_count == 0)
13375 {
13376 acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, 0) ));
13377 gc_heap* hp = acontext->get_home_heap()->pGenGCHeap;
13378 dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
13379 acontext->set_alloc_heap(acontext->get_home_heap());
13380 hp->alloc_context_count++;
13381 }
13382 }
13383 else
13384 {
13385 BOOL set_home_heap = FALSE;
13386 int hint = 0;
13387
13388 if (heap_select::can_find_heap_fast())
13389 {
13390 if (acontext->get_home_heap() != NULL)
13391 hint = acontext->get_home_heap()->pGenGCHeap->heap_number;
13392 if (acontext->get_home_heap() != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
13393 {
13394 set_home_heap = TRUE;
13395 }
13396 }
13397 else
13398 {
13399 // can't use gdt
13400 if ((acontext->alloc_count & 3) == 0)
13401 set_home_heap = TRUE;
13402 }
13403
13404 if (set_home_heap)
13405 {
13406/*
13407 // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
13408 if (n_heaps > MAX_SUPPORTED_CPUS)
13409 {
13410 // on machines with many processors cache affinity is really king, so don't even try
13411 // to balance on these.
13412 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
13413 acontext->alloc_heap = acontext->home_heap;
13414 }
13415 else
13416*/
13417 {
13418 gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13419
13420 dynamic_data* dd = org_hp->dynamic_data_of (0);
13421 ptrdiff_t org_size = dd_new_allocation (dd);
13422 int org_alloc_context_count;
13423 int max_alloc_context_count;
13424 gc_heap* max_hp;
13425 ptrdiff_t max_size;
13426 size_t delta = dd_min_size (dd)/4;
13427
13428 int start, end, finish;
13429 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13430 finish = start + n_heaps;
13431
13432try_again:
13433 do
13434 {
13435 max_hp = org_hp;
13436 max_size = org_size + delta;
13437 acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, hint) ));
13438
13439 if (org_hp == acontext->get_home_heap()->pGenGCHeap)
13440 max_size = max_size + delta;
13441
13442 org_alloc_context_count = org_hp->alloc_context_count;
13443 max_alloc_context_count = org_alloc_context_count;
13444 if (max_alloc_context_count > 1)
13445 max_size /= max_alloc_context_count;
13446
13447 for (int i = start; i < end; i++)
13448 {
13449 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13450 dd = hp->dynamic_data_of (0);
13451 ptrdiff_t size = dd_new_allocation (dd);
13452 if (hp == acontext->get_home_heap()->pGenGCHeap)
13453 size = size + delta;
13454 int hp_alloc_context_count = hp->alloc_context_count;
13455 if (hp_alloc_context_count > 0)
13456 size /= (hp_alloc_context_count + 1);
13457 if (size > max_size)
13458 {
13459 max_hp = hp;
13460 max_size = size;
13461 max_alloc_context_count = hp_alloc_context_count;
13462 }
13463 }
13464 }
13465 while (org_alloc_context_count != org_hp->alloc_context_count ||
13466 max_alloc_context_count != max_hp->alloc_context_count);
13467
13468 if ((max_hp == org_hp) && (end < finish))
13469 {
13470 start = end; end = finish;
13471 delta = dd_min_size(dd)/2; // Make it twice as hard to balance to remote nodes on NUMA.
13472 goto try_again;
13473 }
13474
13475 if (max_hp != org_hp)
13476 {
13477 org_hp->alloc_context_count--;
13478 max_hp->alloc_context_count++;
13479 acontext->set_alloc_heap(GCHeap::GetHeap(max_hp->heap_number));
13480 if (!gc_thread_no_affinitize_p)
13481 {
13482 if (GCToOSInterface::CanEnableGCCPUGroups())
13483 { //only set ideal processor when max_hp and org_hp are in the same cpu
13484 //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
13485 uint16_t org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
13486 uint16_t max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
13487 if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
13488 {
13489 uint16_t group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
13490
13491 GCThreadAffinity affinity;
13492 affinity.Processor = group_proc_no;
13493 affinity.Group = org_gn;
13494 if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13495 {
13496 dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
13497 org_hp->heap_number));
13498 }
13499 }
13500 }
13501 else
13502 {
13503 uint16_t proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
13504
13505 GCThreadAffinity affinity;
13506 affinity.Processor = proc_no;
13507 affinity.Group = GCThreadAffinity::None;
13508
13509 if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13510 {
13511 dprintf (3, ("Failed to set the ideal processor for heap %d.",
13512 org_hp->heap_number));
13513 }
13514 }
13515 }
13516 dprintf (3, ("Switching context %p (home heap %d) ",
13517 acontext,
13518 acontext->get_home_heap()->pGenGCHeap->heap_number));
13519 dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ",
13520 org_hp->heap_number,
13521 org_size,
13522 org_alloc_context_count));
13523 dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n",
13524 max_hp->heap_number,
13525 dd_new_allocation(max_hp->dynamic_data_of(0)),
13526 max_alloc_context_count));
13527 }
13528 }
13529 }
13530 }
13531 acontext->alloc_count++;
13532}
13533
13534gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t /*size*/)
13535{
13536 gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13537 //dprintf (1, ("LA: %Id", size));
13538
13539 //if (size > 128*1024)
13540 if (1)
13541 {
13542 dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
13543
13544 ptrdiff_t org_size = dd_new_allocation (dd);
13545 gc_heap* max_hp;
13546 ptrdiff_t max_size;
13547 size_t delta = dd_min_size (dd) * 4;
13548
13549 int start, end, finish;
13550 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13551 finish = start + n_heaps;
13552
13553try_again:
13554 {
13555 max_hp = org_hp;
13556 max_size = org_size + delta;
13557 dprintf (3, ("orig hp: %d, max size: %d",
13558 org_hp->heap_number,
13559 max_size));
13560
13561 for (int i = start; i < end; i++)
13562 {
13563 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13564 dd = hp->dynamic_data_of (max_generation + 1);
13565 ptrdiff_t size = dd_new_allocation (dd);
13566 dprintf (3, ("hp: %d, size: %d",
13567 hp->heap_number,
13568 size));
13569 if (size > max_size)
13570 {
13571 max_hp = hp;
13572 max_size = size;
13573 dprintf (3, ("max hp: %d, max size: %d",
13574 max_hp->heap_number,
13575 max_size));
13576 }
13577 }
13578 }
13579
13580 if ((max_hp == org_hp) && (end < finish))
13581 {
13582 start = end; end = finish;
13583 delta = dd_min_size(dd) * 4; // Need to tuning delta
13584 goto try_again;
13585 }
13586
13587 if (max_hp != org_hp)
13588 {
13589 dprintf (3, ("loh: %d(%Id)->%d(%Id)",
13590 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
13591 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
13592 }
13593
13594 return max_hp;
13595 }
13596 else
13597 {
13598 return org_hp;
13599 }
13600}
13601#endif //MULTIPLE_HEAPS
13602
13603BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
13604 int alloc_generation_number)
13605{
13606 int status;
13607 do
13608 {
13609#ifdef MULTIPLE_HEAPS
13610 if (alloc_generation_number == 0)
13611 {
13612 balance_heaps (acontext);
13613 status = acontext->get_alloc_heap()->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
13614 }
13615 else
13616 {
13617 gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
13618 status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
13619 }
13620#else
13621 status = try_allocate_more_space (acontext, size, alloc_generation_number);
13622#endif //MULTIPLE_HEAPS
13623 }
13624 while (status == -1);
13625
13626 return (status != 0);
13627}
13628
13629inline
13630CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
13631{
13632 size_t size = Align (jsize);
13633 assert (size >= Align (min_obj_size));
13634 {
13635 retry:
13636 uint8_t* result = acontext->alloc_ptr;
13637 acontext->alloc_ptr+=size;
13638 if (acontext->alloc_ptr <= acontext->alloc_limit)
13639 {
13640 CObjectHeader* obj = (CObjectHeader*)result;
13641 assert (obj != 0);
13642 return obj;
13643 }
13644 else
13645 {
13646 acontext->alloc_ptr -= size;
13647
13648#ifdef _MSC_VER
13649#pragma inline_depth(0)
13650#endif //_MSC_VER
13651
13652 if (! allocate_more_space (acontext, size, 0))
13653 return 0;
13654
13655#ifdef _MSC_VER
13656#pragma inline_depth(20)
13657#endif //_MSC_VER
13658
13659 goto retry;
13660 }
13661 }
13662}
13663
13664inline
13665CObjectHeader* gc_heap::try_fast_alloc (size_t jsize)
13666{
13667 size_t size = Align (jsize);
13668 assert (size >= Align (min_obj_size));
13669 generation* gen = generation_of (0);
13670 uint8_t* result = generation_allocation_pointer (gen);
13671 generation_allocation_pointer (gen) += size;
13672 if (generation_allocation_pointer (gen) <=
13673 generation_allocation_limit (gen))
13674 {
13675 return (CObjectHeader*)result;
13676 }
13677 else
13678 {
13679 generation_allocation_pointer (gen) -= size;
13680 return 0;
13681 }
13682}
13683void gc_heap::leave_allocation_segment (generation* gen)
13684{
13685 adjust_limit (0, 0, gen, max_generation);
13686}
13687
13688void gc_heap::init_free_and_plug()
13689{
13690#ifdef FREE_USAGE_STATS
13691 for (int i = 0; i <= settings.condemned_generation; i++)
13692 {
13693 generation* gen = generation_of (i);
13694 memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
13695 memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13696 memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
13697 }
13698
13699 if (settings.condemned_generation != max_generation)
13700 {
13701 for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
13702 {
13703 generation* gen = generation_of (i);
13704 memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13705 }
13706 }
13707#endif //FREE_USAGE_STATS
13708}
13709
13710void gc_heap::print_free_and_plug (const char* msg)
13711{
13712#if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
13713 int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
13714 for (int i = 0; i <= older_gen; i++)
13715 {
13716 generation* gen = generation_of (i);
13717 for (int j = 0; j < NUM_GEN_POWER2; j++)
13718 {
13719 if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
13720 {
13721 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id",
13722 msg,
13723 heap_number,
13724 (settings.concurrent ? "BGC" : "GC"),
13725 settings.gc_index,
13726 i,
13727 (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
13728 }
13729 }
13730 }
13731#else
13732 UNREFERENCED_PARAMETER(msg);
13733#endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
13734}
13735
13736void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
13737{
13738#ifdef FREE_USAGE_STATS
13739 dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
13740 generation* gen = generation_of (gen_number);
13741 size_t sz = BASE_GEN_SIZE;
13742 int i = 0;
13743
13744 for (; i < NUM_GEN_POWER2; i++)
13745 {
13746 if (plug_size < sz)
13747 {
13748 break;
13749 }
13750 sz = sz * 2;
13751 }
13752
13753 (gen->gen_plugs[i])++;
13754#else
13755 UNREFERENCED_PARAMETER(gen_number);
13756 UNREFERENCED_PARAMETER(plug_size);
13757#endif //FREE_USAGE_STATS
13758}
13759
13760void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
13761{
13762#ifdef FREE_USAGE_STATS
13763 generation* gen = generation_of (gen_number);
13764 size_t sz = BASE_GEN_SIZE;
13765 int i = 0;
13766
13767 for (; i < NUM_GEN_POWER2; i++)
13768 {
13769 if (free_size < sz)
13770 {
13771 break;
13772 }
13773 sz = sz * 2;
13774 }
13775
13776 (gen->gen_current_pinned_free_spaces[i])++;
13777 generation_pinned_free_obj_space (gen) += free_size;
13778 dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)",
13779 free_size, (i + 10), gen_number,
13780 generation_pinned_free_obj_space (gen),
13781 gen->gen_current_pinned_free_spaces[i]));
13782#else
13783 UNREFERENCED_PARAMETER(gen_number);
13784 UNREFERENCED_PARAMETER(free_size);
13785#endif //FREE_USAGE_STATS
13786}
13787
13788void gc_heap::add_gen_free (int gen_number, size_t free_size)
13789{
13790#ifdef FREE_USAGE_STATS
13791 dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13792 generation* gen = generation_of (gen_number);
13793 size_t sz = BASE_GEN_SIZE;
13794 int i = 0;
13795
13796 for (; i < NUM_GEN_POWER2; i++)
13797 {
13798 if (free_size < sz)
13799 {
13800 break;
13801 }
13802 sz = sz * 2;
13803 }
13804
13805 (gen->gen_free_spaces[i])++;
13806#else
13807 UNREFERENCED_PARAMETER(gen_number);
13808 UNREFERENCED_PARAMETER(free_size);
13809#endif //FREE_USAGE_STATS
13810}
13811
13812void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13813{
13814#ifdef FREE_USAGE_STATS
13815 dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13816 generation* gen = generation_of (gen_number);
13817 size_t sz = BASE_GEN_SIZE;
13818 int i = 0;
13819
13820 for (; i < NUM_GEN_POWER2; i++)
13821 {
13822 if (free_size < sz)
13823 {
13824 break;
13825 }
13826 sz = sz * 2;
13827 }
13828
13829 (gen->gen_free_spaces[i])--;
13830#else
13831 UNREFERENCED_PARAMETER(gen_number);
13832 UNREFERENCED_PARAMETER(free_size);
13833#endif //FREE_USAGE_STATS
13834}
13835
13836uint8_t* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13837 int from_gen_number,
13838 uint8_t* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13839{
13840 size = Align (size);
13841 assert (size >= Align (min_obj_size));
13842 assert (from_gen_number < max_generation);
13843 assert (from_gen_number >= 0);
13844 assert (generation_of (from_gen_number + 1) == gen);
13845
13846 allocator* gen_allocator = generation_allocator (gen);
13847 BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13848 int pad_in_front = ((old_loc != 0) && ((from_gen_number+1) != max_generation)) ? USE_PADDING_FRONT : 0;
13849
13850 size_t real_size = size + Align (min_obj_size);
13851 if (pad_in_front)
13852 real_size += Align (min_obj_size);
13853
13854 if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13855 generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13856 {
13857 size_t sz_list = gen_allocator->first_bucket_size();
13858 for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13859 {
13860 if ((real_size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13861 {
13862 uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13863 uint8_t* prev_free_item = 0;
13864 while (free_list != 0)
13865 {
13866 dprintf (3, ("considering free list %Ix", (size_t)free_list));
13867
13868 size_t free_list_size = unused_array_size (free_list);
13869
13870 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
13871 old_loc, USE_PADDING_TAIL | pad_in_front))
13872 {
13873 dprintf (4, ("F:%Ix-%Id",
13874 (size_t)free_list, free_list_size));
13875
13876 gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
13877 generation_free_list_space (gen) -= free_list_size;
13878 remove_gen_free (gen->gen_num, free_list_size);
13879
13880 adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
13881 generation_allocate_end_seg_p (gen) = FALSE;
13882 goto finished;
13883 }
13884 // We do first fit on bucket 0 because we are not guaranteed to find a fit there.
13885 else if (discard_p || (a_l_idx == 0))
13886 {
13887 dprintf (3, ("couldn't use this free area, discarding"));
13888 generation_free_obj_space (gen) += free_list_size;
13889
13890 gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
13891 generation_free_list_space (gen) -= free_list_size;
13892 remove_gen_free (gen->gen_num, free_list_size);
13893 }
13894 else
13895 {
13896 prev_free_item = free_list;
13897 }
13898 free_list = free_list_slot (free_list);
13899 }
13900 }
13901 sz_list = sz_list * 2;
13902 }
13903 //go back to the beginning of the segment list
13904 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
13905 if (seg != generation_allocation_segment (gen))
13906 {
13907 leave_allocation_segment (gen);
13908 generation_allocation_segment (gen) = seg;
13909 }
13910 while (seg != ephemeral_heap_segment)
13911 {
13912 if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13913 heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
13914 {
13915 dprintf (3, ("using what's left in committed"));
13916 adjust_limit (heap_segment_plan_allocated (seg),
13917 heap_segment_committed (seg) -
13918 heap_segment_plan_allocated (seg),
13919 gen, from_gen_number+1);
13920 generation_allocate_end_seg_p (gen) = TRUE;
13921 // dformat (t, 3, "Expanding segment allocation");
13922 heap_segment_plan_allocated (seg) =
13923 heap_segment_committed (seg);
13924 goto finished;
13925 }
13926 else
13927 {
13928 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
13929 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
13930 grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
13931 {
13932 dprintf (3, ("using what's left in reserved"));
13933 adjust_limit (heap_segment_plan_allocated (seg),
13934 heap_segment_committed (seg) -
13935 heap_segment_plan_allocated (seg),
13936 gen, from_gen_number+1);
13937 generation_allocate_end_seg_p (gen) = TRUE;
13938 heap_segment_plan_allocated (seg) =
13939 heap_segment_committed (seg);
13940
13941 goto finished;
13942 }
13943 else
13944 {
13945 leave_allocation_segment (gen);
13946 heap_segment* next_seg = heap_segment_next_rw (seg);
13947 if (next_seg)
13948 {
13949 dprintf (3, ("getting next segment"));
13950 generation_allocation_segment (gen) = next_seg;
13951 generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
13952 generation_allocation_limit (gen) = generation_allocation_pointer (gen);
13953 }
13954 else
13955 {
13956 size = 0;
13957 goto finished;
13958 }
13959 }
13960 }
13961 seg = generation_allocation_segment (gen);
13962 }
13963 //No need to fix the last region. Will be done later
13964 size = 0;
13965 goto finished;
13966 }
13967 finished:
13968 if (0 == size)
13969 {
13970 return 0;
13971 }
13972 else
13973 {
13974 uint8_t* result = generation_allocation_pointer (gen);
13975 size_t pad = 0;
13976
13977#ifdef SHORT_PLUGS
13978 if ((pad_in_front & USE_PADDING_FRONT) &&
13979 (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
13980 ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
13981 {
13982 pad = Align (min_obj_size);
13983 set_plug_padded (old_loc);
13984 }
13985#endif //SHORT_PLUGS
13986
13987#ifdef FEATURE_STRUCTALIGN
13988 _ASSERTE(!old_loc || alignmentOffset != 0);
13989 _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
13990 if (old_loc != 0)
13991 {
13992 size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
13993 set_node_aligninfo (old_loc, requiredAlignment, pad1);
13994 pad += pad1;
13995 }
13996#else // FEATURE_STRUCTALIGN
13997 if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
13998 {
13999 pad += switch_alignment_size (is_plug_padded (old_loc));
14000 set_node_realigned (old_loc);
14001 dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14002 (size_t)old_loc, (size_t)(result+pad)));
14003 assert (same_large_alignment_p (result + pad, old_loc));
14004 }
14005#endif // FEATURE_STRUCTALIGN
14006 dprintf (3, ("Allocate %Id bytes", size));
14007
14008 if ((old_loc == 0) || (pad != 0))
14009 {
14010 //allocating a non plug or a gap, so reset the start region
14011 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14012 }
14013
14014 generation_allocation_pointer (gen) += size + pad;
14015 assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14016 if (generation_allocate_end_seg_p (gen))
14017 {
14018 generation_end_seg_allocated (gen) += size;
14019 }
14020 else
14021 {
14022 generation_free_list_allocated (gen) += size;
14023 }
14024 generation_allocation_size (gen) += size;
14025
14026 dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix",
14027 generation_allocation_pointer (gen), generation_allocation_limit (gen),
14028 generation_allocation_context_start_region (gen)));
14029
14030 return result + pad;;
14031 }
14032}
14033
14034void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
14035{
14036 //make sure that every generation has a planned allocation start
14037 int gen_number = max_generation - 1;
14038 while (gen_number>= 0)
14039 {
14040 generation* gen = generation_of (gen_number);
14041 if (0 == generation_plan_allocation_start (gen))
14042 {
14043 realloc_plan_generation_start (gen, consing_gen);
14044
14045 assert (generation_plan_allocation_start (gen));
14046 }
14047 gen_number--;
14048 }
14049
14050 // now we know the planned allocation size
14051 size_t size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
14052 heap_segment* seg = generation_allocation_segment (consing_gen);
14053 if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
14054 {
14055 if (size != 0)
14056 {
14057 heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14058 }
14059 }
14060 else
14061 {
14062 assert (settings.condemned_generation == max_generation);
14063 uint8_t* first_address = generation_allocation_limit (consing_gen);
14064 //look through the pinned plugs for relevant ones.
14065 //Look for the right pinned plug to start from.
14066 size_t mi = 0;
14067 mark* m = 0;
14068 while (mi != mark_stack_tos)
14069 {
14070 m = pinned_plug_of (mi);
14071 if ((pinned_plug (m) == first_address))
14072 break;
14073 else
14074 mi++;
14075 }
14076 assert (mi != mark_stack_tos);
14077 pinned_len (m) = size;
14078 }
14079}
14080
14081//tododefrag optimize for new segment (plan_allocated == mem)
14082uint8_t* gc_heap::allocate_in_expanded_heap (generation* gen,
14083 size_t size,
14084 BOOL& adjacentp,
14085 uint8_t* old_loc,
14086#ifdef SHORT_PLUGS
14087 BOOL set_padding_on_saved_p,
14088 mark* pinned_plug_entry,
14089#endif //SHORT_PLUGS
14090 BOOL consider_bestfit,
14091 int active_new_gen_number
14092 REQD_ALIGN_AND_OFFSET_DCL)
14093{
14094 UNREFERENCED_PARAMETER(active_new_gen_number);
14095 dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
14096
14097 size = Align (size);
14098 assert (size >= Align (min_obj_size));
14099 int pad_in_front = ((old_loc != 0) && (active_new_gen_number != max_generation)) ? USE_PADDING_FRONT : 0;
14100
14101 if (consider_bestfit && use_bestfit)
14102 {
14103 assert (bestfit_seg);
14104 dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id",
14105 old_loc, size));
14106 return bestfit_seg->fit (old_loc,
14107#ifdef SHORT_PLUGS
14108 set_padding_on_saved_p,
14109 pinned_plug_entry,
14110#endif //SHORT_PLUGS
14111 size REQD_ALIGN_AND_OFFSET_ARG);
14112 }
14113
14114 heap_segment* seg = generation_allocation_segment (gen);
14115
14116 if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14117 generation_allocation_limit (gen), old_loc,
14118 ((generation_allocation_limit (gen) !=
14119 heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
14120 {
14121 dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
14122 generation_allocation_limit (gen)));
14123
14124 adjacentp = FALSE;
14125 uint8_t* first_address = (generation_allocation_limit (gen) ?
14126 generation_allocation_limit (gen) :
14127 heap_segment_mem (seg));
14128 assert (in_range_for_segment (first_address, seg));
14129
14130 uint8_t* end_address = heap_segment_reserved (seg);
14131
14132 dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
14133 first_address, generation_allocation_limit (gen), end_address));
14134
14135 size_t mi = 0;
14136 mark* m = 0;
14137
14138 if (heap_segment_allocated (seg) != heap_segment_mem (seg))
14139 {
14140 assert (settings.condemned_generation == max_generation);
14141 //look through the pinned plugs for relevant ones.
14142 //Look for the right pinned plug to start from.
14143 while (mi != mark_stack_tos)
14144 {
14145 m = pinned_plug_of (mi);
14146 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
14147 {
14148 dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
14149 break;
14150 }
14151 else
14152 mi++;
14153 }
14154 if (mi != mark_stack_tos)
14155 {
14156 //fix old free list.
14157 size_t hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
14158 {
14159 dprintf(3,("gc filling up hole"));
14160 ptrdiff_t mi1 = (ptrdiff_t)mi;
14161 while ((mi1 >= 0) &&
14162 (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
14163 {
14164 dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
14165 mi1--;
14166 }
14167 if (mi1 >= 0)
14168 {
14169 size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
14170 pinned_len (pinned_plug_of(mi1)) = hsize;
14171 dprintf (3, ("changing %Ix len %Ix->%Ix",
14172 pinned_plug (pinned_plug_of(mi1)),
14173 saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
14174 }
14175 }
14176 }
14177 }
14178 else
14179 {
14180 assert (generation_allocation_limit (gen) ==
14181 generation_allocation_pointer (gen));
14182 mi = mark_stack_tos;
14183 }
14184
14185 while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
14186 {
14187 size_t len = pinned_len (m);
14188 uint8_t* free_list = (pinned_plug (m) - len);
14189 dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)",
14190 free_list, (free_list + len), len));
14191 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
14192 {
14193 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
14194 (size_t)free_list, len));
14195 {
14196 generation_allocation_pointer (gen) = free_list;
14197 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14198 generation_allocation_limit (gen) = (free_list + len);
14199 }
14200 goto allocate_in_free;
14201 }
14202 mi++;
14203 m = pinned_plug_of (mi);
14204 }
14205
14206 //switch to the end of the segment.
14207 generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
14208 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14209 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14210 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14211 dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)",
14212 generation_allocation_pointer (gen), generation_allocation_limit (gen),
14213 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
14214
14215 if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14216 generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
14217 {
14218 dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
14219 generation_allocation_limit (gen)));
14220 assert (!"Can't allocate if no free space");
14221 return 0;
14222 }
14223 }
14224 else
14225 {
14226 adjacentp = TRUE;
14227 }
14228
14229allocate_in_free:
14230 {
14231 uint8_t* result = generation_allocation_pointer (gen);
14232 size_t pad = 0;
14233
14234#ifdef SHORT_PLUGS
14235 if ((pad_in_front & USE_PADDING_FRONT) &&
14236 (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14237 ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14238
14239 {
14240 pad = Align (min_obj_size);
14241 set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
14242 }
14243#endif //SHORT_PLUGS
14244
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 adjacentp = FALSE;
14254 }
14255#else // FEATURE_STRUCTALIGN
14256 if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14257 {
14258 pad += switch_alignment_size (is_plug_padded (old_loc));
14259 set_node_realigned (old_loc);
14260 dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14261 (size_t)old_loc, (size_t)(result+pad)));
14262 assert (same_large_alignment_p (result + pad, old_loc));
14263 adjacentp = FALSE;
14264 }
14265#endif // FEATURE_STRUCTALIGN
14266
14267 if ((old_loc == 0) || (pad != 0))
14268 {
14269 //allocating a non plug or a gap, so reset the start region
14270 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14271 }
14272
14273 generation_allocation_pointer (gen) += size + pad;
14274 assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14275 dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
14276
14277 dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix",
14278 generation_allocation_pointer (gen), generation_allocation_limit (gen),
14279 generation_allocation_context_start_region (gen)));
14280
14281 return result + pad;
14282 }
14283}
14284
14285generation* gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
14286{
14287 heap_segment* seg = generation_allocation_segment (consing_gen);
14288 if (seg != ephemeral_heap_segment)
14289 {
14290 assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
14291 assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
14292
14293 //fix the allocated size of the segment.
14294 heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14295
14296 generation* new_consing_gen = generation_of (max_generation - 1);
14297 generation_allocation_pointer (new_consing_gen) =
14298 heap_segment_mem (ephemeral_heap_segment);
14299 generation_allocation_limit (new_consing_gen) =
14300 generation_allocation_pointer (new_consing_gen);
14301 generation_allocation_context_start_region (new_consing_gen) =
14302 generation_allocation_pointer (new_consing_gen);
14303 generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
14304
14305 return new_consing_gen;
14306 }
14307 else
14308 return consing_gen;
14309}
14310
14311uint8_t* gc_heap::allocate_in_condemned_generations (generation* gen,
14312 size_t size,
14313 int from_gen_number,
14314#ifdef SHORT_PLUGS
14315 BOOL* convert_to_pinned_p,
14316 uint8_t* next_pinned_plug,
14317 heap_segment* current_seg,
14318#endif //SHORT_PLUGS
14319 uint8_t* old_loc
14320 REQD_ALIGN_AND_OFFSET_DCL)
14321{
14322 // Make sure that the youngest generation gap hasn't been allocated
14323 if (settings.promotion)
14324 {
14325 assert (generation_plan_allocation_start (youngest_generation) == 0);
14326 }
14327
14328 size = Align (size);
14329 assert (size >= Align (min_obj_size));
14330 int to_gen_number = from_gen_number;
14331 if (from_gen_number != (int)max_generation)
14332 {
14333 to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
14334 }
14335
14336 dprintf (3, ("aic gen%d: s: %Id", gen->gen_num, size));
14337
14338 int pad_in_front = ((old_loc != 0) && (to_gen_number != max_generation)) ? USE_PADDING_FRONT : 0;
14339
14340 if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
14341 {
14342 generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14343 generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14344 }
14345retry:
14346 {
14347 heap_segment* seg = generation_allocation_segment (gen);
14348 if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14349 generation_allocation_limit (gen), old_loc,
14350 ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
14351 {
14352 if ((! (pinned_plug_que_empty_p()) &&
14353 (generation_allocation_limit (gen) ==
14354 pinned_plug (oldest_pin()))))
14355 {
14356 size_t entry = deque_pinned_plug();
14357 mark* pinned_plug_entry = pinned_plug_of (entry);
14358 size_t len = pinned_len (pinned_plug_entry);
14359 uint8_t* plug = pinned_plug (pinned_plug_entry);
14360 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
14361
14362#ifdef FREE_USAGE_STATS
14363 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
14364 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id",
14365 generation_allocated_since_last_pin (gen),
14366 plug,
14367 generation_allocated_in_pinned_free (gen)));
14368 generation_allocated_since_last_pin (gen) = 0;
14369
14370 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
14371#endif //FREE_USAGE_STATS
14372
14373 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix",
14374 mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
14375
14376 assert(mark_stack_array[entry].len == 0 ||
14377 mark_stack_array[entry].len >= Align(min_obj_size));
14378 generation_allocation_pointer (gen) = plug + len;
14379 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14380 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14381 set_allocator_next_pin (gen);
14382
14383 //Add the size of the pinned plug to the right pinned allocations
14384 //find out which gen this pinned plug came from
14385 int frgn = object_gennum (plug);
14386 if ((frgn != (int)max_generation) && settings.promotion)
14387 {
14388 generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
14389 int togn = object_gennum_plan (plug);
14390 if (frgn < togn)
14391 {
14392 generation_pinned_allocation_compact_size (generation_of (togn)) += len;
14393 }
14394 }
14395 goto retry;
14396 }
14397
14398 if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
14399 {
14400 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14401 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
14402 }
14403 else
14404 {
14405 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
14406 {
14407 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14408 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14409 dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
14410 }
14411 else
14412 {
14413#ifndef RESPECT_LARGE_ALIGNMENT
14414 assert (gen != youngest_generation);
14415#endif //RESPECT_LARGE_ALIGNMENT
14416
14417 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14418 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14419 (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
14420 size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
14421 {
14422 dprintf (3, ("Expanded segment allocation by committing more memory"));
14423 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14424 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14425 }
14426 else
14427 {
14428 heap_segment* next_seg = heap_segment_next (seg);
14429 assert (generation_allocation_pointer (gen)>=
14430 heap_segment_mem (seg));
14431 // Verify that all pinned plugs for this segment are consumed
14432 if (!pinned_plug_que_empty_p() &&
14433 ((pinned_plug (oldest_pin()) <
14434 heap_segment_allocated (seg)) &&
14435 (pinned_plug (oldest_pin()) >=
14436 generation_allocation_pointer (gen))))
14437 {
14438 LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
14439 pinned_plug (oldest_pin())));
14440 FATAL_GC_ERROR();
14441 }
14442 assert (generation_allocation_pointer (gen)>=
14443 heap_segment_mem (seg));
14444 assert (generation_allocation_pointer (gen)<=
14445 heap_segment_committed (seg));
14446 heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
14447
14448 if (next_seg)
14449 {
14450 generation_allocation_segment (gen) = next_seg;
14451 generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14452 generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14453 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14454 }
14455 else
14456 {
14457 return 0; //should only happen during allocation of generation 0 gap
14458 // in that case we are going to grow the heap anyway
14459 }
14460 }
14461 }
14462 }
14463 set_allocator_next_pin (gen);
14464
14465 goto retry;
14466 }
14467 }
14468
14469 {
14470 assert (generation_allocation_pointer (gen)>=
14471 heap_segment_mem (generation_allocation_segment (gen)));
14472 uint8_t* result = generation_allocation_pointer (gen);
14473 size_t pad = 0;
14474#ifdef SHORT_PLUGS
14475 if ((pad_in_front & USE_PADDING_FRONT) &&
14476 (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14477 ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14478 {
14479 ptrdiff_t dist = old_loc - result;
14480 if (dist == 0)
14481 {
14482 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
14483 pad = 0;
14484 }
14485 else
14486 {
14487 if ((dist > 0) && (dist < (ptrdiff_t)Align (min_obj_size)))
14488 {
14489 dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
14490 FATAL_GC_ERROR();
14491 }
14492
14493 pad = Align (min_obj_size);
14494 set_plug_padded (old_loc);
14495 }
14496 }
14497#endif //SHORT_PLUGS
14498#ifdef FEATURE_STRUCTALIGN
14499 _ASSERTE(!old_loc || alignmentOffset != 0);
14500 _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14501 if ((old_loc != 0))
14502 {
14503 size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14504 set_node_aligninfo (old_loc, requiredAlignment, pad1);
14505 pad += pad1;
14506 }
14507#else // FEATURE_STRUCTALIGN
14508 if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14509 {
14510 pad += switch_alignment_size (is_plug_padded (old_loc));
14511 set_node_realigned(old_loc);
14512 dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14513 (size_t)old_loc, (size_t)(result+pad)));
14514 assert (same_large_alignment_p (result + pad, old_loc));
14515 }
14516#endif // FEATURE_STRUCTALIGN
14517
14518#ifdef SHORT_PLUGS
14519 if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
14520 {
14521 assert (old_loc != 0);
14522 ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
14523 assert (dist_to_next_pin >= 0);
14524
14525 if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
14526 {
14527 dprintf (3, ("%Ix->(%Ix,%Ix),%Ix(%Ix)(%Ix),NP->PP",
14528 old_loc,
14529 generation_allocation_pointer (gen),
14530 generation_allocation_limit (gen),
14531 next_pinned_plug,
14532 size,
14533 dist_to_next_pin));
14534 clear_plug_padded (old_loc);
14535 pad = 0;
14536 *convert_to_pinned_p = TRUE;
14537 record_interesting_data_point (idp_converted_pin);
14538
14539 return 0;
14540 }
14541 }
14542#endif //SHORT_PLUGS
14543
14544 if ((old_loc == 0) || (pad != 0))
14545 {
14546 //allocating a non plug or a gap, so reset the start region
14547 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14548 }
14549
14550 generation_allocation_pointer (gen) += size + pad;
14551 assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14552
14553#ifdef FREE_USAGE_STATS
14554 generation_allocated_since_last_pin (gen) += size;
14555#endif //FREE_USAGE_STATS
14556
14557 dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix",
14558 generation_allocation_pointer (gen), generation_allocation_limit (gen),
14559 generation_allocation_context_start_region (gen)));
14560
14561 assert (result + pad);
14562 return result + pad;
14563 }
14564}
14565
14566inline int power (int x, int y)
14567{
14568 int z = 1;
14569 for (int i = 0; i < y; i++)
14570 {
14571 z = z*x;
14572 }
14573 return z;
14574}
14575
14576int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation,
14577 int initial_gen,
14578 int current_gen,
14579 BOOL* blocking_collection_p
14580 STRESS_HEAP_ARG(int n_original))
14581{
14582 int n = current_gen;
14583#ifdef MULTIPLE_HEAPS
14584 BOOL joined_last_gc_before_oom = FALSE;
14585 for (int i = 0; i < n_heaps; i++)
14586 {
14587 if (g_heaps[i]->last_gc_before_oom)
14588 {
14589 dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
14590 joined_last_gc_before_oom = TRUE;
14591 break;
14592 }
14593 }
14594#else
14595 BOOL joined_last_gc_before_oom = last_gc_before_oom;
14596#endif //MULTIPLE_HEAPS
14597
14598 if (joined_last_gc_before_oom && settings.pause_mode != pause_low_latency)
14599 {
14600 assert (*blocking_collection_p);
14601 }
14602
14603 if (should_evaluate_elevation && (n == max_generation))
14604 {
14605 dprintf (GTC_LOG, ("lock: %d(%d)",
14606 (settings.should_lock_elevation ? 1 : 0),
14607 settings.elevation_locked_count));
14608
14609 if (settings.should_lock_elevation)
14610 {
14611 settings.elevation_locked_count++;
14612 if (settings.elevation_locked_count == 6)
14613 {
14614 settings.elevation_locked_count = 0;
14615 }
14616 else
14617 {
14618 n = max_generation - 1;
14619 settings.elevation_reduced = TRUE;
14620 }
14621 }
14622 else
14623 {
14624 settings.elevation_locked_count = 0;
14625 }
14626 }
14627 else
14628 {
14629 settings.should_lock_elevation = FALSE;
14630 settings.elevation_locked_count = 0;
14631 }
14632
14633 if (provisional_mode_triggered && (n == max_generation))
14634 {
14635 // There are a few cases where we should not reduce the generation.
14636 if ((initial_gen == max_generation) || (settings.reason == reason_alloc_loh))
14637 {
14638 // If we are doing a full GC in the provisional mode, we always
14639 // make it blocking because we don't want to get into a situation
14640 // where foreground GCs are asking for a compacting full GC right away
14641 // and not getting it.
14642 dprintf (GTC_LOG, ("full GC induced, not reducing gen"));
14643 *blocking_collection_p = TRUE;
14644 }
14645 else if (should_expand_in_full_gc || joined_last_gc_before_oom)
14646 {
14647 dprintf (GTC_LOG, ("need full blocking GCs to expand heap or avoid OOM, not reducing gen"));
14648 assert (*blocking_collection_p);
14649 }
14650 else
14651 {
14652 dprintf (GTC_LOG, ("reducing gen in PM: %d->%d->%d", initial_gen, n, (max_generation - 1)));
14653 n = max_generation - 1;
14654 }
14655 }
14656
14657 if (should_expand_in_full_gc)
14658 {
14659 should_expand_in_full_gc = FALSE;
14660 }
14661
14662 if ((n == max_generation) && (*blocking_collection_p == FALSE))
14663 {
14664 // If we are doing a gen2 we should reset elevation regardless and let the gen2
14665 // decide if we should lock again or in the bgc case by design we will not retract
14666 // gen1 start.
14667 settings.should_lock_elevation = FALSE;
14668 settings.elevation_locked_count = 0;
14669 dprintf (1, ("doing bgc, reset elevation"));
14670 }
14671
14672#ifdef STRESS_HEAP
14673#ifdef BACKGROUND_GC
14674 // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14675 // generations to be collected,
14676 //
14677 // [LOCALGC TODO] STRESS_HEAP is not defined for a standalone GC so there are multiple
14678 // things that need to be fixed in this code block.
14679 if (n_original != max_generation &&
14680 g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
14681 {
14682#ifndef FEATURE_REDHAWK
14683 // for the GC stress mix mode throttle down gen2 collections
14684 if (g_pConfig->IsGCStressMix())
14685 {
14686 size_t current_gc_count = 0;
14687
14688#ifdef MULTIPLE_HEAPS
14689 current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
14690#else
14691 current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
14692#endif //MULTIPLE_HEAPS
14693 // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
14694 if ((current_gc_count % 10) == 0)
14695 {
14696 n = max_generation;
14697 }
14698 }
14699 // for traditional GC stress
14700 else
14701#endif // !FEATURE_REDHAWK
14702 if (*blocking_collection_p)
14703 {
14704 // We call StressHeap() a lot for Concurrent GC Stress. However,
14705 // if we can not do a concurrent collection, no need to stress anymore.
14706 // @TODO: Enable stress when the memory pressure goes down again
14707 GCStressPolicy::GlobalDisable();
14708 }
14709 else
14710 {
14711 n = max_generation;
14712 }
14713 }
14714#endif //BACKGROUND_GC
14715#endif //STRESS_HEAP
14716
14717 return n;
14718}
14719
14720inline
14721size_t get_survived_size (gc_history_per_heap* hist)
14722{
14723 size_t surv_size = 0;
14724 gc_generation_data* gen_data;
14725
14726 for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14727 {
14728 gen_data = &(hist->gen_data[gen_number]);
14729 surv_size += (gen_data->size_after -
14730 gen_data->free_list_space_after -
14731 gen_data->free_obj_space_after);
14732 }
14733
14734 return surv_size;
14735}
14736
14737size_t gc_heap::get_total_survived_size()
14738{
14739 size_t total_surv_size = 0;
14740#ifdef MULTIPLE_HEAPS
14741 for (int i = 0; i < gc_heap::n_heaps; i++)
14742 {
14743 gc_heap* hp = gc_heap::g_heaps[i];
14744 gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
14745 total_surv_size += get_survived_size (current_gc_data_per_heap);
14746 }
14747#else
14748 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14749 total_surv_size = get_survived_size (current_gc_data_per_heap);
14750#endif //MULTIPLE_HEAPS
14751 return total_surv_size;
14752}
14753
14754// Gets what's allocated on both SOH and LOH that hasn't been collected.
14755size_t gc_heap::get_current_allocated()
14756{
14757 dynamic_data* dd = dynamic_data_of (0);
14758 size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
14759 dd = dynamic_data_of (max_generation + 1);
14760 current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
14761
14762 return current_alloc;
14763}
14764
14765size_t gc_heap::get_total_allocated()
14766{
14767 size_t total_current_allocated = 0;
14768#ifdef MULTIPLE_HEAPS
14769 for (int i = 0; i < gc_heap::n_heaps; i++)
14770 {
14771 gc_heap* hp = gc_heap::g_heaps[i];
14772 total_current_allocated += hp->get_current_allocated();
14773 }
14774#else
14775 total_current_allocated = get_current_allocated();
14776#endif //MULTIPLE_HEAPS
14777 return total_current_allocated;
14778}
14779
14780size_t gc_heap::current_generation_size (int gen_number)
14781{
14782 dynamic_data* dd = dynamic_data_of (gen_number);
14783 size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
14784 - dd_new_allocation (dd));
14785
14786 return gen_size;
14787}
14788
14789#ifdef _PREFAST_
14790#pragma warning(push)
14791#pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
14792#endif //_PREFAST_
14793
14794/*
14795 This is called by when we are actually doing a GC, or when we are just checking whether
14796 we would do a full blocking GC, in which case check_only_p is TRUE.
14797
14798 The difference between calling this with check_only_p TRUE and FALSE is that when it's
14799 TRUE:
14800 settings.reason is ignored
14801 budgets are not checked (since they are checked before this is called)
14802 it doesn't change anything non local like generation_skip_ratio
14803*/
14804int gc_heap::generation_to_condemn (int n_initial,
14805 BOOL* blocking_collection_p,
14806 BOOL* elevation_requested_p,
14807 BOOL check_only_p)
14808{
14809 gc_mechanisms temp_settings = settings;
14810 gen_to_condemn_tuning temp_condemn_reasons;
14811 gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
14812 gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
14813 if (!check_only_p)
14814 {
14815 if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
14816 {
14817 assert (n_initial >= 1);
14818 }
14819
14820 assert (settings.reason != reason_empty);
14821 }
14822
14823 local_condemn_reasons->init();
14824
14825 int n = n_initial;
14826 int n_alloc = n;
14827 if (heap_number == 0)
14828 {
14829 dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
14830 }
14831 int i = 0;
14832 int temp_gen = 0;
14833 BOOL low_memory_detected = g_low_memory_status;
14834 uint32_t memory_load = 0;
14835 uint64_t available_physical = 0;
14836 uint64_t available_page_file = 0;
14837 BOOL check_memory = FALSE;
14838 BOOL high_fragmentation = FALSE;
14839 BOOL v_high_memory_load = FALSE;
14840 BOOL high_memory_load = FALSE;
14841 BOOL low_ephemeral_space = FALSE;
14842 BOOL evaluate_elevation = TRUE;
14843 *elevation_requested_p = FALSE;
14844 *blocking_collection_p = FALSE;
14845
14846 BOOL check_max_gen_alloc = TRUE;
14847
14848#ifdef STRESS_HEAP
14849 int orig_gen = n;
14850#endif //STRESS_HEAP
14851
14852 if (!check_only_p)
14853 {
14854 dd_fragmentation (dynamic_data_of (0)) =
14855 generation_free_list_space (youngest_generation) +
14856 generation_free_obj_space (youngest_generation);
14857
14858 dd_fragmentation (dynamic_data_of (max_generation + 1)) =
14859 generation_free_list_space (large_object_generation) +
14860 generation_free_obj_space (large_object_generation);
14861
14862 //save new_allocation
14863 for (i = 0; i <= max_generation+1; i++)
14864 {
14865 dynamic_data* dd = dynamic_data_of (i);
14866 dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)",
14867 heap_number, i,
14868 dd_new_allocation (dd),
14869 dd_desired_allocation (dd)));
14870 dd_gc_new_allocation (dd) = dd_new_allocation (dd);
14871 }
14872
14873 local_condemn_reasons->set_gen (gen_initial, n);
14874 temp_gen = n;
14875
14876#ifdef BACKGROUND_GC
14877 if (recursive_gc_sync::background_running_p())
14878 {
14879 dprintf (GTC_LOG, ("bgc in prog, 1"));
14880 check_max_gen_alloc = FALSE;
14881 }
14882#endif //BACKGROUND_GC
14883
14884 if (check_max_gen_alloc)
14885 {
14886 //figure out if large objects need to be collected.
14887 if (get_new_allocation (max_generation+1) <= 0)
14888 {
14889 n = max_generation;
14890 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14891 }
14892 }
14893
14894 //figure out which generation ran out of allocation
14895 for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
14896 {
14897 if (get_new_allocation (i) <= 0)
14898 {
14899 n = i;
14900 }
14901 else
14902 break;
14903 }
14904 }
14905
14906 if (n > temp_gen)
14907 {
14908 local_condemn_reasons->set_gen (gen_alloc_budget, n);
14909 }
14910
14911 dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
14912
14913 n_alloc = n;
14914
14915#if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
14916 //time based tuning
14917 // if enough time has elapsed since the last gc
14918 // and the number of gc is too low (1/10 of lower gen) then collect
14919 // This should also be enabled if we have memory concerns
14920 int n_time_max = max_generation;
14921
14922 if (!check_only_p)
14923 {
14924 if (recursive_gc_sync::background_running_p())
14925 {
14926 n_time_max = max_generation - 1;
14927 }
14928 }
14929
14930 if ((local_settings->pause_mode == pause_interactive) ||
14931 (local_settings->pause_mode == pause_sustained_low_latency))
14932 {
14933 dynamic_data* dd0 = dynamic_data_of (0);
14934 size_t now = GetHighPrecisionTimeStamp();
14935 temp_gen = n;
14936 for (i = (temp_gen+1); i <= n_time_max; i++)
14937 {
14938 dynamic_data* dd = dynamic_data_of (i);
14939 if ((now > dd_time_clock(dd) + dd_time_clock_interval(dd)) &&
14940 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + dd_gc_clock_interval(dd))) &&
14941 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
14942 {
14943 n = min (i, n_time_max);
14944 dprintf (GTC_LOG, ("time %d", n));
14945 }
14946 }
14947 if (n > temp_gen)
14948 {
14949 local_condemn_reasons->set_gen (gen_time_tuning, n);
14950 }
14951 }
14952
14953 if (n != n_alloc)
14954 {
14955 dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
14956 }
14957#endif //BACKGROUND_GC && !MULTIPLE_HEAPS
14958
14959 if (n < (max_generation - 1))
14960 {
14961 if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
14962 {
14963 n = max (n, max_generation - 1);
14964 local_settings->promotion = TRUE;
14965 dprintf (GTC_LOG, ("h%d: skip %d, c %d",
14966 heap_number, generation_skip_ratio, n));
14967 local_condemn_reasons->set_condition (gen_low_card_p);
14968 }
14969 }
14970
14971 if (!check_only_p)
14972 {
14973 generation_skip_ratio = 100;
14974 }
14975
14976 if (dt_low_ephemeral_space_p (check_only_p ?
14977 tuning_deciding_full_gc :
14978 tuning_deciding_condemned_gen))
14979 {
14980 low_ephemeral_space = TRUE;
14981
14982 n = max (n, max_generation - 1);
14983 local_condemn_reasons->set_condition (gen_low_ephemeral_p);
14984 dprintf (GTC_LOG, ("h%d: low eph", heap_number));
14985
14986 if (!provisional_mode_triggered)
14987 {
14988#ifdef BACKGROUND_GC
14989 if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
14990#endif //BACKGROUND_GC
14991 {
14992 //It is better to defragment first if we are running out of space for
14993 //the ephemeral generation but we have enough fragmentation to make up for it
14994 //in the non ephemeral generation. Essentially we are trading a gen2 for
14995 // having to expand heap in ephemeral collections.
14996 if (dt_high_frag_p (tuning_deciding_condemned_gen,
14997 max_generation - 1,
14998 TRUE))
14999 {
15000 high_fragmentation = TRUE;
15001 local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
15002 dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
15003 }
15004 }
15005 }
15006 }
15007
15008 //figure out which ephemeral generation is too fragramented
15009 temp_gen = n;
15010 for (i = n+1; i < max_generation; i++)
15011 {
15012 if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
15013 {
15014 dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
15015 n = i;
15016 }
15017 else
15018 break;
15019 }
15020
15021 if (low_ephemeral_space)
15022 {
15023 //enable promotion
15024 local_settings->promotion = TRUE;
15025 }
15026
15027 if (n > temp_gen)
15028 {
15029 local_condemn_reasons->set_condition (gen_eph_high_frag_p);
15030 }
15031
15032 if (!check_only_p)
15033 {
15034 if (settings.pause_mode == pause_low_latency)
15035 {
15036 if (!is_induced (settings.reason))
15037 {
15038 n = min (n, max_generation - 1);
15039 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
15040 evaluate_elevation = FALSE;
15041 goto exit;
15042 }
15043 }
15044 }
15045
15046 // It's hard to catch when we get to the point that the memory load is so high
15047 // we get an induced GC from the finalizer thread so we are checking the memory load
15048 // for every gen0 GC.
15049 check_memory = (check_only_p ?
15050 (n >= 0) :
15051 ((n >= 1) || low_memory_detected));
15052
15053 if (check_memory)
15054 {
15055 //find out if we are short on memory
15056 get_memory_info (&memory_load, &available_physical, &available_page_file);
15057 if (heap_number == 0)
15058 {
15059 dprintf (GTC_LOG, ("ml: %d", memory_load));
15060 }
15061
15062 // Need to get it early enough for all heaps to use.
15063 entry_available_physical_mem = available_physical;
15064 local_settings->entry_memory_load = memory_load;
15065
15066 // @TODO: Force compaction more often under GCSTRESS
15067 if (memory_load >= high_memory_load_th || low_memory_detected)
15068 {
15069#ifdef SIMPLE_DPRINTF
15070 // stress log can't handle any parameter that's bigger than a void*.
15071 if (heap_number == 0)
15072 {
15073 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d", total_physical_mem, available_physical));
15074 }
15075#endif //SIMPLE_DPRINTF
15076
15077 high_memory_load = TRUE;
15078
15079 if (memory_load >= v_high_memory_load_th || low_memory_detected)
15080 {
15081 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
15082 // gen1/gen0 may take a lot more memory than gen2.
15083 if (!high_fragmentation)
15084 {
15085 high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation);
15086 }
15087 v_high_memory_load = TRUE;
15088 }
15089 else
15090 {
15091 if (!high_fragmentation)
15092 {
15093 high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, available_physical);
15094 }
15095 }
15096
15097 if (high_fragmentation)
15098 {
15099 if (high_memory_load)
15100 {
15101 local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
15102 }
15103 else if (v_high_memory_load)
15104 {
15105 local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
15106 }
15107 }
15108 }
15109 }
15110
15111 dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
15112 heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
15113 high_fragmentation));
15114
15115 if (should_expand_in_full_gc)
15116 {
15117 dprintf (GTC_LOG, ("h%d: expand_in_full - BLOCK", heap_number));
15118 *blocking_collection_p = TRUE;
15119 evaluate_elevation = FALSE;
15120 n = max_generation;
15121 local_condemn_reasons->set_condition (gen_expand_fullgc_p);
15122 }
15123
15124 if (last_gc_before_oom)
15125 {
15126 dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
15127 n = max_generation;
15128 *blocking_collection_p = TRUE;
15129 if ((local_settings->reason == reason_oos_loh) ||
15130 (local_settings->reason == reason_alloc_loh))
15131 {
15132 evaluate_elevation = FALSE;
15133 }
15134
15135 local_condemn_reasons->set_condition (gen_before_oom);
15136 }
15137
15138 if (!check_only_p)
15139 {
15140 if (is_induced_blocking (settings.reason) &&
15141 n_initial == max_generation
15142 IN_STRESS_HEAP( && !settings.stress_induced ))
15143 {
15144 if (heap_number == 0)
15145 {
15146 dprintf (GTC_LOG, ("induced - BLOCK"));
15147 }
15148
15149 *blocking_collection_p = TRUE;
15150 local_condemn_reasons->set_condition (gen_induced_fullgc_p);
15151 evaluate_elevation = FALSE;
15152 }
15153
15154 if (settings.reason == reason_induced_noforce)
15155 {
15156 local_condemn_reasons->set_condition (gen_induced_noforce_p);
15157 evaluate_elevation = FALSE;
15158 }
15159 }
15160
15161 if (!provisional_mode_triggered && evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
15162 {
15163 *elevation_requested_p = TRUE;
15164#ifdef BIT64
15165 // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
15166 if (high_memory_load || v_high_memory_load)
15167 {
15168 dynamic_data* dd_max = dynamic_data_of (max_generation);
15169 if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
15170 {
15171 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)",
15172 dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
15173 n = max_generation;
15174 local_condemn_reasons->set_condition (gen_almost_max_alloc);
15175 }
15176 }
15177
15178 if (n <= max_generation)
15179 {
15180#endif // BIT64
15181 if (high_fragmentation)
15182 {
15183 //elevate to max_generation
15184 n = max_generation;
15185 dprintf (GTC_LOG, ("h%d: f full", heap_number));
15186
15187#ifdef BACKGROUND_GC
15188 if (high_memory_load || v_high_memory_load)
15189 {
15190 // For background GC we want to do blocking collections more eagerly because we don't
15191 // want to get into the situation where the memory load becomes high while we are in
15192 // a background GC and we'd have to wait for the background GC to finish to start
15193 // a blocking collection (right now the implemenation doesn't handle converting
15194 // a background GC to a blocking collection midway.
15195 dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
15196 *blocking_collection_p = TRUE;
15197 }
15198#else
15199 if (v_high_memory_load)
15200 {
15201 dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
15202 *blocking_collection_p = TRUE;
15203 }
15204#endif //BACKGROUND_GC
15205 }
15206 else
15207 {
15208 n = max (n, max_generation - 1);
15209 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
15210 }
15211#ifdef BIT64
15212 }
15213#endif // BIT64
15214 }
15215
15216 if (!provisional_mode_triggered && (n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
15217 {
15218 dprintf (GTC_LOG, ("h%d: budget %d, check 2",
15219 heap_number, n_alloc));
15220 if (get_new_allocation (max_generation) <= 0)
15221 {
15222 dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
15223 n = max_generation;
15224 local_condemn_reasons->set_condition (gen_max_gen1);
15225 }
15226 }
15227
15228 //figure out if max_generation is too fragmented -> blocking collection
15229 if (!provisional_mode_triggered && (n == max_generation))
15230 {
15231 if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
15232 {
15233 dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
15234 local_condemn_reasons->set_condition (gen_max_high_frag_p);
15235 if (local_settings->pause_mode != pause_sustained_low_latency)
15236 {
15237 *blocking_collection_p = TRUE;
15238 }
15239 }
15240 }
15241
15242#ifdef BACKGROUND_GC
15243 if (n == max_generation)
15244 {
15245 if (heap_number == 0)
15246 {
15247 BOOL bgc_heap_too_small = TRUE;
15248 size_t gen2size = 0;
15249 size_t gen3size = 0;
15250#ifdef MULTIPLE_HEAPS
15251 for (int i = 0; i < n_heaps; i++)
15252 {
15253 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) ||
15254 ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
15255 {
15256 bgc_heap_too_small = FALSE;
15257 break;
15258 }
15259 }
15260#else //MULTIPLE_HEAPS
15261 if ((current_generation_size (max_generation) > bgc_min_per_heap) ||
15262 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
15263 {
15264 bgc_heap_too_small = FALSE;
15265 }
15266#endif //MULTIPLE_HEAPS
15267
15268 if (bgc_heap_too_small)
15269 {
15270 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
15271
15272#ifdef STRESS_HEAP
15273 // do not turn stress-induced collections into blocking GCs
15274 if (!settings.stress_induced)
15275#endif //STRESS_HEAP
15276 {
15277 *blocking_collection_p = TRUE;
15278 }
15279
15280 local_condemn_reasons->set_condition (gen_gen2_too_small);
15281 }
15282 }
15283 }
15284#endif //BACKGROUND_GC
15285
15286exit:
15287 if (!check_only_p)
15288 {
15289#ifdef STRESS_HEAP
15290#ifdef BACKGROUND_GC
15291 // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
15292 // generations to be collected,
15293
15294 if (orig_gen != max_generation &&
15295 g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
15296 {
15297 *elevation_requested_p = FALSE;
15298 }
15299#endif //BACKGROUND_GC
15300#endif //STRESS_HEAP
15301
15302 if (check_memory)
15303 {
15304 fgm_result.available_pagefile_mb = (size_t)(available_page_file / (1024 * 1024));
15305 }
15306
15307 local_condemn_reasons->set_gen (gen_final_per_heap, n);
15308 get_gc_data_per_heap()->gen_to_condemn_reasons.init (local_condemn_reasons);
15309
15310#ifdef DT_LOG
15311 local_condemn_reasons->print (heap_number);
15312#endif //DT_LOG
15313
15314 if ((local_settings->reason == reason_oos_soh) ||
15315 (local_settings->reason == reason_oos_loh))
15316 {
15317 assert (n >= 1);
15318 }
15319 }
15320
15321 return n;
15322}
15323
15324#ifdef _PREFAST_
15325#pragma warning(pop)
15326#endif //_PREFAST_
15327
15328inline
15329size_t gc_heap::min_reclaim_fragmentation_threshold (uint32_t num_heaps)
15330{
15331 // if the memory load is higher, the threshold we'd want to collect gets lower.
15332 size_t min_mem_based_on_available =
15333 (500 - (settings.entry_memory_load - high_memory_load_th) * 40) * 1024 * 1024 / num_heaps;
15334 size_t ten_percent_size = (size_t)((float)generation_size (max_generation) * 0.10);
15335 uint64_t three_percent_mem = mem_one_percent * 3 / num_heaps;
15336
15337#ifdef SIMPLE_DPRINTF
15338 dprintf (GTC_LOG, ("min av: %Id, 10%% gen2: %Id, 3%% mem: %I64d",
15339 min_mem_based_on_available, ten_percent_size, three_percent_mem));
15340#endif //SIMPLE_DPRINTF
15341 return (size_t)(min (min_mem_based_on_available, min (ten_percent_size, three_percent_mem)));
15342}
15343
15344inline
15345uint64_t gc_heap::min_high_fragmentation_threshold(uint64_t available_mem, uint32_t num_heaps)
15346{
15347 return min (available_mem, (256*1024*1024)) / num_heaps;
15348}
15349
15350enum {
15351CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
15352};
15353
15354
15355#ifdef BACKGROUND_GC
15356void gc_heap::init_background_gc ()
15357{
15358 //reset the allocation so foreground gc can allocate into older (max_generation) generation
15359 generation* gen = generation_of (max_generation);
15360 generation_allocation_pointer (gen)= 0;
15361 generation_allocation_limit (gen) = 0;
15362 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
15363
15364 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
15365
15366 //reset the plan allocation for each segment
15367 for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
15368 seg = heap_segment_next_rw (seg))
15369 {
15370 heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
15371 }
15372
15373 if (heap_number == 0)
15374 {
15375 dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix",
15376 heap_number,
15377 background_saved_lowest_address,
15378 background_saved_highest_address));
15379 }
15380
15381 gc_lh_block_event.Reset();
15382}
15383
15384#endif //BACKGROUND_GC
15385
15386inline
15387void fire_drain_mark_list_event (size_t mark_list_objects)
15388{
15389 FIRE_EVENT(BGCDrainMark, mark_list_objects);
15390}
15391
15392inline
15393void fire_revisit_event (size_t dirtied_pages,
15394 size_t marked_objects,
15395 BOOL large_objects_p)
15396{
15397 FIRE_EVENT(BGCRevisit, dirtied_pages, marked_objects, large_objects_p);
15398}
15399
15400inline
15401void fire_overflow_event (uint8_t* overflow_min,
15402 uint8_t* overflow_max,
15403 size_t marked_objects,
15404 int large_objects_p)
15405{
15406 FIRE_EVENT(BGCOverflow, (uint64_t)overflow_min, (uint64_t)overflow_max, marked_objects, large_objects_p);
15407}
15408
15409void gc_heap::concurrent_print_time_delta (const char* msg)
15410{
15411#ifdef TRACE_GC
15412 size_t current_time = GetHighPrecisionTimeStamp();
15413 size_t elapsed_time = current_time - time_bgc_last;
15414 time_bgc_last = current_time;
15415
15416 dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
15417#else
15418 UNREFERENCED_PARAMETER(msg);
15419#endif //TRACE_GC
15420}
15421
15422void gc_heap::free_list_info (int gen_num, const char* msg)
15423{
15424 UNREFERENCED_PARAMETER(gen_num);
15425#if defined (BACKGROUND_GC) && defined (TRACE_GC)
15426 dprintf (3, ("h%d: %s", heap_number, msg));
15427 for (int i = 0; i <= (max_generation + 1); i++)
15428 {
15429 generation* gen = generation_of (i);
15430 if ((generation_allocation_size (gen) == 0) &&
15431 (generation_free_list_space (gen) == 0) &&
15432 (generation_free_obj_space (gen) == 0))
15433 {
15434 // don't print if everything is 0.
15435 }
15436 else
15437 {
15438 dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
15439 heap_number, i,
15440 generation_allocation_size (gen),
15441 generation_free_list_space (gen),
15442 generation_free_obj_space (gen)));
15443 }
15444 }
15445#else
15446 UNREFERENCED_PARAMETER(msg);
15447#endif // BACKGROUND_GC && TRACE_GC
15448}
15449
15450void gc_heap::update_collection_counts_for_no_gc()
15451{
15452 assert (settings.pause_mode == pause_no_gc);
15453
15454 settings.condemned_generation = max_generation;
15455#ifdef MULTIPLE_HEAPS
15456 for (int i = 0; i < n_heaps; i++)
15457 g_heaps[i]->update_collection_counts();
15458#else //MULTIPLE_HEAPS
15459 update_collection_counts();
15460#endif //MULTIPLE_HEAPS
15461
15462 full_gc_counts[gc_type_blocking]++;
15463}
15464
15465BOOL gc_heap::should_proceed_with_gc()
15466{
15467 if (gc_heap::settings.pause_mode == pause_no_gc)
15468 {
15469 if (current_no_gc_region_info.started)
15470 {
15471 // The no_gc mode was already in progress yet we triggered another GC,
15472 // this effectively exits the no_gc mode.
15473 restore_data_for_no_gc();
15474 }
15475 else
15476 return should_proceed_for_no_gc();
15477 }
15478
15479 return TRUE;
15480}
15481
15482//internal part of gc used by the serial and concurrent version
15483void gc_heap::gc1()
15484{
15485#ifdef BACKGROUND_GC
15486 assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15487#endif //BACKGROUND_GC
15488
15489#ifdef TIME_GC
15490 mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
15491#endif //TIME_GC
15492
15493 verify_soh_segment_list();
15494
15495 int n = settings.condemned_generation;
15496
15497 if (settings.reason == reason_pm_full_gc)
15498 {
15499 assert (n == max_generation);
15500 init_records();
15501
15502 gen_to_condemn_tuning* local_condemn_reasons = &(get_gc_data_per_heap()->gen_to_condemn_reasons);
15503 local_condemn_reasons->init();
15504 local_condemn_reasons->set_gen (gen_initial, n);
15505 local_condemn_reasons->set_gen (gen_final_per_heap, n);
15506 }
15507
15508 update_collection_counts ();
15509
15510#ifdef BACKGROUND_GC
15511 bgc_alloc_lock->check();
15512#endif //BACKGROUND_GC
15513
15514 free_list_info (max_generation, "beginning");
15515
15516 vm_heap->GcCondemnedGeneration = settings.condemned_generation;
15517
15518 assert (g_gc_card_table == card_table);
15519
15520#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
15521 assert (g_gc_card_bundle_table == card_bundle_table);
15522#endif
15523
15524 {
15525 if (n == max_generation)
15526 {
15527 gc_low = lowest_address;
15528 gc_high = highest_address;
15529 }
15530 else
15531 {
15532 gc_low = generation_allocation_start (generation_of (n));
15533 gc_high = heap_segment_reserved (ephemeral_heap_segment);
15534 }
15535#ifdef BACKGROUND_GC
15536 if (settings.concurrent)
15537 {
15538#ifdef TRACE_GC
15539 time_bgc_last = GetHighPrecisionTimeStamp();
15540#endif //TRACE_GC
15541
15542 FIRE_EVENT(BGCBegin);
15543
15544 concurrent_print_time_delta ("BGC");
15545
15546//#ifdef WRITE_WATCH
15547 //reset_write_watch (FALSE);
15548//#endif //WRITE_WATCH
15549
15550 concurrent_print_time_delta ("RW");
15551 background_mark_phase();
15552 free_list_info (max_generation, "after mark phase");
15553
15554 background_sweep();
15555 free_list_info (max_generation, "after sweep phase");
15556 }
15557 else
15558#endif //BACKGROUND_GC
15559 {
15560 mark_phase (n, FALSE);
15561
15562 GCScan::GcRuntimeStructuresValid (FALSE);
15563 plan_phase (n);
15564 GCScan::GcRuntimeStructuresValid (TRUE);
15565 }
15566 }
15567
15568 size_t end_gc_time = GetHighPrecisionTimeStamp();
15569// printf ("generation: %d, elapsed time: %Id\n", n, end_gc_time - dd_time_clock (dynamic_data_of (0)));
15570
15571 //adjust the allocation size from the pinned quantities.
15572 for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
15573 {
15574 generation* gn = generation_of (gen_number);
15575 if (settings.compaction)
15576 {
15577 generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
15578 generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
15579 }
15580 else
15581 {
15582 generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
15583 generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
15584 }
15585 generation_pinned_allocation_sweep_size (gn) = 0;
15586 generation_pinned_allocation_compact_size (gn) = 0;
15587 }
15588
15589#ifdef BACKGROUND_GC
15590 if (settings.concurrent)
15591 {
15592 dynamic_data* dd = dynamic_data_of (n);
15593 dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15594
15595 free_list_info (max_generation, "after computing new dynamic data");
15596
15597 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
15598
15599 for (int gen_number = 0; gen_number < max_generation; gen_number++)
15600 {
15601 dprintf (2, ("end of BGC: gen%d new_alloc: %Id",
15602 gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15603 current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
15604 current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15605 current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15606 }
15607 }
15608 else
15609#endif //BACKGROUND_GC
15610 {
15611 free_list_info (max_generation, "end");
15612 for (int gen_number = 0; gen_number <= n; gen_number++)
15613 {
15614 dynamic_data* dd = dynamic_data_of (gen_number);
15615 dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15616 compute_new_dynamic_data (gen_number);
15617 }
15618
15619 if (n != max_generation)
15620 {
15621 int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1));
15622 for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++)
15623 {
15624 get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number);
15625 get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15626 get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15627 }
15628 }
15629
15630 get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100);
15631
15632 free_list_info (max_generation, "after computing new dynamic data");
15633
15634 if (heap_number == 0)
15635 {
15636 dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms",
15637 dd_collection_count (dynamic_data_of (0)),
15638 settings.condemned_generation,
15639 dd_gc_elapsed_time (dynamic_data_of (0))));
15640 }
15641
15642 for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
15643 {
15644 dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id",
15645 gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15646 }
15647 }
15648
15649 if (n < max_generation)
15650 {
15651 compute_promoted_allocation (1 + n);
15652
15653 dynamic_data* dd = dynamic_data_of (1 + n);
15654 size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) +
15655 generation_free_obj_space (generation_of (1 + n));
15656
15657#ifdef BACKGROUND_GC
15658 if (current_c_gc_state != c_gc_state_planning)
15659#endif //BACKGROUND_GC
15660 {
15661 if (settings.promotion)
15662 {
15663 dd_fragmentation (dd) = new_fragmentation;
15664 }
15665 else
15666 {
15667 //assert (dd_fragmentation (dd) == new_fragmentation);
15668 }
15669 }
15670 }
15671
15672#ifdef BACKGROUND_GC
15673 if (!settings.concurrent)
15674#endif //BACKGROUND_GC
15675 {
15676#ifndef FEATURE_REDHAWK
15677 // GCToEEInterface::IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
15678 assert(GCToEEInterface::IsGCThread());
15679#endif // FEATURE_REDHAWK
15680 adjust_ephemeral_limits();
15681 }
15682
15683#ifdef BACKGROUND_GC
15684 assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
15685 assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
15686#endif //BACKGROUND_GC
15687
15688 if (fgn_maxgen_percent)
15689 {
15690 if (settings.condemned_generation == (max_generation - 1))
15691 {
15692 check_for_full_gc (max_generation - 1, 0);
15693 }
15694 else if (settings.condemned_generation == max_generation)
15695 {
15696 if (full_gc_approach_event_set
15697#ifdef MULTIPLE_HEAPS
15698 && (heap_number == 0)
15699#endif //MULTIPLE_HEAPS
15700 )
15701 {
15702 dprintf (2, ("FGN-GC: setting gen2 end event"));
15703
15704 full_gc_approach_event.Reset();
15705#ifdef BACKGROUND_GC
15706 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
15707 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
15708#endif //BACKGROUND_GC
15709 full_gc_end_event.Set();
15710 full_gc_approach_event_set = false;
15711 }
15712 }
15713 }
15714
15715#ifdef BACKGROUND_GC
15716 if (!settings.concurrent)
15717#endif //BACKGROUND_GC
15718 {
15719 //decide on the next allocation quantum
15720 if (alloc_contexts_used >= 1)
15721 {
15722 allocation_quantum = Align (min ((size_t)CLR_SIZE,
15723 (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
15724 get_alignment_constant(FALSE));
15725 dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
15726 }
15727 }
15728
15729 descr_generations (FALSE);
15730
15731 verify_soh_segment_list();
15732
15733#ifdef BACKGROUND_GC
15734 add_to_history_per_heap();
15735 if (heap_number == 0)
15736 {
15737 add_to_history();
15738 }
15739#endif // BACKGROUND_GC
15740
15741#ifdef GC_STATS
15742 if (GCStatistics::Enabled() && heap_number == 0)
15743 g_GCStatistics.AddGCStats(settings,
15744 dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
15745#endif // GC_STATS
15746
15747#ifdef TIME_GC
15748 fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
15749 n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
15750#endif //TIME_GC
15751
15752#ifdef BACKGROUND_GC
15753 assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15754#endif //BACKGROUND_GC
15755
15756#if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15757 if (FALSE
15758#ifdef VERIFY_HEAP
15759 // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same
15760 // value. If we ever allow randomly adjusting this as the process runs,
15761 // we cannot call it this way as joins need to match - we must have the same
15762 // value for all heaps like we do with bgc_heap_walk_for_etw_p.
15763 || (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15764#endif
15765#if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
15766 || (bgc_heap_walk_for_etw_p && settings.concurrent)
15767#endif
15768 )
15769 {
15770#ifdef BACKGROUND_GC
15771 bool cooperative_mode = true;
15772
15773 if (settings.concurrent)
15774 {
15775 cooperative_mode = enable_preemptive ();
15776
15777#ifdef MULTIPLE_HEAPS
15778 bgc_t_join.join(this, gc_join_suspend_ee_verify);
15779 if (bgc_t_join.joined())
15780 {
15781 bgc_threads_sync_event.Reset();
15782
15783 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
15784 bgc_t_join.restart();
15785 }
15786 if (heap_number == 0)
15787 {
15788 suspend_EE();
15789 bgc_threads_sync_event.Set();
15790 }
15791 else
15792 {
15793 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15794 dprintf (2, ("bgc_threads_sync_event is signalled"));
15795 }
15796#else
15797 suspend_EE();
15798#endif //MULTIPLE_HEAPS
15799
15800 //fix the allocation area so verify_heap can proceed.
15801 fix_allocation_contexts (FALSE);
15802 }
15803#endif //BACKGROUND_GC
15804
15805#ifdef BACKGROUND_GC
15806 assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15807#ifdef FEATURE_EVENT_TRACE
15808 if (bgc_heap_walk_for_etw_p && settings.concurrent)
15809 {
15810 GCToEEInterface::DiagWalkBGCSurvivors(__this);
15811
15812#ifdef MULTIPLE_HEAPS
15813 bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
15814 if (bgc_t_join.joined())
15815 {
15816 bgc_t_join.restart();
15817 }
15818#endif // MULTIPLE_HEAPS
15819 }
15820#endif // FEATURE_EVENT_TRACE
15821#endif //BACKGROUND_GC
15822
15823#ifdef VERIFY_HEAP
15824 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15825 verify_heap (FALSE);
15826#endif // VERIFY_HEAP
15827
15828#ifdef BACKGROUND_GC
15829 if (settings.concurrent)
15830 {
15831 repair_allocation_contexts (TRUE);
15832
15833#ifdef MULTIPLE_HEAPS
15834 bgc_t_join.join(this, gc_join_restart_ee_verify);
15835 if (bgc_t_join.joined())
15836 {
15837 bgc_threads_sync_event.Reset();
15838
15839 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
15840 bgc_t_join.restart();
15841 }
15842 if (heap_number == 0)
15843 {
15844 restart_EE();
15845 bgc_threads_sync_event.Set();
15846 }
15847 else
15848 {
15849 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15850 dprintf (2, ("bgc_threads_sync_event is signalled"));
15851 }
15852#else
15853 restart_EE();
15854#endif //MULTIPLE_HEAPS
15855
15856 disable_preemptive (cooperative_mode);
15857 }
15858#endif //BACKGROUND_GC
15859 }
15860#endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15861
15862#ifdef MULTIPLE_HEAPS
15863 if (!settings.concurrent)
15864 {
15865 gc_t_join.join(this, gc_join_done);
15866 if (gc_t_join.joined ())
15867 {
15868 gc_heap::internal_gc_done = false;
15869
15870 //equalize the new desired size of the generations
15871 int limit = settings.condemned_generation;
15872 if (limit == max_generation)
15873 {
15874 limit = max_generation+1;
15875 }
15876 for (int gen = 0; gen <= limit; gen++)
15877 {
15878 size_t total_desired = 0;
15879
15880 for (int i = 0; i < gc_heap::n_heaps; i++)
15881 {
15882 gc_heap* hp = gc_heap::g_heaps[i];
15883 dynamic_data* dd = hp->dynamic_data_of (gen);
15884 size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
15885 if (temp_total_desired < total_desired)
15886 {
15887 // we overflowed.
15888 total_desired = (size_t)MAX_PTR;
15889 break;
15890 }
15891 total_desired = temp_total_desired;
15892 }
15893
15894 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
15895 get_alignment_constant ((gen != (max_generation+1))));
15896
15897 if (gen == 0)
15898 {
15899#if 1 //subsumed by the linear allocation model
15900 // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15901 // apply some smoothing.
15902 static size_t smoothed_desired_per_heap = 0;
15903 size_t smoothing = 3; // exponential smoothing factor
15904 if (smoothing > VolatileLoad(&settings.gc_index))
15905 smoothing = VolatileLoad(&settings.gc_index);
15906 smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
15907 dprintf (1, ("sn = %Id n = %Id", smoothed_desired_per_heap, desired_per_heap));
15908 desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
15909#endif //0
15910
15911 // if desired_per_heap is close to min_gc_size, trim it
15912 // down to min_gc_size to stay in the cache
15913 gc_heap* hp = gc_heap::g_heaps[0];
15914 dynamic_data* dd = hp->dynamic_data_of (gen);
15915 size_t min_gc_size = dd_min_size(dd);
15916 // if min GC size larger than true on die cache, then don't bother
15917 // limiting the desired size
15918 if ((min_gc_size <= GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)) &&
15919 desired_per_heap <= 2*min_gc_size)
15920 {
15921 desired_per_heap = min_gc_size;
15922 }
15923#ifdef BIT64
15924 desired_per_heap = joined_youngest_desired (desired_per_heap);
15925 dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
15926#endif // BIT64
15927
15928 gc_data_global.final_youngest_desired = desired_per_heap;
15929 }
15930#if 1 //subsumed by the linear allocation model
15931 if (gen == (max_generation + 1))
15932 {
15933 // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
15934 // apply some smoothing.
15935 static size_t smoothed_desired_per_heap_loh = 0;
15936 size_t smoothing = 3; // exponential smoothing factor
15937 size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
15938 if (smoothing > loh_count)
15939 smoothing = loh_count;
15940 smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
15941 dprintf( 2, ("smoothed_desired_per_heap_loh = %Id desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
15942 desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
15943 }
15944#endif //0
15945 for (int i = 0; i < gc_heap::n_heaps; i++)
15946 {
15947 gc_heap* hp = gc_heap::g_heaps[i];
15948 dynamic_data* dd = hp->dynamic_data_of (gen);
15949 dd_desired_allocation (dd) = desired_per_heap;
15950 dd_gc_new_allocation (dd) = desired_per_heap;
15951 dd_new_allocation (dd) = desired_per_heap;
15952
15953 if (gen == 0)
15954 {
15955 hp->fgn_last_alloc = desired_per_heap;
15956 }
15957 }
15958 }
15959
15960#ifdef FEATURE_LOH_COMPACTION
15961 BOOL all_heaps_compacted_p = TRUE;
15962#endif //FEATURE_LOH_COMPACTION
15963 for (int i = 0; i < gc_heap::n_heaps; i++)
15964 {
15965 gc_heap* hp = gc_heap::g_heaps[i];
15966 hp->decommit_ephemeral_segment_pages();
15967 hp->rearrange_large_heap_segments();
15968#ifdef FEATURE_LOH_COMPACTION
15969 all_heaps_compacted_p &= hp->loh_compacted_p;
15970#endif //FEATURE_LOH_COMPACTION
15971 }
15972
15973#ifdef FEATURE_LOH_COMPACTION
15974 check_loh_compact_mode (all_heaps_compacted_p);
15975#endif //FEATURE_LOH_COMPACTION
15976
15977 fire_pevents();
15978 pm_full_gc_init_or_clear();
15979
15980 gc_t_join.restart();
15981 }
15982 alloc_context_count = 0;
15983 heap_select::mark_heap (heap_number);
15984 }
15985
15986#else
15987 gc_data_global.final_youngest_desired =
15988 dd_desired_allocation (dynamic_data_of (0));
15989
15990 check_loh_compact_mode (loh_compacted_p);
15991
15992 decommit_ephemeral_segment_pages();
15993 fire_pevents();
15994
15995 if (!(settings.concurrent))
15996 {
15997 rearrange_large_heap_segments();
15998 do_post_gc();
15999 }
16000
16001 pm_full_gc_init_or_clear();
16002
16003#ifdef BACKGROUND_GC
16004 recover_bgc_settings();
16005#endif //BACKGROUND_GC
16006#endif //MULTIPLE_HEAPS
16007}
16008
16009void gc_heap::save_data_for_no_gc()
16010{
16011 current_no_gc_region_info.saved_pause_mode = settings.pause_mode;
16012#ifdef MULTIPLE_HEAPS
16013 // This is to affect heap balancing.
16014 for (int i = 0; i < n_heaps; i++)
16015 {
16016 current_no_gc_region_info.saved_gen0_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (0));
16017 dd_min_size (g_heaps[i]->dynamic_data_of (0)) = min_balance_threshold;
16018 current_no_gc_region_info.saved_gen3_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1));
16019 dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = 0;
16020 }
16021#endif //MULTIPLE_HEAPS
16022}
16023
16024void gc_heap::restore_data_for_no_gc()
16025{
16026 gc_heap::settings.pause_mode = current_no_gc_region_info.saved_pause_mode;
16027#ifdef MULTIPLE_HEAPS
16028 for (int i = 0; i < n_heaps; i++)
16029 {
16030 dd_min_size (g_heaps[i]->dynamic_data_of (0)) = current_no_gc_region_info.saved_gen0_min_size;
16031 dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = current_no_gc_region_info.saved_gen3_min_size;
16032 }
16033#endif //MULTIPLE_HEAPS
16034}
16035
16036start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size,
16037 BOOL loh_size_known,
16038 uint64_t loh_size,
16039 BOOL disallow_full_blocking)
16040{
16041 if (current_no_gc_region_info.started)
16042 {
16043 return start_no_gc_in_progress;
16044 }
16045
16046 start_no_gc_region_status status = start_no_gc_success;
16047
16048 save_data_for_no_gc();
16049 settings.pause_mode = pause_no_gc;
16050 current_no_gc_region_info.start_status = start_no_gc_success;
16051
16052 uint64_t allocation_no_gc_loh = 0;
16053 uint64_t allocation_no_gc_soh = 0;
16054 assert(total_size != 0);
16055 if (loh_size_known)
16056 {
16057 assert(loh_size != 0);
16058 assert(loh_size <= total_size);
16059 allocation_no_gc_loh = loh_size;
16060 allocation_no_gc_soh = total_size - loh_size;
16061 }
16062 else
16063 {
16064 allocation_no_gc_soh = total_size;
16065 allocation_no_gc_loh = total_size;
16066 }
16067
16068 int soh_align_const = get_alignment_constant (TRUE);
16069 size_t max_soh_allocated = soh_segment_size - segment_info_size - eph_gen_starts_size;
16070 size_t size_per_heap = 0;
16071 const double scale_factor = 1.05;
16072
16073 int num_heaps = 1;
16074#ifdef MULTIPLE_HEAPS
16075 num_heaps = n_heaps;
16076#endif // MULTIPLE_HEAPS
16077
16078 uint64_t total_allowed_soh_allocation = max_soh_allocated * num_heaps;
16079 // [LOCALGC TODO]
16080 // In theory, the upper limit here is the physical memory of the machine, not
16081 // SIZE_T_MAX. This is not true today because total_physical_mem can be
16082 // larger than SIZE_T_MAX if running in wow64 on a machine with more than
16083 // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing
16084 // more freely between branches, it would be good to clean this up to use
16085 // total_physical_mem instead of SIZE_T_MAX.
16086 assert(total_allowed_soh_allocation <= SIZE_T_MAX);
16087 uint64_t total_allowed_loh_allocation = SIZE_T_MAX;
16088 uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast<uint64_t>(total_allowed_soh_allocation / scale_factor) : 0;
16089 uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast<uint64_t>(total_allowed_loh_allocation / scale_factor) : 0;
16090
16091 if (allocation_no_gc_soh > total_allowed_soh_alloc_scaled ||
16092 allocation_no_gc_loh > total_allowed_loh_alloc_scaled)
16093 {
16094 status = start_no_gc_too_large;
16095 goto done;
16096 }
16097
16098 if (allocation_no_gc_soh > 0)
16099 {
16100 allocation_no_gc_soh = static_cast<uint64_t>(allocation_no_gc_soh * scale_factor);
16101 allocation_no_gc_soh = min (allocation_no_gc_soh, total_allowed_soh_alloc_scaled);
16102 }
16103
16104 if (allocation_no_gc_loh > 0)
16105 {
16106 allocation_no_gc_loh = static_cast<uint64_t>(allocation_no_gc_loh * scale_factor);
16107 allocation_no_gc_loh = min (allocation_no_gc_loh, total_allowed_loh_alloc_scaled);
16108 }
16109
16110 if (disallow_full_blocking)
16111 current_no_gc_region_info.minimal_gc_p = TRUE;
16112
16113 if (allocation_no_gc_soh != 0)
16114 {
16115 current_no_gc_region_info.soh_allocation_size = static_cast<size_t>(allocation_no_gc_soh);
16116 size_per_heap = current_no_gc_region_info.soh_allocation_size;
16117#ifdef MULTIPLE_HEAPS
16118 size_per_heap /= n_heaps;
16119 for (int i = 0; i < n_heaps; i++)
16120 {
16121 // due to heap balancing we need to allow some room before we even look to balance to another heap.
16122 g_heaps[i]->soh_allocation_no_gc = min (Align ((size_per_heap + min_balance_threshold), soh_align_const), max_soh_allocated);
16123 }
16124#else //MULTIPLE_HEAPS
16125 soh_allocation_no_gc = min (Align (size_per_heap, soh_align_const), max_soh_allocated);
16126#endif //MULTIPLE_HEAPS
16127 }
16128
16129 if (allocation_no_gc_loh != 0)
16130 {
16131 current_no_gc_region_info.loh_allocation_size = static_cast<size_t>(allocation_no_gc_loh);
16132 size_per_heap = current_no_gc_region_info.loh_allocation_size;
16133#ifdef MULTIPLE_HEAPS
16134 size_per_heap /= n_heaps;
16135 for (int i = 0; i < n_heaps; i++)
16136 g_heaps[i]->loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
16137#else //MULTIPLE_HEAPS
16138 loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
16139#endif //MULTIPLE_HEAPS
16140 }
16141
16142done:
16143 if (status != start_no_gc_success)
16144 restore_data_for_no_gc();
16145 return status;
16146}
16147
16148void gc_heap::handle_failure_for_no_gc()
16149{
16150 gc_heap::restore_data_for_no_gc();
16151 // sets current_no_gc_region_info.started to FALSE here.
16152 memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16153}
16154
16155start_no_gc_region_status gc_heap::get_start_no_gc_region_status()
16156{
16157 return current_no_gc_region_info.start_status;
16158}
16159
16160void gc_heap::record_gcs_during_no_gc()
16161{
16162 if (current_no_gc_region_info.started)
16163 {
16164 current_no_gc_region_info.num_gcs++;
16165 if (is_induced (settings.reason))
16166 current_no_gc_region_info.num_gcs_induced++;
16167 }
16168}
16169
16170BOOL gc_heap::find_loh_free_for_no_gc()
16171{
16172 allocator* loh_allocator = generation_allocator (generation_of (max_generation + 1));
16173 size_t sz_list = loh_allocator->first_bucket_size();
16174 size_t size = loh_allocation_no_gc;
16175 for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
16176 {
16177 if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
16178 {
16179 uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
16180 while (free_list)
16181 {
16182 size_t free_list_size = unused_array_size(free_list);
16183
16184 if (free_list_size > loh_allocation_no_gc)
16185 {
16186 dprintf (3, ("free item %Ix(%Id) for no gc", (size_t)free_list, free_list_size));
16187 return TRUE;
16188 }
16189
16190 free_list = free_list_slot (free_list);
16191 }
16192 }
16193 sz_list = sz_list * 2;
16194 }
16195
16196 return FALSE;
16197}
16198
16199BOOL gc_heap::find_loh_space_for_no_gc()
16200{
16201 saved_loh_segment_no_gc = 0;
16202
16203 if (find_loh_free_for_no_gc())
16204 return TRUE;
16205
16206 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16207
16208 while (seg)
16209 {
16210 size_t remaining = heap_segment_reserved (seg) - heap_segment_allocated (seg);
16211 if (remaining >= loh_allocation_no_gc)
16212 {
16213 saved_loh_segment_no_gc = seg;
16214 break;
16215 }
16216 seg = heap_segment_next (seg);
16217 }
16218
16219 if (!saved_loh_segment_no_gc && current_no_gc_region_info.minimal_gc_p)
16220 {
16221 // If no full GC is allowed, we try to get a new seg right away.
16222 saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc)
16223#ifdef MULTIPLE_HEAPS
16224 , this
16225#endif //MULTIPLE_HEAPS
16226 );
16227 }
16228
16229 return (saved_loh_segment_no_gc != 0);
16230}
16231
16232BOOL gc_heap::loh_allocated_for_no_gc()
16233{
16234 if (!saved_loh_segment_no_gc)
16235 return FALSE;
16236
16237 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16238 do
16239 {
16240 if (seg == saved_loh_segment_no_gc)
16241 {
16242 return FALSE;
16243 }
16244 seg = heap_segment_next (seg);
16245 } while (seg);
16246
16247 return TRUE;
16248}
16249
16250BOOL gc_heap::commit_loh_for_no_gc (heap_segment* seg)
16251{
16252 uint8_t* end_committed = heap_segment_allocated (seg) + loh_allocation_no_gc;
16253 assert (end_committed <= heap_segment_reserved (seg));
16254 return (grow_heap_segment (seg, end_committed));
16255}
16256
16257void gc_heap::thread_no_gc_loh_segments()
16258{
16259#ifdef MULTIPLE_HEAPS
16260 for (int i = 0; i < n_heaps; i++)
16261 {
16262 gc_heap* hp = g_heaps[i];
16263 if (hp->loh_allocated_for_no_gc())
16264 {
16265 hp->thread_loh_segment (hp->saved_loh_segment_no_gc);
16266 hp->saved_loh_segment_no_gc = 0;
16267 }
16268 }
16269#else //MULTIPLE_HEAPS
16270 if (loh_allocated_for_no_gc())
16271 {
16272 thread_loh_segment (saved_loh_segment_no_gc);
16273 saved_loh_segment_no_gc = 0;
16274 }
16275#endif //MULTIPLE_HEAPS
16276}
16277
16278void gc_heap::set_loh_allocations_for_no_gc()
16279{
16280 if (current_no_gc_region_info.loh_allocation_size != 0)
16281 {
16282 dynamic_data* dd = dynamic_data_of (max_generation + 1);
16283 dd_new_allocation (dd) = loh_allocation_no_gc;
16284 dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16285 }
16286}
16287
16288void gc_heap::set_soh_allocations_for_no_gc()
16289{
16290 if (current_no_gc_region_info.soh_allocation_size != 0)
16291 {
16292 dynamic_data* dd = dynamic_data_of (0);
16293 dd_new_allocation (dd) = soh_allocation_no_gc;
16294 dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16295#ifdef MULTIPLE_HEAPS
16296 alloc_context_count = 0;
16297#endif //MULTIPLE_HEAPS
16298 }
16299}
16300
16301void gc_heap::set_allocations_for_no_gc()
16302{
16303#ifdef MULTIPLE_HEAPS
16304 for (int i = 0; i < n_heaps; i++)
16305 {
16306 gc_heap* hp = g_heaps[i];
16307 hp->set_loh_allocations_for_no_gc();
16308 hp->set_soh_allocations_for_no_gc();
16309 }
16310#else //MULTIPLE_HEAPS
16311 set_loh_allocations_for_no_gc();
16312 set_soh_allocations_for_no_gc();
16313#endif //MULTIPLE_HEAPS
16314}
16315
16316BOOL gc_heap::should_proceed_for_no_gc()
16317{
16318 BOOL gc_requested = FALSE;
16319 BOOL loh_full_gc_requested = FALSE;
16320 BOOL soh_full_gc_requested = FALSE;
16321 BOOL no_gc_requested = FALSE;
16322 BOOL get_new_loh_segments = FALSE;
16323
16324 if (current_no_gc_region_info.soh_allocation_size)
16325 {
16326#ifdef MULTIPLE_HEAPS
16327 for (int i = 0; i < n_heaps; i++)
16328 {
16329 gc_heap* hp = g_heaps[i];
16330 if ((size_t)(heap_segment_reserved (hp->ephemeral_heap_segment) - hp->alloc_allocated) < hp->soh_allocation_no_gc)
16331 {
16332 gc_requested = TRUE;
16333 break;
16334 }
16335 }
16336#else //MULTIPLE_HEAPS
16337 if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated) < soh_allocation_no_gc)
16338 gc_requested = TRUE;
16339#endif //MULTIPLE_HEAPS
16340
16341 if (!gc_requested)
16342 {
16343#ifdef MULTIPLE_HEAPS
16344 for (int i = 0; i < n_heaps; i++)
16345 {
16346 gc_heap* hp = g_heaps[i];
16347 if (!(hp->grow_heap_segment (hp->ephemeral_heap_segment, (hp->alloc_allocated + hp->soh_allocation_no_gc))))
16348 {
16349 soh_full_gc_requested = TRUE;
16350 break;
16351 }
16352 }
16353#else //MULTIPLE_HEAPS
16354 if (!grow_heap_segment (ephemeral_heap_segment, (alloc_allocated + soh_allocation_no_gc)))
16355 soh_full_gc_requested = TRUE;
16356#endif //MULTIPLE_HEAPS
16357 }
16358 }
16359
16360 if (!current_no_gc_region_info.minimal_gc_p && gc_requested)
16361 {
16362 soh_full_gc_requested = TRUE;
16363 }
16364
16365 no_gc_requested = !(soh_full_gc_requested || gc_requested);
16366
16367 if (soh_full_gc_requested && current_no_gc_region_info.minimal_gc_p)
16368 {
16369 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16370 goto done;
16371 }
16372
16373 if (!soh_full_gc_requested && current_no_gc_region_info.loh_allocation_size)
16374 {
16375 // Check to see if we have enough reserved space.
16376#ifdef MULTIPLE_HEAPS
16377 for (int i = 0; i < n_heaps; i++)
16378 {
16379 gc_heap* hp = g_heaps[i];
16380 if (!hp->find_loh_space_for_no_gc())
16381 {
16382 loh_full_gc_requested = TRUE;
16383 break;
16384 }
16385 }
16386#else //MULTIPLE_HEAPS
16387 if (!find_loh_space_for_no_gc())
16388 loh_full_gc_requested = TRUE;
16389#endif //MULTIPLE_HEAPS
16390
16391 // Check to see if we have committed space.
16392 if (!loh_full_gc_requested)
16393 {
16394#ifdef MULTIPLE_HEAPS
16395 for (int i = 0; i < n_heaps; i++)
16396 {
16397 gc_heap* hp = g_heaps[i];
16398 if (hp->saved_loh_segment_no_gc &&!hp->commit_loh_for_no_gc (hp->saved_loh_segment_no_gc))
16399 {
16400 loh_full_gc_requested = TRUE;
16401 break;
16402 }
16403 }
16404#else //MULTIPLE_HEAPS
16405 if (saved_loh_segment_no_gc && !commit_loh_for_no_gc (saved_loh_segment_no_gc))
16406 loh_full_gc_requested = TRUE;
16407#endif //MULTIPLE_HEAPS
16408 }
16409 }
16410
16411 if (loh_full_gc_requested || soh_full_gc_requested)
16412 {
16413 if (current_no_gc_region_info.minimal_gc_p)
16414 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16415 }
16416
16417 no_gc_requested = !(loh_full_gc_requested || soh_full_gc_requested || gc_requested);
16418
16419 if (current_no_gc_region_info.start_status == start_no_gc_success)
16420 {
16421 if (no_gc_requested)
16422 set_allocations_for_no_gc();
16423 }
16424
16425done:
16426
16427 if ((current_no_gc_region_info.start_status == start_no_gc_success) && !no_gc_requested)
16428 return TRUE;
16429 else
16430 {
16431 // We are done with starting the no_gc_region.
16432 current_no_gc_region_info.started = TRUE;
16433 return FALSE;
16434 }
16435}
16436
16437end_no_gc_region_status gc_heap::end_no_gc_region()
16438{
16439 dprintf (1, ("end no gc called"));
16440
16441 end_no_gc_region_status status = end_no_gc_success;
16442
16443 if (!(current_no_gc_region_info.started))
16444 status = end_no_gc_not_in_progress;
16445 if (current_no_gc_region_info.num_gcs_induced)
16446 status = end_no_gc_induced;
16447 else if (current_no_gc_region_info.num_gcs)
16448 status = end_no_gc_alloc_exceeded;
16449
16450 if (settings.pause_mode == pause_no_gc)
16451 restore_data_for_no_gc();
16452
16453 // sets current_no_gc_region_info.started to FALSE here.
16454 memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16455
16456 return status;
16457}
16458
16459//update counters
16460void gc_heap::update_collection_counts ()
16461{
16462 dynamic_data* dd0 = dynamic_data_of (0);
16463 dd_gc_clock (dd0) += 1;
16464
16465 size_t now = GetHighPrecisionTimeStamp();
16466
16467 for (int i = 0; i <= settings.condemned_generation;i++)
16468 {
16469 dynamic_data* dd = dynamic_data_of (i);
16470 dd_collection_count (dd)++;
16471 //this is needed by the linear allocation model
16472 if (i == max_generation)
16473 dd_collection_count (dynamic_data_of (max_generation+1))++;
16474 dd_gc_clock (dd) = dd_gc_clock (dd0);
16475 dd_time_clock (dd) = now;
16476 }
16477}
16478
16479BOOL gc_heap::expand_soh_with_minimal_gc()
16480{
16481 if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) >= soh_allocation_no_gc)
16482 return TRUE;
16483
16484 heap_segment* new_seg = soh_get_segment_to_expand();
16485 if (new_seg)
16486 {
16487 if (g_gc_card_table != card_table)
16488 copy_brick_card_table();
16489
16490 settings.promotion = TRUE;
16491 settings.demotion = FALSE;
16492 ephemeral_promotion = TRUE;
16493 int condemned_gen_number = max_generation - 1;
16494
16495 generation* gen = 0;
16496 int align_const = get_alignment_constant (TRUE);
16497
16498 for (int i = 0; i <= condemned_gen_number; i++)
16499 {
16500 gen = generation_of (i);
16501 saved_ephemeral_plan_start[i] = generation_allocation_start (gen);
16502 saved_ephemeral_plan_start_size[i] = Align (size (generation_allocation_start (gen)), align_const);
16503 }
16504
16505 // We do need to clear the bricks here as we are converting a bunch of ephemeral objects to gen2
16506 // and need to make sure that there are no left over bricks from the previous GCs for the space
16507 // we just used for gen0 allocation. We will need to go through the bricks for these objects for
16508 // ephemeral GCs later.
16509 for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
16510 b < brick_of (align_on_brick (heap_segment_allocated (ephemeral_heap_segment)));
16511 b++)
16512 {
16513 set_brick (b, -1);
16514 }
16515
16516 size_t ephemeral_size = (heap_segment_allocated (ephemeral_heap_segment) -
16517 generation_allocation_start (generation_of (max_generation - 1)));
16518 heap_segment_next (ephemeral_heap_segment) = new_seg;
16519 ephemeral_heap_segment = new_seg;
16520 uint8_t* start = heap_segment_mem (ephemeral_heap_segment);
16521
16522 for (int i = condemned_gen_number; i >= 0; i--)
16523 {
16524 gen = generation_of (i);
16525 size_t gen_start_size = Align (min_obj_size);
16526 make_generation (generation_table[i], ephemeral_heap_segment, start, 0);
16527 generation_plan_allocation_start (gen) = start;
16528 generation_plan_allocation_start_size (gen) = gen_start_size;
16529 start += gen_start_size;
16530 }
16531 heap_segment_used (ephemeral_heap_segment) = start - plug_skew;
16532 heap_segment_plan_allocated (ephemeral_heap_segment) = start;
16533
16534 fix_generation_bounds (condemned_gen_number, generation_of (0));
16535
16536 dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
16537 dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));
16538
16539 adjust_ephemeral_limits();
16540 return TRUE;
16541 }
16542 else
16543 return FALSE;
16544}
16545
16546// Only to be done on the thread that calls restart in a join for server GC
16547// and reset the oom status per heap.
16548void gc_heap::check_and_set_no_gc_oom()
16549{
16550#ifdef MULTIPLE_HEAPS
16551 for (int i = 0; i < n_heaps; i++)
16552 {
16553 gc_heap* hp = g_heaps[i];
16554 if (hp->no_gc_oom_p)
16555 {
16556 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16557 hp->no_gc_oom_p = false;
16558 }
16559 }
16560#else
16561 if (no_gc_oom_p)
16562 {
16563 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16564 no_gc_oom_p = false;
16565 }
16566#endif //MULTIPLE_HEAPS
16567}
16568
16569void gc_heap::allocate_for_no_gc_after_gc()
16570{
16571 if (current_no_gc_region_info.minimal_gc_p)
16572 repair_allocation_contexts (TRUE);
16573
16574 no_gc_oom_p = false;
16575
16576 if (current_no_gc_region_info.start_status != start_no_gc_no_memory)
16577 {
16578 if (current_no_gc_region_info.soh_allocation_size != 0)
16579 {
16580 if (((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) < soh_allocation_no_gc) ||
16581 (!grow_heap_segment (ephemeral_heap_segment, (heap_segment_allocated (ephemeral_heap_segment) + soh_allocation_no_gc))))
16582 {
16583 no_gc_oom_p = true;
16584 }
16585
16586#ifdef MULTIPLE_HEAPS
16587 gc_t_join.join(this, gc_join_after_commit_soh_no_gc);
16588 if (gc_t_join.joined())
16589 {
16590#endif //MULTIPLE_HEAPS
16591
16592 check_and_set_no_gc_oom();
16593
16594#ifdef MULTIPLE_HEAPS
16595 gc_t_join.restart();
16596 }
16597#endif //MULTIPLE_HEAPS
16598 }
16599
16600 if ((current_no_gc_region_info.start_status == start_no_gc_success) &&
16601 !(current_no_gc_region_info.minimal_gc_p) &&
16602 (current_no_gc_region_info.loh_allocation_size != 0))
16603 {
16604 gc_policy = policy_compact;
16605 saved_loh_segment_no_gc = 0;
16606
16607 if (!find_loh_free_for_no_gc())
16608 {
16609 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16610 BOOL found_seg_p = FALSE;
16611 while (seg)
16612 {
16613 if ((size_t)(heap_segment_reserved (seg) - heap_segment_allocated (seg)) >= loh_allocation_no_gc)
16614 {
16615 found_seg_p = TRUE;
16616 if (!commit_loh_for_no_gc (seg))
16617 {
16618 no_gc_oom_p = true;
16619 break;
16620 }
16621 }
16622 seg = heap_segment_next (seg);
16623 }
16624
16625 if (!found_seg_p)
16626 gc_policy = policy_expand;
16627 }
16628
16629#ifdef MULTIPLE_HEAPS
16630 gc_t_join.join(this, gc_join_expand_loh_no_gc);
16631 if (gc_t_join.joined())
16632 {
16633 check_and_set_no_gc_oom();
16634
16635 if (current_no_gc_region_info.start_status == start_no_gc_success)
16636 {
16637 for (int i = 0; i < n_heaps; i++)
16638 {
16639 gc_heap* hp = g_heaps[i];
16640 if (hp->gc_policy == policy_expand)
16641 {
16642 hp->saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc), hp);
16643 if (!(hp->saved_loh_segment_no_gc))
16644 {
16645 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16646 break;
16647 }
16648 }
16649 }
16650 }
16651
16652 gc_t_join.restart();
16653 }
16654#else //MULTIPLE_HEAPS
16655 check_and_set_no_gc_oom();
16656
16657 if ((current_no_gc_region_info.start_status == start_no_gc_success) && (gc_policy == policy_expand))
16658 {
16659 saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc));
16660 if (!saved_loh_segment_no_gc)
16661 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16662 }
16663#endif //MULTIPLE_HEAPS
16664
16665 if ((current_no_gc_region_info.start_status == start_no_gc_success) && saved_loh_segment_no_gc)
16666 {
16667 if (!commit_loh_for_no_gc (saved_loh_segment_no_gc))
16668 {
16669 no_gc_oom_p = true;
16670 }
16671 }
16672 }
16673 }
16674
16675#ifdef MULTIPLE_HEAPS
16676 gc_t_join.join(this, gc_join_final_no_gc);
16677 if (gc_t_join.joined())
16678 {
16679#endif //MULTIPLE_HEAPS
16680
16681 check_and_set_no_gc_oom();
16682
16683 if (current_no_gc_region_info.start_status == start_no_gc_success)
16684 {
16685 set_allocations_for_no_gc();
16686 current_no_gc_region_info.started = TRUE;
16687 }
16688
16689#ifdef MULTIPLE_HEAPS
16690 gc_t_join.restart();
16691 }
16692#endif //MULTIPLE_HEAPS
16693}
16694
16695void gc_heap::init_records()
16696{
16697 // An option is to move this to be after we figure out which gen to condemn so we don't
16698 // need to clear some generations' data 'cause we know they don't change, but that also means
16699 // we can't simply call memset here.
16700 memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
16701 gc_data_per_heap.heap_index = heap_number;
16702 if (heap_number == 0)
16703 memset (&gc_data_global, 0, sizeof (gc_data_global));
16704
16705#ifdef GC_CONFIG_DRIVEN
16706 memset (interesting_data_per_gc, 0, sizeof (interesting_data_per_gc));
16707#endif //GC_CONFIG_DRIVEN
16708 memset (&fgm_result, 0, sizeof (fgm_result));
16709
16710 for (int i = 0; i <= (max_generation + 1); i++)
16711 {
16712 gc_data_per_heap.gen_data[i].size_before = generation_size (i);
16713 generation* gen = generation_of (i);
16714 gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
16715 gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
16716 }
16717
16718 sufficient_gen0_space_p = FALSE;
16719#if defined (_DEBUG) && defined (VERIFY_HEAP)
16720 verify_pinned_queue_p = FALSE;
16721#endif // _DEBUG && VERIFY_HEAP
16722}
16723
16724void gc_heap::pm_full_gc_init_or_clear()
16725{
16726 // This means the next GC will be a full blocking GC and we need to init.
16727 if (settings.condemned_generation == (max_generation - 1))
16728 {
16729 if (pm_trigger_full_gc)
16730 {
16731#ifdef MULTIPLE_HEAPS
16732 do_post_gc();
16733#endif //MULTIPLE_HEAPS
16734 dprintf (GTC_LOG, ("init for PM triggered full GC"));
16735 uint32_t saved_entry_memory_load = settings.entry_memory_load;
16736 settings.init_mechanisms();
16737 settings.reason = reason_pm_full_gc;
16738 settings.condemned_generation = max_generation;
16739 settings.entry_memory_load = saved_entry_memory_load;
16740 // Can't assert this since we only check at the end of gen2 GCs,
16741 // during gen1 the memory load could have already dropped.
16742 // Although arguably we should just turn off PM then...
16743 //assert (settings.entry_memory_load >= high_memory_load_th);
16744 assert (settings.entry_memory_load > 0);
16745 settings.gc_index += 1;
16746 do_pre_gc();
16747 }
16748 }
16749 // This means we are in the progress of a full blocking GC triggered by
16750 // this PM mode.
16751 else if (settings.reason == reason_pm_full_gc)
16752 {
16753 assert (settings.condemned_generation == max_generation);
16754 assert (pm_trigger_full_gc);
16755 pm_trigger_full_gc = false;
16756
16757 dprintf (GTC_LOG, ("PM triggered full GC done"));
16758 }
16759}
16760
16761void gc_heap::garbage_collect_pm_full_gc()
16762{
16763 assert (settings.condemned_generation == max_generation);
16764 assert (settings.reason == reason_pm_full_gc);
16765 assert (!settings.concurrent);
16766 gc1();
16767}
16768
16769void gc_heap::garbage_collect (int n)
16770{
16771 //reset the number of alloc contexts
16772 alloc_contexts_used = 0;
16773
16774 fix_allocation_contexts (TRUE);
16775#ifdef MULTIPLE_HEAPS
16776#ifdef JOIN_STATS
16777 gc_t_join.start_ts(this);
16778#endif //JOIN_STATS
16779 clear_gen0_bricks();
16780#endif //MULTIPLE_HEAPS
16781
16782 if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p)
16783 {
16784#ifdef MULTIPLE_HEAPS
16785 gc_t_join.join(this, gc_join_minimal_gc);
16786 if (gc_t_join.joined())
16787 {
16788#endif //MULTIPLE_HEAPS
16789
16790#ifdef MULTIPLE_HEAPS
16791 // this is serialized because we need to get a segment
16792 for (int i = 0; i < n_heaps; i++)
16793 {
16794 if (!(g_heaps[i]->expand_soh_with_minimal_gc()))
16795 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16796 }
16797#else
16798 if (!expand_soh_with_minimal_gc())
16799 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16800#endif //MULTIPLE_HEAPS
16801
16802 update_collection_counts_for_no_gc();
16803
16804#ifdef MULTIPLE_HEAPS
16805 gc_t_join.restart();
16806 }
16807#endif //MULTIPLE_HEAPS
16808
16809 goto done;
16810 }
16811
16812 init_records();
16813
16814 settings.reason = gc_trigger_reason;
16815#if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
16816 num_pinned_objects = 0;
16817#endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
16818
16819#ifdef STRESS_HEAP
16820 if (settings.reason == reason_gcstress)
16821 {
16822 settings.reason = reason_induced;
16823 settings.stress_induced = TRUE;
16824 }
16825#endif // STRESS_HEAP
16826
16827#ifdef MULTIPLE_HEAPS
16828 //align all heaps on the max generation to condemn
16829 dprintf (3, ("Joining for max generation to condemn"));
16830 condemned_generation_num = generation_to_condemn (n,
16831 &blocking_collection,
16832 &elevation_requested,
16833 FALSE);
16834 gc_t_join.join(this, gc_join_generation_determined);
16835 if (gc_t_join.joined())
16836#endif //MULTIPLE_HEAPS
16837 {
16838#if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
16839 //delete old slots from the segment table
16840 seg_table->delete_old_slots();
16841#endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
16842
16843#ifdef MULTIPLE_HEAPS
16844 for (int i = 0; i < n_heaps; i++)
16845 {
16846 gc_heap* hp = g_heaps[i];
16847 // check for card table growth
16848 if (g_gc_card_table != hp->card_table)
16849 hp->copy_brick_card_table();
16850
16851 hp->rearrange_large_heap_segments();
16852#ifdef BACKGROUND_GC
16853 hp->background_delay_delete_loh_segments();
16854 if (!recursive_gc_sync::background_running_p())
16855 hp->rearrange_small_heap_segments();
16856#endif //BACKGROUND_GC
16857 }
16858#else //MULTIPLE_HEAPS
16859 if (g_gc_card_table != card_table)
16860 copy_brick_card_table();
16861
16862 rearrange_large_heap_segments();
16863#ifdef BACKGROUND_GC
16864 background_delay_delete_loh_segments();
16865 if (!recursive_gc_sync::background_running_p())
16866 rearrange_small_heap_segments();
16867#endif //BACKGROUND_GC
16868#endif //MULTIPLE_HEAPS
16869
16870 BOOL should_evaluate_elevation = FALSE;
16871 BOOL should_do_blocking_collection = FALSE;
16872
16873#ifdef MULTIPLE_HEAPS
16874 int gen_max = condemned_generation_num;
16875 for (int i = 0; i < n_heaps; i++)
16876 {
16877 if (gen_max < g_heaps[i]->condemned_generation_num)
16878 gen_max = g_heaps[i]->condemned_generation_num;
16879 if ((!should_evaluate_elevation) && (g_heaps[i]->elevation_requested))
16880 should_evaluate_elevation = TRUE;
16881 if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
16882 should_do_blocking_collection = TRUE;
16883 }
16884
16885 settings.condemned_generation = gen_max;
16886#else //MULTIPLE_HEAPS
16887 settings.condemned_generation = generation_to_condemn (n,
16888 &blocking_collection,
16889 &elevation_requested,
16890 FALSE);
16891 should_evaluate_elevation = elevation_requested;
16892 should_do_blocking_collection = blocking_collection;
16893#endif //MULTIPLE_HEAPS
16894
16895 settings.condemned_generation = joined_generation_to_condemn (
16896 should_evaluate_elevation,
16897 n,
16898 settings.condemned_generation,
16899 &should_do_blocking_collection
16900 STRESS_HEAP_ARG(n)
16901 );
16902
16903 STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10,
16904 "condemned generation num: %d\n", settings.condemned_generation);
16905
16906 record_gcs_during_no_gc();
16907
16908 if (settings.condemned_generation > 1)
16909 settings.promotion = TRUE;
16910
16911#ifdef HEAP_ANALYZE
16912 // At this point we've decided what generation is condemned
16913 // See if we've been requested to analyze survivors after the mark phase
16914 if (GCToEEInterface::AnalyzeSurvivorsRequested(settings.condemned_generation))
16915 {
16916 heap_analyze_enabled = TRUE;
16917 }
16918#endif // HEAP_ANALYZE
16919
16920 GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced);
16921
16922#ifdef BACKGROUND_GC
16923 if ((settings.condemned_generation == max_generation) &&
16924 (recursive_gc_sync::background_running_p()))
16925 {
16926 //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work
16927 // because we have to collect 0 and 1 properly
16928 // in particular, the allocation contexts are gone.
16929 // For now, it is simpler to collect max_generation-1
16930 settings.condemned_generation = max_generation - 1;
16931 dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
16932 }
16933
16934 if ((settings.condemned_generation == max_generation) &&
16935 (should_do_blocking_collection == FALSE) &&
16936 gc_can_use_concurrent &&
16937 !temp_disable_concurrent_p &&
16938 ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
16939 {
16940 keep_bgc_threads_p = TRUE;
16941 c_write (settings.concurrent, TRUE);
16942 }
16943#endif //BACKGROUND_GC
16944
16945 settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1;
16946
16947 // Call the EE for start of GC work
16948 // just one thread for MP GC
16949 GCToEEInterface::GcStartWork (settings.condemned_generation,
16950 max_generation);
16951
16952 // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
16953 // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
16954 // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
16955 // fired in gc1.
16956 do_pre_gc();
16957
16958#ifdef MULTIPLE_HEAPS
16959 gc_start_event.Reset();
16960 //start all threads on the roots.
16961 dprintf(3, ("Starting all gc threads for gc"));
16962 gc_t_join.restart();
16963#endif //MULTIPLE_HEAPS
16964 }
16965
16966 descr_generations (TRUE);
16967
16968#ifdef VERIFY_HEAP
16969 if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
16970 !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_POST_GC_ONLY))
16971 {
16972 verify_heap (TRUE);
16973 }
16974 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK)
16975 checkGCWriteBarrier();
16976
16977#endif // VERIFY_HEAP
16978
16979#ifdef BACKGROUND_GC
16980 if (settings.concurrent)
16981 {
16982 // We need to save the settings because we'll need to restore it after each FGC.
16983 assert (settings.condemned_generation == max_generation);
16984 settings.compaction = FALSE;
16985 saved_bgc_settings = settings;
16986
16987#ifdef MULTIPLE_HEAPS
16988 if (heap_number == 0)
16989 {
16990 for (int i = 0; i < n_heaps; i++)
16991 {
16992 prepare_bgc_thread (g_heaps[i]);
16993 }
16994 dprintf (2, ("setting bgc_threads_sync_event"));
16995 bgc_threads_sync_event.Set();
16996 }
16997 else
16998 {
16999 bgc_threads_sync_event.Wait(INFINITE, FALSE);
17000 dprintf (2, ("bgc_threads_sync_event is signalled"));
17001 }
17002#else
17003 prepare_bgc_thread(0);
17004#endif //MULTIPLE_HEAPS
17005
17006#ifdef MULTIPLE_HEAPS
17007 gc_t_join.join(this, gc_join_start_bgc);
17008 if (gc_t_join.joined())
17009#endif //MULTIPLE_HEAPS
17010 {
17011 do_concurrent_p = TRUE;
17012 do_ephemeral_gc_p = FALSE;
17013#ifdef MULTIPLE_HEAPS
17014 dprintf(2, ("Joined to perform a background GC"));
17015
17016 for (int i = 0; i < n_heaps; i++)
17017 {
17018 gc_heap* hp = g_heaps[i];
17019 if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
17020 {
17021 do_concurrent_p = FALSE;
17022 break;
17023 }
17024 else
17025 {
17026 hp->background_saved_lowest_address = hp->lowest_address;
17027 hp->background_saved_highest_address = hp->highest_address;
17028 }
17029 }
17030#else
17031 do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
17032 if (do_concurrent_p)
17033 {
17034 background_saved_lowest_address = lowest_address;
17035 background_saved_highest_address = highest_address;
17036 }
17037#endif //MULTIPLE_HEAPS
17038
17039 if (do_concurrent_p)
17040 {
17041#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
17042 SoftwareWriteWatch::EnableForGCHeap();
17043#endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
17044
17045#ifdef MULTIPLE_HEAPS
17046 for (int i = 0; i < n_heaps; i++)
17047 g_heaps[i]->current_bgc_state = bgc_initialized;
17048#else
17049 current_bgc_state = bgc_initialized;
17050#endif //MULTIPLE_HEAPS
17051
17052 int gen = check_for_ephemeral_alloc();
17053 // always do a gen1 GC before we start BGC.
17054 // This is temporary for testing purpose.
17055 //int gen = max_generation - 1;
17056 dont_restart_ee_p = TRUE;
17057 if (gen == -1)
17058 {
17059 // If we decide to not do a GC before the BGC we need to
17060 // restore the gen0 alloc context.
17061#ifdef MULTIPLE_HEAPS
17062 for (int i = 0; i < n_heaps; i++)
17063 {
17064 generation_allocation_pointer (g_heaps[i]->generation_of (0)) = 0;
17065 generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
17066 }
17067#else
17068 generation_allocation_pointer (youngest_generation) = 0;
17069 generation_allocation_limit (youngest_generation) = 0;
17070#endif //MULTIPLE_HEAPS
17071 }
17072 else
17073 {
17074 do_ephemeral_gc_p = TRUE;
17075
17076 settings.init_mechanisms();
17077 settings.condemned_generation = gen;
17078 settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2;
17079 do_pre_gc();
17080
17081 // TODO BACKGROUND_GC need to add the profiling stuff here.
17082 dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
17083 }
17084
17085 //clear the cards so they don't bleed in gen 1 during collection
17086 // shouldn't this always be done at the beginning of any GC?
17087 //clear_card_for_addresses (
17088 // generation_allocation_start (generation_of (0)),
17089 // heap_segment_allocated (ephemeral_heap_segment));
17090
17091 if (!do_ephemeral_gc_p)
17092 {
17093 do_background_gc();
17094 }
17095 }
17096 else
17097 {
17098 settings.compaction = TRUE;
17099 c_write (settings.concurrent, FALSE);
17100 }
17101
17102#ifdef MULTIPLE_HEAPS
17103 gc_t_join.restart();
17104#endif //MULTIPLE_HEAPS
17105 }
17106
17107 if (do_concurrent_p)
17108 {
17109 // At this point we are sure we'll be starting a BGC, so save its per heap data here.
17110 // global data is only calculated at the end of the GC so we don't need to worry about
17111 // FGCs overwriting it.
17112 memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap));
17113 memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
17114
17115 if (do_ephemeral_gc_p)
17116 {
17117 dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
17118
17119 gen_to_condemn_reasons.init();
17120 gen_to_condemn_reasons.set_condition (gen_before_bgc);
17121 gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
17122 gc1();
17123#ifdef MULTIPLE_HEAPS
17124 gc_t_join.join(this, gc_join_bgc_after_ephemeral);
17125 if (gc_t_join.joined())
17126#endif //MULTIPLE_HEAPS
17127 {
17128#ifdef MULTIPLE_HEAPS
17129 do_post_gc();
17130#endif //MULTIPLE_HEAPS
17131 settings = saved_bgc_settings;
17132 assert (settings.concurrent);
17133
17134 do_background_gc();
17135
17136#ifdef MULTIPLE_HEAPS
17137 gc_t_join.restart();
17138#endif //MULTIPLE_HEAPS
17139 }
17140 }
17141 }
17142 else
17143 {
17144 dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
17145 gc1();
17146 }
17147 }
17148 else
17149#endif //BACKGROUND_GC
17150 {
17151 gc1();
17152 }
17153#ifndef MULTIPLE_HEAPS
17154 allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp();
17155 allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
17156 fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
17157#endif //MULTIPLE_HEAPS
17158
17159done:
17160 if (settings.pause_mode == pause_no_gc)
17161 allocate_for_no_gc_after_gc();
17162
17163}
17164
17165#define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
17166
17167inline
17168size_t& gc_heap::promoted_bytes(int thread)
17169{
17170#ifdef MULTIPLE_HEAPS
17171 return g_promoted [thread*16];
17172#else //MULTIPLE_HEAPS
17173 UNREFERENCED_PARAMETER(thread);
17174 return g_promoted;
17175#endif //MULTIPLE_HEAPS
17176}
17177
17178#ifdef INTERIOR_POINTERS
17179heap_segment* gc_heap::find_segment (uint8_t* interior, BOOL small_segment_only_p)
17180{
17181#ifdef SEG_MAPPING_TABLE
17182 heap_segment* seg = seg_mapping_table_segment_of (interior);
17183 if (seg)
17184 {
17185 if (small_segment_only_p && heap_segment_loh_p (seg))
17186 return 0;
17187 }
17188 return seg;
17189#else //SEG_MAPPING_TABLE
17190#ifdef MULTIPLE_HEAPS
17191 for (int i = 0; i < gc_heap::n_heaps; i++)
17192 {
17193 gc_heap* h = gc_heap::g_heaps [i];
17194 hs = h->find_segment_per_heap (o, small_segment_only_p);
17195 if (hs)
17196 {
17197 break;
17198 }
17199 }
17200#else
17201 {
17202 gc_heap* h = pGenGCHeap;
17203 hs = h->find_segment_per_heap (o, small_segment_only_p);
17204 }
17205#endif //MULTIPLE_HEAPS
17206#endif //SEG_MAPPING_TABLE
17207}
17208
17209heap_segment* gc_heap::find_segment_per_heap (uint8_t* interior, BOOL small_segment_only_p)
17210{
17211#ifdef SEG_MAPPING_TABLE
17212 return find_segment (interior, small_segment_only_p);
17213#else //SEG_MAPPING_TABLE
17214 if (in_range_for_segment (interior, ephemeral_heap_segment))
17215 {
17216 return ephemeral_heap_segment;
17217 }
17218 else
17219 {
17220 heap_segment* found_seg = 0;
17221
17222 {
17223 heap_segment* seg = generation_start_segment (generation_of (max_generation));
17224 do
17225 {
17226 if (in_range_for_segment (interior, seg))
17227 {
17228 found_seg = seg;
17229 goto end_find_segment;
17230 }
17231
17232 } while ((seg = heap_segment_next (seg)) != 0);
17233 }
17234 if (!small_segment_only_p)
17235 {
17236#ifdef BACKGROUND_GC
17237 {
17238 ptrdiff_t delta = 0;
17239 heap_segment* seg = segment_of (interior, delta);
17240 if (seg && in_range_for_segment (interior, seg))
17241 {
17242 found_seg = seg;
17243 }
17244 goto end_find_segment;
17245 }
17246#else //BACKGROUND_GC
17247 heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
17248 do
17249 {
17250 if (in_range_for_segment(interior, seg))
17251 {
17252 found_seg = seg;
17253 goto end_find_segment;
17254 }
17255
17256 } while ((seg = heap_segment_next (seg)) != 0);
17257#endif //BACKGROUND_GC
17258 }
17259end_find_segment:
17260
17261 return found_seg;
17262 }
17263#endif //SEG_MAPPING_TABLE
17264}
17265#endif //INTERIOR_POINTERS
17266
17267#if !defined(_DEBUG) && !defined(__GNUC__)
17268inline // This causes link errors if global optimization is off
17269#endif //!_DEBUG && !__GNUC__
17270gc_heap* gc_heap::heap_of (uint8_t* o)
17271{
17272#ifdef MULTIPLE_HEAPS
17273 if (o == 0)
17274 return g_heaps [0];
17275#ifdef SEG_MAPPING_TABLE
17276 gc_heap* hp = seg_mapping_table_heap_of (o);
17277 return (hp ? hp : g_heaps[0]);
17278#else //SEG_MAPPING_TABLE
17279 ptrdiff_t delta = 0;
17280 heap_segment* seg = segment_of (o, delta);
17281 return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17282#endif //SEG_MAPPING_TABLE
17283#else //MULTIPLE_HEAPS
17284 UNREFERENCED_PARAMETER(o);
17285 return __this;
17286#endif //MULTIPLE_HEAPS
17287}
17288
17289inline
17290gc_heap* gc_heap::heap_of_gc (uint8_t* o)
17291{
17292#ifdef MULTIPLE_HEAPS
17293 if (o == 0)
17294 return g_heaps [0];
17295#ifdef SEG_MAPPING_TABLE
17296 gc_heap* hp = seg_mapping_table_heap_of_gc (o);
17297 return (hp ? hp : g_heaps[0]);
17298#else //SEG_MAPPING_TABLE
17299 ptrdiff_t delta = 0;
17300 heap_segment* seg = segment_of (o, delta);
17301 return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17302#endif //SEG_MAPPING_TABLE
17303#else //MULTIPLE_HEAPS
17304 UNREFERENCED_PARAMETER(o);
17305 return __this;
17306#endif //MULTIPLE_HEAPS
17307}
17308
17309#ifdef INTERIOR_POINTERS
17310// will find all heap objects (large and small)
17311uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
17312{
17313 if (!gen0_bricks_cleared)
17314 {
17315#ifdef MULTIPLE_HEAPS
17316 assert (!"Should have already been done in server GC");
17317#endif //MULTIPLE_HEAPS
17318 gen0_bricks_cleared = TRUE;
17319 //initialize brick table for gen 0
17320 for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
17321 b < brick_of (align_on_brick
17322 (heap_segment_allocated (ephemeral_heap_segment)));
17323 b++)
17324 {
17325 set_brick (b, -1);
17326 }
17327 }
17328#ifdef FFIND_OBJECT
17329 //indicate that in the future this needs to be done during allocation
17330#ifdef MULTIPLE_HEAPS
17331 gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
17332#else
17333 gen0_must_clear_bricks = FFIND_DECAY;
17334#endif //MULTIPLE_HEAPS
17335#endif //FFIND_OBJECT
17336
17337 int brick_entry = get_brick_entry(brick_of (interior));
17338 if (brick_entry == 0)
17339 {
17340 // this is a pointer to a large object
17341 heap_segment* seg = find_segment_per_heap (interior, FALSE);
17342 if (seg
17343#ifdef FEATURE_CONSERVATIVE_GC
17344 && (GCConfig::GetConservativeGC() || interior <= heap_segment_allocated(seg))
17345#endif
17346 )
17347 {
17348 // If interior falls within the first free object at the beginning of a generation,
17349 // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
17350 int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
17351#ifdef FEATURE_CONSERVATIVE_GC
17352 || (GCConfig::GetConservativeGC() && !heap_segment_loh_p (seg))
17353#endif
17354 );
17355 //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
17356 assert (interior < heap_segment_allocated (seg));
17357
17358 uint8_t* o = heap_segment_mem (seg);
17359 while (o < heap_segment_allocated (seg))
17360 {
17361 uint8_t* next_o = o + Align (size (o), align_const);
17362 assert (next_o > o);
17363 if ((o <= interior) && (interior < next_o))
17364 return o;
17365 o = next_o;
17366 }
17367 return 0;
17368 }
17369 else
17370 {
17371 return 0;
17372 }
17373 }
17374 else if (interior >= low)
17375 {
17376 heap_segment* seg = find_segment_per_heap (interior, TRUE);
17377 if (seg)
17378 {
17379#ifdef FEATURE_CONSERVATIVE_GC
17380 if (interior >= heap_segment_allocated (seg))
17381 return 0;
17382#else
17383 assert (interior < heap_segment_allocated (seg));
17384#endif
17385 uint8_t* o = find_first_object (interior, heap_segment_mem (seg));
17386 return o;
17387 }
17388 else
17389 return 0;
17390 }
17391 else
17392 return 0;
17393}
17394
17395uint8_t*
17396gc_heap::find_object_for_relocation (uint8_t* interior, uint8_t* low, uint8_t* high)
17397{
17398 uint8_t* old_address = interior;
17399 if (!((old_address >= low) && (old_address < high)))
17400 return 0;
17401 uint8_t* plug = 0;
17402 size_t brick = brick_of (old_address);
17403 int brick_entry = brick_table [ brick ];
17404 if (brick_entry != 0)
17405 {
17406 retry:
17407 {
17408 while (brick_entry < 0)
17409 {
17410 brick = (brick + brick_entry);
17411 brick_entry = brick_table [ brick ];
17412 }
17413 uint8_t* old_loc = old_address;
17414 uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
17415 old_loc);
17416 if (node <= old_loc)
17417 plug = node;
17418 else
17419 {
17420 brick = brick - 1;
17421 brick_entry = brick_table [ brick ];
17422 goto retry;
17423 }
17424
17425 }
17426 assert (plug);
17427 //find the object by going along the plug
17428 uint8_t* o = plug;
17429 while (o <= interior)
17430 {
17431 uint8_t* next_o = o + Align (size (o));
17432 assert (next_o > o);
17433 if (next_o > interior)
17434 {
17435 break;
17436 }
17437 o = next_o;
17438 }
17439 assert ((o <= interior) && ((o + Align (size (o))) > interior));
17440 return o;
17441 }
17442 else
17443 {
17444 // this is a pointer to a large object
17445 heap_segment* seg = find_segment_per_heap (interior, FALSE);
17446 if (seg)
17447 {
17448 assert (interior < heap_segment_allocated (seg));
17449
17450 uint8_t* o = heap_segment_mem (seg);
17451 while (o < heap_segment_allocated (seg))
17452 {
17453 uint8_t* next_o = o + Align (size (o));
17454 assert (next_o > o);
17455 if ((o < interior) && (interior < next_o))
17456 return o;
17457 o = next_o;
17458 }
17459 return 0;
17460 }
17461 else
17462 {
17463 return 0;
17464 }
17465 }
17466}
17467#else //INTERIOR_POINTERS
17468inline
17469uint8_t* gc_heap::find_object (uint8_t* o, uint8_t* low)
17470{
17471 return o;
17472}
17473#endif //INTERIOR_POINTERS
17474
17475#ifdef MULTIPLE_HEAPS
17476
17477#ifdef MARK_LIST
17478#ifdef GC_CONFIG_DRIVEN
17479#define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;} else {mark_list_index++;}}
17480#else //GC_CONFIG_DRIVEN
17481#define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;}}
17482#endif //GC_CONFIG_DRIVEN
17483#else //MARK_LIST
17484#define m_boundary(o) {}
17485#endif //MARK_LIST
17486
17487#define m_boundary_fullgc(o) {}
17488
17489#else //MULTIPLE_HEAPS
17490
17491#ifdef MARK_LIST
17492#ifdef GC_CONFIG_DRIVEN
17493#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;}
17494#else
17495#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;}
17496#endif //GC_CONFIG_DRIVEN
17497#else //MARK_LIST
17498#define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17499#endif //MARK_LIST
17500
17501#define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17502
17503#endif //MULTIPLE_HEAPS
17504
17505#define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
17506
17507inline
17508BOOL gc_heap::gc_mark1 (uint8_t* o)
17509{
17510 BOOL marked = !marked (o);
17511 set_marked (o);
17512 dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
17513 return marked;
17514}
17515
17516inline
17517BOOL gc_heap::gc_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17518{
17519 BOOL marked = FALSE;
17520 if ((o >= low) && (o < high))
17521 marked = gc_mark1 (o);
17522#ifdef MULTIPLE_HEAPS
17523 else if (o)
17524 {
17525 //find the heap
17526 gc_heap* hp = heap_of_gc (o);
17527 assert (hp);
17528 if ((o >= hp->gc_low) && (o < hp->gc_high))
17529 marked = gc_mark1 (o);
17530 }
17531#ifdef SNOOP_STATS
17532 snoop_stat.objects_checked_count++;
17533
17534 if (marked)
17535 {
17536 snoop_stat.objects_marked_count++;
17537 }
17538 if (!o)
17539 {
17540 snoop_stat.zero_ref_count++;
17541 }
17542
17543#endif //SNOOP_STATS
17544#endif //MULTIPLE_HEAPS
17545 return marked;
17546}
17547
17548#ifdef BACKGROUND_GC
17549
17550inline
17551BOOL gc_heap::background_marked (uint8_t* o)
17552{
17553 return mark_array_marked (o);
17554}
17555inline
17556BOOL gc_heap::background_mark1 (uint8_t* o)
17557{
17558 BOOL to_mark = !mark_array_marked (o);
17559
17560 dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
17561 if (to_mark)
17562 {
17563 mark_array_set_marked (o);
17564 dprintf (4, ("n*%Ix*n", (size_t)o));
17565 return TRUE;
17566 }
17567 else
17568 return FALSE;
17569}
17570
17571// TODO: we could consider filtering out NULL's here instead of going to
17572// look for it on other heaps
17573inline
17574BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17575{
17576 BOOL marked = FALSE;
17577 if ((o >= low) && (o < high))
17578 marked = background_mark1 (o);
17579#ifdef MULTIPLE_HEAPS
17580 else if (o)
17581 {
17582 //find the heap
17583 gc_heap* hp = heap_of (o);
17584 assert (hp);
17585 if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
17586 marked = background_mark1 (o);
17587 }
17588#endif //MULTIPLE_HEAPS
17589 return marked;
17590}
17591
17592#endif //BACKGROUND_GC
17593
17594inline
17595uint8_t* gc_heap::next_end (heap_segment* seg, uint8_t* f)
17596{
17597 if (seg == ephemeral_heap_segment)
17598 return f;
17599 else
17600 return heap_segment_allocated (seg);
17601}
17602
17603#define new_start() {if (ppstop <= start) {break;} else {parm = start}}
17604#define ignore_start 0
17605#define use_start 1
17606
17607#define go_through_object(mt,o,size,parm,start,start_useful,limit,exp) \
17608{ \
17609 CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt)); \
17610 CGCDescSeries* cur = map->GetHighestSeries(); \
17611 ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries(); \
17612 \
17613 if (cnt >= 0) \
17614 { \
17615 CGCDescSeries* last = map->GetLowestSeries(); \
17616 uint8_t** parm = 0; \
17617 do \
17618 { \
17619 assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset())); \
17620 parm = (uint8_t**)((o) + cur->GetSeriesOffset()); \
17621 uint8_t** ppstop = \
17622 (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\
17623 if (!start_useful || (uint8_t*)ppstop > (start)) \
17624 { \
17625 if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\
17626 while (parm < ppstop) \
17627 { \
17628 {exp} \
17629 parm++; \
17630 } \
17631 } \
17632 cur--; \
17633 \
17634 } while (cur >= last); \
17635 } \
17636 else \
17637 { \
17638 /* Handle the repeating case - array of valuetypes */ \
17639 uint8_t** parm = (uint8_t**)((o) + cur->startoffset); \
17640 if (start_useful && start > (uint8_t*)parm) \
17641 { \
17642 ptrdiff_t cs = mt->RawGetComponentSize(); \
17643 parm = (uint8_t**)((uint8_t*)parm + (((start) - (uint8_t*)parm)/cs)*cs); \
17644 } \
17645 while ((uint8_t*)parm < ((o)+(size)-plug_skew)) \
17646 { \
17647 for (ptrdiff_t __i = 0; __i > cnt; __i--) \
17648 { \
17649 HALF_SIZE_T skip = cur->val_serie[__i].skip; \
17650 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs; \
17651 uint8_t** ppstop = parm + nptrs; \
17652 if (!start_useful || (uint8_t*)ppstop > (start)) \
17653 { \
17654 if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start); \
17655 do \
17656 { \
17657 {exp} \
17658 parm++; \
17659 } while (parm < ppstop); \
17660 } \
17661 parm = (uint8_t**)((uint8_t*)ppstop + skip); \
17662 } \
17663 } \
17664 } \
17665}
17666
17667#define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
17668
17669// 1 thing to note about this macro:
17670// 1) you can use *parm safely but in general you don't want to use parm
17671// because for the collectible types it's not an address on the managed heap.
17672#ifndef COLLECTIBLE_CLASS
17673#define go_through_object_cl(mt,o,size,parm,exp) \
17674{ \
17675 if (header(o)->ContainsPointers()) \
17676 { \
17677 go_through_object_nostart(mt,o,size,parm,exp); \
17678 } \
17679}
17680#else //COLLECTIBLE_CLASS
17681#define go_through_object_cl(mt,o,size,parm,exp) \
17682{ \
17683 if (header(o)->Collectible()) \
17684 { \
17685 uint8_t* class_obj = get_class_object (o); \
17686 uint8_t** parm = &class_obj; \
17687 do {exp} while (false); \
17688 } \
17689 if (header(o)->ContainsPointers()) \
17690 { \
17691 go_through_object_nostart(mt,o,size,parm,exp); \
17692 } \
17693}
17694#endif //COLLECTIBLE_CLASS
17695
17696// This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
17697void gc_heap::enque_pinned_plug (uint8_t* plug,
17698 BOOL save_pre_plug_info_p,
17699 uint8_t* last_object_in_last_plug)
17700{
17701 if (mark_stack_array_length <= mark_stack_tos)
17702 {
17703 if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
17704 {
17705 // we don't want to continue here due to security
17706 // risks. This happens very rarely and fixing it in the
17707 // way so that we can continue is a bit involved and will
17708 // not be done in Dev10.
17709 GCToEEInterface::HandleFatalError(CORINFO_EXCEPTION_GC);
17710 }
17711 }
17712
17713 dprintf (3, ("enqueuing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d",
17714 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)));
17715 mark& m = mark_stack_array[mark_stack_tos];
17716 m.first = plug;
17717 // Must be set now because if we have a short object we'll need the value of saved_pre_p.
17718 m.saved_pre_p = save_pre_plug_info_p;
17719
17720 if (save_pre_plug_info_p)
17721 {
17722#ifdef SHORT_PLUGS
17723 BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17724 if (is_padded)
17725 clear_plug_padded (last_object_in_last_plug);
17726#endif //SHORT_PLUGS
17727 memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17728#ifdef SHORT_PLUGS
17729 if (is_padded)
17730 set_plug_padded (last_object_in_last_plug);
17731#endif //SHORT_PLUGS
17732
17733 memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17734
17735 // If the last object in the last plug is too short, it requires special handling.
17736 size_t last_obj_size = plug - last_object_in_last_plug;
17737 if (last_obj_size < min_pre_pin_obj_size)
17738 {
17739 record_interesting_data_point (idp_pre_short);
17740#ifdef SHORT_PLUGS
17741 if (is_padded)
17742 record_interesting_data_point (idp_pre_short_padded);
17743#endif //SHORT_PLUGS
17744 dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!",
17745 last_object_in_last_plug, plug));
17746 // Need to set the short bit regardless of having refs or not because we need to
17747 // indicate that this object is not walkable.
17748 m.set_pre_short();
17749
17750#ifdef COLLECTIBLE_CLASS
17751 if (is_collectible (last_object_in_last_plug))
17752 {
17753 m.set_pre_short_collectible();
17754 }
17755#endif //COLLECTIBLE_CLASS
17756
17757 if (contain_pointers (last_object_in_last_plug))
17758 {
17759 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17760
17761 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17762 {
17763 size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17764 dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17765 m.set_pre_short_bit (gap_offset);
17766 }
17767 );
17768 }
17769 }
17770 }
17771
17772 m.saved_post_p = FALSE;
17773}
17774
17775void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_object_in_last_plug, uint8_t* post_plug)
17776{
17777 UNREFERENCED_PARAMETER(last_pinned_plug);
17778
17779 mark& m = mark_stack_array[mark_stack_tos - 1];
17780 assert (last_pinned_plug == m.first);
17781 m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]);
17782
17783#ifdef SHORT_PLUGS
17784 BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17785 if (is_padded)
17786 clear_plug_padded (last_object_in_last_plug);
17787#endif //SHORT_PLUGS
17788 memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17789#ifdef SHORT_PLUGS
17790 if (is_padded)
17791 set_plug_padded (last_object_in_last_plug);
17792#endif //SHORT_PLUGS
17793
17794 memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17795
17796 // This is important - we need to clear all bits here except the last one.
17797 m.saved_post_p = TRUE;
17798
17799#ifdef _DEBUG
17800 m.saved_post_plug_debug.gap = 1;
17801#endif //_DEBUG
17802
17803 dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
17804
17805 size_t last_obj_size = post_plug - last_object_in_last_plug;
17806 if (last_obj_size < min_pre_pin_obj_size)
17807 {
17808 dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
17809 record_interesting_data_point (idp_post_short);
17810#ifdef SHORT_PLUGS
17811 if (is_padded)
17812 record_interesting_data_point (idp_post_short_padded);
17813#endif //SHORT_PLUGS
17814 m.set_post_short();
17815#if defined (_DEBUG) && defined (VERIFY_HEAP)
17816 verify_pinned_queue_p = TRUE;
17817#endif // _DEBUG && VERIFY_HEAP
17818
17819#ifdef COLLECTIBLE_CLASS
17820 if (is_collectible (last_object_in_last_plug))
17821 {
17822 m.set_post_short_collectible();
17823 }
17824#endif //COLLECTIBLE_CLASS
17825
17826 if (contain_pointers (last_object_in_last_plug))
17827 {
17828 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17829
17830 // TODO: since we won't be able to walk this object in relocation, we still need to
17831 // take care of collectible assemblies here.
17832 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17833 {
17834 size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17835 dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17836 m.set_post_short_bit (gap_offset);
17837 }
17838 );
17839 }
17840 }
17841}
17842
17843//#define PREFETCH
17844#ifdef PREFETCH
17845__declspec(naked) void __fastcall Prefetch(void* addr)
17846{
17847 __asm {
17848 PREFETCHT0 [ECX]
17849 ret
17850 };
17851}
17852#else //PREFETCH
17853inline void Prefetch (void* addr)
17854{
17855 UNREFERENCED_PARAMETER(addr);
17856}
17857#endif //PREFETCH
17858#ifdef MH_SC_MARK
17859inline
17860VOLATILE(uint8_t*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
17861{
17862 return ((VOLATILE(uint8_t*)*)(hp->mark_stack_array))[index];
17863}
17864
17865#endif //MH_SC_MARK
17866
17867#define stolen 2
17868#define partial 1
17869#define partial_object 3
17870inline
17871uint8_t* ref_from_slot (uint8_t* r)
17872{
17873 return (uint8_t*)((size_t)r & ~(stolen | partial));
17874}
17875inline
17876BOOL stolen_p (uint8_t* r)
17877{
17878 return (((size_t)r&2) && !((size_t)r&1));
17879}
17880inline
17881BOOL ready_p (uint8_t* r)
17882{
17883 return ((size_t)r != 1);
17884}
17885inline
17886BOOL partial_p (uint8_t* r)
17887{
17888 return (((size_t)r&1) && !((size_t)r&2));
17889}
17890inline
17891BOOL straight_ref_p (uint8_t* r)
17892{
17893 return (!stolen_p (r) && !partial_p (r));
17894}
17895inline
17896BOOL partial_object_p (uint8_t* r)
17897{
17898 return (((size_t)r & partial_object) == partial_object);
17899}
17900inline
17901BOOL ref_p (uint8_t* r)
17902{
17903 return (straight_ref_p (r) || partial_object_p (r));
17904}
17905
17906void gc_heap::mark_object_simple1 (uint8_t* oo, uint8_t* start THREAD_NUMBER_DCL)
17907{
17908 SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)mark_stack_array;
17909 SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)&mark_stack_array[mark_stack_array_length];
17910 SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_base = mark_stack_tos;
17911#ifdef SORT_MARK_STACK
17912 SERVER_SC_MARK_VOLATILE(uint8_t*)* sorted_tos = mark_stack_base;
17913#endif //SORT_MARK_STACK
17914
17915 // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't
17916 // update mark list.
17917 BOOL full_p = (settings.condemned_generation == max_generation);
17918
17919 assert ((start >= oo) && (start < oo+size(oo)));
17920
17921#ifndef MH_SC_MARK
17922 *mark_stack_tos = oo;
17923#endif //!MH_SC_MARK
17924
17925 while (1)
17926 {
17927#ifdef MULTIPLE_HEAPS
17928#else //MULTIPLE_HEAPS
17929 const int thread = 0;
17930#endif //MULTIPLE_HEAPS
17931
17932 if (oo && ((size_t)oo != 4))
17933 {
17934 size_t s = 0;
17935 if (stolen_p (oo))
17936 {
17937 --mark_stack_tos;
17938 goto next_level;
17939 }
17940 else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
17941 {
17942 BOOL overflow_p = FALSE;
17943
17944 if (mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1))
17945 {
17946 size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
17947 if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
17948 {
17949 overflow_p = TRUE;
17950 }
17951 }
17952
17953 if (overflow_p == FALSE)
17954 {
17955 dprintf(3,("pushing mark for %Ix ", (size_t)oo));
17956
17957 go_through_object_cl (method_table(oo), oo, s, ppslot,
17958 {
17959 uint8_t* o = *ppslot;
17960 Prefetch(o);
17961 if (gc_mark (o, gc_low, gc_high))
17962 {
17963 if (full_p)
17964 {
17965 m_boundary_fullgc (o);
17966 }
17967 else
17968 {
17969 m_boundary (o);
17970 }
17971 size_t obj_size = size (o);
17972 promoted_bytes (thread) += obj_size;
17973 if (contain_pointers_or_collectible (o))
17974 {
17975 *(mark_stack_tos++) = o;
17976 }
17977 }
17978 }
17979 );
17980 }
17981 else
17982 {
17983 dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
17984 min_overflow_address = min (min_overflow_address, oo);
17985 max_overflow_address = max (max_overflow_address, oo);
17986 }
17987 }
17988 else
17989 {
17990 if (partial_p (oo))
17991 {
17992 start = ref_from_slot (oo);
17993 oo = ref_from_slot (*(--mark_stack_tos));
17994 dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
17995 assert ((oo < start) && (start < (oo + size (oo))));
17996 }
17997#ifdef COLLECTIBLE_CLASS
17998 else
17999 {
18000 // If there's a class object, push it now. We are guaranteed to have the slot since
18001 // we just popped one object off.
18002 if (is_collectible (oo))
18003 {
18004 uint8_t* class_obj = get_class_object (oo);
18005 if (gc_mark (class_obj, gc_low, gc_high))
18006 {
18007 if (full_p)
18008 {
18009 m_boundary_fullgc (class_obj);
18010 }
18011 else
18012 {
18013 m_boundary (class_obj);
18014 }
18015
18016 size_t obj_size = size (class_obj);
18017 promoted_bytes (thread) += obj_size;
18018 *(mark_stack_tos++) = class_obj;
18019 // The code below expects that the oo is still stored in the stack slot that was
18020 // just popped and it "pushes" it back just by incrementing the mark_stack_tos.
18021 // But the class_obj has just overwritten that stack slot and so the oo needs to
18022 // be stored to the new slot that's pointed to by the mark_stack_tos.
18023 *mark_stack_tos = oo;
18024 }
18025 }
18026
18027 if (!contain_pointers (oo))
18028 {
18029 goto next_level;
18030 }
18031 }
18032#endif //COLLECTIBLE_CLASS
18033
18034 s = size (oo);
18035
18036 BOOL overflow_p = FALSE;
18037
18038 if (mark_stack_tos + (num_partial_refs + 2) >= mark_stack_limit)
18039 {
18040 overflow_p = TRUE;
18041 }
18042 if (overflow_p == FALSE)
18043 {
18044 dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18045
18046 //push the object and its current
18047 SERVER_SC_MARK_VOLATILE(uint8_t*)* place = ++mark_stack_tos;
18048 mark_stack_tos++;
18049#ifdef MH_SC_MARK
18050 *(place-1) = 0;
18051 *(place) = (uint8_t*)partial;
18052#endif //MH_SC_MARK
18053 int i = num_partial_refs;
18054 uint8_t* ref_to_continue = 0;
18055
18056 go_through_object (method_table(oo), oo, s, ppslot,
18057 start, use_start, (oo + s),
18058 {
18059 uint8_t* o = *ppslot;
18060 Prefetch(o);
18061 if (gc_mark (o, gc_low, gc_high))
18062 {
18063 if (full_p)
18064 {
18065 m_boundary_fullgc (o);
18066 }
18067 else
18068 {
18069 m_boundary (o);
18070 }
18071 size_t obj_size = size (o);
18072 promoted_bytes (thread) += obj_size;
18073 if (contain_pointers_or_collectible (o))
18074 {
18075 *(mark_stack_tos++) = o;
18076 if (--i == 0)
18077 {
18078 ref_to_continue = (uint8_t*)((size_t)(ppslot+1) | partial);
18079 goto more_to_do;
18080 }
18081
18082 }
18083 }
18084
18085 }
18086 );
18087 //we are finished with this object
18088 assert (ref_to_continue == 0);
18089#ifdef MH_SC_MARK
18090 assert ((*(place-1)) == (uint8_t*)0);
18091#else //MH_SC_MARK
18092 *(place-1) = 0;
18093#endif //MH_SC_MARK
18094 *place = 0;
18095 // shouldn't we decrease tos by 2 here??
18096
18097more_to_do:
18098 if (ref_to_continue)
18099 {
18100 //update the start
18101#ifdef MH_SC_MARK
18102 assert ((*(place-1)) == (uint8_t*)0);
18103 *(place-1) = (uint8_t*)((size_t)oo | partial_object);
18104 assert (((*place) == (uint8_t*)1) || ((*place) == (uint8_t*)2));
18105#endif //MH_SC_MARK
18106 *place = ref_to_continue;
18107 }
18108 }
18109 else
18110 {
18111 dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
18112 min_overflow_address = min (min_overflow_address, oo);
18113 max_overflow_address = max (max_overflow_address, oo);
18114 }
18115 }
18116#ifdef SORT_MARK_STACK
18117 if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18118 {
18119 rqsort1 (sorted_tos, mark_stack_tos-1);
18120 sorted_tos = mark_stack_tos-1;
18121 }
18122#endif //SORT_MARK_STACK
18123 }
18124 next_level:
18125 if (!(mark_stack_empty_p()))
18126 {
18127 oo = *(--mark_stack_tos);
18128 start = oo;
18129
18130#ifdef SORT_MARK_STACK
18131 sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
18132#endif //SORT_MARK_STACK
18133 }
18134 else
18135 break;
18136 }
18137}
18138
18139#ifdef MH_SC_MARK
18140BOOL same_numa_node_p (int hn1, int hn2)
18141{
18142 return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
18143}
18144
18145int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
18146{
18147 int hn = (current_buddy+1)%n_heaps;
18148 while (hn != current_buddy)
18149 {
18150 if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
18151 return hn;
18152 hn = (hn+1)%n_heaps;
18153 }
18154 return current_buddy;
18155}
18156
18157void
18158gc_heap::mark_steal()
18159{
18160 mark_stack_busy() = 0;
18161 //clear the mark stack in the snooping range
18162 for (int i = 0; i < max_snoop_level; i++)
18163 {
18164 ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18165 }
18166
18167 //pick the next heap as our buddy
18168 int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
18169
18170#ifdef SNOOP_STATS
18171 dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
18172 uint32_t begin_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
18173#endif //SNOOP_STATS
18174
18175 int idle_loop_count = 0;
18176 int first_not_ready_level = 0;
18177
18178 while (1)
18179 {
18180 gc_heap* hp = g_heaps [thpn];
18181 int level = first_not_ready_level;
18182 first_not_ready_level = 0;
18183
18184 while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
18185 {
18186 idle_loop_count = 0;
18187#ifdef SNOOP_STATS
18188 snoop_stat.busy_count++;
18189 dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix",
18190 heap_number, level, (int)((uint8_t**)(hp->mark_stack_array))[level]));
18191#endif //SNOOP_STATS
18192
18193 uint8_t* o = ref_mark_stack (hp, level);
18194
18195 uint8_t* start = o;
18196 if (ref_p (o))
18197 {
18198 mark_stack_busy() = 1;
18199
18200 BOOL success = TRUE;
18201 uint8_t* next = (ref_mark_stack (hp, level+1));
18202 if (ref_p (next))
18203 {
18204 if (((size_t)o > 4) && !partial_object_p (o))
18205 {
18206 //this is a normal object, not a partial mark tuple
18207 //success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
18208 success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), (uint8_t*)4, o)==o);
18209#ifdef SNOOP_STATS
18210 snoop_stat.interlocked_count++;
18211 if (success)
18212 snoop_stat.normal_count++;
18213#endif //SNOOP_STATS
18214 }
18215 else
18216 {
18217 //it is a stolen entry, or beginning/ending of a partial mark
18218 level++;
18219#ifdef SNOOP_STATS
18220 snoop_stat.stolen_or_pm_count++;
18221#endif //SNOOP_STATS
18222 success = FALSE;
18223 }
18224 }
18225 else if (stolen_p (next))
18226 {
18227 //ignore the stolen guy and go to the next level
18228 success = FALSE;
18229 level+=2;
18230#ifdef SNOOP_STATS
18231 snoop_stat.stolen_entry_count++;
18232#endif //SNOOP_STATS
18233 }
18234 else
18235 {
18236 assert (partial_p (next));
18237 start = ref_from_slot (next);
18238 //re-read the object
18239 o = ref_from_slot (ref_mark_stack (hp, level));
18240 if (o && start)
18241 {
18242 //steal the object
18243 success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level+1), (uint8_t*)stolen, next)==next);
18244#ifdef SNOOP_STATS
18245 snoop_stat.interlocked_count++;
18246 if (success)
18247 {
18248 snoop_stat.partial_mark_parent_count++;
18249 }
18250#endif //SNOOP_STATS
18251 }
18252 else
18253 {
18254 // stack is not ready, or o is completely different from the last time we read from this stack level.
18255 // go up 2 levels to steal children or totally unrelated objects.
18256 success = FALSE;
18257 if (first_not_ready_level == 0)
18258 {
18259 first_not_ready_level = level;
18260 }
18261 level+=2;
18262#ifdef SNOOP_STATS
18263 snoop_stat.pm_not_ready_count++;
18264#endif //SNOOP_STATS
18265 }
18266 }
18267 if (success)
18268 {
18269
18270#ifdef SNOOP_STATS
18271 dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
18272 heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18273 (GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18274 uint32_t start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
18275#endif //SNOOP_STATS
18276
18277 mark_object_simple1 (o, start, heap_number);
18278
18279#ifdef SNOOP_STATS
18280 dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
18281 heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18282 (GCToOSInterface::GetLowPrecisionTimeStamp()-start_tick),(GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18283#endif //SNOOP_STATS
18284
18285 mark_stack_busy() = 0;
18286
18287 //clear the mark stack in snooping range
18288 for (int i = 0; i < max_snoop_level; i++)
18289 {
18290 if (((uint8_t**)mark_stack_array)[i] != 0)
18291 {
18292 ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18293#ifdef SNOOP_STATS
18294 snoop_stat.stack_bottom_clear_count++;
18295#endif //SNOOP_STATS
18296 }
18297 }
18298
18299 level = 0;
18300 }
18301 mark_stack_busy() = 0;
18302 }
18303 else
18304 {
18305 //slot is either partial or stolen
18306 level++;
18307 }
18308 }
18309 if ((first_not_ready_level != 0) && hp->mark_stack_busy())
18310 {
18311 continue;
18312 }
18313 if (!hp->mark_stack_busy())
18314 {
18315 first_not_ready_level = 0;
18316 idle_loop_count++;
18317
18318 if ((idle_loop_count % (6) )==1)
18319 {
18320#ifdef SNOOP_STATS
18321 snoop_stat.switch_to_thread_count++;
18322#endif //SNOOP_STATS
18323 GCToOSInterface::Sleep(1);
18324 }
18325 int free_count = 1;
18326#ifdef SNOOP_STATS
18327 snoop_stat.stack_idle_count++;
18328 //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
18329#endif //SNOOP_STATS
18330 for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
18331 {
18332 if (!((g_heaps [hpn])->mark_stack_busy()))
18333 {
18334 free_count++;
18335#ifdef SNOOP_STATS
18336 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
18337#endif //SNOOP_STATS
18338 }
18339 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
18340 {
18341 thpn = hpn;
18342 break;
18343 }
18344 hpn = (hpn+1)%n_heaps;
18345 YieldProcessor();
18346 }
18347 if (free_count == n_heaps)
18348 {
18349 break;
18350 }
18351 }
18352 }
18353}
18354
18355inline
18356BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
18357{
18358#ifdef SNOOP_STATS
18359 snoop_stat.check_level_count++;
18360#endif //SNOOP_STATS
18361 return (next_heap->mark_stack_busy()>=1);
18362}
18363#endif //MH_SC_MARK
18364
18365#ifdef SNOOP_STATS
18366void gc_heap::print_snoop_stat()
18367{
18368 dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s",
18369 "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
18370 dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
18371 snoop_stat.heap_index,
18372 snoop_stat.objects_checked_count,
18373 snoop_stat.zero_ref_count,
18374 snoop_stat.objects_marked_count,
18375 snoop_stat.stolen_stack_count,
18376 snoop_stat.partial_stack_count,
18377 snoop_stat.normal_stack_count,
18378 snoop_stat.non_stack_count));
18379 dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s",
18380 "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
18381 dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18382 snoop_stat.heap_index,
18383 snoop_stat.check_level_count,
18384 snoop_stat.busy_count,
18385 snoop_stat.interlocked_count,
18386 snoop_stat.partial_mark_parent_count,
18387 snoop_stat.stolen_or_pm_count,
18388 snoop_stat.stolen_entry_count,
18389 snoop_stat.pm_not_ready_count,
18390 snoop_stat.normal_count,
18391 snoop_stat.stack_bottom_clear_count));
18392
18393 printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n",
18394 "heap", "check", "zero", "mark", "idle", "switch");
18395 printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
18396 snoop_stat.heap_index,
18397 snoop_stat.objects_checked_count,
18398 snoop_stat.zero_ref_count,
18399 snoop_stat.objects_marked_count,
18400 snoop_stat.stack_idle_count,
18401 snoop_stat.switch_to_thread_count);
18402 printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n",
18403 "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18404 printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18405 snoop_stat.heap_index,
18406 snoop_stat.check_level_count,
18407 snoop_stat.busy_count,
18408 snoop_stat.interlocked_count,
18409 snoop_stat.partial_mark_parent_count,
18410 snoop_stat.stolen_or_pm_count,
18411 snoop_stat.stolen_entry_count,
18412 snoop_stat.pm_not_ready_count,
18413 snoop_stat.normal_count,
18414 snoop_stat.stack_bottom_clear_count);
18415}
18416#endif //SNOOP_STATS
18417
18418#ifdef HEAP_ANALYZE
18419void
18420gc_heap::ha_mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18421{
18422 if (!internal_root_array)
18423 {
18424 internal_root_array = new (nothrow) uint8_t* [internal_root_array_length];
18425 if (!internal_root_array)
18426 {
18427 heap_analyze_success = FALSE;
18428 }
18429 }
18430
18431 if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
18432 {
18433 size_t new_size = 2*internal_root_array_length;
18434
18435 uint64_t available_physical = 0;
18436 get_memory_info (NULL, &available_physical);
18437 if (new_size > (size_t)(available_physical / 10))
18438 {
18439 heap_analyze_success = FALSE;
18440 }
18441 else
18442 {
18443 uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18444 if (tmp)
18445 {
18446 memcpy (tmp, internal_root_array,
18447 internal_root_array_length*sizeof (uint8_t*));
18448 delete[] internal_root_array;
18449 internal_root_array = tmp;
18450 internal_root_array_length = new_size;
18451 }
18452 else
18453 {
18454 heap_analyze_success = FALSE;
18455 }
18456 }
18457 }
18458
18459 if (heap_analyze_success)
18460 {
18461 PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
18462
18463 uint8_t* ref = (uint8_t*)po;
18464 if (!current_obj ||
18465 !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
18466 {
18467 gc_heap* hp = gc_heap::heap_of (ref);
18468 current_obj = hp->find_object (ref, hp->lowest_address);
18469 current_obj_size = size (current_obj);
18470
18471 internal_root_array[internal_root_array_index] = current_obj;
18472 internal_root_array_index++;
18473 }
18474 }
18475
18476 mark_object_simple (po THREAD_NUMBER_ARG);
18477}
18478#endif //HEAP_ANALYZE
18479
18480//this method assumes that *po is in the [low. high[ range
18481void
18482gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18483{
18484 uint8_t* o = *po;
18485#ifdef MULTIPLE_HEAPS
18486#else //MULTIPLE_HEAPS
18487 const int thread = 0;
18488#endif //MULTIPLE_HEAPS
18489 {
18490#ifdef SNOOP_STATS
18491 snoop_stat.objects_checked_count++;
18492#endif //SNOOP_STATS
18493
18494 if (gc_mark1 (o))
18495 {
18496 m_boundary (o);
18497 size_t s = size (o);
18498 promoted_bytes (thread) += s;
18499 {
18500 go_through_object_cl (method_table(o), o, s, poo,
18501 {
18502 uint8_t* oo = *poo;
18503 if (gc_mark (oo, gc_low, gc_high))
18504 {
18505 m_boundary (oo);
18506 size_t obj_size = size (oo);
18507 promoted_bytes (thread) += obj_size;
18508
18509 if (contain_pointers_or_collectible (oo))
18510 mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
18511 }
18512 }
18513 );
18514 }
18515 }
18516 }
18517}
18518
18519inline
18520uint8_t* gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL)
18521{
18522 if ((o >= gc_low) && (o < gc_high))
18523 mark_object_simple (&o THREAD_NUMBER_ARG);
18524#ifdef MULTIPLE_HEAPS
18525 else if (o)
18526 {
18527 //find the heap
18528 gc_heap* hp = heap_of (o);
18529 assert (hp);
18530 if ((o >= hp->gc_low) && (o < hp->gc_high))
18531 mark_object_simple (&o THREAD_NUMBER_ARG);
18532 }
18533#endif //MULTIPLE_HEAPS
18534
18535 return o;
18536}
18537
18538#ifdef BACKGROUND_GC
18539
18540void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL)
18541{
18542 uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
18543
18544#ifdef SORT_MARK_STACK
18545 uint8_t** sorted_tos = background_mark_stack_array;
18546#endif //SORT_MARK_STACK
18547
18548 background_mark_stack_tos = background_mark_stack_array;
18549
18550 while (1)
18551 {
18552#ifdef MULTIPLE_HEAPS
18553#else //MULTIPLE_HEAPS
18554 const int thread = 0;
18555#endif //MULTIPLE_HEAPS
18556 if (oo)
18557 {
18558 size_t s = 0;
18559 if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18560 {
18561 BOOL overflow_p = FALSE;
18562
18563 if (background_mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1))
18564 {
18565 size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18566 size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18567 if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
18568 {
18569 dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs",
18570 heap_number,
18571 (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
18572 method_table(oo),
18573 num_pointers));
18574
18575 bgc_overflow_count++;
18576 overflow_p = TRUE;
18577 }
18578 }
18579
18580 if (overflow_p == FALSE)
18581 {
18582 dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18583
18584 go_through_object_cl (method_table(oo), oo, s, ppslot,
18585 {
18586 uint8_t* o = *ppslot;
18587 Prefetch(o);
18588 if (background_mark (o,
18589 background_saved_lowest_address,
18590 background_saved_highest_address))
18591 {
18592 //m_boundary (o);
18593 size_t obj_size = size (o);
18594 bpromoted_bytes (thread) += obj_size;
18595 if (contain_pointers_or_collectible (o))
18596 {
18597 *(background_mark_stack_tos++) = o;
18598
18599 }
18600 }
18601 }
18602 );
18603 }
18604 else
18605 {
18606 dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18607 background_min_overflow_address = min (background_min_overflow_address, oo);
18608 background_max_overflow_address = max (background_max_overflow_address, oo);
18609 }
18610 }
18611 else
18612 {
18613 uint8_t* start = oo;
18614 if ((size_t)oo & 1)
18615 {
18616 oo = (uint8_t*)((size_t)oo & ~1);
18617 start = *(--background_mark_stack_tos);
18618 dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18619 }
18620#ifdef COLLECTIBLE_CLASS
18621 else
18622 {
18623 // If there's a class object, push it now. We are guaranteed to have the slot since
18624 // we just popped one object off.
18625 if (is_collectible (oo))
18626 {
18627 uint8_t* class_obj = get_class_object (oo);
18628 if (background_mark (class_obj,
18629 background_saved_lowest_address,
18630 background_saved_highest_address))
18631 {
18632 size_t obj_size = size (class_obj);
18633 bpromoted_bytes (thread) += obj_size;
18634
18635 *(background_mark_stack_tos++) = class_obj;
18636 }
18637 }
18638
18639 if (!contain_pointers (oo))
18640 {
18641 goto next_level;
18642 }
18643 }
18644#endif //COLLECTIBLE_CLASS
18645
18646 s = size (oo);
18647
18648 BOOL overflow_p = FALSE;
18649
18650 if (background_mark_stack_tos + (num_partial_refs + 2) >= mark_stack_limit)
18651 {
18652 size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18653 size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18654
18655 dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id",
18656 heap_number,
18657 (size_t)(mark_stack_limit - background_mark_stack_tos),
18658 oo,
18659 method_table(oo),
18660 start,
18661 num_pointers));
18662
18663 bgc_overflow_count++;
18664 overflow_p = TRUE;
18665 }
18666 if (overflow_p == FALSE)
18667 {
18668 dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18669
18670 //push the object and its current
18671 uint8_t** place = background_mark_stack_tos++;
18672 *(place) = start;
18673 *(background_mark_stack_tos++) = (uint8_t*)((size_t)oo | 1);
18674
18675 int i = num_partial_refs;
18676
18677 go_through_object (method_table(oo), oo, s, ppslot,
18678 start, use_start, (oo + s),
18679 {
18680 uint8_t* o = *ppslot;
18681 Prefetch(o);
18682
18683 if (background_mark (o,
18684 background_saved_lowest_address,
18685 background_saved_highest_address))
18686 {
18687 //m_boundary (o);
18688 size_t obj_size = size (o);
18689 bpromoted_bytes (thread) += obj_size;
18690 if (contain_pointers_or_collectible (o))
18691 {
18692 *(background_mark_stack_tos++) = o;
18693 if (--i == 0)
18694 {
18695 //update the start
18696 *place = (uint8_t*)(ppslot+1);
18697 goto more_to_do;
18698 }
18699
18700 }
18701 }
18702
18703 }
18704 );
18705 //we are finished with this object
18706 *place = 0;
18707 *(place+1) = 0;
18708
18709 more_to_do:;
18710 }
18711 else
18712 {
18713 dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18714 background_min_overflow_address = min (background_min_overflow_address, oo);
18715 background_max_overflow_address = max (background_max_overflow_address, oo);
18716 }
18717 }
18718 }
18719#ifdef SORT_MARK_STACK
18720 if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18721 {
18722 rqsort1 (sorted_tos, background_mark_stack_tos-1);
18723 sorted_tos = background_mark_stack_tos-1;
18724 }
18725#endif //SORT_MARK_STACK
18726
18727#ifdef COLLECTIBLE_CLASS
18728next_level:
18729#endif // COLLECTIBLE_CLASS
18730 allow_fgc();
18731
18732 if (!(background_mark_stack_tos == background_mark_stack_array))
18733 {
18734 oo = *(--background_mark_stack_tos);
18735
18736#ifdef SORT_MARK_STACK
18737 sorted_tos = (uint8_t**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
18738#endif //SORT_MARK_STACK
18739 }
18740 else
18741 break;
18742 }
18743
18744 assert (background_mark_stack_tos == background_mark_stack_array);
18745
18746
18747}
18748
18749//this version is different than the foreground GC because
18750//it can't keep pointers to the inside of an object
18751//while calling background_mark_simple1. The object could be moved
18752//by an intervening foreground gc.
18753//this method assumes that *po is in the [low. high[ range
18754void
18755gc_heap::background_mark_simple (uint8_t* o THREAD_NUMBER_DCL)
18756{
18757#ifdef MULTIPLE_HEAPS
18758#else //MULTIPLE_HEAPS
18759 const int thread = 0;
18760#endif //MULTIPLE_HEAPS
18761 {
18762 dprintf (3, ("bmarking %Ix", o));
18763
18764 if (background_mark1 (o))
18765 {
18766 //m_boundary (o);
18767 size_t s = size (o);
18768 bpromoted_bytes (thread) += s;
18769
18770 if (contain_pointers_or_collectible (o))
18771 {
18772 background_mark_simple1 (o THREAD_NUMBER_ARG);
18773 }
18774 }
18775 }
18776}
18777
18778inline
18779uint8_t* gc_heap::background_mark_object (uint8_t* o THREAD_NUMBER_DCL)
18780{
18781 if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
18782 {
18783 background_mark_simple (o THREAD_NUMBER_ARG);
18784 }
18785 else
18786 {
18787 if (o)
18788 {
18789 dprintf (3, ("or-%Ix", o));
18790 }
18791 }
18792 return o;
18793}
18794
18795void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, uint32_t flags)
18796{
18797 UNREFERENCED_PARAMETER(sc);
18798
18799 assert (settings.concurrent);
18800 uint8_t* o = (uint8_t*)object;
18801
18802 gc_heap* hp = gc_heap::heap_of (o);
18803#ifdef INTERIOR_POINTERS
18804 if (flags & GC_CALL_INTERIOR)
18805 {
18806 o = hp->find_object (o, background_saved_lowest_address);
18807 }
18808#endif //INTERIOR_POINTERS
18809
18810 if (!background_object_marked (o, FALSE))
18811 {
18812 FATAL_GC_ERROR();
18813 }
18814}
18815
18816void gc_heap::background_promote (Object** ppObject, ScanContext* sc, uint32_t flags)
18817{
18818 UNREFERENCED_PARAMETER(sc);
18819 //in order to save space on the array, mark the object,
18820 //knowing that it will be visited later
18821 assert (settings.concurrent);
18822
18823 THREAD_NUMBER_FROM_CONTEXT;
18824#ifndef MULTIPLE_HEAPS
18825 const int thread = 0;
18826#endif //!MULTIPLE_HEAPS
18827
18828 uint8_t* o = (uint8_t*)*ppObject;
18829
18830 if (o == 0)
18831 return;
18832
18833#ifdef DEBUG_DestroyedHandleValue
18834 // we can race with destroy handle during concurrent scan
18835 if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
18836 return;
18837#endif //DEBUG_DestroyedHandleValue
18838
18839 HEAP_FROM_THREAD;
18840
18841 gc_heap* hp = gc_heap::heap_of (o);
18842
18843 if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
18844 {
18845 return;
18846 }
18847
18848#ifdef INTERIOR_POINTERS
18849 if (flags & GC_CALL_INTERIOR)
18850 {
18851 o = hp->find_object (o, hp->background_saved_lowest_address);
18852 if (o == 0)
18853 return;
18854 }
18855#endif //INTERIOR_POINTERS
18856
18857#ifdef FEATURE_CONSERVATIVE_GC
18858 // For conservative GC, a value on stack may point to middle of a free object.
18859 // In this case, we don't need to promote the pointer.
18860 if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
18861 {
18862 return;
18863 }
18864#endif //FEATURE_CONSERVATIVE_GC
18865
18866#ifdef _DEBUG
18867 ((CObjectHeader*)o)->Validate();
18868#endif //_DEBUG
18869
18870 dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
18871
18872 //needs to be called before the marking because it is possible for a foreground
18873 //gc to take place during the mark and move the object
18874 STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, " GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
18875
18876 hpt->background_mark_simple (o THREAD_NUMBER_ARG);
18877}
18878
18879//used by the ephemeral collection to scan the local background structures
18880//containing references.
18881void
18882gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
18883{
18884 ScanContext sc;
18885 if (pSC == 0)
18886 pSC = &sc;
18887
18888 pSC->thread_number = hn;
18889
18890#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
18891 pSC->pCurrentDomain = 0;
18892#endif
18893
18894 BOOL relocate_p = (fn == &GCHeap::Relocate);
18895
18896 dprintf (3, ("Scanning background mark list"));
18897
18898 //scan mark_list
18899 size_t mark_list_finger = 0;
18900 while (mark_list_finger < c_mark_list_index)
18901 {
18902 uint8_t** o = &c_mark_list [mark_list_finger];
18903 if (!relocate_p)
18904 {
18905 // We may not be able to calculate the size during relocate as POPO
18906 // may have written over the object.
18907 size_t s = size (*o);
18908 assert (Align (s) >= Align (min_obj_size));
18909 dprintf(3,("background root %Ix", (size_t)*o));
18910 }
18911 (*fn) ((Object**)o, pSC, 0);
18912 mark_list_finger++;
18913 }
18914
18915 //scan the mark stack
18916 dprintf (3, ("Scanning background mark stack"));
18917
18918 uint8_t** finger = background_mark_stack_array;
18919 while (finger < background_mark_stack_tos)
18920 {
18921 if ((finger + 1) < background_mark_stack_tos)
18922 {
18923 // We need to check for the partial mark case here.
18924 uint8_t* parent_obj = *(finger + 1);
18925 if ((size_t)parent_obj & 1)
18926 {
18927 uint8_t* place = *finger;
18928 size_t place_offset = 0;
18929 uint8_t* real_parent_obj = (uint8_t*)((size_t)parent_obj & ~1);
18930
18931 if (relocate_p)
18932 {
18933 *(finger + 1) = real_parent_obj;
18934 place_offset = place - real_parent_obj;
18935 dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
18936 (*fn) ((Object**)(finger + 1), pSC, 0);
18937 real_parent_obj = *(finger + 1);
18938 *finger = real_parent_obj + place_offset;
18939 *(finger + 1) = (uint8_t*)((size_t)real_parent_obj | 1);
18940 dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
18941 }
18942 else
18943 {
18944 uint8_t** temp = &real_parent_obj;
18945 dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
18946 (*fn) ((Object**)temp, pSC, 0);
18947 }
18948
18949 finger += 2;
18950 continue;
18951 }
18952 }
18953 dprintf(3,("background root %Ix", (size_t)*finger));
18954 (*fn) ((Object**)finger, pSC, 0);
18955 finger++;
18956 }
18957}
18958
18959inline
18960void gc_heap::background_mark_through_object (uint8_t* oo THREAD_NUMBER_DCL)
18961{
18962 if (contain_pointers (oo))
18963 {
18964 size_t total_refs = 0;
18965 size_t s = size (oo);
18966 go_through_object_nostart (method_table(oo), oo, s, po,
18967 {
18968 uint8_t* o = *po;
18969 total_refs++;
18970 background_mark_object (o THREAD_NUMBER_ARG);
18971 }
18972 );
18973
18974 dprintf (3,("Background marking through %Ix went through %Id refs",
18975 (size_t)oo,
18976 total_refs));
18977 }
18978}
18979
18980uint8_t* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
18981{
18982 if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
18983 {
18984 // for now we stop at where gen1 started when we started processing
18985 return background_min_soh_overflow_address;
18986 }
18987 else
18988 {
18989 return heap_segment_allocated (seg);
18990 }
18991}
18992
18993uint8_t* gc_heap::background_first_overflow (uint8_t* min_add,
18994 heap_segment* seg,
18995 BOOL concurrent_p,
18996 BOOL small_object_p)
18997{
18998 uint8_t* o = 0;
18999
19000 if (small_object_p)
19001 {
19002 if (in_range_for_segment (min_add, seg))
19003 {
19004 // min_add was the beginning of gen1 when we did the concurrent
19005 // overflow. Now we could be in a situation where min_add is
19006 // actually the same as allocated for that segment (because
19007 // we expanded heap), in which case we can not call
19008 // find first on this address or we will AV.
19009 if (min_add >= heap_segment_allocated (seg))
19010 {
19011 return min_add;
19012 }
19013 else
19014 {
19015 if (concurrent_p &&
19016 ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
19017 {
19018 return background_min_soh_overflow_address;
19019 }
19020 else
19021 {
19022 o = find_first_object (min_add, heap_segment_mem (seg));
19023 return o;
19024 }
19025 }
19026 }
19027 }
19028
19029 o = max (heap_segment_mem (seg), min_add);
19030 return o;
19031}
19032
19033void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
19034 uint8_t* min_add, uint8_t* max_add,
19035 BOOL concurrent_p)
19036{
19037 if (concurrent_p)
19038 {
19039 current_bgc_state = bgc_overflow_soh;
19040 }
19041
19042 size_t total_marked_objects = 0;
19043
19044#ifdef MULTIPLE_HEAPS
19045 int thread = heap_number;
19046#endif //MULTIPLE_HEAPS
19047
19048 exclusive_sync* loh_alloc_lock = 0;
19049
19050 dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19051#ifdef MULTIPLE_HEAPS
19052 // We don't have each heap scan all heaps concurrently because we are worried about
19053 // multiple threads calling things like find_first_object.
19054 int h_start = (concurrent_p ? heap_number : 0);
19055 int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
19056 for (int hi = h_start; hi < h_end; hi++)
19057 {
19058 gc_heap* hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
19059
19060#else
19061 {
19062 gc_heap* hp = 0;
19063
19064#endif //MULTIPLE_HEAPS
19065 BOOL small_object_segments = TRUE;
19066 int align_const = get_alignment_constant (small_object_segments);
19067 generation* gen = hp->generation_of (condemned_gen_number);
19068 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19069 PREFIX_ASSUME(seg != NULL);
19070 loh_alloc_lock = hp->bgc_alloc_lock;
19071
19072 uint8_t* o = hp->background_first_overflow (min_add,
19073 seg,
19074 concurrent_p,
19075 small_object_segments);
19076
19077 while (1)
19078 {
19079 while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
19080 {
19081 dprintf (3, ("considering %Ix", (size_t)o));
19082
19083 size_t s;
19084
19085 if (concurrent_p && !small_object_segments)
19086 {
19087 loh_alloc_lock->bgc_mark_set (o);
19088
19089 if (((CObjectHeader*)o)->IsFree())
19090 {
19091 s = unused_array_size (o);
19092 }
19093 else
19094 {
19095 s = size (o);
19096 }
19097 }
19098 else
19099 {
19100 s = size (o);
19101 }
19102
19103 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
19104 {
19105 total_marked_objects++;
19106 go_through_object_cl (method_table(o), o, s, poo,
19107 uint8_t* oo = *poo;
19108 background_mark_object (oo THREAD_NUMBER_ARG);
19109 );
19110 }
19111
19112 if (concurrent_p && !small_object_segments)
19113 {
19114 loh_alloc_lock->bgc_mark_done ();
19115 }
19116
19117 o = o + Align (s, align_const);
19118
19119 if (concurrent_p)
19120 {
19121 allow_fgc();
19122 }
19123 }
19124
19125 dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)",
19126 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
19127
19128 if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
19129 (seg = heap_segment_next_in_range (seg)) == 0)
19130 {
19131 if (small_object_segments)
19132 {
19133 if (concurrent_p)
19134 {
19135 current_bgc_state = bgc_overflow_loh;
19136 }
19137
19138 dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
19139 fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
19140 concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
19141 total_marked_objects = 0;
19142 small_object_segments = FALSE;
19143 align_const = get_alignment_constant (small_object_segments);
19144 seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19145
19146 PREFIX_ASSUME(seg != NULL);
19147
19148 o = max (heap_segment_mem (seg), min_add);
19149 continue;
19150 }
19151 else
19152 {
19153 dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
19154 fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
19155 break;
19156 }
19157 }
19158 else
19159 {
19160 o = hp->background_first_overflow (min_add,
19161 seg,
19162 concurrent_p,
19163 small_object_segments);
19164 continue;
19165 }
19166 }
19167 }
19168}
19169
19170BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
19171{
19172 BOOL grow_mark_array_p = TRUE;
19173
19174 if (concurrent_p)
19175 {
19176 assert (!processed_soh_overflow_p);
19177
19178 if ((background_max_overflow_address != 0) &&
19179 (background_min_overflow_address != MAX_PTR))
19180 {
19181 // We have overflow to process but we know we can't process the ephemeral generations
19182 // now (we actually could process till the current gen1 start but since we are going to
19183 // make overflow per segment, for now I'll just stop at the saved gen1 start.
19184 saved_overflow_ephemeral_seg = ephemeral_heap_segment;
19185 background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
19186 background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
19187 }
19188 }
19189 else
19190 {
19191 assert ((saved_overflow_ephemeral_seg == 0) ||
19192 ((background_max_soh_overflow_address != 0) &&
19193 (background_min_soh_overflow_address != MAX_PTR)));
19194
19195 if (!processed_soh_overflow_p)
19196 {
19197 // if there was no more overflow we just need to process what we didn't process
19198 // on the saved ephemeral segment.
19199 if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
19200 {
19201 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
19202 grow_mark_array_p = FALSE;
19203 }
19204
19205 background_min_overflow_address = min (background_min_overflow_address,
19206 background_min_soh_overflow_address);
19207 background_max_overflow_address = max (background_max_overflow_address,
19208 background_max_soh_overflow_address);
19209 processed_soh_overflow_p = TRUE;
19210 }
19211 }
19212
19213 BOOL overflow_p = FALSE;
19214recheck:
19215 if ((! ((background_max_overflow_address == 0)) ||
19216 ! ((background_min_overflow_address == MAX_PTR))))
19217 {
19218 overflow_p = TRUE;
19219
19220 if (grow_mark_array_p)
19221 {
19222 // Try to grow the array.
19223 size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
19224
19225 if ((new_size * sizeof(mark)) > 100*1024)
19226 {
19227 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19228
19229 new_size = min(new_max_size, new_size);
19230 }
19231
19232 if ((background_mark_stack_array_length < new_size) &&
19233 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
19234 {
19235 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
19236
19237 uint8_t** tmp = new (nothrow) uint8_t* [new_size];
19238 if (tmp)
19239 {
19240 delete background_mark_stack_array;
19241 background_mark_stack_array = tmp;
19242 background_mark_stack_array_length = new_size;
19243 background_mark_stack_tos = background_mark_stack_array;
19244 }
19245 }
19246 }
19247 else
19248 {
19249 grow_mark_array_p = TRUE;
19250 }
19251
19252 uint8_t* min_add = background_min_overflow_address;
19253 uint8_t* max_add = background_max_overflow_address;
19254
19255 background_max_overflow_address = 0;
19256 background_min_overflow_address = MAX_PTR;
19257
19258 background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
19259 if (!concurrent_p)
19260 {
19261 goto recheck;
19262 }
19263 }
19264
19265 return overflow_p;
19266}
19267
19268#endif //BACKGROUND_GC
19269
19270inline
19271void gc_heap::mark_through_object (uint8_t* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
19272{
19273#ifndef COLLECTIBLE_CLASS
19274 UNREFERENCED_PARAMETER(mark_class_object_p);
19275 BOOL to_mark_class_object = FALSE;
19276#else //COLLECTIBLE_CLASS
19277 BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
19278#endif //COLLECTIBLE_CLASS
19279 if (contain_pointers (oo) || to_mark_class_object)
19280 {
19281 dprintf(3,( "Marking through %Ix", (size_t)oo));
19282 size_t s = size (oo);
19283
19284#ifdef COLLECTIBLE_CLASS
19285 if (to_mark_class_object)
19286 {
19287 uint8_t* class_obj = get_class_object (oo);
19288 mark_object (class_obj THREAD_NUMBER_ARG);
19289 }
19290#endif //COLLECTIBLE_CLASS
19291
19292 if (contain_pointers (oo))
19293 {
19294 go_through_object_nostart (method_table(oo), oo, s, po,
19295 uint8_t* o = *po;
19296 mark_object (o THREAD_NUMBER_ARG);
19297 );
19298 }
19299 }
19300}
19301
19302size_t gc_heap::get_total_heap_size()
19303{
19304 size_t total_heap_size = 0;
19305
19306#ifdef MULTIPLE_HEAPS
19307 int hn = 0;
19308
19309 for (hn = 0; hn < gc_heap::n_heaps; hn++)
19310 {
19311 gc_heap* hp2 = gc_heap::g_heaps [hn];
19312 total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
19313 }
19314#else
19315 total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
19316#endif //MULTIPLE_HEAPS
19317
19318 return total_heap_size;
19319}
19320
19321size_t gc_heap::get_total_fragmentation()
19322{
19323 size_t total_fragmentation = 0;
19324
19325#ifdef MULTIPLE_HEAPS
19326 for (int i = 0; i < gc_heap::n_heaps; i++)
19327 {
19328 gc_heap* hp = gc_heap::g_heaps[i];
19329#else //MULTIPLE_HEAPS
19330 {
19331 gc_heap* hp = pGenGCHeap;
19332#endif //MULTIPLE_HEAPS
19333 for (int i = 0; i <= (max_generation + 1); i++)
19334 {
19335 generation* gen = hp->generation_of (i);
19336 total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen));
19337 }
19338 }
19339
19340 return total_fragmentation;
19341}
19342
19343size_t gc_heap::committed_size()
19344{
19345 generation* gen = generation_of (max_generation);
19346 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19347 size_t total_committed = 0;
19348
19349 while (1)
19350 {
19351 total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19352
19353 seg = heap_segment_next (seg);
19354 if (!seg)
19355 {
19356 if (gen != large_object_generation)
19357 {
19358 gen = generation_of (max_generation + 1);
19359 seg = generation_start_segment (gen);
19360 }
19361 else
19362 break;
19363 }
19364 }
19365
19366 return total_committed;
19367}
19368
19369size_t gc_heap::get_total_committed_size()
19370{
19371 size_t total_committed = 0;
19372
19373#ifdef MULTIPLE_HEAPS
19374 int hn = 0;
19375
19376 for (hn = 0; hn < gc_heap::n_heaps; hn++)
19377 {
19378 gc_heap* hp = gc_heap::g_heaps [hn];
19379 total_committed += hp->committed_size();
19380 }
19381#else
19382 total_committed = committed_size();
19383#endif //MULTIPLE_HEAPS
19384
19385 return total_committed;
19386}
19387
19388void gc_heap::get_memory_info (uint32_t* memory_load,
19389 uint64_t* available_physical,
19390 uint64_t* available_page_file)
19391{
19392 GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file);
19393}
19394
19395void fire_mark_event (int heap_num, int root_type, size_t bytes_marked)
19396{
19397 dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id", heap_num, root_type, bytes_marked));
19398 FIRE_EVENT(GCMarkWithType, heap_num, root_type, bytes_marked);
19399}
19400
19401//returns TRUE is an overflow happened.
19402BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
19403{
19404 size_t last_promoted_bytes = promoted_bytes (heap_number);
19405 BOOL overflow_p = FALSE;
19406recheck:
19407 if ((! (max_overflow_address == 0) ||
19408 ! (min_overflow_address == MAX_PTR)))
19409 {
19410 overflow_p = TRUE;
19411 // Try to grow the array.
19412 size_t new_size =
19413 max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
19414
19415 if ((new_size * sizeof(mark)) > 100*1024)
19416 {
19417 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19418
19419 new_size = min(new_max_size, new_size);
19420 }
19421
19422 if ((mark_stack_array_length < new_size) &&
19423 ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
19424 {
19425 mark* tmp = new (nothrow) mark [new_size];
19426 if (tmp)
19427 {
19428 delete mark_stack_array;
19429 mark_stack_array = tmp;
19430 mark_stack_array_length = new_size;
19431 }
19432 }
19433
19434 uint8_t* min_add = min_overflow_address;
19435 uint8_t* max_add = max_overflow_address;
19436 max_overflow_address = 0;
19437 min_overflow_address = MAX_PTR;
19438 process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
19439 goto recheck;
19440 }
19441
19442 size_t current_promoted_bytes = promoted_bytes (heap_number);
19443
19444 if (current_promoted_bytes != last_promoted_bytes)
19445 fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes));
19446 return overflow_p;
19447}
19448
19449void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
19450 uint8_t* min_add, uint8_t* max_add)
19451{
19452#ifdef MULTIPLE_HEAPS
19453 int thread = heap_number;
19454#endif //MULTIPLE_HEAPS
19455 BOOL full_p = (condemned_gen_number == max_generation);
19456
19457 dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19458#ifdef MULTIPLE_HEAPS
19459 for (int hi = 0; hi < n_heaps; hi++)
19460 {
19461 gc_heap* hp = g_heaps [(heap_number + hi) % n_heaps];
19462
19463#else
19464 {
19465 gc_heap* hp = 0;
19466
19467#endif //MULTIPLE_HEAPS
19468 BOOL small_object_segments = TRUE;
19469 int align_const = get_alignment_constant (small_object_segments);
19470 generation* gen = hp->generation_of (condemned_gen_number);
19471 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19472
19473 PREFIX_ASSUME(seg != NULL);
19474 uint8_t* o = max (heap_segment_mem (seg), min_add);
19475 while (1)
19476 {
19477 uint8_t* end = heap_segment_allocated (seg);
19478
19479 while ((o < end) && (o <= max_add))
19480 {
19481 assert ((min_add <= o) && (max_add >= o));
19482 dprintf (3, ("considering %Ix", (size_t)o));
19483 if (marked (o))
19484 {
19485 mark_through_object (o, TRUE THREAD_NUMBER_ARG);
19486 }
19487
19488 o = o + Align (size (o), align_const);
19489 }
19490
19491 if (( seg = heap_segment_next_in_range (seg)) == 0)
19492 {
19493 if (small_object_segments && full_p)
19494 {
19495 small_object_segments = FALSE;
19496 align_const = get_alignment_constant (small_object_segments);
19497 seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19498
19499 PREFIX_ASSUME(seg != NULL);
19500
19501 o = max (heap_segment_mem (seg), min_add);
19502 continue;
19503 }
19504 else
19505 {
19506 break;
19507 }
19508 }
19509 else
19510 {
19511 o = max (heap_segment_mem (seg), min_add);
19512 continue;
19513 }
19514 }
19515 }
19516}
19517
19518// Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
19519// reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
19520// promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
19521// promotion scan multiple times.
19522// This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
19523// also has the effect of processing any mark stack overflow.
19524
19525#ifdef MULTIPLE_HEAPS
19526// When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
19527// worker threads synchronized. The algorithms are sufficiently divergent that we have different
19528// implementations based on whether MULTIPLE_HEAPS is defined or not.
19529//
19530// Define some static variables used for synchronization in the method below. These should really be defined
19531// locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
19532//
19533// A note about the synchronization used within this method. Communication between the worker threads is
19534// achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
19535// false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
19536// protection of a join.
19537static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
19538static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
19539static VOLATILE(BOOL) s_fScanRequired;
19540void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19541{
19542 // Whenever we call this method there may have been preceding object promotions. So set
19543 // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19544 // based on the how the scanning proceeded).
19545 s_fUnscannedPromotions = TRUE;
19546
19547 // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
19548 // the state of this thread's portion of the dependent handle table. That's because promotions on other
19549 // threads could cause handle promotions to become necessary here. Even if there are definitely no more
19550 // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
19551 // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
19552 // as all the others or they'll get out of step).
19553 while (true)
19554 {
19555 // The various worker threads are all currently racing in this code. We need to work out if at least
19556 // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
19557 // dependent handle table when both of the following conditions apply:
19558 // 1) At least one (arbitrary) object might have been promoted since the last scan (because if this
19559 // object happens to correspond to a primary in one of our handles we might potentially have to
19560 // promote the associated secondary).
19561 // 2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
19562 //
19563 // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
19564 // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
19565 // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
19566 // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
19567 // follows below. Note that we can't read this outside of the join since on any iteration apart from
19568 // the first threads will be racing between reading this value and completing their previous
19569 // iteration's table scan.
19570 //
19571 // The second condition is tracked by the dependent handle code itself on a per worker thread basis
19572 // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
19573 // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
19574 // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
19575 // we're safely joined.
19576 if (GCScan::GcDhUnpromotedHandlesExist(sc))
19577 s_fUnpromotedHandles = TRUE;
19578
19579 // Synchronize all the threads so we can read our state variables safely. The shared variable
19580 // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
19581 // a single thread inside the join.
19582 gc_t_join.join(this, gc_join_scan_dependent_handles);
19583 if (gc_t_join.joined())
19584 {
19585 // We're synchronized so it's safe to read our shared state variables. We update another shared
19586 // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
19587 // the loop. We scan if there has been at least one object promotion since last time and at least
19588 // one thread has a dependent handle table with a potential handle promotion possible.
19589 s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
19590
19591 // Reset our shared state variables (ready to be set again on this scan or with a good initial
19592 // value for the next call if we're terminating the loop).
19593 s_fUnscannedPromotions = FALSE;
19594 s_fUnpromotedHandles = FALSE;
19595
19596 if (!s_fScanRequired)
19597 {
19598 // We're terminating the loop. Perform any last operations that require single threaded access.
19599 if (!initial_scan_p)
19600 {
19601 // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
19602 // load balance if some of the heaps have an abnormally large workload.
19603 uint8_t* all_heaps_max = 0;
19604 uint8_t* all_heaps_min = MAX_PTR;
19605 int i;
19606 for (i = 0; i < n_heaps; i++)
19607 {
19608 if (all_heaps_max < g_heaps[i]->max_overflow_address)
19609 all_heaps_max = g_heaps[i]->max_overflow_address;
19610 if (all_heaps_min > g_heaps[i]->min_overflow_address)
19611 all_heaps_min = g_heaps[i]->min_overflow_address;
19612 }
19613 for (i = 0; i < n_heaps; i++)
19614 {
19615 g_heaps[i]->max_overflow_address = all_heaps_max;
19616 g_heaps[i]->min_overflow_address = all_heaps_min;
19617 }
19618 }
19619 }
19620
19621 // Restart all the workers.
19622 dprintf(3, ("Starting all gc thread mark stack overflow processing"));
19623 gc_t_join.restart();
19624 }
19625
19626 // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19627 // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
19628 // global flag indicating that at least one object promotion may have occurred (the usual comment
19629 // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
19630 // exit the method since we unconditionally set this variable on method entry anyway).
19631 if (process_mark_overflow(condemned_gen_number))
19632 s_fUnscannedPromotions = TRUE;
19633
19634 // If we decided that no scan was required we can terminate the loop now.
19635 if (!s_fScanRequired)
19636 break;
19637
19638 // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
19639 // processed before we start scanning dependent handle tables (if overflows remain while we scan we
19640 // could miss noting the promotion of some primary objects).
19641 gc_t_join.join(this, gc_join_rescan_dependent_handles);
19642 if (gc_t_join.joined())
19643 {
19644 // Restart all the workers.
19645 dprintf(3, ("Starting all gc thread for dependent handle promotion"));
19646 gc_t_join.restart();
19647 }
19648
19649 // If the portion of the dependent handle table managed by this worker has handles that could still be
19650 // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
19651 // could require a rescan of handles on this or other workers.
19652 if (GCScan::GcDhUnpromotedHandlesExist(sc))
19653 if (GCScan::GcDhReScan(sc))
19654 s_fUnscannedPromotions = TRUE;
19655 }
19656}
19657#else //MULTIPLE_HEAPS
19658// Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
19659// threads synchronized.
19660void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19661{
19662 UNREFERENCED_PARAMETER(initial_scan_p);
19663
19664 // Whenever we call this method there may have been preceding object promotions. So set
19665 // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19666 // based on the how the scanning proceeded).
19667 bool fUnscannedPromotions = true;
19668
19669 // Loop until there are either no more dependent handles that can have their secondary promoted or we've
19670 // managed to perform a scan without promoting anything new.
19671 while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
19672 {
19673 // On each iteration of the loop start with the assumption that no further objects have been promoted.
19674 fUnscannedPromotions = false;
19675
19676 // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19677 // being visible. If there was an overflow (process_mark_overflow returned true) then additional
19678 // objects now appear to be promoted and we should set the flag.
19679 if (process_mark_overflow(condemned_gen_number))
19680 fUnscannedPromotions = true;
19681
19682 // Perform the scan and set the flag if any promotions resulted.
19683 if (GCScan::GcDhReScan(sc))
19684 fUnscannedPromotions = true;
19685 }
19686
19687 // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
19688 // scan any handles at all this is the processing of overflows that may have occurred prior to this method
19689 // invocation).
19690 process_mark_overflow(condemned_gen_number);
19691}
19692#endif //MULTIPLE_HEAPS
19693
19694void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
19695{
19696 assert (settings.concurrent == FALSE);
19697
19698 ScanContext sc;
19699 sc.thread_number = heap_number;
19700 sc.promotion = TRUE;
19701 sc.concurrent = FALSE;
19702
19703 dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
19704 BOOL full_p = (condemned_gen_number == max_generation);
19705
19706#ifdef TIME_GC
19707 unsigned start;
19708 unsigned finish;
19709 start = GetCycleCount32();
19710#endif //TIME_GC
19711
19712 int gen_to_init = condemned_gen_number;
19713 if (condemned_gen_number == max_generation)
19714 {
19715 gen_to_init = max_generation + 1;
19716 }
19717 for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
19718 {
19719 dynamic_data* dd = dynamic_data_of (gen_idx);
19720 dd_begin_data_size (dd) = generation_size (gen_idx) -
19721 dd_fragmentation (dd) -
19722 Align (size (generation_allocation_start (generation_of (gen_idx))));
19723 dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
19724 dd_survived_size (dd) = 0;
19725 dd_pinned_survived_size (dd) = 0;
19726 dd_artificial_pinned_survived_size (dd) = 0;
19727 dd_added_pinned_size (dd) = 0;
19728#ifdef SHORT_PLUGS
19729 dd_padding_size (dd) = 0;
19730#endif //SHORT_PLUGS
19731#if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
19732 dd_num_npinned_plugs (dd) = 0;
19733#endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
19734 }
19735
19736#ifdef FFIND_OBJECT
19737 if (gen0_must_clear_bricks > 0)
19738 gen0_must_clear_bricks--;
19739#endif //FFIND_OBJECT
19740
19741 size_t last_promoted_bytes = 0;
19742
19743 promoted_bytes (heap_number) = 0;
19744 reset_mark_stack();
19745
19746#ifdef SNOOP_STATS
19747 memset (&snoop_stat, 0, sizeof(snoop_stat));
19748 snoop_stat.heap_index = heap_number;
19749#endif //SNOOP_STATS
19750
19751#ifdef MH_SC_MARK
19752 if (full_p)
19753 {
19754 //initialize the mark stack
19755 for (int i = 0; i < max_snoop_level; i++)
19756 {
19757 ((uint8_t**)(mark_stack_array))[i] = 0;
19758 }
19759
19760 mark_stack_busy() = 1;
19761 }
19762#endif //MH_SC_MARK
19763
19764 static uint32_t num_sizedrefs = 0;
19765
19766#ifdef MH_SC_MARK
19767 static BOOL do_mark_steal_p = FALSE;
19768#endif //MH_SC_MARK
19769
19770#ifdef MULTIPLE_HEAPS
19771 gc_t_join.join(this, gc_join_begin_mark_phase);
19772 if (gc_t_join.joined())
19773 {
19774#endif //MULTIPLE_HEAPS
19775
19776 maxgen_size_inc_p = false;
19777
19778 num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
19779
19780#ifdef MULTIPLE_HEAPS
19781
19782#ifdef MH_SC_MARK
19783 if (full_p)
19784 {
19785 size_t total_heap_size = get_total_heap_size();
19786
19787 if (total_heap_size > (100 * 1024 * 1024))
19788 {
19789 do_mark_steal_p = TRUE;
19790 }
19791 else
19792 {
19793 do_mark_steal_p = FALSE;
19794 }
19795 }
19796 else
19797 {
19798 do_mark_steal_p = FALSE;
19799 }
19800#endif //MH_SC_MARK
19801
19802 gc_t_join.restart();
19803 }
19804#endif //MULTIPLE_HEAPS
19805
19806 {
19807
19808#ifdef MARK_LIST
19809 //set up the mark lists from g_mark_list
19810 assert (g_mark_list);
19811#ifdef MULTIPLE_HEAPS
19812 mark_list = &g_mark_list [heap_number*mark_list_size];
19813#else
19814 mark_list = g_mark_list;
19815#endif //MULTIPLE_HEAPS
19816 //dont use the mark list for full gc
19817 //because multiple segments are more complex to handle and the list
19818 //is likely to overflow
19819 if (condemned_gen_number != max_generation)
19820 mark_list_end = &mark_list [mark_list_size-1];
19821 else
19822 mark_list_end = &mark_list [0];
19823 mark_list_index = &mark_list [0];
19824#endif //MARK_LIST
19825
19826#ifndef MULTIPLE_HEAPS
19827 shigh = (uint8_t*) 0;
19828 slow = MAX_PTR;
19829#endif //MULTIPLE_HEAPS
19830
19831 //%type% category = quote (mark);
19832
19833 if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
19834 {
19835 GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19836 fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes));
19837 last_promoted_bytes = promoted_bytes (heap_number);
19838
19839#ifdef MULTIPLE_HEAPS
19840 gc_t_join.join(this, gc_join_scan_sizedref_done);
19841 if (gc_t_join.joined())
19842 {
19843 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
19844 gc_t_join.restart();
19845 }
19846#endif //MULTIPLE_HEAPS
19847 }
19848
19849 dprintf(3,("Marking Roots"));
19850
19851 GCScan::GcScanRoots(GCHeap::Promote,
19852 condemned_gen_number, max_generation,
19853 &sc);
19854
19855 fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes));
19856 last_promoted_bytes = promoted_bytes (heap_number);
19857
19858#ifdef BACKGROUND_GC
19859 if (recursive_gc_sync::background_running_p())
19860 {
19861 scan_background_roots (GCHeap::Promote, heap_number, &sc);
19862 }
19863#endif //BACKGROUND_GC
19864
19865#ifdef FEATURE_PREMORTEM_FINALIZATION
19866 dprintf(3, ("Marking finalization data"));
19867 finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
19868#endif // FEATURE_PREMORTEM_FINALIZATION
19869
19870 fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes));
19871 last_promoted_bytes = promoted_bytes (heap_number);
19872
19873// MTHTS
19874 {
19875
19876 dprintf(3,("Marking handle table"));
19877 GCScan::GcScanHandles(GCHeap::Promote,
19878 condemned_gen_number, max_generation,
19879 &sc);
19880 fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes));
19881 last_promoted_bytes = promoted_bytes (heap_number);
19882 }
19883
19884#ifdef TRACE_GC
19885 size_t promoted_before_cards = promoted_bytes (heap_number);
19886#endif //TRACE_GC
19887
19888 dprintf (3, ("before cards: %Id", promoted_before_cards));
19889 if (!full_p)
19890 {
19891#ifdef CARD_BUNDLE
19892#ifdef MULTIPLE_HEAPS
19893 if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
19894 {
19895#endif //MULTIPLE_HEAPS
19896
19897#ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
19898 // If we are manually managing card bundles, every write to the card table should already be
19899 // accounted for in the card bundle table so there's nothing to update here.
19900 update_card_table_bundle();
19901#endif
19902 if (card_bundles_enabled())
19903 {
19904 verify_card_bundles();
19905 }
19906
19907#ifdef MULTIPLE_HEAPS
19908 gc_t_join.r_restart();
19909 }
19910#endif //MULTIPLE_HEAPS
19911#endif //CARD_BUNDLE
19912
19913 card_fn mark_object_fn = &gc_heap::mark_object_simple;
19914#ifdef HEAP_ANALYZE
19915 heap_analyze_success = TRUE;
19916 if (heap_analyze_enabled)
19917 {
19918 internal_root_array_index = 0;
19919 current_obj = 0;
19920 current_obj_size = 0;
19921 mark_object_fn = &gc_heap::ha_mark_object_simple;
19922 }
19923#endif //HEAP_ANALYZE
19924
19925 dprintf(3,("Marking cross generation pointers"));
19926 mark_through_cards_for_segments (mark_object_fn, FALSE);
19927
19928 dprintf(3,("Marking cross generation pointers for large objects"));
19929 mark_through_cards_for_large_objects (mark_object_fn, FALSE);
19930
19931 dprintf (3, ("marked by cards: %Id",
19932 (promoted_bytes (heap_number) - promoted_before_cards)));
19933 fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes));
19934 last_promoted_bytes = promoted_bytes (heap_number);
19935 }
19936 }
19937
19938#ifdef MH_SC_MARK
19939 if (do_mark_steal_p)
19940 {
19941 mark_steal();
19942 }
19943#endif //MH_SC_MARK
19944
19945 // Dependent handles need to be scanned with a special algorithm (see the header comment on
19946 // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
19947 // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
19948 // but in a common case (where there are no dependent handles that are due to be collected) it allows us
19949 // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
19950 // iterations if required and will also perform processing of any mark stack overflow once the dependent
19951 // handle table has been fully promoted.
19952 GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
19953 scan_dependent_handles(condemned_gen_number, &sc, true);
19954
19955#ifdef MULTIPLE_HEAPS
19956 dprintf(3, ("Joining for short weak handle scan"));
19957 gc_t_join.join(this, gc_join_null_dead_short_weak);
19958 if (gc_t_join.joined())
19959#endif //MULTIPLE_HEAPS
19960 {
19961#ifdef HEAP_ANALYZE
19962 heap_analyze_enabled = FALSE;
19963 GCToEEInterface::AnalyzeSurvivorsFinished(condemned_gen_number);
19964#endif // HEAP_ANALYZE
19965 GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
19966
19967#ifdef MULTIPLE_HEAPS
19968 if (!full_p)
19969 {
19970 // we used r_join and need to reinitialize states for it here.
19971 gc_t_join.r_init();
19972 }
19973
19974 //start all threads on the roots.
19975 dprintf(3, ("Starting all gc thread for short weak handle scan"));
19976 gc_t_join.restart();
19977#endif //MULTIPLE_HEAPS
19978
19979 }
19980
19981 // null out the target of short weakref that were not promoted.
19982 GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
19983
19984// MTHTS: keep by single thread
19985#ifdef MULTIPLE_HEAPS
19986 dprintf(3, ("Joining for finalization"));
19987 gc_t_join.join(this, gc_join_scan_finalization);
19988 if (gc_t_join.joined())
19989#endif //MULTIPLE_HEAPS
19990
19991 {
19992#ifdef MULTIPLE_HEAPS
19993 //start all threads on the roots.
19994 dprintf(3, ("Starting all gc thread for Finalization"));
19995 gc_t_join.restart();
19996#endif //MULTIPLE_HEAPS
19997 }
19998
19999 //Handle finalization.
20000 size_t promoted_bytes_live = promoted_bytes (heap_number);
20001
20002#ifdef FEATURE_PREMORTEM_FINALIZATION
20003 dprintf (3, ("Finalize marking"));
20004 finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
20005
20006 GCToEEInterface::DiagWalkFReachableObjects(__this);
20007#endif // FEATURE_PREMORTEM_FINALIZATION
20008
20009 // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
20010 // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
20011 scan_dependent_handles(condemned_gen_number, &sc, false);
20012
20013#ifdef MULTIPLE_HEAPS
20014 dprintf(3, ("Joining for weak pointer deletion"));
20015 gc_t_join.join(this, gc_join_null_dead_long_weak);
20016 if (gc_t_join.joined())
20017 {
20018 //start all threads on the roots.
20019 dprintf(3, ("Starting all gc thread for weak pointer deletion"));
20020 gc_t_join.restart();
20021 }
20022#endif //MULTIPLE_HEAPS
20023
20024 // null out the target of long weakref that were not promoted.
20025 GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
20026
20027// MTHTS: keep by single thread
20028#ifdef MULTIPLE_HEAPS
20029#ifdef MARK_LIST
20030#ifdef PARALLEL_MARK_LIST_SORT
20031// unsigned long start = GetCycleCount32();
20032 sort_mark_list();
20033// printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
20034#endif //PARALLEL_MARK_LIST_SORT
20035#endif //MARK_LIST
20036
20037 dprintf (3, ("Joining for sync block cache entry scanning"));
20038 gc_t_join.join(this, gc_join_null_dead_syncblk);
20039 if (gc_t_join.joined())
20040#endif //MULTIPLE_HEAPS
20041 {
20042 // scan for deleted entries in the syncblk cache
20043 GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
20044
20045#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
20046 if (g_fEnableAppDomainMonitoring)
20047 {
20048 size_t promoted_all_heaps = 0;
20049#ifdef MULTIPLE_HEAPS
20050 for (int i = 0; i < n_heaps; i++)
20051 {
20052 promoted_all_heaps += promoted_bytes (i);
20053 }
20054#else
20055 promoted_all_heaps = promoted_bytes (heap_number);
20056#endif //MULTIPLE_HEAPS
20057 GCToEEInterface::RecordTotalSurvivedBytes(promoted_all_heaps);
20058 }
20059#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
20060
20061#ifdef MULTIPLE_HEAPS
20062
20063#ifdef MARK_LIST
20064#ifndef PARALLEL_MARK_LIST_SORT
20065 //compact g_mark_list and sort it.
20066 combine_mark_lists();
20067#endif //PARALLEL_MARK_LIST_SORT
20068#endif //MARK_LIST
20069
20070 //decide on promotion
20071 if (!settings.promotion)
20072 {
20073 size_t m = 0;
20074 for (int n = 0; n <= condemned_gen_number;n++)
20075 {
20076 m += (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.1);
20077 }
20078
20079 for (int i = 0; i < n_heaps; i++)
20080 {
20081 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
20082 max_generation));
20083 size_t older_gen_size = (dd_current_size (dd) +
20084 (dd_desired_allocation (dd) -
20085 dd_new_allocation (dd)));
20086
20087 if ((m > (older_gen_size)) ||
20088 (promoted_bytes (i) > m))
20089 {
20090 settings.promotion = TRUE;
20091 }
20092 }
20093 }
20094
20095#ifdef SNOOP_STATS
20096 if (do_mark_steal_p)
20097 {
20098 size_t objects_checked_count = 0;
20099 size_t zero_ref_count = 0;
20100 size_t objects_marked_count = 0;
20101 size_t check_level_count = 0;
20102 size_t busy_count = 0;
20103 size_t interlocked_count = 0;
20104 size_t partial_mark_parent_count = 0;
20105 size_t stolen_or_pm_count = 0;
20106 size_t stolen_entry_count = 0;
20107 size_t pm_not_ready_count = 0;
20108 size_t normal_count = 0;
20109 size_t stack_bottom_clear_count = 0;
20110
20111 for (int i = 0; i < n_heaps; i++)
20112 {
20113 gc_heap* hp = g_heaps[i];
20114 hp->print_snoop_stat();
20115 objects_checked_count += hp->snoop_stat.objects_checked_count;
20116 zero_ref_count += hp->snoop_stat.zero_ref_count;
20117 objects_marked_count += hp->snoop_stat.objects_marked_count;
20118 check_level_count += hp->snoop_stat.check_level_count;
20119 busy_count += hp->snoop_stat.busy_count;
20120 interlocked_count += hp->snoop_stat.interlocked_count;
20121 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
20122 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
20123 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
20124 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
20125 normal_count += hp->snoop_stat.normal_count;
20126 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
20127 }
20128
20129 fflush (stdout);
20130
20131 printf ("-------total stats-------\n");
20132 printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n",
20133 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
20134 printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
20135 objects_checked_count,
20136 zero_ref_count,
20137 objects_marked_count,
20138 check_level_count,
20139 busy_count,
20140 interlocked_count,
20141 partial_mark_parent_count,
20142 stolen_or_pm_count,
20143 stolen_entry_count,
20144 pm_not_ready_count,
20145 normal_count,
20146 stack_bottom_clear_count);
20147 }
20148#endif //SNOOP_STATS
20149
20150 //start all threads.
20151 dprintf(3, ("Starting all threads for end of mark phase"));
20152 gc_t_join.restart();
20153#else //MULTIPLE_HEAPS
20154
20155 //decide on promotion
20156 if (!settings.promotion)
20157 {
20158 size_t m = 0;
20159 for (int n = 0; n <= condemned_gen_number;n++)
20160 {
20161 m += (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06);
20162 }
20163 dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
20164 max_generation));
20165 size_t older_gen_size = (dd_current_size (dd) +
20166 (dd_desired_allocation (dd) -
20167 dd_new_allocation (dd)));
20168
20169 dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
20170 m, promoted_bytes (heap_number), older_gen_size));
20171
20172 if ((m > older_gen_size) ||
20173 (promoted_bytes (heap_number) > m))
20174 {
20175 settings.promotion = TRUE;
20176 }
20177 }
20178
20179#endif //MULTIPLE_HEAPS
20180 }
20181
20182#ifdef MULTIPLE_HEAPS
20183#ifdef MARK_LIST
20184#ifdef PARALLEL_MARK_LIST_SORT
20185// start = GetCycleCount32();
20186 merge_mark_lists();
20187// printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
20188#endif //PARALLEL_MARK_LIST_SORT
20189#endif //MARK_LIST
20190#endif //MULTIPLE_HEAPS
20191
20192#ifdef BACKGROUND_GC
20193 total_promoted_bytes = promoted_bytes (heap_number);
20194#endif //BACKGROUND_GC
20195
20196 promoted_bytes (heap_number) -= promoted_bytes_live;
20197
20198#ifdef TIME_GC
20199 finish = GetCycleCount32();
20200 mark_time = finish - start;
20201#endif //TIME_GC
20202
20203 dprintf(2,("---- End of mark phase ----"));
20204}
20205
20206inline
20207void gc_heap::pin_object (uint8_t* o, uint8_t** ppObject, uint8_t* low, uint8_t* high)
20208{
20209 dprintf (3, ("Pinning %Ix", (size_t)o));
20210 if ((o >= low) && (o < high))
20211 {
20212 dprintf(3,("^%Ix^", (size_t)o));
20213 set_pinned (o);
20214
20215#ifdef FEATURE_EVENT_TRACE
20216 if(EVENT_ENABLED(PinObjectAtGCTime))
20217 {
20218 fire_etw_pin_object_event(o, ppObject);
20219 }
20220#endif // FEATURE_EVENT_TRACE
20221
20222#if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
20223 num_pinned_objects++;
20224#endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
20225 }
20226}
20227
20228#if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
20229size_t gc_heap::get_total_pinned_objects()
20230{
20231#ifdef MULTIPLE_HEAPS
20232 size_t total_num_pinned_objects = 0;
20233 for (int i = 0; i < gc_heap::n_heaps; i++)
20234 {
20235 gc_heap* hp = gc_heap::g_heaps[i];
20236 total_num_pinned_objects += hp->num_pinned_objects;
20237 }
20238 return total_num_pinned_objects;
20239#else //MULTIPLE_HEAPS
20240 return num_pinned_objects;
20241#endif //MULTIPLE_HEAPS
20242}
20243#endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
20244
20245void gc_heap::reset_mark_stack ()
20246{
20247 reset_pinned_queue();
20248 max_overflow_address = 0;
20249 min_overflow_address = MAX_PTR;
20250}
20251
20252#ifdef FEATURE_STRUCTALIGN
20253//
20254// The word with left child, right child, and align info is laid out as follows:
20255//
20256// | upper short word | lower short word |
20257// |<------------> <----->|<------------> <----->|
20258// | left child info hi| right child info lo|
20259// x86: | 10 bits 6 bits| 10 bits 6 bits|
20260//
20261// where left/right child are signed values and concat(info hi, info lo) is unsigned.
20262//
20263// The "align info" encodes two numbers: the required alignment (a power of two)
20264// and the misalignment (the number of machine words the destination address needs
20265// to be adjusted by to provide alignment - so this number is always smaller than
20266// the required alignment). Thus, the two can be represented as the "logical or"
20267// of the two numbers. Note that the actual pad is computed from the misalignment
20268// by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
20269//
20270
20271// The number of bits in a brick.
20272#if defined (_TARGET_AMD64_)
20273#define brick_bits (12)
20274#else
20275#define brick_bits (11)
20276#endif //_TARGET_AMD64_
20277C_ASSERT(brick_size == (1 << brick_bits));
20278
20279// The number of bits needed to represent the offset to a child node.
20280// "brick_bits + 1" allows us to represent a signed offset within a brick.
20281#define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
20282
20283// The number of bits in each of the pad hi, pad lo fields.
20284#define pad_bits (sizeof(short) * 8 - child_bits)
20285
20286#define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
20287#define pad_mask ((1 << pad_bits) - 1)
20288#define pad_from_short(w) ((size_t)(w) & pad_mask)
20289#else // FEATURE_STRUCTALIGN
20290#define child_from_short(w) (w)
20291#endif // FEATURE_STRUCTALIGN
20292
20293inline
20294short node_left_child(uint8_t* node)
20295{
20296 return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20297}
20298
20299inline
20300void set_node_left_child(uint8_t* node, ptrdiff_t val)
20301{
20302 assert (val > -(ptrdiff_t)brick_size);
20303 assert (val < (ptrdiff_t)brick_size);
20304 assert (Aligned (val));
20305#ifdef FEATURE_STRUCTALIGN
20306 size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20307 ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20308#else // FEATURE_STRUCTALIGN
20309 ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
20310#endif // FEATURE_STRUCTALIGN
20311 assert (node_left_child (node) == val);
20312}
20313
20314inline
20315short node_right_child(uint8_t* node)
20316{
20317 return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20318}
20319
20320inline
20321void set_node_right_child(uint8_t* node, ptrdiff_t val)
20322{
20323 assert (val > -(ptrdiff_t)brick_size);
20324 assert (val < (ptrdiff_t)brick_size);
20325 assert (Aligned (val));
20326#ifdef FEATURE_STRUCTALIGN
20327 size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20328 ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20329#else // FEATURE_STRUCTALIGN
20330 ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
20331#endif // FEATURE_STRUCTALIGN
20332 assert (node_right_child (node) == val);
20333}
20334
20335#ifdef FEATURE_STRUCTALIGN
20336void node_aligninfo (uint8_t* node, int& requiredAlignment, ptrdiff_t& pad)
20337{
20338 // Extract the single-number aligninfo from the fields.
20339 short left = ((plug_and_pair*)node)[-1].m_pair.left;
20340 short right = ((plug_and_pair*)node)[-1].m_pair.right;
20341 ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
20342 ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
20343
20344 // Replicate the topmost bit into all lower bits.
20345 ptrdiff_t x = aligninfo;
20346 x |= x >> 8;
20347 x |= x >> 4;
20348 x |= x >> 2;
20349 x |= x >> 1;
20350
20351 // Clear all bits but the highest.
20352 requiredAlignment = (int)(x ^ (x >> 1));
20353 pad = aligninfo - requiredAlignment;
20354 pad += AdjustmentForMinPadSize(pad, requiredAlignment);
20355}
20356
20357inline
20358ptrdiff_t node_alignpad (uint8_t* node)
20359{
20360 int requiredAlignment;
20361 ptrdiff_t alignpad;
20362 node_aligninfo (node, requiredAlignment, alignpad);
20363 return alignpad;
20364}
20365
20366void clear_node_aligninfo (uint8_t* node)
20367{
20368 ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
20369 ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
20370}
20371
20372void set_node_aligninfo (uint8_t* node, int requiredAlignment, ptrdiff_t pad)
20373{
20374 // Encode the alignment requirement and alignment offset as a single number
20375 // as described above.
20376 ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
20377 assert (Aligned (aligninfo));
20378 ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
20379 assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
20380
20381 ptrdiff_t hi = aligninfo_shifted >> pad_bits;
20382 assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
20383 ((plug_and_pair*)node)[-1].m_pair.left |= hi;
20384
20385 ptrdiff_t lo = aligninfo_shifted & pad_mask;
20386 assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
20387 ((plug_and_pair*)node)[-1].m_pair.right |= lo;
20388
20389#ifdef _DEBUG
20390 int requiredAlignment2;
20391 ptrdiff_t pad2;
20392 node_aligninfo (node, requiredAlignment2, pad2);
20393 assert (requiredAlignment == requiredAlignment2);
20394 assert (pad == pad2);
20395#endif // _DEBUG
20396}
20397#endif // FEATURE_STRUCTALIGN
20398
20399inline
20400void loh_set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20401{
20402 ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
20403 *place = val;
20404}
20405
20406inline
20407ptrdiff_t loh_node_relocation_distance(uint8_t* node)
20408{
20409 return (((loh_obj_and_pad*)node)[-1].reloc);
20410}
20411
20412inline
20413ptrdiff_t node_relocation_distance (uint8_t* node)
20414{
20415 return (((plug_and_reloc*)(node))[-1].reloc & ~3);
20416}
20417
20418inline
20419void set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20420{
20421 assert (val == (val & ~3));
20422 ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
20423 //clear the left bit and the relocation field
20424 *place &= 1;
20425 // store the value
20426 *place |= val;
20427}
20428
20429#define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
20430
20431#define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
20432
20433#ifndef FEATURE_STRUCTALIGN
20434void set_node_realigned(uint8_t* node)
20435{
20436 ((plug_and_reloc*)(node))[-1].reloc |= 1;
20437}
20438
20439void clear_node_realigned(uint8_t* node)
20440{
20441#ifdef RESPECT_LARGE_ALIGNMENT
20442 ((plug_and_reloc*)(node))[-1].reloc &= ~1;
20443#else
20444 UNREFERENCED_PARAMETER(node);
20445#endif //RESPECT_LARGE_ALIGNMENT
20446}
20447#endif // FEATURE_STRUCTALIGN
20448
20449inline
20450size_t node_gap_size (uint8_t* node)
20451{
20452 return ((plug_and_gap *)node)[-1].gap;
20453}
20454
20455void set_gap_size (uint8_t* node, size_t size)
20456{
20457 assert (Aligned (size));
20458
20459 // clear the 2 uint32_t used by the node.
20460 ((plug_and_gap *)node)[-1].reloc = 0;
20461 ((plug_and_gap *)node)[-1].lr =0;
20462 ((plug_and_gap *)node)[-1].gap = size;
20463
20464 assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
20465
20466}
20467
20468uint8_t* gc_heap::insert_node (uint8_t* new_node, size_t sequence_number,
20469 uint8_t* tree, uint8_t* last_node)
20470{
20471 dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
20472 (size_t)new_node, brick_of(new_node),
20473 (size_t)tree, brick_of(tree),
20474 (size_t)last_node, brick_of(last_node),
20475 sequence_number));
20476 if (power_of_two_p (sequence_number))
20477 {
20478 set_node_left_child (new_node, (tree - new_node));
20479 dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
20480 tree = new_node;
20481 }
20482 else
20483 {
20484 if (oddp (sequence_number))
20485 {
20486 set_node_right_child (last_node, (new_node - last_node));
20487 dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
20488 }
20489 else
20490 {
20491 uint8_t* earlier_node = tree;
20492 size_t imax = logcount(sequence_number) - 2;
20493 for (size_t i = 0; i != imax; i++)
20494 {
20495 earlier_node = earlier_node + node_right_child (earlier_node);
20496 }
20497 int tmp_offset = node_right_child (earlier_node);
20498 assert (tmp_offset); // should never be empty
20499 set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
20500 set_node_right_child (earlier_node, (new_node - earlier_node));
20501
20502 dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix",
20503 new_node, ((earlier_node + tmp_offset ) - new_node),
20504 earlier_node, (new_node - earlier_node)));
20505 }
20506 }
20507 return tree;
20508}
20509
20510size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick,
20511 uint8_t* x, uint8_t* plug_end)
20512{
20513 dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
20514 tree, current_brick, x, plug_end));
20515
20516 if (tree != NULL)
20517 {
20518 dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix",
20519 current_brick, (size_t)(tree - brick_address (current_brick)), tree));
20520 set_brick (current_brick, (tree - brick_address (current_brick)));
20521 }
20522 else
20523 {
20524 dprintf (3, ("b- %Ix->-1", current_brick));
20525 set_brick (current_brick, -1);
20526 }
20527 size_t b = 1 + current_brick;
20528 ptrdiff_t offset = 0;
20529 size_t last_br = brick_of (plug_end-1);
20530 current_brick = brick_of (x-1);
20531 dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
20532 while (b <= current_brick)
20533 {
20534 if (b <= last_br)
20535 {
20536 set_brick (b, --offset);
20537 }
20538 else
20539 {
20540 set_brick (b,-1);
20541 }
20542 b++;
20543 }
20544 return brick_of (x);
20545}
20546
20547void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate)
20548{
20549#ifdef BIT64
20550 // We should never demote big plugs to gen0.
20551 if (gen == youngest_generation)
20552 {
20553 heap_segment* seg = ephemeral_heap_segment;
20554 size_t mark_stack_large_bos = mark_stack_bos;
20555 size_t large_plug_pos = 0;
20556 while (mark_stack_large_bos < mark_stack_tos)
20557 {
20558 if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
20559 {
20560 while (mark_stack_bos <= mark_stack_large_bos)
20561 {
20562 size_t entry = deque_pinned_plug();
20563 size_t len = pinned_len (pinned_plug_of (entry));
20564 uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20565 if (len > demotion_plug_len_th)
20566 {
20567 dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
20568 }
20569 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
20570 assert(mark_stack_array[entry].len == 0 ||
20571 mark_stack_array[entry].len >= Align(min_obj_size));
20572 generation_allocation_pointer (consing_gen) = plug + len;
20573 generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
20574 set_allocator_next_pin (consing_gen);
20575 }
20576 }
20577
20578 mark_stack_large_bos++;
20579 }
20580 }
20581#endif // BIT64
20582
20583 generation_plan_allocation_start (gen) =
20584 allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
20585 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20586 size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20587 if (next_plug_to_allocate)
20588 {
20589 size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
20590 if (allocation_left > dist_to_next_plug)
20591 {
20592 allocation_left = dist_to_next_plug;
20593 }
20594 }
20595 if (allocation_left < Align (min_obj_size))
20596 {
20597 generation_plan_allocation_start_size (gen) += allocation_left;
20598 generation_allocation_pointer (consing_gen) += allocation_left;
20599 }
20600
20601 dprintf (1, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)", gen->gen_num,
20602 generation_plan_allocation_start (gen),
20603 generation_plan_allocation_start_size (gen),
20604 generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen),
20605 next_plug_to_allocate));
20606}
20607
20608void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
20609{
20610 BOOL adjacentp = FALSE;
20611
20612 generation_plan_allocation_start (gen) =
20613 allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0,
20614#ifdef SHORT_PLUGS
20615 FALSE, NULL,
20616#endif //SHORT_PLUGS
20617 FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
20618
20619 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20620 size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20621 if ((allocation_left < Align (min_obj_size)) &&
20622 (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
20623 {
20624 generation_plan_allocation_start_size (gen) += allocation_left;
20625 generation_allocation_pointer (consing_gen) += allocation_left;
20626 }
20627
20628 dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num,
20629 generation_plan_allocation_start (consing_gen),
20630 generation_allocation_pointer (consing_gen),
20631 generation_allocation_limit (consing_gen)));
20632}
20633
20634void gc_heap::plan_generation_starts (generation*& consing_gen)
20635{
20636 //make sure that every generation has a planned allocation start
20637 int gen_number = settings.condemned_generation;
20638 while (gen_number >= 0)
20639 {
20640 if (gen_number < max_generation)
20641 {
20642 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20643 }
20644 generation* gen = generation_of (gen_number);
20645 if (0 == generation_plan_allocation_start (gen))
20646 {
20647 plan_generation_start (gen, consing_gen, 0);
20648 assert (generation_plan_allocation_start (gen));
20649 }
20650 gen_number--;
20651 }
20652 // now we know the planned allocation size
20653 heap_segment_plan_allocated (ephemeral_heap_segment) =
20654 generation_allocation_pointer (consing_gen);
20655}
20656
20657void gc_heap::advance_pins_for_demotion (generation* gen)
20658{
20659 uint8_t* original_youngest_start = generation_allocation_start (youngest_generation);
20660 heap_segment* seg = ephemeral_heap_segment;
20661
20662 if ((!(pinned_plug_que_empty_p())))
20663 {
20664 size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
20665 size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
20666 size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
20667 float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
20668 float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
20669 if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
20670 {
20671 while (!pinned_plug_que_empty_p() &&
20672 (pinned_plug (oldest_pin()) < original_youngest_start))
20673 {
20674 size_t entry = deque_pinned_plug();
20675 size_t len = pinned_len (pinned_plug_of (entry));
20676 uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20677 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
20678 assert(mark_stack_array[entry].len == 0 ||
20679 mark_stack_array[entry].len >= Align(min_obj_size));
20680 generation_allocation_pointer (gen) = plug + len;
20681 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20682 set_allocator_next_pin (gen);
20683
20684 //Add the size of the pinned plug to the right pinned allocations
20685 //find out which gen this pinned plug came from
20686 int frgn = object_gennum (plug);
20687 if ((frgn != (int)max_generation) && settings.promotion)
20688 {
20689 int togn = object_gennum_plan (plug);
20690 generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20691 if (frgn < togn)
20692 {
20693 generation_pinned_allocation_compact_size (generation_of (togn)) += len;
20694 }
20695 }
20696
20697 dprintf (2, ("skipping gap %d, pin %Ix (%Id)",
20698 pinned_len (pinned_plug_of (entry)), plug, len));
20699 }
20700 }
20701 dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d",
20702 gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
20703 }
20704}
20705
20706void gc_heap::process_ephemeral_boundaries (uint8_t* x,
20707 int& active_new_gen_number,
20708 int& active_old_gen_number,
20709 generation*& consing_gen,
20710 BOOL& allocate_in_condemned)
20711{
20712retry:
20713 if ((active_old_gen_number > 0) &&
20714 (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
20715 {
20716 dprintf (1, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
20717
20718 if (!pinned_plug_que_empty_p())
20719 {
20720 dprintf (1, ("oldest pin: %Ix(%Id)",
20721 pinned_plug (oldest_pin()),
20722 (x - pinned_plug (oldest_pin()))));
20723 }
20724
20725 if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
20726 {
20727 active_new_gen_number--;
20728 }
20729
20730 active_old_gen_number--;
20731 assert ((!settings.promotion) || (active_new_gen_number>0));
20732
20733 if (active_new_gen_number == (max_generation - 1))
20734 {
20735#ifdef FREE_USAGE_STATS
20736 if (settings.condemned_generation == max_generation)
20737 {
20738 // We need to do this before we skip the rest of the pinned plugs.
20739 generation* gen_2 = generation_of (max_generation);
20740 generation* gen_1 = generation_of (max_generation - 1);
20741
20742 size_t total_num_pinned_free_spaces_left = 0;
20743
20744 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
20745 for (int j = 0; j < NUM_GEN_POWER2; j++)
20746 {
20747 dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)",
20748 heap_number,
20749 settings.gc_index,
20750 (j + 10),
20751 gen_2->gen_current_pinned_free_spaces[j],
20752 gen_2->gen_plugs[j], gen_1->gen_plugs[j],
20753 (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
20754
20755 total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
20756 }
20757
20758 float pinned_free_list_efficiency = 0;
20759 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
20760 if (total_pinned_free_space != 0)
20761 {
20762 pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
20763 }
20764
20765 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
20766 heap_number,
20767 generation_allocated_in_pinned_free (gen_2),
20768 total_pinned_free_space,
20769 (int)(pinned_free_list_efficiency * 100),
20770 generation_pinned_free_obj_space (gen_2),
20771 total_num_pinned_free_spaces_left));
20772 }
20773#endif //FREE_USAGE_STATS
20774
20775 //Go past all of the pinned plugs for this generation.
20776 while (!pinned_plug_que_empty_p() &&
20777 (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
20778 {
20779 size_t entry = deque_pinned_plug();
20780 mark* m = pinned_plug_of (entry);
20781 uint8_t* plug = pinned_plug (m);
20782 size_t len = pinned_len (m);
20783 // detect pinned block in different segment (later) than
20784 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
20785 // adjust the allocation segment along the way (at the end it will
20786 // be the ephemeral segment.
20787 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
20788
20789 PREFIX_ASSUME(nseg != NULL);
20790
20791 while (!((plug >= generation_allocation_pointer (consing_gen))&&
20792 (plug < heap_segment_allocated (nseg))))
20793 {
20794 //adjust the end of the segment to be the end of the plug
20795 assert (generation_allocation_pointer (consing_gen)>=
20796 heap_segment_mem (nseg));
20797 assert (generation_allocation_pointer (consing_gen)<=
20798 heap_segment_committed (nseg));
20799
20800 heap_segment_plan_allocated (nseg) =
20801 generation_allocation_pointer (consing_gen);
20802 //switch allocation segment
20803 nseg = heap_segment_next_rw (nseg);
20804 generation_allocation_segment (consing_gen) = nseg;
20805 //reset the allocation pointer and limits
20806 generation_allocation_pointer (consing_gen) =
20807 heap_segment_mem (nseg);
20808 }
20809 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
20810 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
20811 generation_allocation_pointer (consing_gen) = plug + len;
20812 generation_allocation_limit (consing_gen) =
20813 generation_allocation_pointer (consing_gen);
20814 }
20815 allocate_in_condemned = TRUE;
20816 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20817 }
20818
20819 if (active_new_gen_number != max_generation)
20820 {
20821 if (active_new_gen_number == (max_generation - 1))
20822 {
20823 maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
20824 if (!demote_gen1_p)
20825 advance_pins_for_demotion (consing_gen);
20826 }
20827
20828 plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
20829
20830 dprintf (1, ("process eph: allocated gen%d start at %Ix",
20831 active_new_gen_number,
20832 generation_plan_allocation_start (generation_of (active_new_gen_number))));
20833
20834 if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
20835 {
20836 uint8_t* pplug = pinned_plug (oldest_pin());
20837 if (object_gennum (pplug) > 0)
20838 {
20839 demotion_low = pplug;
20840 dprintf (3, ("process eph: dlow->%Ix", demotion_low));
20841 }
20842 }
20843
20844 assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
20845 }
20846
20847 goto retry;
20848 }
20849}
20850
20851inline
20852void gc_heap::seg_clear_mark_bits (heap_segment* seg)
20853{
20854 uint8_t* o = heap_segment_mem (seg);
20855 while (o < heap_segment_allocated (seg))
20856 {
20857 if (marked (o))
20858 {
20859 clear_marked (o);
20860 }
20861 o = o + Align (size (o));
20862 }
20863}
20864
20865#ifdef FEATURE_BASICFREEZE
20866void gc_heap::sweep_ro_segments (heap_segment* start_seg)
20867{
20868 //go through all of the segment in range and reset the mark bit
20869 //TODO works only on small object segments
20870
20871 heap_segment* seg = start_seg;
20872
20873 while (seg)
20874 {
20875 if (heap_segment_read_only_p (seg) &&
20876 heap_segment_in_range_p (seg))
20877 {
20878#ifdef BACKGROUND_GC
20879 if (settings.concurrent)
20880 {
20881 seg_clear_mark_array_bits_soh (seg);
20882 }
20883 else
20884 {
20885 seg_clear_mark_bits (seg);
20886 }
20887#else //BACKGROUND_GC
20888
20889#ifdef MARK_ARRAY
20890 if(gc_can_use_concurrent)
20891 {
20892 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
20893 min (heap_segment_allocated (seg), highest_address),
20894 FALSE); // read_only segments need the mark clear
20895 }
20896#else //MARK_ARRAY
20897 seg_clear_mark_bits (seg);
20898#endif //MARK_ARRAY
20899
20900#endif //BACKGROUND_GC
20901 }
20902 seg = heap_segment_next (seg);
20903 }
20904}
20905#endif // FEATURE_BASICFREEZE
20906
20907#ifdef FEATURE_LOH_COMPACTION
20908inline
20909BOOL gc_heap::loh_pinned_plug_que_empty_p()
20910{
20911 return (loh_pinned_queue_bos == loh_pinned_queue_tos);
20912}
20913
20914void gc_heap::loh_set_allocator_next_pin()
20915{
20916 if (!(loh_pinned_plug_que_empty_p()))
20917 {
20918 mark* oldest_entry = loh_oldest_pin();
20919 uint8_t* plug = pinned_plug (oldest_entry);
20920 generation* gen = large_object_generation;
20921 if ((plug >= generation_allocation_pointer (gen)) &&
20922 (plug < generation_allocation_limit (gen)))
20923 {
20924 generation_allocation_limit (gen) = pinned_plug (oldest_entry);
20925 }
20926 else
20927 assert (!((plug < generation_allocation_pointer (gen)) &&
20928 (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
20929 }
20930}
20931
20932size_t gc_heap::loh_deque_pinned_plug ()
20933{
20934 size_t m = loh_pinned_queue_bos;
20935 loh_pinned_queue_bos++;
20936 return m;
20937}
20938
20939inline
20940mark* gc_heap::loh_pinned_plug_of (size_t bos)
20941{
20942 return &loh_pinned_queue[bos];
20943}
20944
20945inline
20946mark* gc_heap::loh_oldest_pin()
20947{
20948 return loh_pinned_plug_of (loh_pinned_queue_bos);
20949}
20950
20951// If we can't grow the queue, then don't compact.
20952BOOL gc_heap::loh_enque_pinned_plug (uint8_t* plug, size_t len)
20953{
20954 assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
20955
20956 if (loh_pinned_queue_length <= loh_pinned_queue_tos)
20957 {
20958 if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
20959 {
20960 return FALSE;
20961 }
20962 }
20963 dprintf (3, (" P: %Ix(%Id)", plug, len));
20964 mark& m = loh_pinned_queue[loh_pinned_queue_tos];
20965 m.first = plug;
20966 m.len = len;
20967 loh_pinned_queue_tos++;
20968 loh_set_allocator_next_pin();
20969 return TRUE;
20970}
20971
20972inline
20973BOOL gc_heap::loh_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit)
20974{
20975 dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)",
20976 size,
20977 (2* AlignQword (loh_padding_obj_size) + size),
20978 alloc_pointer,
20979 alloc_limit,
20980 (alloc_limit - alloc_pointer)));
20981
20982 return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) + size) <= alloc_limit);
20983}
20984
20985uint8_t* gc_heap::loh_allocate_in_condemned (uint8_t* old_loc, size_t size)
20986{
20987 UNREFERENCED_PARAMETER(old_loc);
20988
20989 generation* gen = large_object_generation;
20990 dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id",
20991 generation_allocation_pointer (gen),
20992 generation_allocation_limit (gen),
20993 size));
20994
20995retry:
20996 {
20997 heap_segment* seg = generation_allocation_segment (gen);
20998 if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
20999 {
21000 if ((!(loh_pinned_plug_que_empty_p()) &&
21001 (generation_allocation_limit (gen) ==
21002 pinned_plug (loh_oldest_pin()))))
21003 {
21004 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21005 size_t len = pinned_len (m);
21006 uint8_t* plug = pinned_plug (m);
21007 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21008 pinned_len (m) = plug - generation_allocation_pointer (gen);
21009 generation_allocation_pointer (gen) = plug + len;
21010
21011 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21012 loh_set_allocator_next_pin();
21013 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)",
21014 generation_allocation_pointer (gen),
21015 generation_allocation_limit (gen),
21016 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21017
21018 goto retry;
21019 }
21020
21021 if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
21022 {
21023 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21024 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
21025 }
21026 else
21027 {
21028 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
21029 {
21030 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
21031 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21032 dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
21033 }
21034 else
21035 {
21036 if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
21037 (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
21038 {
21039 dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
21040 (generation_allocation_pointer (gen) + size)));
21041
21042 heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
21043 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21044
21045 dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)",
21046 generation_allocation_pointer (gen),
21047 generation_allocation_limit (gen),
21048 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21049 }
21050 else
21051 {
21052 heap_segment* next_seg = heap_segment_next (seg);
21053 assert (generation_allocation_pointer (gen)>=
21054 heap_segment_mem (seg));
21055 // Verify that all pinned plugs for this segment are consumed
21056 if (!loh_pinned_plug_que_empty_p() &&
21057 ((pinned_plug (loh_oldest_pin()) <
21058 heap_segment_allocated (seg)) &&
21059 (pinned_plug (loh_oldest_pin()) >=
21060 generation_allocation_pointer (gen))))
21061 {
21062 LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
21063 pinned_plug (loh_oldest_pin())));
21064 dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
21065 FATAL_GC_ERROR();
21066 }
21067 assert (generation_allocation_pointer (gen)>=
21068 heap_segment_mem (seg));
21069 assert (generation_allocation_pointer (gen)<=
21070 heap_segment_committed (seg));
21071 heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
21072
21073 if (next_seg)
21074 {
21075 // for LOH do we want to try starting from the first LOH every time though?
21076 generation_allocation_segment (gen) = next_seg;
21077 generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
21078 generation_allocation_limit (gen) = generation_allocation_pointer (gen);
21079
21080 dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)",
21081 generation_allocation_pointer (gen),
21082 generation_allocation_limit (gen),
21083 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21084 }
21085 else
21086 {
21087 dprintf (1, ("We ran out of space compacting, shouldn't happen"));
21088 FATAL_GC_ERROR();
21089 }
21090 }
21091 }
21092 }
21093 loh_set_allocator_next_pin();
21094
21095 dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)",
21096 generation_allocation_pointer (gen),
21097 generation_allocation_limit (gen),
21098 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21099
21100 goto retry;
21101 }
21102 }
21103
21104 {
21105 assert (generation_allocation_pointer (gen)>=
21106 heap_segment_mem (generation_allocation_segment (gen)));
21107 uint8_t* result = generation_allocation_pointer (gen);
21108 size_t loh_pad = AlignQword (loh_padding_obj_size);
21109
21110 generation_allocation_pointer (gen) += size + loh_pad;
21111 assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
21112
21113 dprintf (1235, ("p: %Ix, l: %Ix (%Id)",
21114 generation_allocation_pointer (gen),
21115 generation_allocation_limit (gen),
21116 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21117
21118 assert (result + loh_pad);
21119 return result + loh_pad;
21120 }
21121}
21122
21123BOOL gc_heap::should_compact_loh()
21124{
21125 return (loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
21126}
21127
21128inline
21129void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
21130{
21131 if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
21132 {
21133 if (all_heaps_compacted_p)
21134 {
21135 // If the compaction mode says to compact once and we are going to compact LOH,
21136 // we need to revert it back to no compaction.
21137 loh_compaction_mode = loh_compaction_default;
21138 }
21139 }
21140}
21141
21142BOOL gc_heap::plan_loh()
21143{
21144 if (!loh_pinned_queue)
21145 {
21146 loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
21147 if (!loh_pinned_queue)
21148 {
21149 dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction",
21150 LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
21151 return FALSE;
21152 }
21153
21154 loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
21155 }
21156
21157 if (heap_number == 0)
21158 loh_pinned_queue_decay = LOH_PIN_DECAY;
21159
21160 loh_pinned_queue_tos = 0;
21161 loh_pinned_queue_bos = 0;
21162
21163 generation* gen = large_object_generation;
21164 heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21165 PREFIX_ASSUME(start_seg != NULL);
21166 heap_segment* seg = start_seg;
21167 uint8_t* o = generation_allocation_start (gen);
21168
21169 dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n",
21170 generation_size (max_generation + 1),
21171 generation_free_list_space (gen),
21172 generation_free_obj_space (gen)));
21173
21174 while (seg)
21175 {
21176 heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
21177 seg = heap_segment_next (seg);
21178 }
21179
21180 seg = start_seg;
21181
21182 //Skip the generation gap object
21183 o = o + AlignQword (size (o));
21184 // We don't need to ever realloc gen3 start so don't touch it.
21185 heap_segment_plan_allocated (seg) = o;
21186 generation_allocation_pointer (gen) = o;
21187 generation_allocation_limit (gen) = generation_allocation_pointer (gen);
21188 generation_allocation_segment (gen) = start_seg;
21189
21190 uint8_t* free_space_start = o;
21191 uint8_t* free_space_end = o;
21192 uint8_t* new_address = 0;
21193
21194 while (1)
21195 {
21196 if (o >= heap_segment_allocated (seg))
21197 {
21198 seg = heap_segment_next (seg);
21199 if (seg == 0)
21200 {
21201 break;
21202 }
21203
21204 o = heap_segment_mem (seg);
21205 }
21206
21207 if (marked (o))
21208 {
21209 free_space_end = o;
21210 size_t size = AlignQword (size (o));
21211 dprintf (1235, ("%Ix(%Id) M", o, size));
21212
21213 if (pinned (o))
21214 {
21215 // We don't clear the pinned bit yet so we can check in
21216 // compact phase how big a free object we should allocate
21217 // in front of the pinned object. We use the reloc address
21218 // field to store this.
21219 if (!loh_enque_pinned_plug (o, size))
21220 {
21221 return FALSE;
21222 }
21223 new_address = o;
21224 }
21225 else
21226 {
21227 new_address = loh_allocate_in_condemned (o, size);
21228 }
21229
21230 loh_set_node_relocation_distance (o, (new_address - o));
21231 dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
21232
21233 o = o + size;
21234 free_space_start = o;
21235 if (o < heap_segment_allocated (seg))
21236 {
21237 assert (!marked (o));
21238 }
21239 }
21240 else
21241 {
21242 while (o < heap_segment_allocated (seg) && !marked (o))
21243 {
21244 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_gc_pFreeObjectMethodTable) ? 1 : 0)));
21245 o = o + AlignQword (size (o));
21246 }
21247 }
21248 }
21249
21250 while (!loh_pinned_plug_que_empty_p())
21251 {
21252 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21253 size_t len = pinned_len (m);
21254 uint8_t* plug = pinned_plug (m);
21255
21256 // detect pinned block in different segment (later) than
21257 // allocation segment
21258 heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
21259
21260 while ((plug < generation_allocation_pointer (gen)) ||
21261 (plug >= heap_segment_allocated (nseg)))
21262 {
21263 assert ((plug < heap_segment_mem (nseg)) ||
21264 (plug > heap_segment_reserved (nseg)));
21265 //adjust the end of the segment to be the end of the plug
21266 assert (generation_allocation_pointer (gen)>=
21267 heap_segment_mem (nseg));
21268 assert (generation_allocation_pointer (gen)<=
21269 heap_segment_committed (nseg));
21270
21271 heap_segment_plan_allocated (nseg) =
21272 generation_allocation_pointer (gen);
21273 //switch allocation segment
21274 nseg = heap_segment_next_rw (nseg);
21275 generation_allocation_segment (gen) = nseg;
21276 //reset the allocation pointer and limits
21277 generation_allocation_pointer (gen) =
21278 heap_segment_mem (nseg);
21279 }
21280
21281 dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21282 pinned_len (m) = plug - generation_allocation_pointer (gen);
21283 generation_allocation_pointer (gen) = plug + len;
21284 }
21285
21286 heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
21287 generation_allocation_pointer (gen) = 0;
21288 generation_allocation_limit (gen) = 0;
21289
21290 return TRUE;
21291}
21292
21293void gc_heap::compact_loh()
21294{
21295 assert (should_compact_loh());
21296
21297 generation* gen = large_object_generation;
21298 heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21299 PREFIX_ASSUME(start_seg != NULL);
21300 heap_segment* seg = start_seg;
21301 heap_segment* prev_seg = 0;
21302 uint8_t* o = generation_allocation_start (gen);
21303
21304 //Skip the generation gap object
21305 o = o + AlignQword (size (o));
21306 // We don't need to ever realloc gen3 start so don't touch it.
21307 uint8_t* free_space_start = o;
21308 uint8_t* free_space_end = o;
21309 generation_allocator (gen)->clear();
21310 generation_free_list_space (gen) = 0;
21311 generation_free_obj_space (gen) = 0;
21312
21313 loh_pinned_queue_bos = 0;
21314
21315 while (1)
21316 {
21317 if (o >= heap_segment_allocated (seg))
21318 {
21319 heap_segment* next_seg = heap_segment_next (seg);
21320
21321 if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
21322 (seg != start_seg) && !heap_segment_read_only_p (seg))
21323 {
21324 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
21325 assert (prev_seg);
21326 heap_segment_next (prev_seg) = next_seg;
21327 heap_segment_next (seg) = freeable_large_heap_segment;
21328 freeable_large_heap_segment = seg;
21329 }
21330 else
21331 {
21332 if (!heap_segment_read_only_p (seg))
21333 {
21334 // We grew the segment to accommodate allocations.
21335 if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
21336 {
21337 if ((heap_segment_plan_allocated (seg) - plug_skew) > heap_segment_used (seg))
21338 {
21339 heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
21340 }
21341 }
21342
21343 heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
21344 dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
21345 decommit_heap_segment_pages (seg, 0);
21346 dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
21347 seg,
21348 heap_segment_allocated (seg),
21349 heap_segment_used (seg),
21350 heap_segment_committed (seg)));
21351 //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
21352 dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
21353 }
21354 prev_seg = seg;
21355 }
21356
21357 seg = next_seg;
21358 if (seg == 0)
21359 break;
21360 else
21361 {
21362 o = heap_segment_mem (seg);
21363 }
21364 }
21365
21366 if (marked (o))
21367 {
21368 free_space_end = o;
21369 size_t size = AlignQword (size (o));
21370
21371 size_t loh_pad;
21372 uint8_t* reloc = o;
21373 clear_marked (o);
21374
21375 if (pinned (o))
21376 {
21377 // We are relying on the fact the pinned objects are always looked at in the same order
21378 // in plan phase and in compact phase.
21379 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21380 uint8_t* plug = pinned_plug (m);
21381 assert (plug == o);
21382
21383 loh_pad = pinned_len (m);
21384 clear_pinned (o);
21385 }
21386 else
21387 {
21388 loh_pad = AlignQword (loh_padding_obj_size);
21389
21390 reloc += loh_node_relocation_distance (o);
21391 gcmemcopy (reloc, o, size, TRUE);
21392 }
21393
21394 thread_gap ((reloc - loh_pad), loh_pad, gen);
21395
21396 o = o + size;
21397 free_space_start = o;
21398 if (o < heap_segment_allocated (seg))
21399 {
21400 assert (!marked (o));
21401 }
21402 }
21403 else
21404 {
21405 while (o < heap_segment_allocated (seg) && !marked (o))
21406 {
21407 o = o + AlignQword (size (o));
21408 }
21409 }
21410 }
21411
21412 assert (loh_pinned_plug_que_empty_p());
21413
21414 dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n",
21415 generation_size (max_generation + 1),
21416 generation_free_list_space (gen),
21417 generation_free_obj_space (gen)));
21418}
21419
21420void gc_heap::relocate_in_loh_compact()
21421{
21422 generation* gen = large_object_generation;
21423 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
21424 uint8_t* o = generation_allocation_start (gen);
21425
21426 //Skip the generation gap object
21427 o = o + AlignQword (size (o));
21428
21429 relocate_args args;
21430 args.low = gc_low;
21431 args.high = gc_high;
21432 args.last_plug = 0;
21433
21434 while (1)
21435 {
21436 if (o >= heap_segment_allocated (seg))
21437 {
21438 seg = heap_segment_next (seg);
21439 if (seg == 0)
21440 {
21441 break;
21442 }
21443
21444 o = heap_segment_mem (seg);
21445 }
21446
21447 if (marked (o))
21448 {
21449 size_t size = AlignQword (size (o));
21450
21451 check_class_object_demotion (o);
21452 if (contain_pointers (o))
21453 {
21454 go_through_object_nostart (method_table (o), o, size(o), pval,
21455 {
21456 reloc_survivor_helper (pval);
21457 });
21458 }
21459
21460 o = o + size;
21461 if (o < heap_segment_allocated (seg))
21462 {
21463 assert (!marked (o));
21464 }
21465 }
21466 else
21467 {
21468 while (o < heap_segment_allocated (seg) && !marked (o))
21469 {
21470 o = o + AlignQword (size (o));
21471 }
21472 }
21473 }
21474
21475 dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n",
21476 generation_size (max_generation + 1),
21477 generation_free_list_space (gen),
21478 generation_free_obj_space (gen)));
21479}
21480
21481void gc_heap::walk_relocation_for_loh (void* profiling_context, record_surv_fn fn)
21482{
21483 generation* gen = large_object_generation;
21484 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
21485 uint8_t* o = generation_allocation_start (gen);
21486
21487 //Skip the generation gap object
21488 o = o + AlignQword (size (o));
21489
21490 while (1)
21491 {
21492 if (o >= heap_segment_allocated (seg))
21493 {
21494 seg = heap_segment_next (seg);
21495 if (seg == 0)
21496 {
21497 break;
21498 }
21499
21500 o = heap_segment_mem (seg);
21501 }
21502
21503 if (marked (o))
21504 {
21505 size_t size = AlignQword (size (o));
21506
21507 ptrdiff_t reloc = loh_node_relocation_distance (o);
21508
21509 STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
21510
21511 fn (o, (o + size), reloc, profiling_context, !!settings.compaction, false);
21512
21513 o = o + size;
21514 if (o < heap_segment_allocated (seg))
21515 {
21516 assert (!marked (o));
21517 }
21518 }
21519 else
21520 {
21521 while (o < heap_segment_allocated (seg) && !marked (o))
21522 {
21523 o = o + AlignQword (size (o));
21524 }
21525 }
21526 }
21527}
21528
21529BOOL gc_heap::loh_object_p (uint8_t* o)
21530{
21531#ifdef MULTIPLE_HEAPS
21532 gc_heap* hp = gc_heap::g_heaps [0];
21533 int brick_entry = hp->brick_table[hp->brick_of (o)];
21534#else //MULTIPLE_HEAPS
21535 int brick_entry = brick_table[brick_of (o)];
21536#endif //MULTIPLE_HEAPS
21537
21538 return (brick_entry == 0);
21539}
21540#endif //FEATURE_LOH_COMPACTION
21541
21542void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p,
21543 BOOL& last_pinned_plug_p,
21544 BOOL& pinned_plug_p,
21545 size_t ps,
21546 size_t& artificial_pinned_size)
21547{
21548 last_npinned_plug_p = FALSE;
21549 last_pinned_plug_p = TRUE;
21550 pinned_plug_p = TRUE;
21551 artificial_pinned_size = ps;
21552}
21553
21554// Because we have the artificial pinning, we can't guarantee that pinned and npinned
21555// plugs are always interleaved.
21556void gc_heap::store_plug_gap_info (uint8_t* plug_start,
21557 uint8_t* plug_end,
21558 BOOL& last_npinned_plug_p,
21559 BOOL& last_pinned_plug_p,
21560 uint8_t*& last_pinned_plug,
21561 BOOL& pinned_plug_p,
21562 uint8_t* last_object_in_last_plug,
21563 BOOL& merge_with_last_pin_p,
21564 // this is only for verification purpose
21565 size_t last_plug_len)
21566{
21567 UNREFERENCED_PARAMETER(last_plug_len);
21568
21569 if (!last_npinned_plug_p && !last_pinned_plug_p)
21570 {
21571 //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
21572 dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
21573 assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
21574 set_gap_size (plug_start, plug_start - plug_end);
21575 }
21576
21577 if (pinned (plug_start))
21578 {
21579 BOOL save_pre_plug_info_p = FALSE;
21580
21581 if (last_npinned_plug_p || last_pinned_plug_p)
21582 {
21583 //if (last_plug_len == Align (min_obj_size))
21584 //{
21585 // dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
21586 // GCToOSInterface::DebugBreak();
21587 //}
21588 save_pre_plug_info_p = TRUE;
21589 }
21590
21591 pinned_plug_p = TRUE;
21592 last_npinned_plug_p = FALSE;
21593
21594 if (last_pinned_plug_p)
21595 {
21596 dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
21597 merge_with_last_pin_p = TRUE;
21598 }
21599 else
21600 {
21601 last_pinned_plug_p = TRUE;
21602 last_pinned_plug = plug_start;
21603
21604 enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
21605
21606 if (save_pre_plug_info_p)
21607 {
21608 set_gap_size (plug_start, sizeof (gap_reloc_pair));
21609 }
21610 }
21611 }
21612 else
21613 {
21614 if (last_pinned_plug_p)
21615 {
21616 //if (Align (last_plug_len) < min_pre_pin_obj_size)
21617 //{
21618 // dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
21619 // GCToOSInterface::DebugBreak();
21620 //}
21621
21622 save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
21623 set_gap_size (plug_start, sizeof (gap_reloc_pair));
21624
21625 verify_pins_with_post_plug_info("after saving post plug info");
21626 }
21627 last_npinned_plug_p = TRUE;
21628 last_pinned_plug_p = FALSE;
21629 }
21630}
21631
21632void gc_heap::record_interesting_data_point (interesting_data_point idp)
21633{
21634#ifdef GC_CONFIG_DRIVEN
21635 (interesting_data_per_gc[idp])++;
21636#else
21637 UNREFERENCED_PARAMETER(idp);
21638#endif //GC_CONFIG_DRIVEN
21639}
21640
21641#ifdef _PREFAST_
21642#pragma warning(push)
21643#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
21644#endif //_PREFAST_
21645void gc_heap::plan_phase (int condemned_gen_number)
21646{
21647 size_t old_gen2_allocated = 0;
21648 size_t old_gen2_size = 0;
21649
21650 if (condemned_gen_number == (max_generation - 1))
21651 {
21652 old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
21653 old_gen2_size = generation_size (max_generation);
21654 }
21655
21656 assert (settings.concurrent == FALSE);
21657
21658 // %type% category = quote (plan);
21659#ifdef TIME_GC
21660 unsigned start;
21661 unsigned finish;
21662 start = GetCycleCount32();
21663#endif //TIME_GC
21664
21665 dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
21666 condemned_gen_number, settings.promotion ? 1 : 0));
21667
21668 generation* condemned_gen1 = generation_of (condemned_gen_number);
21669
21670#ifdef MARK_LIST
21671 BOOL use_mark_list = FALSE;
21672 uint8_t** mark_list_next = &mark_list[0];
21673#ifdef GC_CONFIG_DRIVEN
21674 dprintf (3, ("total number of marked objects: %Id (%Id)",
21675 (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0]))));
21676
21677 if (mark_list_index >= (mark_list_end + 1))
21678 mark_list_index = mark_list_end + 1;
21679#else
21680 dprintf (3, ("mark_list length: %Id",
21681 (mark_list_index - &mark_list[0])));
21682#endif //GC_CONFIG_DRIVEN
21683
21684 if ((condemned_gen_number < max_generation) &&
21685 (mark_list_index <= mark_list_end)
21686#ifdef BACKGROUND_GC
21687 && (!recursive_gc_sync::background_running_p())
21688#endif //BACKGROUND_GC
21689 )
21690 {
21691#ifndef MULTIPLE_HEAPS
21692 _sort (&mark_list[0], mark_list_index-1, 0);
21693 //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
21694 //verify_qsort_array (&mark_list[0], mark_list_index-1);
21695#endif //!MULTIPLE_HEAPS
21696 use_mark_list = TRUE;
21697 get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit);
21698 }
21699 else
21700 {
21701 dprintf (3, ("mark_list not used"));
21702 }
21703
21704#endif //MARK_LIST
21705
21706#ifdef FEATURE_BASICFREEZE
21707 if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
21708 ro_segments_in_range)
21709 {
21710 sweep_ro_segments (generation_start_segment (condemned_gen1));
21711 }
21712#endif // FEATURE_BASICFREEZE
21713
21714#ifndef MULTIPLE_HEAPS
21715 if (shigh != (uint8_t*)0)
21716 {
21717 heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21718
21719 PREFIX_ASSUME(seg != NULL);
21720
21721 heap_segment* fseg = seg;
21722 do
21723 {
21724 if (slow > heap_segment_mem (seg) &&
21725 slow < heap_segment_reserved (seg))
21726 {
21727 if (seg == fseg)
21728 {
21729 uint8_t* o = generation_allocation_start (condemned_gen1) +
21730 Align (size (generation_allocation_start (condemned_gen1)));
21731 if (slow > o)
21732 {
21733 assert ((slow - o) >= (int)Align (min_obj_size));
21734#ifdef BACKGROUND_GC
21735 if (current_c_gc_state == c_gc_state_marking)
21736 {
21737 bgc_clear_batch_mark_array_bits (o, slow);
21738 }
21739#endif //BACKGROUND_GC
21740 make_unused_array (o, slow - o);
21741 }
21742 }
21743 else
21744 {
21745 assert (condemned_gen_number == max_generation);
21746 make_unused_array (heap_segment_mem (seg),
21747 slow - heap_segment_mem (seg));
21748 }
21749 }
21750 if (in_range_for_segment (shigh, seg))
21751 {
21752#ifdef BACKGROUND_GC
21753 if (current_c_gc_state == c_gc_state_marking)
21754 {
21755 bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
21756 }
21757#endif //BACKGROUND_GC
21758 heap_segment_allocated (seg) = shigh + Align (size (shigh));
21759 }
21760 // test if the segment is in the range of [slow, shigh]
21761 if (!((heap_segment_reserved (seg) >= slow) &&
21762 (heap_segment_mem (seg) <= shigh)))
21763 {
21764 // shorten it to minimum
21765 heap_segment_allocated (seg) = heap_segment_mem (seg);
21766 }
21767 seg = heap_segment_next_rw (seg);
21768 } while (seg);
21769 }
21770 else
21771 {
21772 heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21773
21774 PREFIX_ASSUME(seg != NULL);
21775
21776 heap_segment* sseg = seg;
21777 do
21778 {
21779 // shorten it to minimum
21780 if (seg == sseg)
21781 {
21782 // no survivors make all generations look empty
21783 uint8_t* o = generation_allocation_start (condemned_gen1) +
21784 Align (size (generation_allocation_start (condemned_gen1)));
21785#ifdef BACKGROUND_GC
21786 if (current_c_gc_state == c_gc_state_marking)
21787 {
21788 bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
21789 }
21790#endif //BACKGROUND_GC
21791 heap_segment_allocated (seg) = o;
21792 }
21793 else
21794 {
21795 assert (condemned_gen_number == max_generation);
21796#ifdef BACKGROUND_GC
21797 if (current_c_gc_state == c_gc_state_marking)
21798 {
21799 bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
21800 }
21801#endif //BACKGROUND_GC
21802 heap_segment_allocated (seg) = heap_segment_mem (seg);
21803 }
21804 seg = heap_segment_next_rw (seg);
21805 } while (seg);
21806 }
21807
21808#endif //MULTIPLE_HEAPS
21809
21810 heap_segment* seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
21811
21812 PREFIX_ASSUME(seg1 != NULL);
21813
21814 uint8_t* end = heap_segment_allocated (seg1);
21815 uint8_t* first_condemned_address = generation_allocation_start (condemned_gen1);
21816 uint8_t* x = first_condemned_address;
21817
21818 assert (!marked (x));
21819 uint8_t* plug_end = x;
21820 uint8_t* tree = 0;
21821 size_t sequence_number = 0;
21822 uint8_t* last_node = 0;
21823 size_t current_brick = brick_of (x);
21824 BOOL allocate_in_condemned = ((condemned_gen_number == max_generation)||
21825 (settings.promotion == FALSE));
21826 int active_old_gen_number = condemned_gen_number;
21827 int active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
21828 (1 + condemned_gen_number));
21829 generation* older_gen = 0;
21830 generation* consing_gen = condemned_gen1;
21831 alloc_list r_free_list [MAX_BUCKET_COUNT];
21832
21833 size_t r_free_list_space = 0;
21834 size_t r_free_obj_space = 0;
21835 size_t r_older_gen_free_list_allocated = 0;
21836 size_t r_older_gen_condemned_allocated = 0;
21837 size_t r_older_gen_end_seg_allocated = 0;
21838 uint8_t* r_allocation_pointer = 0;
21839 uint8_t* r_allocation_limit = 0;
21840 uint8_t* r_allocation_start_region = 0;
21841 heap_segment* r_allocation_segment = 0;
21842#ifdef FREE_USAGE_STATS
21843 size_t r_older_gen_free_space[NUM_GEN_POWER2];
21844#endif //FREE_USAGE_STATS
21845
21846 if ((condemned_gen_number < max_generation))
21847 {
21848 older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
21849 generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
21850
21851 r_free_list_space = generation_free_list_space (older_gen);
21852 r_free_obj_space = generation_free_obj_space (older_gen);
21853#ifdef FREE_USAGE_STATS
21854 memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
21855#endif //FREE_USAGE_STATS
21856 generation_allocate_end_seg_p (older_gen) = FALSE;
21857 r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
21858 r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
21859 r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
21860 r_allocation_limit = generation_allocation_limit (older_gen);
21861 r_allocation_pointer = generation_allocation_pointer (older_gen);
21862 r_allocation_start_region = generation_allocation_context_start_region (older_gen);
21863 r_allocation_segment = generation_allocation_segment (older_gen);
21864 heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
21865
21866 PREFIX_ASSUME(start_seg != NULL);
21867
21868 if (start_seg != ephemeral_heap_segment)
21869 {
21870 assert (condemned_gen_number == (max_generation - 1));
21871 while (start_seg && (start_seg != ephemeral_heap_segment))
21872 {
21873 assert (heap_segment_allocated (start_seg) >=
21874 heap_segment_mem (start_seg));
21875 assert (heap_segment_allocated (start_seg) <=
21876 heap_segment_reserved (start_seg));
21877 heap_segment_plan_allocated (start_seg) =
21878 heap_segment_allocated (start_seg);
21879 start_seg = heap_segment_next_rw (start_seg);
21880 }
21881 }
21882 }
21883
21884 //reset all of the segment allocated sizes
21885 {
21886 heap_segment* seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
21887
21888 PREFIX_ASSUME(seg2 != NULL);
21889
21890 while (seg2)
21891 {
21892 heap_segment_plan_allocated (seg2) =
21893 heap_segment_mem (seg2);
21894 seg2 = heap_segment_next_rw (seg2);
21895 }
21896 }
21897 int condemned_gn = condemned_gen_number;
21898
21899 int bottom_gen = 0;
21900 init_free_and_plug();
21901
21902 while (condemned_gn >= bottom_gen)
21903 {
21904 generation* condemned_gen2 = generation_of (condemned_gn);
21905 generation_allocator (condemned_gen2)->clear();
21906 generation_free_list_space (condemned_gen2) = 0;
21907 generation_free_obj_space (condemned_gen2) = 0;
21908 generation_allocation_size (condemned_gen2) = 0;
21909 generation_condemned_allocated (condemned_gen2) = 0;
21910 generation_pinned_allocated (condemned_gen2) = 0;
21911 generation_free_list_allocated(condemned_gen2) = 0;
21912 generation_end_seg_allocated (condemned_gen2) = 0;
21913 generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
21914 generation_pinned_allocation_compact_size (condemned_gen2) = 0;
21915#ifdef FREE_USAGE_STATS
21916 generation_pinned_free_obj_space (condemned_gen2) = 0;
21917 generation_allocated_in_pinned_free (condemned_gen2) = 0;
21918 generation_allocated_since_last_pin (condemned_gen2) = 0;
21919#endif //FREE_USAGE_STATS
21920 generation_plan_allocation_start (condemned_gen2) = 0;
21921 generation_allocation_segment (condemned_gen2) =
21922 heap_segment_rw (generation_start_segment (condemned_gen2));
21923
21924 PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
21925
21926 if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
21927 {
21928 generation_allocation_pointer (condemned_gen2) =
21929 heap_segment_mem (generation_allocation_segment (condemned_gen2));
21930 }
21931 else
21932 {
21933 generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
21934 }
21935
21936 generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21937 generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
21938
21939 condemned_gn--;
21940 }
21941
21942 BOOL allocate_first_generation_start = FALSE;
21943
21944 if (allocate_in_condemned)
21945 {
21946 allocate_first_generation_start = TRUE;
21947 }
21948
21949 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21950
21951 demotion_low = MAX_PTR;
21952 demotion_high = heap_segment_allocated (ephemeral_heap_segment);
21953
21954 // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
21955 // from gen1. They should get promoted to gen2.
21956 demote_gen1_p = !(settings.promotion &&
21957 (settings.condemned_generation == (max_generation - 1)) &&
21958 gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
21959
21960 total_ephemeral_size = 0;
21961
21962 print_free_and_plug ("BP");
21963
21964 for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
21965 {
21966 generation* temp_gen = generation_of (gen_idx);
21967
21968 dprintf (2, ("gen%d start %Ix, plan start %Ix",
21969 gen_idx,
21970 generation_allocation_start (temp_gen),
21971 generation_plan_allocation_start (temp_gen)));
21972 }
21973
21974 BOOL fire_pinned_plug_events_p = EVENT_ENABLED(PinPlugAtGCTime);
21975 size_t last_plug_len = 0;
21976
21977 while (1)
21978 {
21979 if (x >= end)
21980 {
21981 assert (x == end);
21982 assert (heap_segment_allocated (seg1) == end);
21983 heap_segment_allocated (seg1) = plug_end;
21984
21985 current_brick = update_brick_table (tree, current_brick, x, plug_end);
21986 dprintf (3, ("end of seg: new tree, sequence# 0"));
21987 sequence_number = 0;
21988 tree = 0;
21989
21990 if (heap_segment_next_rw (seg1))
21991 {
21992 seg1 = heap_segment_next_rw (seg1);
21993 end = heap_segment_allocated (seg1);
21994 plug_end = x = heap_segment_mem (seg1);
21995 current_brick = brick_of (x);
21996 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
21997 continue;
21998 }
21999 else
22000 {
22001 break;
22002 }
22003 }
22004
22005 BOOL last_npinned_plug_p = FALSE;
22006 BOOL last_pinned_plug_p = FALSE;
22007
22008 // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
22009 // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
22010 // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
22011 uint8_t* last_pinned_plug = 0;
22012 size_t num_pinned_plugs_in_plug = 0;
22013
22014 uint8_t* last_object_in_plug = 0;
22015
22016 while ((x < end) && marked (x))
22017 {
22018 uint8_t* plug_start = x;
22019 uint8_t* saved_plug_end = plug_end;
22020 BOOL pinned_plug_p = FALSE;
22021 BOOL npin_before_pin_p = FALSE;
22022 BOOL saved_last_npinned_plug_p = last_npinned_plug_p;
22023 uint8_t* saved_last_object_in_plug = last_object_in_plug;
22024 BOOL merge_with_last_pin_p = FALSE;
22025
22026 size_t added_pinning_size = 0;
22027 size_t artificial_pinned_size = 0;
22028
22029 store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p,
22030 last_pinned_plug, pinned_plug_p, last_object_in_plug,
22031 merge_with_last_pin_p, last_plug_len);
22032
22033#ifdef FEATURE_STRUCTALIGN
22034 int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
22035 size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
22036#endif // FEATURE_STRUCTALIGN
22037
22038 {
22039 uint8_t* xl = x;
22040 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
22041 {
22042 assert (xl < end);
22043 if (pinned(xl))
22044 {
22045 clear_pinned (xl);
22046 }
22047#ifdef FEATURE_STRUCTALIGN
22048 else
22049 {
22050 int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
22051 if (obj_requiredAlignment > requiredAlignment)
22052 {
22053 requiredAlignment = obj_requiredAlignment;
22054 alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
22055 }
22056 }
22057#endif // FEATURE_STRUCTALIGN
22058
22059 clear_marked (xl);
22060
22061 dprintf(4, ("+%Ix+", (size_t)xl));
22062 assert ((size (xl) > 0));
22063 assert ((size (xl) <= loh_size_threshold));
22064
22065 last_object_in_plug = xl;
22066
22067 xl = xl + Align (size (xl));
22068 Prefetch (xl);
22069 }
22070
22071 BOOL next_object_marked_p = ((xl < end) && marked (xl));
22072
22073 if (pinned_plug_p)
22074 {
22075 // If it is pinned we need to extend to the next marked object as we can't use part of
22076 // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
22077 // references but for now I am just using the next non pinned object for that).
22078 if (next_object_marked_p)
22079 {
22080 clear_marked (xl);
22081 last_object_in_plug = xl;
22082 size_t extra_size = Align (size (xl));
22083 xl = xl + extra_size;
22084 added_pinning_size = extra_size;
22085 }
22086 }
22087 else
22088 {
22089 if (next_object_marked_p)
22090 npin_before_pin_p = TRUE;
22091 }
22092
22093 assert (xl <= end);
22094 x = xl;
22095 }
22096 dprintf (3, ( "%Ix[", (size_t)x));
22097 plug_end = x;
22098 size_t ps = plug_end - plug_start;
22099 last_plug_len = ps;
22100 dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
22101 uint8_t* new_address = 0;
22102
22103 if (!pinned_plug_p)
22104 {
22105 if (allocate_in_condemned &&
22106 (settings.condemned_generation == max_generation) &&
22107 (ps > OS_PAGE_SIZE))
22108 {
22109 ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
22110 //reloc should >=0 except when we relocate
22111 //across segments and the dest seg is higher then the src
22112
22113 if ((ps > (8*OS_PAGE_SIZE)) &&
22114 (reloc > 0) &&
22115 ((size_t)reloc < (ps/16)))
22116 {
22117 dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
22118 (size_t)plug_start, reloc));
22119 // The last plug couldn't have been a npinned plug or it would have
22120 // included this plug.
22121 assert (!saved_last_npinned_plug_p);
22122
22123 if (last_pinned_plug)
22124 {
22125 dprintf (3, ("artificially pinned plug merged with last pinned plug"));
22126 merge_with_last_pin_p = TRUE;
22127 }
22128 else
22129 {
22130 enque_pinned_plug (plug_start, FALSE, 0);
22131 last_pinned_plug = plug_start;
22132 }
22133
22134 convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
22135 ps, artificial_pinned_size);
22136 }
22137 }
22138 }
22139
22140 if (allocate_first_generation_start)
22141 {
22142 allocate_first_generation_start = FALSE;
22143 plan_generation_start (condemned_gen1, consing_gen, plug_start);
22144 assert (generation_plan_allocation_start (condemned_gen1));
22145 }
22146
22147 if (seg1 == ephemeral_heap_segment)
22148 {
22149 process_ephemeral_boundaries (plug_start, active_new_gen_number,
22150 active_old_gen_number,
22151 consing_gen,
22152 allocate_in_condemned);
22153 }
22154
22155 dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
22156
22157 dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
22158 dd_survived_size (dd_active_old) += ps;
22159
22160 BOOL convert_to_pinned_p = FALSE;
22161
22162 if (!pinned_plug_p)
22163 {
22164#if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
22165 dd_num_npinned_plugs (dd_active_old)++;
22166#endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
22167
22168 add_gen_plug (active_old_gen_number, ps);
22169
22170 if (allocate_in_condemned)
22171 {
22172 verify_pins_with_post_plug_info("before aic");
22173
22174 new_address =
22175 allocate_in_condemned_generations (consing_gen,
22176 ps,
22177 active_old_gen_number,
22178#ifdef SHORT_PLUGS
22179 &convert_to_pinned_p,
22180 (npin_before_pin_p ? plug_end : 0),
22181 seg1,
22182#endif //SHORT_PLUGS
22183 plug_start REQD_ALIGN_AND_OFFSET_ARG);
22184 verify_pins_with_post_plug_info("after aic");
22185 }
22186 else
22187 {
22188 new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
22189
22190 if (new_address != 0)
22191 {
22192 if (settings.condemned_generation == (max_generation - 1))
22193 {
22194 dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
22195 plug_start, plug_end,
22196 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
22197 (size_t)(plug_end - plug_start)));
22198 }
22199 }
22200 else
22201 {
22202 if (generation_allocator(older_gen)->discard_if_no_fit_p())
22203 {
22204 allocate_in_condemned = TRUE;
22205 }
22206
22207 new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number,
22208#ifdef SHORT_PLUGS
22209 &convert_to_pinned_p,
22210 (npin_before_pin_p ? plug_end : 0),
22211 seg1,
22212#endif //SHORT_PLUGS
22213 plug_start REQD_ALIGN_AND_OFFSET_ARG);
22214 }
22215 }
22216
22217 if (convert_to_pinned_p)
22218 {
22219 assert (last_npinned_plug_p != FALSE);
22220 assert (last_pinned_plug_p == FALSE);
22221 convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
22222 ps, artificial_pinned_size);
22223 enque_pinned_plug (plug_start, FALSE, 0);
22224 last_pinned_plug = plug_start;
22225 }
22226 else
22227 {
22228 if (!new_address)
22229 {
22230 //verify that we are at then end of the ephemeral segment
22231 assert (generation_allocation_segment (consing_gen) ==
22232 ephemeral_heap_segment);
22233 //verify that we are near the end
22234 assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
22235 heap_segment_allocated (ephemeral_heap_segment));
22236 assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
22237 (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
22238 }
22239 else
22240 {
22241#ifdef SIMPLE_DPRINTF
22242 dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)",
22243 (size_t)(node_gap_size (plug_start)),
22244 plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
22245 (size_t)new_address + ps, ps,
22246 (is_plug_padded (plug_start) ? 1 : 0)));
22247#endif //SIMPLE_DPRINTF
22248
22249#ifdef SHORT_PLUGS
22250 if (is_plug_padded (plug_start))
22251 {
22252 dprintf (3, ("%Ix was padded", plug_start));
22253 dd_padding_size (dd_active_old) += Align (min_obj_size);
22254 }
22255#endif //SHORT_PLUGS
22256 }
22257 }
22258 }
22259
22260 if (pinned_plug_p)
22261 {
22262 if (fire_pinned_plug_events_p)
22263 {
22264 FIRE_EVENT(PinPlugAtGCTime, plug_start, plug_end,
22265 (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start)));
22266 }
22267
22268 if (merge_with_last_pin_p)
22269 {
22270 merge_with_last_pinned_plug (last_pinned_plug, ps);
22271 }
22272 else
22273 {
22274 assert (last_pinned_plug == plug_start);
22275 set_pinned_info (plug_start, ps, consing_gen);
22276 }
22277
22278 new_address = plug_start;
22279
22280 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)",
22281 (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
22282 (size_t)plug_end, ps,
22283 (merge_with_last_pin_p ? 1 : 0)));
22284
22285 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
22286 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
22287 dd_added_pinned_size (dd_active_old) += added_pinning_size;
22288 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
22289
22290 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
22291 {
22292 last_gen1_pin_end = plug_end;
22293 }
22294 }
22295
22296#ifdef _DEBUG
22297 // detect forward allocation in the same segment
22298 assert (!((new_address > plug_start) &&
22299 (new_address < heap_segment_reserved (seg1))));
22300#endif //_DEBUG
22301
22302 if (!merge_with_last_pin_p)
22303 {
22304 if (current_brick != brick_of (plug_start))
22305 {
22306 current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
22307 sequence_number = 0;
22308 tree = 0;
22309 }
22310
22311 set_node_relocation_distance (plug_start, (new_address - plug_start));
22312 if (last_node && (node_relocation_distance (last_node) ==
22313 (node_relocation_distance (plug_start) +
22314 (ptrdiff_t)node_gap_size (plug_start))))
22315 {
22316 //dprintf(3,( " Lb"));
22317 dprintf (3, ("%Ix Lb", plug_start));
22318 set_node_left (plug_start);
22319 }
22320 if (0 == sequence_number)
22321 {
22322 dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
22323 tree = plug_start;
22324 }
22325
22326 verify_pins_with_post_plug_info("before insert node");
22327
22328 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
22329 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
22330 last_node = plug_start;
22331
22332#ifdef _DEBUG
22333 // If we detect if the last plug is pinned plug right before us, we should save this gap info
22334 if (!pinned_plug_p)
22335 {
22336 if (mark_stack_tos > 0)
22337 {
22338 mark& m = mark_stack_array[mark_stack_tos - 1];
22339 if (m.has_post_plug_info())
22340 {
22341 uint8_t* post_plug_info_start = m.saved_post_plug_info_start;
22342 size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
22343 if ((uint8_t*)current_plug_gap_start == post_plug_info_start)
22344 {
22345 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
22346 *current_plug_gap_start, *(current_plug_gap_start + 1),
22347 *(current_plug_gap_start + 2)));
22348 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
22349 }
22350 }
22351 }
22352 }
22353#endif //_DEBUG
22354
22355 verify_pins_with_post_plug_info("after insert node");
22356 }
22357 }
22358
22359 if (num_pinned_plugs_in_plug > 1)
22360 {
22361 dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
22362 }
22363
22364 {
22365#ifdef MARK_LIST
22366 if (use_mark_list)
22367 {
22368 while ((mark_list_next < mark_list_index) &&
22369 (*mark_list_next <= x))
22370 {
22371 mark_list_next++;
22372 }
22373 if ((mark_list_next < mark_list_index)
22374#ifdef MULTIPLE_HEAPS
22375 && (*mark_list_next < end) //for multiple segments
22376#endif //MULTIPLE_HEAPS
22377 )
22378 x = *mark_list_next;
22379 else
22380 x = end;
22381 }
22382 else
22383#endif //MARK_LIST
22384 {
22385 uint8_t* xl = x;
22386#ifdef BACKGROUND_GC
22387 if (current_c_gc_state == c_gc_state_marking)
22388 {
22389 assert (recursive_gc_sync::background_running_p());
22390 while ((xl < end) && !marked (xl))
22391 {
22392 dprintf (4, ("-%Ix-", (size_t)xl));
22393 assert ((size (xl) > 0));
22394 background_object_marked (xl, TRUE);
22395 xl = xl + Align (size (xl));
22396 Prefetch (xl);
22397 }
22398 }
22399 else
22400#endif //BACKGROUND_GC
22401 {
22402 while ((xl < end) && !marked (xl))
22403 {
22404 dprintf (4, ("-%Ix-", (size_t)xl));
22405 assert ((size (xl) > 0));
22406 xl = xl + Align (size (xl));
22407 Prefetch (xl);
22408 }
22409 }
22410 assert (xl <= end);
22411 x = xl;
22412 }
22413 }
22414 }
22415
22416 while (!pinned_plug_que_empty_p())
22417 {
22418 if (settings.promotion)
22419 {
22420 uint8_t* pplug = pinned_plug (oldest_pin());
22421 if (in_range_for_segment (pplug, ephemeral_heap_segment))
22422 {
22423 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
22424 //allocate all of the generation gaps
22425 while (active_new_gen_number > 0)
22426 {
22427 active_new_gen_number--;
22428
22429 if (active_new_gen_number == (max_generation - 1))
22430 {
22431 maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
22432 if (!demote_gen1_p)
22433 advance_pins_for_demotion (consing_gen);
22434 }
22435
22436 generation* gen = generation_of (active_new_gen_number);
22437 plan_generation_start (gen, consing_gen, 0);
22438
22439 if (demotion_low == MAX_PTR)
22440 {
22441 demotion_low = pplug;
22442 dprintf (3, ("end plan: dlow->%Ix", demotion_low));
22443 }
22444
22445 dprintf (2, ("(%d)gen%d plan start: %Ix",
22446 heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
22447 assert (generation_plan_allocation_start (gen));
22448 }
22449 }
22450 }
22451
22452 if (pinned_plug_que_empty_p())
22453 break;
22454
22455 size_t entry = deque_pinned_plug();
22456 mark* m = pinned_plug_of (entry);
22457 uint8_t* plug = pinned_plug (m);
22458 size_t len = pinned_len (m);
22459
22460 // detect pinned block in different segment (later) than
22461 // allocation segment
22462 heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
22463
22464 while ((plug < generation_allocation_pointer (consing_gen)) ||
22465 (plug >= heap_segment_allocated (nseg)))
22466 {
22467 assert ((plug < heap_segment_mem (nseg)) ||
22468 (plug > heap_segment_reserved (nseg)));
22469 //adjust the end of the segment to be the end of the plug
22470 assert (generation_allocation_pointer (consing_gen)>=
22471 heap_segment_mem (nseg));
22472 assert (generation_allocation_pointer (consing_gen)<=
22473 heap_segment_committed (nseg));
22474
22475 heap_segment_plan_allocated (nseg) =
22476 generation_allocation_pointer (consing_gen);
22477 //switch allocation segment
22478 nseg = heap_segment_next_rw (nseg);
22479 generation_allocation_segment (consing_gen) = nseg;
22480 //reset the allocation pointer and limits
22481 generation_allocation_pointer (consing_gen) =
22482 heap_segment_mem (nseg);
22483 }
22484
22485 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
22486 dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
22487 (size_t)(brick_table[brick_of (plug)])));
22488
22489 generation_allocation_pointer (consing_gen) = plug + len;
22490 generation_allocation_limit (consing_gen) =
22491 generation_allocation_pointer (consing_gen);
22492 //Add the size of the pinned plug to the right pinned allocations
22493 //find out which gen this pinned plug came from
22494 int frgn = object_gennum (plug);
22495 if ((frgn != (int)max_generation) && settings.promotion)
22496 {
22497 generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
22498 }
22499
22500 }
22501
22502 plan_generation_starts (consing_gen);
22503 print_free_and_plug ("AP");
22504
22505 {
22506#ifdef SIMPLE_DPRINTF
22507 for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22508 {
22509 generation* temp_gen = generation_of (gen_idx);
22510 dynamic_data* temp_dd = dynamic_data_of (gen_idx);
22511
22512 int added_pinning_ratio = 0;
22513 int artificial_pinned_ratio = 0;
22514
22515 if (dd_pinned_survived_size (temp_dd) != 0)
22516 {
22517 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22518 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22519 }
22520
22521 size_t padding_size =
22522#ifdef SHORT_PLUGS
22523 dd_padding_size (temp_dd);
22524#else
22525 0;
22526#endif //SHORT_PLUGS
22527 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",
22528 gen_idx,
22529 generation_allocation_start (temp_gen),
22530 generation_plan_allocation_start (temp_gen),
22531 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
22532 generation_allocation_size (temp_gen),
22533 generation_pinned_allocation_compact_size (temp_gen),
22534 generation_pinned_allocation_sweep_size (temp_gen),
22535 dd_survived_size (temp_dd),
22536 dd_pinned_survived_size (temp_dd),
22537 added_pinning_ratio,
22538 artificial_pinned_ratio,
22539 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
22540 padding_size));
22541 }
22542#endif //SIMPLE_DPRINTF
22543 }
22544
22545 if (settings.condemned_generation == (max_generation - 1 ))
22546 {
22547 size_t plan_gen2_size = generation_plan_size (max_generation);
22548 size_t growth = plan_gen2_size - old_gen2_size;
22549
22550 if (growth > 0)
22551 {
22552 dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, condemned alloc: %Id",
22553 growth, end_seg_allocated, condemned_allocated));
22554
22555 maxgen_size_inc_p = true;
22556 }
22557 else
22558 {
22559 dprintf (2, ("gen2 shrank %Id (end seg alloc: %Id, gen1 c alloc: %Id",
22560 (old_gen2_size - plan_gen2_size), generation_end_seg_allocated (generation_of (max_generation)),
22561 generation_condemned_allocated (generation_of (max_generation - 1))));
22562 }
22563
22564 generation* older_gen = generation_of (settings.condemned_generation + 1);
22565 size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
22566 size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
22567 size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated;
22568 size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated;
22569
22570 dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
22571 r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
22572 r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen),
22573 r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
22574
22575 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",
22576 free_list_allocated, rejected_free_space, end_seg_allocated,
22577 condemned_allocated, generation_condemned_allocated (generation_of (settings.condemned_generation))));
22578
22579 maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info);
22580 maxgen_size_info->free_list_allocated = free_list_allocated;
22581 maxgen_size_info->free_list_rejected = rejected_free_space;
22582 maxgen_size_info->end_seg_allocated = end_seg_allocated;
22583 maxgen_size_info->condemned_allocated = condemned_allocated;
22584 maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance;
22585 maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance;
22586
22587#ifdef FREE_USAGE_STATS
22588 int free_list_efficiency = 0;
22589 if ((free_list_allocated + rejected_free_space) != 0)
22590 free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100);
22591
22592 int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100);
22593
22594 dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%",
22595 older_gen->gen_num,
22596 free_list_efficiency, running_free_list_efficiency));
22597
22598 dprintf (1, ("gen2 free list change"));
22599 for (int j = 0; j < NUM_GEN_POWER2; j++)
22600 {
22601 dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id",
22602 heap_number,
22603 settings.gc_index,
22604 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j],
22605 (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
22606 (generation_of(max_generation - 1))->gen_plugs[j]));
22607 }
22608#endif //FREE_USAGE_STATS
22609 }
22610
22611 size_t fragmentation =
22612 generation_fragmentation (generation_of (condemned_gen_number),
22613 consing_gen,
22614 heap_segment_allocated (ephemeral_heap_segment));
22615
22616 dprintf (2,("Fragmentation: %Id", fragmentation));
22617 dprintf (2,("---- End of Plan phase ----"));
22618
22619#ifdef TIME_GC
22620 finish = GetCycleCount32();
22621 plan_time = finish - start;
22622#endif //TIME_GC
22623
22624 // We may update write barrier code. We assume here EE has been suspended if we are on a GC thread.
22625 assert(IsGCInProgress());
22626
22627 BOOL should_expand = FALSE;
22628 BOOL should_compact= FALSE;
22629 ephemeral_promotion = FALSE;
22630
22631#ifdef BIT64
22632 if ((!settings.concurrent) &&
22633 !provisional_mode_triggered &&
22634 ((condemned_gen_number < max_generation) &&
22635 ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
22636 {
22637 dprintf (GTC_LOG, ("gen0 reduction count is %d, condemning %d, mem load %d",
22638 settings.gen0_reduction_count,
22639 condemned_gen_number,
22640 settings.entry_memory_load));
22641 should_compact = TRUE;
22642
22643 get_gc_data_per_heap()->set_mechanism (gc_heap_compact,
22644 ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load));
22645
22646 if ((condemned_gen_number >= (max_generation - 1)) &&
22647 dt_low_ephemeral_space_p (tuning_deciding_expansion))
22648 {
22649 dprintf (GTC_LOG, ("Not enough space for all ephemeral generations with compaction"));
22650 should_expand = TRUE;
22651 }
22652 }
22653 else
22654 {
22655#endif // BIT64
22656 should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
22657#ifdef BIT64
22658 }
22659#endif // BIT64
22660
22661#ifdef FEATURE_LOH_COMPACTION
22662 loh_compacted_p = FALSE;
22663#endif //FEATURE_LOH_COMPACTION
22664
22665 if (condemned_gen_number == max_generation)
22666 {
22667#ifdef FEATURE_LOH_COMPACTION
22668 if (settings.loh_compaction)
22669 {
22670 if (plan_loh())
22671 {
22672 should_compact = TRUE;
22673 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced);
22674 loh_compacted_p = TRUE;
22675 }
22676 }
22677 else
22678 {
22679 if ((heap_number == 0) && (loh_pinned_queue))
22680 {
22681 loh_pinned_queue_decay--;
22682
22683 if (!loh_pinned_queue_decay)
22684 {
22685 delete loh_pinned_queue;
22686 loh_pinned_queue = 0;
22687 }
22688 }
22689 }
22690
22691 if (!loh_compacted_p)
22692#endif //FEATURE_LOH_COMPACTION
22693 {
22694 GCToEEInterface::DiagWalkLOHSurvivors(__this);
22695 sweep_large_objects();
22696 }
22697 }
22698 else
22699 {
22700 settings.loh_compaction = FALSE;
22701 }
22702
22703#ifdef MULTIPLE_HEAPS
22704
22705 new_heap_segment = NULL;
22706
22707 if (should_compact && should_expand)
22708 gc_policy = policy_expand;
22709 else if (should_compact)
22710 gc_policy = policy_compact;
22711 else
22712 gc_policy = policy_sweep;
22713
22714 //vote for result of should_compact
22715 dprintf (3, ("Joining for compaction decision"));
22716 gc_t_join.join(this, gc_join_decide_on_compaction);
22717 if (gc_t_join.joined())
22718 {
22719 //safe place to delete large heap segments
22720 if (condemned_gen_number == max_generation)
22721 {
22722 for (int i = 0; i < n_heaps; i++)
22723 {
22724 g_heaps [i]->rearrange_large_heap_segments ();
22725 }
22726 }
22727
22728 if (maxgen_size_inc_p && provisional_mode_triggered)
22729 {
22730 pm_trigger_full_gc = true;
22731 dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2"));
22732 }
22733 else
22734 {
22735 settings.demotion = FALSE;
22736 int pol_max = policy_sweep;
22737#ifdef GC_CONFIG_DRIVEN
22738 BOOL is_compaction_mandatory = FALSE;
22739#endif //GC_CONFIG_DRIVEN
22740
22741 int i;
22742 for (i = 0; i < n_heaps; i++)
22743 {
22744 if (pol_max < g_heaps[i]->gc_policy)
22745 pol_max = policy_compact;
22746 // set the demotion flag is any of the heap has demotion
22747 if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
22748 {
22749 (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit);
22750 settings.demotion = TRUE;
22751 }
22752
22753#ifdef GC_CONFIG_DRIVEN
22754 if (!is_compaction_mandatory)
22755 {
22756 int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact);
22757 if (compact_reason >= 0)
22758 {
22759 if (gc_heap_compact_reason_mandatory_p[compact_reason])
22760 is_compaction_mandatory = TRUE;
22761 }
22762 }
22763#endif //GC_CONFIG_DRIVEN
22764 }
22765
22766#ifdef GC_CONFIG_DRIVEN
22767 if (!is_compaction_mandatory)
22768 {
22769 // If compaction is not mandatory we can feel free to change it to a sweeping GC.
22770 // Note that we may want to change this to only checking every so often instead of every single GC.
22771 if (should_do_sweeping_gc (pol_max >= policy_compact))
22772 {
22773 pol_max = policy_sweep;
22774 }
22775 else
22776 {
22777 if (pol_max == policy_sweep)
22778 pol_max = policy_compact;
22779 }
22780 }
22781#endif //GC_CONFIG_DRIVEN
22782
22783 for (i = 0; i < n_heaps; i++)
22784 {
22785 if (pol_max > g_heaps[i]->gc_policy)
22786 g_heaps[i]->gc_policy = pol_max;
22787 //get the segment while we are serialized
22788 if (g_heaps[i]->gc_policy == policy_expand)
22789 {
22790 g_heaps[i]->new_heap_segment =
22791 g_heaps[i]->soh_get_segment_to_expand();
22792 if (!g_heaps[i]->new_heap_segment)
22793 {
22794 set_expand_in_full_gc (condemned_gen_number);
22795 //we are out of memory, cancel the expansion
22796 g_heaps[i]->gc_policy = policy_compact;
22797 }
22798 }
22799 }
22800
22801 BOOL is_full_compacting_gc = FALSE;
22802
22803 if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
22804 {
22805 full_gc_counts[gc_type_compacting]++;
22806 is_full_compacting_gc = TRUE;
22807 }
22808
22809 for (i = 0; i < n_heaps; i++)
22810 {
22811 //copy the card and brick tables
22812 if (g_gc_card_table!= g_heaps[i]->card_table)
22813 {
22814 g_heaps[i]->copy_brick_card_table();
22815 }
22816
22817 if (is_full_compacting_gc)
22818 {
22819 g_heaps[i]->loh_alloc_since_cg = 0;
22820 }
22821 }
22822 }
22823
22824 //start all threads on the roots.
22825 dprintf(3, ("Starting all gc threads after compaction decision"));
22826 gc_t_join.restart();
22827 }
22828
22829 //reset the local variable accordingly
22830 should_compact = (gc_policy >= policy_compact);
22831 should_expand = (gc_policy >= policy_expand);
22832
22833#else //MULTIPLE_HEAPS
22834
22835 //safe place to delete large heap segments
22836 if (condemned_gen_number == max_generation)
22837 {
22838 rearrange_large_heap_segments ();
22839 }
22840
22841 if (maxgen_size_inc_p && provisional_mode_triggered)
22842 {
22843 pm_trigger_full_gc = true;
22844 dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2"));
22845 }
22846 else
22847 {
22848 settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
22849 if (settings.demotion)
22850 get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
22851
22852#ifdef GC_CONFIG_DRIVEN
22853 BOOL is_compaction_mandatory = FALSE;
22854 int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
22855 if (compact_reason >= 0)
22856 is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason];
22857
22858 if (!is_compaction_mandatory)
22859 {
22860 if (should_do_sweeping_gc (should_compact))
22861 should_compact = FALSE;
22862 else
22863 should_compact = TRUE;
22864 }
22865#endif //GC_CONFIG_DRIVEN
22866
22867 if (should_compact && (condemned_gen_number == max_generation))
22868 {
22869 full_gc_counts[gc_type_compacting]++;
22870 loh_alloc_since_cg = 0;
22871 }
22872 }
22873#endif //MULTIPLE_HEAPS
22874
22875 if (!pm_trigger_full_gc && pm_stress_on && provisional_mode_triggered)
22876 {
22877 if ((settings.condemned_generation == (max_generation - 1)) &&
22878 ((settings.gc_index % 5) == 0))
22879 {
22880 pm_trigger_full_gc = true;
22881 }
22882 }
22883
22884 if (settings.condemned_generation == (max_generation - 1))
22885 {
22886 if (provisional_mode_triggered)
22887 {
22888 if (should_expand)
22889 {
22890 should_expand = FALSE;
22891 dprintf (GTC_LOG, ("h%d in PM cannot expand", heap_number));
22892 }
22893 }
22894
22895 if (pm_trigger_full_gc)
22896 {
22897 should_compact = FALSE;
22898 dprintf (GTC_LOG, ("h%d PM doing sweeping", heap_number));
22899 }
22900 }
22901
22902 if (should_compact)
22903 {
22904 dprintf (2,( "**** Doing Compacting GC ****"));
22905
22906 if (should_expand)
22907 {
22908#ifndef MULTIPLE_HEAPS
22909 heap_segment* new_heap_segment = soh_get_segment_to_expand();
22910#endif //!MULTIPLE_HEAPS
22911 if (new_heap_segment)
22912 {
22913 consing_gen = expand_heap(condemned_gen_number,
22914 consing_gen,
22915 new_heap_segment);
22916 }
22917
22918 // If we couldn't get a new segment, or we were able to
22919 // reserve one but no space to commit, we couldn't
22920 // expand heap.
22921 if (ephemeral_heap_segment != new_heap_segment)
22922 {
22923 set_expand_in_full_gc (condemned_gen_number);
22924 should_expand = FALSE;
22925 }
22926 }
22927 generation_allocation_limit (condemned_gen1) =
22928 generation_allocation_pointer (condemned_gen1);
22929 if ((condemned_gen_number < max_generation))
22930 {
22931 generation_allocator (older_gen)->commit_alloc_list_changes();
22932
22933 // Fix the allocation area of the older generation
22934 fix_older_allocation_area (older_gen);
22935 }
22936 assert (generation_allocation_segment (consing_gen) ==
22937 ephemeral_heap_segment);
22938
22939 GCToEEInterface::DiagWalkSurvivors(__this);
22940
22941 relocate_phase (condemned_gen_number, first_condemned_address);
22942 compact_phase (condemned_gen_number, first_condemned_address,
22943 (!settings.demotion && settings.promotion));
22944 fix_generation_bounds (condemned_gen_number, consing_gen);
22945 assert (generation_allocation_limit (youngest_generation) ==
22946 generation_allocation_pointer (youngest_generation));
22947 if (condemned_gen_number >= (max_generation -1))
22948 {
22949#ifdef MULTIPLE_HEAPS
22950 // this needs be serialized just because we have one
22951 // segment_standby_list/seg_table for all heaps. We should make it at least
22952 // so that when hoarding is not on we don't need this join because
22953 // decommitting memory can take a long time.
22954 //must serialize on deleting segments
22955 gc_t_join.join(this, gc_join_rearrange_segs_compaction);
22956 if (gc_t_join.joined())
22957 {
22958 for (int i = 0; i < n_heaps; i++)
22959 {
22960 g_heaps[i]->rearrange_heap_segments(TRUE);
22961 }
22962 gc_t_join.restart();
22963 }
22964#else
22965 rearrange_heap_segments(TRUE);
22966#endif //MULTIPLE_HEAPS
22967
22968 if (should_expand)
22969 {
22970 //fix the start_segment for the ephemeral generations
22971 for (int i = 0; i < max_generation; i++)
22972 {
22973 generation* gen = generation_of (i);
22974 generation_start_segment (gen) = ephemeral_heap_segment;
22975 generation_allocation_segment (gen) = ephemeral_heap_segment;
22976 }
22977 }
22978 }
22979
22980 {
22981#ifdef FEATURE_PREMORTEM_FINALIZATION
22982 finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
22983 (!settings.demotion && settings.promotion));
22984#endif // FEATURE_PREMORTEM_FINALIZATION
22985
22986#ifdef MULTIPLE_HEAPS
22987 dprintf(3, ("Joining after end of compaction"));
22988 gc_t_join.join(this, gc_join_adjust_handle_age_compact);
22989 if (gc_t_join.joined())
22990#endif //MULTIPLE_HEAPS
22991 {
22992#ifdef MULTIPLE_HEAPS
22993 //join all threads to make sure they are synchronized
22994 dprintf(3, ("Restarting after Promotion granted"));
22995 gc_t_join.restart();
22996#endif //MULTIPLE_HEAPS
22997 }
22998
22999 ScanContext sc;
23000 sc.thread_number = heap_number;
23001 sc.promotion = FALSE;
23002 sc.concurrent = FALSE;
23003 // new generations bounds are set can call this guy
23004 if (settings.promotion && !settings.demotion)
23005 {
23006 dprintf (2, ("Promoting EE roots for gen %d",
23007 condemned_gen_number));
23008 GCScan::GcPromotionsGranted(condemned_gen_number,
23009 max_generation, &sc);
23010 }
23011 else if (settings.demotion)
23012 {
23013 dprintf (2, ("Demoting EE roots for gen %d",
23014 condemned_gen_number));
23015 GCScan::GcDemote (condemned_gen_number, max_generation, &sc);
23016 }
23017 }
23018
23019 {
23020 gen0_big_free_spaces = 0;
23021
23022 reset_pinned_queue_bos();
23023 unsigned int gen_number = min (max_generation, 1 + condemned_gen_number);
23024 generation* gen = generation_of (gen_number);
23025 uint8_t* low = generation_allocation_start (generation_of (gen_number-1));
23026 uint8_t* high = heap_segment_allocated (ephemeral_heap_segment);
23027
23028 while (!pinned_plug_que_empty_p())
23029 {
23030 mark* m = pinned_plug_of (deque_pinned_plug());
23031 size_t len = pinned_len (m);
23032 uint8_t* arr = (pinned_plug (m) - len);
23033 dprintf(3,("free [%Ix %Ix[ pin",
23034 (size_t)arr, (size_t)arr + len));
23035 if (len != 0)
23036 {
23037 assert (len >= Align (min_obj_size));
23038 make_unused_array (arr, len);
23039 // fix fully contained bricks + first one
23040 // if the array goes beyond the first brick
23041 size_t start_brick = brick_of (arr);
23042 size_t end_brick = brick_of (arr + len);
23043 if (end_brick != start_brick)
23044 {
23045 dprintf (3,
23046 ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
23047 start_brick, end_brick, (size_t)arr));
23048 set_brick (start_brick,
23049 arr - brick_address (start_brick));
23050 size_t brick = start_brick+1;
23051 while (brick < end_brick)
23052 {
23053 set_brick (brick, start_brick - brick);
23054 brick++;
23055 }
23056 }
23057
23058 //when we take an old segment to make the new
23059 //ephemeral segment. we can have a bunch of
23060 //pinned plugs out of order going to the new ephemeral seg
23061 //and then the next plugs go back to max_generation
23062 if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
23063 (heap_segment_reserved (ephemeral_heap_segment) > arr))
23064 {
23065
23066 while ((low <= arr) && (high > arr))
23067 {
23068 gen_number--;
23069 assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
23070 settings.demotion || !settings.promotion);
23071 dprintf (3, ("new free list generation %d", gen_number));
23072
23073 gen = generation_of (gen_number);
23074 if (gen_number >= 1)
23075 low = generation_allocation_start (generation_of (gen_number-1));
23076 else
23077 low = high;
23078 }
23079 }
23080 else
23081 {
23082 dprintf (3, ("new free list generation %d", max_generation));
23083 gen_number = max_generation;
23084 gen = generation_of (gen_number);
23085 }
23086
23087 dprintf(3,("threading it into generation %d", gen_number));
23088 thread_gap (arr, len, gen);
23089 add_gen_free (gen_number, len);
23090 }
23091 }
23092 }
23093
23094#ifdef _DEBUG
23095 for (int x = 0; x <= max_generation; x++)
23096 {
23097 assert (generation_allocation_start (generation_of (x)));
23098 }
23099#endif //_DEBUG
23100
23101 if (!settings.demotion && settings.promotion)
23102 {
23103 //clear card for generation 1. generation 0 is empty
23104 clear_card_for_addresses (
23105 generation_allocation_start (generation_of (1)),
23106 generation_allocation_start (generation_of (0)));
23107 }
23108 if (settings.promotion && !settings.demotion)
23109 {
23110 uint8_t* start = generation_allocation_start (youngest_generation);
23111 MAYBE_UNUSED_VAR(start);
23112 assert (heap_segment_allocated (ephemeral_heap_segment) ==
23113 (start + Align (size (start))));
23114 }
23115 }
23116 else
23117 {
23118 //force promotion for sweep
23119 settings.promotion = TRUE;
23120 settings.compaction = FALSE;
23121
23122 ScanContext sc;
23123 sc.thread_number = heap_number;
23124 sc.promotion = FALSE;
23125 sc.concurrent = FALSE;
23126
23127 dprintf (2, ("**** Doing Mark and Sweep GC****"));
23128
23129 if ((condemned_gen_number < max_generation))
23130 {
23131 generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
23132 generation_free_list_space (older_gen) = r_free_list_space;
23133 generation_free_obj_space (older_gen) = r_free_obj_space;
23134 generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
23135 generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
23136 generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
23137 generation_allocation_limit (older_gen) = r_allocation_limit;
23138 generation_allocation_pointer (older_gen) = r_allocation_pointer;
23139 generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
23140 generation_allocation_segment (older_gen) = r_allocation_segment;
23141 }
23142
23143 if ((condemned_gen_number < max_generation))
23144 {
23145 // Fix the allocation area of the older generation
23146 fix_older_allocation_area (older_gen);
23147 }
23148
23149 GCToEEInterface::DiagWalkSurvivors(__this);
23150
23151 gen0_big_free_spaces = 0;
23152 make_free_lists (condemned_gen_number);
23153 recover_saved_pinned_info();
23154
23155#ifdef FEATURE_PREMORTEM_FINALIZATION
23156 finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
23157#endif // FEATURE_PREMORTEM_FINALIZATION
23158// MTHTS: leave single thread for HT processing on plan_phase
23159#ifdef MULTIPLE_HEAPS
23160 dprintf(3, ("Joining after end of sweep"));
23161 gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
23162 if (gc_t_join.joined())
23163#endif //MULTIPLE_HEAPS
23164 {
23165 GCScan::GcPromotionsGranted(condemned_gen_number,
23166 max_generation, &sc);
23167 if (condemned_gen_number >= (max_generation -1))
23168 {
23169#ifdef MULTIPLE_HEAPS
23170 for (int i = 0; i < n_heaps; i++)
23171 {
23172 g_heaps[i]->rearrange_heap_segments(FALSE);
23173 }
23174#else
23175 rearrange_heap_segments(FALSE);
23176#endif //MULTIPLE_HEAPS
23177 }
23178
23179#ifdef MULTIPLE_HEAPS
23180 //join all threads to make sure they are synchronized
23181 dprintf(3, ("Restarting after Promotion granted"));
23182 gc_t_join.restart();
23183#endif //MULTIPLE_HEAPS
23184 }
23185
23186#ifdef _DEBUG
23187 for (int x = 0; x <= max_generation; x++)
23188 {
23189 assert (generation_allocation_start (generation_of (x)));
23190 }
23191#endif //_DEBUG
23192
23193 //clear card for generation 1. generation 0 is empty
23194 clear_card_for_addresses (
23195 generation_allocation_start (generation_of (1)),
23196 generation_allocation_start (generation_of (0)));
23197 assert ((heap_segment_allocated (ephemeral_heap_segment) ==
23198 (generation_allocation_start (youngest_generation) +
23199 Align (min_obj_size))));
23200 }
23201
23202 //verify_partial();
23203}
23204#ifdef _PREFAST_
23205#pragma warning(pop)
23206#endif //_PREFAST_
23207
23208
23209/*****************************
23210Called after compact phase to fix all generation gaps
23211********************************/
23212void gc_heap::fix_generation_bounds (int condemned_gen_number,
23213 generation* consing_gen)
23214{
23215 UNREFERENCED_PARAMETER(consing_gen);
23216
23217 assert (generation_allocation_segment (consing_gen) ==
23218 ephemeral_heap_segment);
23219
23220 //assign the planned allocation start to the generation
23221 int gen_number = condemned_gen_number;
23222 int bottom_gen = 0;
23223
23224 while (gen_number >= bottom_gen)
23225 {
23226 generation* gen = generation_of (gen_number);
23227 dprintf(3,("Fixing generation pointers for %Ix", gen_number));
23228 if ((gen_number < max_generation) && ephemeral_promotion)
23229 {
23230 make_unused_array (saved_ephemeral_plan_start[gen_number],
23231 saved_ephemeral_plan_start_size[gen_number]);
23232 }
23233 reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
23234 make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
23235 dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
23236 gen_number--;
23237 }
23238#ifdef MULTIPLE_HEAPS
23239 if (ephemeral_promotion)
23240 {
23241 //we are creating a generation fault. set the cards.
23242 // and we are only doing this for multiple heaps because in the single heap scenario the
23243 // new ephemeral generations will be empty and there'll be no need to set cards for the
23244 // old ephemeral generations that got promoted into max_generation.
23245 ptrdiff_t delta = 0;
23246#ifdef SEG_MAPPING_TABLE
23247 heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
23248#else //SEG_MAPPING_TABLE
23249 heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
23250#endif //SEG_MAPPING_TABLE
23251
23252 assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
23253 size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
23254 size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
23255 while (card != end_card)
23256 {
23257 set_card (card);
23258 card++;
23259 }
23260 }
23261#endif //MULTIPLE_HEAPS
23262 {
23263 alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
23264 //reset the allocated size
23265 uint8_t* start = generation_allocation_start (youngest_generation);
23266 MAYBE_UNUSED_VAR(start);
23267 if (settings.promotion && !settings.demotion)
23268 {
23269 assert ((start + Align (size (start))) ==
23270 heap_segment_plan_allocated(ephemeral_heap_segment));
23271 }
23272
23273 heap_segment_allocated(ephemeral_heap_segment)=
23274 heap_segment_plan_allocated(ephemeral_heap_segment);
23275 }
23276}
23277
23278uint8_t* gc_heap::generation_limit (int gen_number)
23279{
23280 if (settings.promotion)
23281 {
23282 if (gen_number <= 1)
23283 return heap_segment_reserved (ephemeral_heap_segment);
23284 else
23285 return generation_allocation_start (generation_of ((gen_number - 2)));
23286 }
23287 else
23288 {
23289 if (gen_number <= 0)
23290 return heap_segment_reserved (ephemeral_heap_segment);
23291 else
23292 return generation_allocation_start (generation_of ((gen_number - 1)));
23293 }
23294}
23295
23296BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
23297{
23298 uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23299 size_t size = Align (min_obj_size)*(condemned_gen_number+1);
23300 assert ((start + size) <=
23301 heap_segment_reserved (ephemeral_heap_segment));
23302 if ((start + size) >
23303 heap_segment_committed (ephemeral_heap_segment))
23304 {
23305 if (!grow_heap_segment (ephemeral_heap_segment, start + size))
23306 {
23307 return FALSE;
23308 }
23309 }
23310 return TRUE;
23311}
23312
23313uint8_t* gc_heap::allocate_at_end (size_t size)
23314{
23315 uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23316 size = Align (size);
23317 uint8_t* result = start;
23318 // only called to allocate a min obj so can't overflow here.
23319 assert ((start + size) <=
23320 heap_segment_reserved (ephemeral_heap_segment));
23321 //ensure_gap_allocation took care of it
23322 assert ((start + size) <=
23323 heap_segment_committed (ephemeral_heap_segment));
23324 heap_segment_allocated (ephemeral_heap_segment) += size;
23325 return result;
23326}
23327
23328
23329void gc_heap::make_free_lists (int condemned_gen_number)
23330{
23331#ifdef TIME_GC
23332 unsigned start;
23333 unsigned finish;
23334 start = GetCycleCount32();
23335#endif //TIME_GC
23336
23337 //Promotion has to happen in sweep case.
23338 assert (settings.promotion);
23339
23340 generation* condemned_gen = generation_of (condemned_gen_number);
23341 uint8_t* start_address = generation_allocation_start (condemned_gen);
23342
23343 size_t current_brick = brick_of (start_address);
23344 heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23345
23346 PREFIX_ASSUME(current_heap_segment != NULL);
23347
23348 uint8_t* end_address = heap_segment_allocated (current_heap_segment);
23349 size_t end_brick = brick_of (end_address-1);
23350 make_free_args args;
23351 args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
23352 args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
23353 MAX_PTR :
23354 (generation_limit (args.free_list_gen_number)));
23355 args.free_list_gen = generation_of (args.free_list_gen_number);
23356 args.highest_plug = 0;
23357
23358 if ((start_address < end_address) ||
23359 (condemned_gen_number == max_generation))
23360 {
23361 while (1)
23362 {
23363 if ((current_brick > end_brick))
23364 {
23365 if (args.current_gen_limit == MAX_PTR)
23366 {
23367 //We had an empty segment
23368 //need to allocate the generation start
23369
23370 generation* gen = generation_of (max_generation);
23371
23372 heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
23373
23374 PREFIX_ASSUME(start_seg != NULL);
23375
23376 uint8_t* gap = heap_segment_mem (start_seg);
23377
23378 generation_allocation_start (gen) = gap;
23379 heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
23380 make_unused_array (gap, Align (min_obj_size));
23381 reset_allocation_pointers (gen, gap);
23382 dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
23383 max_generation, (size_t)gap));
23384 args.current_gen_limit = generation_limit (args.free_list_gen_number);
23385 }
23386 if (heap_segment_next_rw (current_heap_segment))
23387 {
23388 current_heap_segment = heap_segment_next_rw (current_heap_segment);
23389 current_brick = brick_of (heap_segment_mem (current_heap_segment));
23390 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23391
23392 continue;
23393 }
23394 else
23395 {
23396 break;
23397 }
23398 }
23399 {
23400 int brick_entry = brick_table [ current_brick ];
23401 if ((brick_entry >= 0))
23402 {
23403 make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
23404 dprintf(3,("Fixing brick entry %Ix to %Ix",
23405 current_brick, (size_t)args.highest_plug));
23406 set_brick (current_brick,
23407 (args.highest_plug - brick_address (current_brick)));
23408 }
23409 else
23410 {
23411 if ((brick_entry > -32768))
23412 {
23413
23414#ifdef _DEBUG
23415 ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
23416 if ((brick_entry != -32767) && (! ((offset == brick_entry))))
23417 {
23418 assert ((brick_entry == -1));
23419 }
23420#endif //_DEBUG
23421 //init to -1 for faster find_first_object
23422 set_brick (current_brick, -1);
23423 }
23424 }
23425 }
23426 current_brick++;
23427 }
23428 }
23429 {
23430 int bottom_gen = 0;
23431 args.free_list_gen_number--;
23432 while (args.free_list_gen_number >= bottom_gen)
23433 {
23434 uint8_t* gap = 0;
23435 generation* gen2 = generation_of (args.free_list_gen_number);
23436 gap = allocate_at_end (Align(min_obj_size));
23437 generation_allocation_start (gen2) = gap;
23438 reset_allocation_pointers (gen2, gap);
23439 dprintf(3,("Fixing generation start of %d to: %Ix",
23440 args.free_list_gen_number, (size_t)gap));
23441 PREFIX_ASSUME(gap != NULL);
23442 make_unused_array (gap, Align (min_obj_size));
23443
23444 args.free_list_gen_number--;
23445 }
23446
23447 //reset the allocated size
23448 uint8_t* start2 = generation_allocation_start (youngest_generation);
23449 alloc_allocated = start2 + Align (size (start2));
23450 }
23451
23452#ifdef TIME_GC
23453 finish = GetCycleCount32();
23454 sweep_time = finish - start;
23455#endif //TIME_GC
23456}
23457
23458void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args)
23459{
23460 assert ((tree != NULL));
23461 {
23462 int right_node = node_right_child (tree);
23463 int left_node = node_left_child (tree);
23464 args->highest_plug = 0;
23465 if (! (0 == tree))
23466 {
23467 if (! (0 == left_node))
23468 {
23469 make_free_list_in_brick (tree + left_node, args);
23470
23471 }
23472 {
23473 uint8_t* plug = tree;
23474 size_t gap_size = node_gap_size (tree);
23475 uint8_t* gap = (plug - gap_size);
23476 dprintf (3,("Making free list %Ix len %d in %d",
23477 //dprintf (3,("F: %Ix len %Ix in %d",
23478 (size_t)gap, gap_size, args->free_list_gen_number));
23479 args->highest_plug = tree;
23480#ifdef SHORT_PLUGS
23481 if (is_plug_padded (plug))
23482 {
23483 dprintf (3, ("%Ix padded", plug));
23484 clear_plug_padded (plug);
23485 }
23486#endif //SHORT_PLUGS
23487 gen_crossing:
23488 {
23489 if ((args->current_gen_limit == MAX_PTR) ||
23490 ((plug >= args->current_gen_limit) &&
23491 ephemeral_pointer_p (plug)))
23492 {
23493 dprintf(3,(" Crossing Generation boundary at %Ix",
23494 (size_t)args->current_gen_limit));
23495 if (!(args->current_gen_limit == MAX_PTR))
23496 {
23497 args->free_list_gen_number--;
23498 args->free_list_gen = generation_of (args->free_list_gen_number);
23499 }
23500 dprintf(3,( " Fixing generation start of %d to: %Ix",
23501 args->free_list_gen_number, (size_t)gap));
23502
23503 reset_allocation_pointers (args->free_list_gen, gap);
23504 args->current_gen_limit = generation_limit (args->free_list_gen_number);
23505
23506 if ((gap_size >= (2*Align (min_obj_size))))
23507 {
23508 dprintf(3,(" Splitting the gap in two %Id left",
23509 gap_size));
23510 make_unused_array (gap, Align(min_obj_size));
23511 gap_size = (gap_size - Align(min_obj_size));
23512 gap = (gap + Align(min_obj_size));
23513 }
23514 else
23515 {
23516 make_unused_array (gap, gap_size);
23517 gap_size = 0;
23518 }
23519 goto gen_crossing;
23520 }
23521 }
23522
23523 thread_gap (gap, gap_size, args->free_list_gen);
23524 add_gen_free (args->free_list_gen->gen_num, gap_size);
23525 }
23526 if (! (0 == right_node))
23527 {
23528 make_free_list_in_brick (tree + right_node, args);
23529 }
23530 }
23531 }
23532}
23533
23534void gc_heap::thread_gap (uint8_t* gap_start, size_t size, generation* gen)
23535{
23536 assert (generation_allocation_start (gen));
23537 if ((size > 0))
23538 {
23539 if ((gen->gen_num == 0) && (size > CLR_SIZE))
23540 {
23541 gen0_big_free_spaces += size;
23542 }
23543
23544 assert ((heap_segment_rw (generation_start_segment (gen))!=
23545 ephemeral_heap_segment) ||
23546 (gap_start > generation_allocation_start (gen)));
23547 // The beginning of a segment gap is not aligned
23548 assert (size >= Align (min_obj_size));
23549 make_unused_array (gap_start, size,
23550 (!settings.concurrent && (gen != youngest_generation)),
23551 (gen->gen_num == max_generation));
23552 dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
23553
23554 if ((size >= min_free_list))
23555 {
23556 generation_free_list_space (gen) += size;
23557 generation_allocator (gen)->thread_item (gap_start, size);
23558 }
23559 else
23560 {
23561 generation_free_obj_space (gen) += size;
23562 }
23563 }
23564}
23565
23566void gc_heap::loh_thread_gap_front (uint8_t* gap_start, size_t size, generation* gen)
23567{
23568 assert (generation_allocation_start (gen));
23569 if (size >= min_free_list)
23570 {
23571 generation_free_list_space (gen) += size;
23572 generation_allocator (gen)->thread_item_front (gap_start, size);
23573 }
23574}
23575
23576void gc_heap::make_unused_array (uint8_t* x, size_t size, BOOL clearp, BOOL resetp)
23577{
23578 dprintf (3, ("Making unused array [%Ix, %Ix[",
23579 (size_t)x, (size_t)(x+size)));
23580 assert (size >= Align (min_obj_size));
23581
23582//#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
23583// check_batch_mark_array_bits (x, x+size);
23584//#endif //VERIFY_HEAP && BACKGROUND_GC
23585
23586 if (resetp)
23587 reset_memory (x, size);
23588
23589 ((CObjectHeader*)x)->SetFree(size);
23590
23591#ifdef BIT64
23592
23593#if BIGENDIAN
23594#error "This won't work on big endian platforms"
23595#endif
23596
23597 size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23598
23599 if (size_as_object < size)
23600 {
23601 //
23602 // If the size is more than 4GB, we need to create multiple objects because of
23603 // the Array::m_NumComponents is uint32_t and the high 32 bits of unused array
23604 // size is ignored in regular object size computation.
23605 //
23606 uint8_t * tmp = x + size_as_object;
23607 size_t remaining_size = size - size_as_object;
23608
23609 while (remaining_size > UINT32_MAX)
23610 {
23611 // Make sure that there will be at least Align(min_obj_size) left
23612 size_t current_size = UINT32_MAX - get_alignment_constant (FALSE)
23613 - Align (min_obj_size, get_alignment_constant (FALSE));
23614
23615 ((CObjectHeader*)tmp)->SetFree(current_size);
23616
23617 remaining_size -= current_size;
23618 tmp += current_size;
23619 }
23620
23621 ((CObjectHeader*)tmp)->SetFree(remaining_size);
23622 }
23623#endif
23624
23625 if (clearp)
23626 clear_card_for_addresses (x, x + Align(size));
23627}
23628
23629// Clear memory set by make_unused_array.
23630void gc_heap::clear_unused_array (uint8_t* x, size_t size)
23631{
23632 // Also clear the sync block
23633 *(((PTR_PTR)x)-1) = 0;
23634
23635 ((CObjectHeader*)x)->UnsetFree();
23636
23637#ifdef BIT64
23638
23639#if BIGENDIAN
23640#error "This won't work on big endian platforms"
23641#endif
23642
23643 // The memory could have been cleared in the meantime. We have to mirror the algorithm
23644 // from make_unused_array since we cannot depend on the object sizes in memory.
23645 size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23646
23647 if (size_as_object < size)
23648 {
23649 uint8_t * tmp = x + size_as_object;
23650 size_t remaining_size = size - size_as_object;
23651
23652 while (remaining_size > UINT32_MAX)
23653 {
23654 size_t current_size = UINT32_MAX - get_alignment_constant (FALSE)
23655 - Align (min_obj_size, get_alignment_constant (FALSE));
23656
23657 ((CObjectHeader*)tmp)->UnsetFree();
23658
23659 remaining_size -= current_size;
23660 tmp += current_size;
23661 }
23662
23663 ((CObjectHeader*)tmp)->UnsetFree();
23664 }
23665#else
23666 UNREFERENCED_PARAMETER(size);
23667#endif
23668}
23669
23670inline
23671uint8_t* tree_search (uint8_t* tree, uint8_t* old_address)
23672{
23673 uint8_t* candidate = 0;
23674 int cn;
23675 while (1)
23676 {
23677 if (tree < old_address)
23678 {
23679 if ((cn = node_right_child (tree)) != 0)
23680 {
23681 assert (candidate < tree);
23682 candidate = tree;
23683 tree = tree + cn;
23684 Prefetch (tree - 8);
23685 continue;
23686 }
23687 else
23688 break;
23689 }
23690 else if (tree > old_address)
23691 {
23692 if ((cn = node_left_child (tree)) != 0)
23693 {
23694 tree = tree + cn;
23695 Prefetch (tree - 8);
23696 continue;
23697 }
23698 else
23699 break;
23700 } else
23701 break;
23702 }
23703 if (tree <= old_address)
23704 return tree;
23705 else if (candidate)
23706 return candidate;
23707 else
23708 return tree;
23709}
23710
23711#ifdef FEATURE_BASICFREEZE
23712bool gc_heap::frozen_object_p (Object* obj)
23713{
23714#ifdef MULTIPLE_HEAPS
23715 ptrdiff_t delta = 0;
23716 heap_segment* pSegment = segment_of ((uint8_t*)obj, delta);
23717#else //MULTIPLE_HEAPS
23718 heap_segment* pSegment = gc_heap::find_segment ((uint8_t*)obj, FALSE);
23719 _ASSERTE(pSegment);
23720#endif //MULTIPLE_HEAPS
23721
23722 return heap_segment_read_only_p(pSegment);
23723}
23724#endif // FEATURE_BASICFREEZE
23725
23726#ifdef FEATURE_REDHAWK
23727// TODO: this was added on RH, we have not done perf runs to see if this is the right
23728// thing to do for other versions of the CLR.
23729inline
23730#endif // FEATURE_REDHAWK
23731void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL)
23732{
23733 uint8_t* old_address = *pold_address;
23734 if (!((old_address >= gc_low) && (old_address < gc_high)))
23735#ifdef MULTIPLE_HEAPS
23736 {
23737 UNREFERENCED_PARAMETER(thread);
23738 if (old_address == 0)
23739 return;
23740 gc_heap* hp = heap_of (old_address);
23741 if ((hp == this) ||
23742 !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
23743 return;
23744 }
23745#else //MULTIPLE_HEAPS
23746 return ;
23747#endif //MULTIPLE_HEAPS
23748 // delta translates old_address into address_gc (old_address);
23749 size_t brick = brick_of (old_address);
23750 int brick_entry = brick_table [ brick ];
23751 uint8_t* new_address = old_address;
23752 if (! ((brick_entry == 0)))
23753 {
23754 retry:
23755 {
23756 while (brick_entry < 0)
23757 {
23758 brick = (brick + brick_entry);
23759 brick_entry = brick_table [ brick ];
23760 }
23761 uint8_t* old_loc = old_address;
23762
23763 uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
23764 old_loc);
23765 if ((node <= old_loc))
23766 new_address = (old_address + node_relocation_distance (node));
23767 else
23768 {
23769 if (node_left_p (node))
23770 {
23771 dprintf(3,(" L: %Ix", (size_t)node));
23772 new_address = (old_address +
23773 (node_relocation_distance (node) +
23774 node_gap_size (node)));
23775 }
23776 else
23777 {
23778 brick = brick - 1;
23779 brick_entry = brick_table [ brick ];
23780 goto retry;
23781 }
23782 }
23783 }
23784
23785 *pold_address = new_address;
23786 return;
23787 }
23788
23789#ifdef FEATURE_LOH_COMPACTION
23790 if (loh_compacted_p
23791#ifdef FEATURE_BASICFREEZE
23792 && !frozen_object_p((Object*)old_address)
23793#endif // FEATURE_BASICFREEZE
23794 )
23795 {
23796 *pold_address = old_address + loh_node_relocation_distance (old_address);
23797 }
23798 else
23799#endif //FEATURE_LOH_COMPACTION
23800 {
23801 *pold_address = new_address;
23802 }
23803}
23804
23805inline void
23806gc_heap::check_class_object_demotion (uint8_t* obj)
23807{
23808#ifdef COLLECTIBLE_CLASS
23809 if (is_collectible(obj))
23810 {
23811 check_class_object_demotion_internal (obj);
23812 }
23813#else
23814 UNREFERENCED_PARAMETER(obj);
23815#endif //COLLECTIBLE_CLASS
23816}
23817
23818#ifdef COLLECTIBLE_CLASS
23819NOINLINE void
23820gc_heap::check_class_object_demotion_internal (uint8_t* obj)
23821{
23822 if (settings.demotion)
23823 {
23824#ifdef MULTIPLE_HEAPS
23825 // We set the card without checking the demotion range 'cause at this point
23826 // the handle that points to the loader allocator object may or may not have
23827 // been relocated by other GC threads.
23828 set_card (card_of (obj));
23829#else
23830 THREAD_FROM_HEAP;
23831 uint8_t* class_obj = get_class_object (obj);
23832 dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
23833 uint8_t* temp_class_obj = class_obj;
23834 uint8_t** temp = &temp_class_obj;
23835 relocate_address (temp THREAD_NUMBER_ARG);
23836
23837 check_demotion_helper (temp, obj);
23838#endif //MULTIPLE_HEAPS
23839 }
23840}
23841
23842#endif //COLLECTIBLE_CLASS
23843
23844inline void
23845gc_heap::check_demotion_helper (uint8_t** pval, uint8_t* parent_obj)
23846{
23847 // detect if we are demoting an object
23848 if ((*pval < demotion_high) &&
23849 (*pval >= demotion_low))
23850 {
23851 dprintf(3, ("setting card %Ix:%Ix",
23852 card_of((uint8_t*)pval),
23853 (size_t)pval));
23854
23855 set_card (card_of (parent_obj));
23856 }
23857#ifdef MULTIPLE_HEAPS
23858 else if (settings.demotion)
23859 {
23860 dprintf (4, ("Demotion active, computing heap_of object"));
23861 gc_heap* hp = heap_of (*pval);
23862 if ((*pval < hp->demotion_high) &&
23863 (*pval >= hp->demotion_low))
23864 {
23865 dprintf(3, ("setting card %Ix:%Ix",
23866 card_of((uint8_t*)pval),
23867 (size_t)pval));
23868
23869 set_card (card_of (parent_obj));
23870 }
23871 }
23872#endif //MULTIPLE_HEAPS
23873}
23874
23875inline void
23876gc_heap::reloc_survivor_helper (uint8_t** pval)
23877{
23878 THREAD_FROM_HEAP;
23879 relocate_address (pval THREAD_NUMBER_ARG);
23880
23881 check_demotion_helper (pval, (uint8_t*)pval);
23882}
23883
23884inline void
23885gc_heap::relocate_obj_helper (uint8_t* x, size_t s)
23886{
23887 THREAD_FROM_HEAP;
23888 if (contain_pointers (x))
23889 {
23890 dprintf (3, ("$%Ix$", (size_t)x));
23891
23892 go_through_object_nostart (method_table(x), x, s, pval,
23893 {
23894 uint8_t* child = *pval;
23895 reloc_survivor_helper (pval);
23896 if (child)
23897 {
23898 dprintf (3, ("%Ix->%Ix->%Ix", (uint8_t*)pval, child, *pval));
23899 }
23900 });
23901
23902 }
23903 check_class_object_demotion (x);
23904}
23905
23906inline
23907void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc)
23908{
23909 THREAD_FROM_HEAP;
23910
23911 uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0);
23912 relocate_address (address_to_reloc THREAD_NUMBER_ARG);
23913 if (address_to_reloc)
23914 {
23915 dprintf (3, ("SR %Ix: %Ix->%Ix", (uint8_t*)address_to_reloc, old_val, *address_to_reloc));
23916 }
23917
23918 //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval);
23919 uint8_t* relocated_addr = *address_to_reloc;
23920 if ((relocated_addr < demotion_high) &&
23921 (relocated_addr >= demotion_low))
23922 {
23923 dprintf (3, ("set card for location %Ix(%Ix)",
23924 (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23925
23926 set_card (card_of ((uint8_t*)address_to_set_card));
23927 }
23928#ifdef MULTIPLE_HEAPS
23929 else if (settings.demotion)
23930 {
23931 gc_heap* hp = heap_of (relocated_addr);
23932 if ((relocated_addr < hp->demotion_high) &&
23933 (relocated_addr >= hp->demotion_low))
23934 {
23935 dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)",
23936 relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
23937
23938 set_card (card_of ((uint8_t*)address_to_set_card));
23939 }
23940 }
23941#endif //MULTIPLE_HEAPS
23942}
23943
23944void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
23945{
23946 THREAD_FROM_HEAP;
23947 uint8_t* plug = pinned_plug (pinned_plug_entry);
23948 uint8_t* pre_plug_start = plug - sizeof (plug_and_gap);
23949 // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
23950 // address. Consider this scenario:
23951 // gen1 start | 3-ptr sized NP | PP
23952 // 0 | 0x18 | 0x30
23953 // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
23954 // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
23955 // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree).
23956 pre_plug_start += sizeof (uint8_t*);
23957 uint8_t** old_address = &pre_plug_start;
23958
23959 uint8_t* old_val = (old_address ? *old_address : 0);
23960 relocate_address (old_address THREAD_NUMBER_ARG);
23961 if (old_address)
23962 {
23963 dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix",
23964 (uint8_t*)old_address, old_val, *old_address, (pre_plug_start - sizeof (uint8_t*))));
23965 }
23966
23967 pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (uint8_t*));
23968}
23969
23970inline
23971void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned)
23972{
23973 THREAD_FROM_HEAP;
23974 uint8_t* plug = pinned_plug (pinned_plug_entry);
23975
23976 if (!is_pinned)
23977 {
23978 //// Temporary - we just wanna make sure we are doing things right when padding is needed.
23979 //if ((x + s) < plug)
23980 //{
23981 // dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix",
23982 // x, (x + s), (plug- (x + s)), plug));
23983 // GCToOSInterface::DebugBreak();
23984 //}
23985
23986 relocate_pre_plug_info (pinned_plug_entry);
23987 }
23988
23989 verify_pins_with_post_plug_info("after relocate_pre_plug_info");
23990
23991 uint8_t* saved_plug_info_start = 0;
23992 uint8_t** saved_info_to_relocate = 0;
23993
23994 if (is_pinned)
23995 {
23996 saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start());
23997 saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
23998 }
23999 else
24000 {
24001 saved_plug_info_start = (plug - sizeof (plug_and_gap));
24002 saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
24003 }
24004
24005 uint8_t** current_saved_info_to_relocate = 0;
24006 uint8_t* child = 0;
24007
24008 dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
24009
24010 if (contain_pointers (x))
24011 {
24012 dprintf (3,("$%Ix$", (size_t)x));
24013
24014 go_through_object_nostart (method_table(x), x, s, pval,
24015 {
24016 dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (uint8_t*)pval, *pval));
24017
24018 if ((uint8_t*)pval >= end)
24019 {
24020 current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**);
24021 child = *current_saved_info_to_relocate;
24022 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
24023 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
24024 (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
24025 }
24026 else
24027 {
24028 reloc_survivor_helper (pval);
24029 }
24030 });
24031 }
24032
24033 check_class_object_demotion (x);
24034}
24035
24036void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end)
24037{
24038 uint8_t* x = plug;
24039 while (x < plug_end)
24040 {
24041 size_t s = size (x);
24042 uint8_t* next_obj = x + Align (s);
24043 Prefetch (next_obj);
24044 relocate_obj_helper (x, s);
24045 assert (s > 0);
24046 x = next_obj;
24047 }
24048}
24049
24050// if we expanded, right now we are not handling it as We are not saving the new reloc info.
24051void gc_heap::verify_pins_with_post_plug_info (const char* msg)
24052{
24053#if defined (_DEBUG) && defined (VERIFY_HEAP)
24054 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
24055 {
24056 if (!verify_pinned_queue_p)
24057 return;
24058
24059 if (settings.heap_expansion)
24060 return;
24061
24062 for (size_t i = 0; i < mark_stack_tos; i++)
24063 {
24064 mark& m = mark_stack_array[i];
24065
24066 mark* pinned_plug_entry = pinned_plug_of(i);
24067
24068 if (pinned_plug_entry->has_post_plug_info() &&
24069 pinned_plug_entry->post_short_p() &&
24070 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
24071 {
24072 uint8_t* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
24073 // object after pin
24074 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d",
24075 next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
24076 (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
24077
24078 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
24079
24080 if (node_gap_size (next_obj) != *post_plug_debug)
24081 {
24082 dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix",
24083 next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
24084 FATAL_GC_ERROR();
24085 }
24086 post_plug_debug++;
24087 // can't do node_relocation_distance here as it clears the left bit.
24088 //if (node_relocation_distance (next_obj) != *post_plug_debug)
24089 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
24090 {
24091 dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix",
24092 next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
24093 FATAL_GC_ERROR();
24094 }
24095 if (node_left_child (next_obj) > 0)
24096 {
24097 dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
24098 FATAL_GC_ERROR();
24099 }
24100 }
24101 }
24102
24103 dprintf (3, ("%s verified", msg));
24104 }
24105#else // _DEBUG && VERIFY_HEAP
24106 UNREFERENCED_PARAMETER(msg);
24107#endif // _DEBUG && VERIFY_HEAP
24108}
24109
24110#ifdef COLLECTIBLE_CLASS
24111// We don't want to burn another ptr size space for pinned plugs to record this so just
24112// set the card unconditionally for collectible objects if we are demoting.
24113inline void
24114gc_heap::unconditional_set_card_collectible (uint8_t* obj)
24115{
24116 if (settings.demotion)
24117 {
24118 set_card (card_of (obj));
24119 }
24120}
24121#endif //COLLECTIBLE_CLASS
24122
24123void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry)
24124{
24125 uint8_t* x = plug;
24126 uint8_t* p_plug = pinned_plug (pinned_plug_entry);
24127 BOOL is_pinned = (plug == p_plug);
24128 BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
24129
24130 plug_end += sizeof (gap_reloc_pair);
24131
24132 //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
24133 dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
24134
24135 verify_pins_with_post_plug_info("begin reloc short surv");
24136
24137 while (x < plug_end)
24138 {
24139 if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
24140 {
24141 dprintf (3, ("last obj %Ix is short", x));
24142
24143 if (is_pinned)
24144 {
24145#ifdef COLLECTIBLE_CLASS
24146 if (pinned_plug_entry->post_short_collectible_p())
24147 unconditional_set_card_collectible (x);
24148#endif //COLLECTIBLE_CLASS
24149
24150 // Relocate the saved references based on bits set.
24151 uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start());
24152 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
24153 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
24154 {
24155 if (pinned_plug_entry->post_short_bit_p (i))
24156 {
24157 reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
24158 }
24159 }
24160 }
24161 else
24162 {
24163#ifdef COLLECTIBLE_CLASS
24164 if (pinned_plug_entry->pre_short_collectible_p())
24165 unconditional_set_card_collectible (x);
24166#endif //COLLECTIBLE_CLASS
24167
24168 relocate_pre_plug_info (pinned_plug_entry);
24169
24170 // Relocate the saved references based on bits set.
24171 uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap));
24172 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
24173 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
24174 {
24175 if (pinned_plug_entry->pre_short_bit_p (i))
24176 {
24177 reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
24178 }
24179 }
24180 }
24181
24182 break;
24183 }
24184
24185 size_t s = size (x);
24186 uint8_t* next_obj = x + Align (s);
24187 Prefetch (next_obj);
24188
24189 if (next_obj >= plug_end)
24190 {
24191 dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix",
24192 next_obj, plug, plug_end));
24193
24194 verify_pins_with_post_plug_info("before reloc short obj");
24195
24196 relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
24197 }
24198 else
24199 {
24200 relocate_obj_helper (x, s);
24201 }
24202
24203 assert (s > 0);
24204 x = next_obj;
24205 }
24206
24207 verify_pins_with_post_plug_info("end reloc short surv");
24208}
24209
24210void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end,
24211 BOOL check_last_object_p,
24212 mark* pinned_plug_entry)
24213{
24214 //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
24215 dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
24216
24217 if (check_last_object_p)
24218 {
24219 relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
24220 }
24221 else
24222 {
24223 relocate_survivor_helper (plug, plug_end);
24224 }
24225}
24226
24227void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args)
24228{
24229 assert ((tree != NULL));
24230
24231 dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
24232 tree, args->last_plug,
24233 (tree + node_left_child (tree)),
24234 (tree + node_right_child (tree)),
24235 node_gap_size (tree)));
24236
24237 if (node_left_child (tree))
24238 {
24239 relocate_survivors_in_brick (tree + node_left_child (tree), args);
24240 }
24241 {
24242 uint8_t* plug = tree;
24243 BOOL has_post_plug_info_p = FALSE;
24244 BOOL has_pre_plug_info_p = FALSE;
24245
24246 if (tree == oldest_pinned_plug)
24247 {
24248 args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24249 &has_post_plug_info_p);
24250 assert (tree == pinned_plug (args->pinned_plug_entry));
24251
24252 dprintf (3, ("tree is the oldest pin: %Ix", tree));
24253 }
24254 if (args->last_plug)
24255 {
24256 size_t gap_size = node_gap_size (tree);
24257 uint8_t* gap = (plug - gap_size);
24258 dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
24259 assert (gap_size >= Align (min_obj_size));
24260 uint8_t* last_plug_end = gap;
24261
24262 BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24263
24264 {
24265 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
24266 }
24267 }
24268 else
24269 {
24270 assert (!has_pre_plug_info_p);
24271 }
24272
24273 args->last_plug = plug;
24274 args->is_shortened = has_post_plug_info_p;
24275 if (has_post_plug_info_p)
24276 {
24277 dprintf (3, ("setting %Ix as shortened", plug));
24278 }
24279 dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
24280 }
24281 if (node_right_child (tree))
24282 {
24283 relocate_survivors_in_brick (tree + node_right_child (tree), args);
24284 }
24285}
24286
24287inline
24288void gc_heap::update_oldest_pinned_plug()
24289{
24290 oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
24291}
24292
24293void gc_heap::relocate_survivors (int condemned_gen_number,
24294 uint8_t* first_condemned_address)
24295{
24296 generation* condemned_gen = generation_of (condemned_gen_number);
24297 uint8_t* start_address = first_condemned_address;
24298 size_t current_brick = brick_of (start_address);
24299 heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24300
24301 PREFIX_ASSUME(current_heap_segment != NULL);
24302
24303 uint8_t* end_address = 0;
24304
24305 reset_pinned_queue_bos();
24306 update_oldest_pinned_plug();
24307
24308 end_address = heap_segment_allocated (current_heap_segment);
24309
24310 size_t end_brick = brick_of (end_address - 1);
24311 relocate_args args;
24312 args.low = gc_low;
24313 args.high = gc_high;
24314 args.is_shortened = FALSE;
24315 args.pinned_plug_entry = 0;
24316 args.last_plug = 0;
24317 while (1)
24318 {
24319 if (current_brick > end_brick)
24320 {
24321 if (args.last_plug)
24322 {
24323 {
24324 assert (!(args.is_shortened));
24325 relocate_survivors_in_plug (args.last_plug,
24326 heap_segment_allocated (current_heap_segment),
24327 args.is_shortened,
24328 args.pinned_plug_entry);
24329 }
24330
24331 args.last_plug = 0;
24332 }
24333
24334 if (heap_segment_next_rw (current_heap_segment))
24335 {
24336 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24337 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24338 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24339 continue;
24340 }
24341 else
24342 {
24343 break;
24344 }
24345 }
24346 {
24347 int brick_entry = brick_table [ current_brick ];
24348
24349 if (brick_entry >= 0)
24350 {
24351 relocate_survivors_in_brick (brick_address (current_brick) +
24352 brick_entry -1,
24353 &args);
24354 }
24355 }
24356 current_brick++;
24357 }
24358}
24359
24360void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args)
24361{
24362 if (check_last_object_p)
24363 {
24364 size += sizeof (gap_reloc_pair);
24365 mark* entry = args->pinned_plug_entry;
24366
24367 if (args->is_shortened)
24368 {
24369 assert (entry->has_post_plug_info());
24370 entry->swap_post_plug_and_saved_for_profiler();
24371 }
24372 else
24373 {
24374 assert (entry->has_pre_plug_info());
24375 entry->swap_pre_plug_and_saved_for_profiler();
24376 }
24377 }
24378
24379 ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
24380 STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
24381 ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
24382
24383 (args->fn) (plug, (plug + size), reloc, args->profiling_context, !!settings.compaction, false);
24384
24385 if (check_last_object_p)
24386 {
24387 mark* entry = args->pinned_plug_entry;
24388
24389 if (args->is_shortened)
24390 {
24391 entry->swap_post_plug_and_saved_for_profiler();
24392 }
24393 else
24394 {
24395 entry->swap_pre_plug_and_saved_for_profiler();
24396 }
24397 }
24398}
24399
24400void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args)
24401{
24402 assert ((tree != NULL));
24403 if (node_left_child (tree))
24404 {
24405 walk_relocation_in_brick (tree + node_left_child (tree), args);
24406 }
24407
24408 uint8_t* plug = tree;
24409 BOOL has_pre_plug_info_p = FALSE;
24410 BOOL has_post_plug_info_p = FALSE;
24411
24412 if (tree == oldest_pinned_plug)
24413 {
24414 args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24415 &has_post_plug_info_p);
24416 assert (tree == pinned_plug (args->pinned_plug_entry));
24417 }
24418
24419 if (args->last_plug != 0)
24420 {
24421 size_t gap_size = node_gap_size (tree);
24422 uint8_t* gap = (plug - gap_size);
24423 uint8_t* last_plug_end = gap;
24424 size_t last_plug_size = (last_plug_end - args->last_plug);
24425 dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix",
24426 tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24427
24428 BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24429 if (!check_last_object_p)
24430 {
24431 assert (last_plug_size >= Align (min_obj_size));
24432 }
24433
24434 walk_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24435 }
24436 else
24437 {
24438 assert (!has_pre_plug_info_p);
24439 }
24440
24441 dprintf (3, ("set args last plug to plug: %Ix", plug));
24442 args->last_plug = plug;
24443 args->is_shortened = has_post_plug_info_p;
24444
24445 if (node_right_child (tree))
24446 {
24447 walk_relocation_in_brick (tree + node_right_child (tree), args);
24448 }
24449}
24450
24451void gc_heap::walk_relocation (void* profiling_context, record_surv_fn fn)
24452{
24453 generation* condemned_gen = generation_of (settings.condemned_generation);
24454 uint8_t* start_address = generation_allocation_start (condemned_gen);
24455 size_t current_brick = brick_of (start_address);
24456 heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24457
24458 PREFIX_ASSUME(current_heap_segment != NULL);
24459
24460 reset_pinned_queue_bos();
24461 update_oldest_pinned_plug();
24462 size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24463 walk_relocate_args args;
24464 args.is_shortened = FALSE;
24465 args.pinned_plug_entry = 0;
24466 args.last_plug = 0;
24467 args.profiling_context = profiling_context;
24468 args.fn = fn;
24469
24470 while (1)
24471 {
24472 if (current_brick > end_brick)
24473 {
24474 if (args.last_plug)
24475 {
24476 walk_plug (args.last_plug,
24477 (heap_segment_allocated (current_heap_segment) - args.last_plug),
24478 args.is_shortened,
24479 &args);
24480 args.last_plug = 0;
24481 }
24482 if (heap_segment_next_rw (current_heap_segment))
24483 {
24484 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24485 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24486 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24487 continue;
24488 }
24489 else
24490 {
24491 break;
24492 }
24493 }
24494 {
24495 int brick_entry = brick_table [ current_brick ];
24496 if (brick_entry >= 0)
24497 {
24498 walk_relocation_in_brick (brick_address (current_brick) +
24499 brick_entry - 1,
24500 &args);
24501 }
24502 }
24503 current_brick++;
24504 }
24505}
24506
24507void gc_heap::walk_survivors (record_surv_fn fn, void* context, walk_surv_type type)
24508{
24509 if (type == walk_for_gc)
24510 walk_survivors_relocation (context, fn);
24511#if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24512 else if (type == walk_for_bgc)
24513 walk_survivors_for_bgc (context, fn);
24514#endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
24515 else if (type == walk_for_loh)
24516 walk_survivors_for_loh (context, fn);
24517 else
24518 assert (!"unknown type!");
24519}
24520
24521#if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24522void gc_heap::walk_survivors_for_bgc (void* profiling_context, record_surv_fn fn)
24523{
24524 // This should only be called for BGCs
24525 assert(settings.concurrent);
24526
24527 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24528
24529 BOOL small_object_segments = TRUE;
24530 int align_const = get_alignment_constant (small_object_segments);
24531
24532 while (1)
24533 {
24534 if (seg == 0)
24535 {
24536 if (small_object_segments)
24537 {
24538 //switch to large segment
24539 small_object_segments = FALSE;
24540
24541 align_const = get_alignment_constant (small_object_segments);
24542 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24543
24544 PREFIX_ASSUME(seg != NULL);
24545
24546 continue;
24547 }
24548 else
24549 break;
24550 }
24551
24552 uint8_t* o = heap_segment_mem (seg);
24553 uint8_t* end = heap_segment_allocated (seg);
24554
24555 while (o < end)
24556 {
24557 if (method_table(o) == g_gc_pFreeObjectMethodTable)
24558 {
24559 o += Align (size (o), align_const);
24560 continue;
24561 }
24562
24563 // It's survived. Make a fake plug, starting at o,
24564 // and send the event
24565
24566 uint8_t* plug_start = o;
24567
24568 while (method_table(o) != g_gc_pFreeObjectMethodTable)
24569 {
24570 o += Align (size (o), align_const);
24571 if (o >= end)
24572 {
24573 break;
24574 }
24575 }
24576
24577 uint8_t* plug_end = o;
24578
24579 fn (plug_start,
24580 plug_end,
24581 0, // Reloc distance == 0 as this is non-compacting
24582 profiling_context,
24583 false, // Non-compacting
24584 true); // BGC
24585 }
24586
24587 seg = heap_segment_next (seg);
24588 }
24589}
24590#endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24591
24592void gc_heap::relocate_phase (int condemned_gen_number,
24593 uint8_t* first_condemned_address)
24594{
24595 ScanContext sc;
24596 sc.thread_number = heap_number;
24597 sc.promotion = FALSE;
24598 sc.concurrent = FALSE;
24599
24600
24601#ifdef TIME_GC
24602 unsigned start;
24603 unsigned finish;
24604 start = GetCycleCount32();
24605#endif //TIME_GC
24606
24607// %type% category = quote (relocate);
24608 dprintf (2,("---- Relocate phase -----"));
24609
24610#ifdef MULTIPLE_HEAPS
24611 //join all threads to make sure they are synchronized
24612 dprintf(3, ("Joining after end of plan"));
24613 gc_t_join.join(this, gc_join_begin_relocate_phase);
24614 if (gc_t_join.joined())
24615#endif //MULTIPLE_HEAPS
24616
24617 {
24618#ifdef MULTIPLE_HEAPS
24619
24620 //join all threads to make sure they are synchronized
24621 dprintf(3, ("Restarting for relocation"));
24622 gc_t_join.restart();
24623#endif //MULTIPLE_HEAPS
24624 }
24625
24626 dprintf(3,("Relocating roots"));
24627 GCScan::GcScanRoots(GCHeap::Relocate,
24628 condemned_gen_number, max_generation, &sc);
24629
24630 verify_pins_with_post_plug_info("after reloc stack");
24631
24632#ifdef BACKGROUND_GC
24633 if (recursive_gc_sync::background_running_p())
24634 {
24635 scan_background_roots (GCHeap::Relocate, heap_number, &sc);
24636 }
24637#endif //BACKGROUND_GC
24638
24639 if (condemned_gen_number != max_generation)
24640 {
24641 dprintf(3,("Relocating cross generation pointers"));
24642 mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
24643 verify_pins_with_post_plug_info("after reloc cards");
24644 }
24645 if (condemned_gen_number != max_generation)
24646 {
24647 dprintf(3,("Relocating cross generation pointers for large objects"));
24648 mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
24649 }
24650 else
24651 {
24652#ifdef FEATURE_LOH_COMPACTION
24653 if (loh_compacted_p)
24654 {
24655 assert (settings.condemned_generation == max_generation);
24656 relocate_in_loh_compact();
24657 }
24658 else
24659#endif //FEATURE_LOH_COMPACTION
24660 {
24661 relocate_in_large_objects ();
24662 }
24663 }
24664 {
24665 dprintf(3,("Relocating survivors"));
24666 relocate_survivors (condemned_gen_number,
24667 first_condemned_address);
24668 }
24669
24670#ifdef FEATURE_PREMORTEM_FINALIZATION
24671 dprintf(3,("Relocating finalization data"));
24672 finalize_queue->RelocateFinalizationData (condemned_gen_number,
24673 __this);
24674#endif // FEATURE_PREMORTEM_FINALIZATION
24675
24676
24677// MTHTS
24678 {
24679 dprintf(3,("Relocating handle table"));
24680 GCScan::GcScanHandles(GCHeap::Relocate,
24681 condemned_gen_number, max_generation, &sc);
24682 }
24683
24684#ifdef MULTIPLE_HEAPS
24685 //join all threads to make sure they are synchronized
24686 dprintf(3, ("Joining after end of relocation"));
24687 gc_t_join.join(this, gc_join_relocate_phase_done);
24688
24689#endif //MULTIPLE_HEAPS
24690
24691#ifdef TIME_GC
24692 finish = GetCycleCount32();
24693 reloc_time = finish - start;
24694#endif //TIME_GC
24695
24696 dprintf(2,( "---- End of Relocate phase ----"));
24697}
24698
24699// This compares to see if tree is the current pinned plug and returns info
24700// for this pinned plug. Also advances the pinned queue if that's the case.
24701//
24702// We don't change the values of the plug info if tree is not the same as
24703// the current pinned plug - the caller is responsible for setting the right
24704// values to begin with.
24705//
24706// POPO TODO: We are keeping this temporarily as this is also used by realloc
24707// where it passes FALSE to deque_p, change it to use the same optimization
24708// as relocate. Not as essential since realloc is already a slow path.
24709mark* gc_heap::get_next_pinned_entry (uint8_t* tree,
24710 BOOL* has_pre_plug_info_p,
24711 BOOL* has_post_plug_info_p,
24712 BOOL deque_p)
24713{
24714 if (!pinned_plug_que_empty_p())
24715 {
24716 mark* oldest_entry = oldest_pin();
24717 uint8_t* oldest_plug = pinned_plug (oldest_entry);
24718 if (tree == oldest_plug)
24719 {
24720 *has_pre_plug_info_p = oldest_entry->has_pre_plug_info();
24721 *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24722
24723 if (deque_p)
24724 {
24725 deque_pinned_plug();
24726 }
24727
24728 dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d",
24729 tree,
24730 (*has_pre_plug_info_p ? 1 : 0),
24731 (*has_post_plug_info_p ? 1 : 0)));
24732
24733 return oldest_entry;
24734 }
24735 }
24736
24737 return NULL;
24738}
24739
24740// This also deques the oldest entry and update the oldest plug
24741mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p,
24742 BOOL* has_post_plug_info_p)
24743{
24744 mark* oldest_entry = oldest_pin();
24745 *has_pre_plug_info_p = oldest_entry->has_pre_plug_info();
24746 *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24747
24748 deque_pinned_plug();
24749 update_oldest_pinned_plug();
24750 return oldest_entry;
24751}
24752
24753inline
24754void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24755{
24756 if (copy_cards_p)
24757 copy_cards_for_addresses (dest, src, len);
24758 else
24759 clear_card_for_addresses (dest, dest + len);
24760}
24761
24762// POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
24763// we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
24764// we won't need to individually recover each overwritten part of plugs.
24765inline
24766void gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
24767{
24768 if (dest != src)
24769 {
24770#ifdef BACKGROUND_GC
24771 if (current_c_gc_state == c_gc_state_marking)
24772 {
24773 //TODO: should look to see whether we should consider changing this
24774 // to copy a consecutive region of the mark array instead.
24775 copy_mark_bits_for_addresses (dest, src, len);
24776 }
24777#endif //BACKGROUND_GC
24778 //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24779 dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
24780 memcopy (dest - plug_skew, src - plug_skew, len);
24781#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24782 if (SoftwareWriteWatch::IsEnabledForGCHeap())
24783 {
24784 // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC
24785 // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the
24786 // object at (src + len), so it can be ignored anyway.
24787 SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew);
24788 }
24789#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
24790 copy_cards_range (dest, src, len, copy_cards_p);
24791 }
24792}
24793
24794void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args)
24795{
24796 args->print();
24797 uint8_t* reloc_plug = plug + args->last_plug_relocation;
24798
24799 if (check_last_object_p)
24800 {
24801 size += sizeof (gap_reloc_pair);
24802 mark* entry = args->pinned_plug_entry;
24803
24804 if (args->is_shortened)
24805 {
24806 assert (entry->has_post_plug_info());
24807 entry->swap_post_plug_and_saved();
24808 }
24809 else
24810 {
24811 assert (entry->has_pre_plug_info());
24812 entry->swap_pre_plug_and_saved();
24813 }
24814 }
24815
24816 int old_brick_entry = brick_table [brick_of (plug)];
24817
24818 assert (node_relocation_distance (plug) == args->last_plug_relocation);
24819
24820#ifdef FEATURE_STRUCTALIGN
24821 ptrdiff_t alignpad = node_alignpad(plug);
24822 if (alignpad)
24823 {
24824 make_unused_array (reloc_plug - alignpad, alignpad);
24825 if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
24826 {
24827 // The alignment padding is straddling one or more bricks;
24828 // it has to be the last "object" of its first brick.
24829 fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
24830 }
24831 }
24832#else // FEATURE_STRUCTALIGN
24833 size_t unused_arr_size = 0;
24834 BOOL already_padded_p = FALSE;
24835#ifdef SHORT_PLUGS
24836 if (is_plug_padded (plug))
24837 {
24838 already_padded_p = TRUE;
24839 clear_plug_padded (plug);
24840 unused_arr_size = Align (min_obj_size);
24841 }
24842#endif //SHORT_PLUGS
24843 if (node_realigned (plug))
24844 {
24845 unused_arr_size += switch_alignment_size (already_padded_p);
24846 }
24847
24848 if (unused_arr_size != 0)
24849 {
24850 make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
24851
24852 if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
24853 {
24854 dprintf (3, ("fix B for padding: %Id: %Ix->%Ix",
24855 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
24856 // The alignment padding is straddling one or more bricks;
24857 // it has to be the last "object" of its first brick.
24858 fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
24859 }
24860 }
24861#endif // FEATURE_STRUCTALIGN
24862
24863#ifdef SHORT_PLUGS
24864 if (is_plug_padded (plug))
24865 {
24866 make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
24867
24868 if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
24869 {
24870 // The alignment padding is straddling one or more bricks;
24871 // it has to be the last "object" of its first brick.
24872 fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
24873 }
24874 }
24875#endif //SHORT_PLUGS
24876
24877 gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
24878
24879 if (args->check_gennum_p)
24880 {
24881 int src_gennum = args->src_gennum;
24882 if (src_gennum == -1)
24883 {
24884 src_gennum = object_gennum (plug);
24885 }
24886
24887 int dest_gennum = object_gennum_plan (reloc_plug);
24888
24889 if (src_gennum < dest_gennum)
24890 {
24891 generation_allocation_size (generation_of (dest_gennum)) += size;
24892 }
24893 }
24894
24895 size_t current_reloc_brick = args->current_compacted_brick;
24896
24897 if (brick_of (reloc_plug) != current_reloc_brick)
24898 {
24899 dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix",
24900 current_reloc_brick, brick_of (reloc_plug)));
24901
24902 if (args->before_last_plug)
24903 {
24904 dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
24905 current_reloc_brick,
24906 args->before_last_plug,
24907 (args->before_last_plug - brick_address (current_reloc_brick))));
24908
24909 {
24910 set_brick (current_reloc_brick,
24911 args->before_last_plug - brick_address (current_reloc_brick));
24912 }
24913 }
24914 current_reloc_brick = brick_of (reloc_plug);
24915 }
24916 size_t end_brick = brick_of (reloc_plug + size-1);
24917 if (end_brick != current_reloc_brick)
24918 {
24919 // The plug is straddling one or more bricks
24920 // It has to be the last plug of its first brick
24921 dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
24922 current_reloc_brick, (size_t)reloc_plug,
24923 (reloc_plug - brick_address (current_reloc_brick))));
24924
24925 {
24926 set_brick (current_reloc_brick,
24927 reloc_plug - brick_address (current_reloc_brick));
24928 }
24929 // update all intervening brick
24930 size_t brick = current_reloc_brick + 1;
24931 dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
24932 brick, (end_brick - 1)));
24933 while (brick < end_brick)
24934 {
24935 set_brick (brick, -1);
24936 brick++;
24937 }
24938 // code last brick offset as a plug address
24939 args->before_last_plug = brick_address (end_brick) -1;
24940 current_reloc_brick = end_brick;
24941 dprintf (3, ("setting before last to %Ix, last brick to %Ix",
24942 args->before_last_plug, current_reloc_brick));
24943 }
24944 else
24945 {
24946 dprintf (3, ("still in the same brick: %Ix", end_brick));
24947 args->before_last_plug = reloc_plug;
24948 }
24949 args->current_compacted_brick = current_reloc_brick;
24950
24951 if (check_last_object_p)
24952 {
24953 mark* entry = args->pinned_plug_entry;
24954
24955 if (args->is_shortened)
24956 {
24957 entry->swap_post_plug_and_saved();
24958 }
24959 else
24960 {
24961 entry->swap_pre_plug_and_saved();
24962 }
24963 }
24964}
24965
24966void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args)
24967{
24968 assert (tree != NULL);
24969 int left_node = node_left_child (tree);
24970 int right_node = node_right_child (tree);
24971 ptrdiff_t relocation = node_relocation_distance (tree);
24972
24973 args->print();
24974
24975 if (left_node)
24976 {
24977 dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
24978 compact_in_brick ((tree + left_node), args);
24979 }
24980
24981 uint8_t* plug = tree;
24982 BOOL has_pre_plug_info_p = FALSE;
24983 BOOL has_post_plug_info_p = FALSE;
24984
24985 if (tree == oldest_pinned_plug)
24986 {
24987 args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24988 &has_post_plug_info_p);
24989 assert (tree == pinned_plug (args->pinned_plug_entry));
24990 }
24991
24992 if (args->last_plug != 0)
24993 {
24994 size_t gap_size = node_gap_size (tree);
24995 uint8_t* gap = (plug - gap_size);
24996 uint8_t* last_plug_end = gap;
24997 size_t last_plug_size = (last_plug_end - args->last_plug);
24998 dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix",
24999 tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
25000
25001 BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
25002 if (!check_last_object_p)
25003 {
25004 assert (last_plug_size >= Align (min_obj_size));
25005 }
25006
25007 compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
25008 }
25009 else
25010 {
25011 assert (!has_pre_plug_info_p);
25012 }
25013
25014 dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
25015 args->last_plug = plug;
25016 args->last_plug_relocation = relocation;
25017 args->is_shortened = has_post_plug_info_p;
25018
25019 if (right_node)
25020 {
25021 dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
25022 compact_in_brick ((tree + right_node), args);
25023 }
25024}
25025
25026void gc_heap::recover_saved_pinned_info()
25027{
25028 reset_pinned_queue_bos();
25029
25030 while (!(pinned_plug_que_empty_p()))
25031 {
25032 mark* oldest_entry = oldest_pin();
25033 oldest_entry->recover_plug_info();
25034#ifdef GC_CONFIG_DRIVEN
25035 if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info())
25036 record_interesting_data_point (idp_pre_and_post_pin);
25037 else if (oldest_entry->has_pre_plug_info())
25038 record_interesting_data_point (idp_pre_pin);
25039 else if (oldest_entry->has_post_plug_info())
25040 record_interesting_data_point (idp_post_pin);
25041#endif //GC_CONFIG_DRIVEN
25042
25043 deque_pinned_plug();
25044 }
25045}
25046
25047void gc_heap::compact_phase (int condemned_gen_number,
25048 uint8_t* first_condemned_address,
25049 BOOL clear_cards)
25050{
25051// %type% category = quote (compact);
25052#ifdef TIME_GC
25053 unsigned start;
25054 unsigned finish;
25055 start = GetCycleCount32();
25056#endif //TIME_GC
25057 generation* condemned_gen = generation_of (condemned_gen_number);
25058 uint8_t* start_address = first_condemned_address;
25059 size_t current_brick = brick_of (start_address);
25060 heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
25061
25062 PREFIX_ASSUME(current_heap_segment != NULL);
25063
25064 reset_pinned_queue_bos();
25065 update_oldest_pinned_plug();
25066
25067 BOOL reused_seg = expand_reused_seg_p();
25068 if (reused_seg)
25069 {
25070 for (int i = 1; i <= max_generation; i++)
25071 {
25072 generation_allocation_size (generation_of (i)) = 0;
25073 }
25074 }
25075
25076 uint8_t* end_address = heap_segment_allocated (current_heap_segment);
25077
25078 size_t end_brick = brick_of (end_address-1);
25079 compact_args args;
25080 args.last_plug = 0;
25081 args.before_last_plug = 0;
25082 args.current_compacted_brick = ~((size_t)1);
25083 args.is_shortened = FALSE;
25084 args.pinned_plug_entry = 0;
25085 args.copy_cards_p = (condemned_gen_number >= 1) || !clear_cards;
25086 args.check_gennum_p = reused_seg;
25087 if (args.check_gennum_p)
25088 {
25089 args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
25090 }
25091
25092 dprintf (2,("---- Compact Phase: %Ix(%Ix)----",
25093 first_condemned_address, brick_of (first_condemned_address)));
25094
25095#ifdef MULTIPLE_HEAPS
25096 //restart
25097 if (gc_t_join.joined())
25098 {
25099#endif //MULTIPLE_HEAPS
25100
25101#ifdef MULTIPLE_HEAPS
25102 dprintf(3, ("Restarting for compaction"));
25103 gc_t_join.restart();
25104 }
25105#endif //MULTIPLE_HEAPS
25106
25107 reset_pinned_queue_bos();
25108
25109#ifdef FEATURE_LOH_COMPACTION
25110 if (loh_compacted_p)
25111 {
25112 compact_loh();
25113 }
25114#endif //FEATURE_LOH_COMPACTION
25115
25116 if ((start_address < end_address) ||
25117 (condemned_gen_number == max_generation))
25118 {
25119 while (1)
25120 {
25121 if (current_brick > end_brick)
25122 {
25123 if (args.last_plug != 0)
25124 {
25125 dprintf (3, ("compacting last plug: %Ix", args.last_plug))
25126 compact_plug (args.last_plug,
25127 (heap_segment_allocated (current_heap_segment) - args.last_plug),
25128 args.is_shortened,
25129 &args);
25130 }
25131
25132 if (heap_segment_next_rw (current_heap_segment))
25133 {
25134 current_heap_segment = heap_segment_next_rw (current_heap_segment);
25135 current_brick = brick_of (heap_segment_mem (current_heap_segment));
25136 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
25137 args.last_plug = 0;
25138 if (args.check_gennum_p)
25139 {
25140 args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
25141 }
25142 continue;
25143 }
25144 else
25145 {
25146 if (args.before_last_plug !=0)
25147 {
25148 dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
25149 args.current_compacted_brick, (size_t)args.before_last_plug));
25150 assert (args.current_compacted_brick != ~1u);
25151 set_brick (args.current_compacted_brick,
25152 args.before_last_plug - brick_address (args.current_compacted_brick));
25153 }
25154 break;
25155 }
25156 }
25157 {
25158 int brick_entry = brick_table [ current_brick ];
25159 dprintf (3, ("B: %Ix(%Ix)->%Ix",
25160 current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
25161
25162 if (brick_entry >= 0)
25163 {
25164 compact_in_brick ((brick_address (current_brick) + brick_entry -1),
25165 &args);
25166
25167 }
25168 }
25169 current_brick++;
25170 }
25171 }
25172
25173 recover_saved_pinned_info();
25174
25175#ifdef TIME_GC
25176 finish = GetCycleCount32();
25177 compact_time = finish - start;
25178#endif //TIME_GC
25179
25180 concurrent_print_time_delta ("compact end");
25181
25182 dprintf(2,("---- End of Compact phase ----"));
25183}
25184
25185#ifdef MULTIPLE_HEAPS
25186
25187#ifdef _MSC_VER
25188#pragma warning(push)
25189#pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
25190#endif //_MSC_VER
25191void gc_heap::gc_thread_stub (void* arg)
25192{
25193 gc_heap* heap = (gc_heap*)arg;
25194 if (!gc_thread_no_affinitize_p)
25195 {
25196 GCThreadAffinity affinity;
25197 affinity.Group = GCThreadAffinity::None;
25198 affinity.Processor = GCThreadAffinity::None;
25199
25200 // We are about to set affinity for GC threads. It is a good place to set up NUMA and
25201 // CPU groups because the process mask, processor number, and group number are all
25202 // readily available.
25203 if (GCToOSInterface::CanEnableGCCPUGroups())
25204 set_thread_group_affinity_for_heap(heap->heap_number, &affinity);
25205 else
25206 set_thread_affinity_mask_for_heap(heap->heap_number, &affinity);
25207
25208 if (!GCToOSInterface::SetThreadAffinity(&affinity))
25209 {
25210 dprintf(1, ("Failed to set thread affinity for server GC thread"));
25211 }
25212 }
25213
25214 // server GC threads run at a higher priority than normal.
25215 GCToOSInterface::BoostThreadPriority();
25216 _alloca (256*heap->heap_number);
25217 heap->gc_thread_function();
25218}
25219#ifdef _MSC_VER
25220#pragma warning(pop)
25221#endif //_MSC_VER
25222
25223#endif //MULTIPLE_HEAPS
25224
25225#ifdef BACKGROUND_GC
25226
25227#ifdef _MSC_VER
25228#pragma warning(push)
25229#pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
25230#endif //_MSC_VER
25231void gc_heap::bgc_thread_stub (void* arg)
25232{
25233 gc_heap* heap = (gc_heap*)arg;
25234 heap->bgc_thread = GCToEEInterface::GetThread();
25235 assert(heap->bgc_thread != nullptr);
25236 heap->bgc_thread_function();
25237}
25238#ifdef _MSC_VER
25239#pragma warning(pop)
25240#endif //_MSC_VER
25241
25242#endif //BACKGROUND_GC
25243
25244/*------------------ Background GC ----------------------------*/
25245
25246#ifdef BACKGROUND_GC
25247
25248void gc_heap::background_drain_mark_list (int thread)
25249{
25250 UNREFERENCED_PARAMETER(thread);
25251
25252 size_t saved_c_mark_list_index = c_mark_list_index;
25253
25254 if (saved_c_mark_list_index)
25255 {
25256 concurrent_print_time_delta ("SML");
25257 }
25258 while (c_mark_list_index != 0)
25259 {
25260 size_t current_index = c_mark_list_index - 1;
25261 uint8_t* o = c_mark_list [current_index];
25262 background_mark_object (o THREAD_NUMBER_ARG);
25263 c_mark_list_index--;
25264 }
25265 if (saved_c_mark_list_index)
25266 {
25267
25268 concurrent_print_time_delta ("EML");
25269 }
25270
25271 fire_drain_mark_list_event (saved_c_mark_list_index);
25272}
25273
25274
25275// The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
25276#ifdef MULTIPLE_HEAPS
25277// Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
25278// them. So we can use the same static variables.
25279void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25280{
25281 // Whenever we call this method there may have been preceding object promotions. So set
25282 // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25283 // based on the how the scanning proceeded).
25284 s_fUnscannedPromotions = TRUE;
25285
25286 // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
25287 // the state of this thread's portion of the dependent handle table. That's because promotions on other
25288 // threads could cause handle promotions to become necessary here. Even if there are definitely no more
25289 // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
25290 // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
25291 // as all the others or they'll get out of step).
25292 while (true)
25293 {
25294 // The various worker threads are all currently racing in this code. We need to work out if at least
25295 // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
25296 // dependent handle table when both of the following conditions apply:
25297 // 1) At least one (arbitrary) object might have been promoted since the last scan (because if this
25298 // object happens to correspond to a primary in one of our handles we might potentially have to
25299 // promote the associated secondary).
25300 // 2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
25301 //
25302 // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
25303 // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
25304 // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
25305 // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
25306 // follows below. Note that we can't read this outside of the join since on any iteration apart from
25307 // the first threads will be racing between reading this value and completing their previous
25308 // iteration's table scan.
25309 //
25310 // The second condition is tracked by the dependent handle code itself on a per worker thread basis
25311 // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
25312 // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
25313 // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
25314 // we're safely joined.
25315 if (GCScan::GcDhUnpromotedHandlesExist(sc))
25316 s_fUnpromotedHandles = TRUE;
25317
25318 // Synchronize all the threads so we can read our state variables safely. The following shared
25319 // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
25320 // single thread inside the join.
25321 bgc_t_join.join(this, gc_join_scan_dependent_handles);
25322 if (bgc_t_join.joined())
25323 {
25324 // We're synchronized so it's safe to read our shared state variables. We update another shared
25325 // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
25326 // the loop. We scan if there has been at least one object promotion since last time and at least
25327 // one thread has a dependent handle table with a potential handle promotion possible.
25328 s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
25329
25330 // Reset our shared state variables (ready to be set again on this scan or with a good initial
25331 // value for the next call if we're terminating the loop).
25332 s_fUnscannedPromotions = FALSE;
25333 s_fUnpromotedHandles = FALSE;
25334
25335 if (!s_fScanRequired)
25336 {
25337 uint8_t* all_heaps_max = 0;
25338 uint8_t* all_heaps_min = MAX_PTR;
25339 int i;
25340 for (i = 0; i < n_heaps; i++)
25341 {
25342 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25343 all_heaps_max = g_heaps[i]->background_max_overflow_address;
25344 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25345 all_heaps_min = g_heaps[i]->background_min_overflow_address;
25346 }
25347 for (i = 0; i < n_heaps; i++)
25348 {
25349 g_heaps[i]->background_max_overflow_address = all_heaps_max;
25350 g_heaps[i]->background_min_overflow_address = all_heaps_min;
25351 }
25352 }
25353
25354 // Restart all the workers.
25355 dprintf(2, ("Starting all gc thread mark stack overflow processing"));
25356 bgc_t_join.restart();
25357 }
25358
25359 // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25360 // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
25361 // global flag indicating that at least one object promotion may have occurred (the usual comment
25362 // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
25363 // exit the method since we unconditionally set this variable on method entry anyway).
25364 if (background_process_mark_overflow (sc->concurrent))
25365 s_fUnscannedPromotions = TRUE;
25366
25367 // If we decided that no scan was required we can terminate the loop now.
25368 if (!s_fScanRequired)
25369 break;
25370
25371 // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
25372 // processed before we start scanning dependent handle tables (if overflows remain while we scan we
25373 // could miss noting the promotion of some primary objects).
25374 bgc_t_join.join(this, gc_join_rescan_dependent_handles);
25375 if (bgc_t_join.joined())
25376 {
25377 // Restart all the workers.
25378 dprintf(3, ("Starting all gc thread for dependent handle promotion"));
25379 bgc_t_join.restart();
25380 }
25381
25382 // If the portion of the dependent handle table managed by this worker has handles that could still be
25383 // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
25384 // could require a rescan of handles on this or other workers.
25385 if (GCScan::GcDhUnpromotedHandlesExist(sc))
25386 if (GCScan::GcDhReScan(sc))
25387 s_fUnscannedPromotions = TRUE;
25388 }
25389}
25390#else
25391void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25392{
25393 // Whenever we call this method there may have been preceding object promotions. So set
25394 // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25395 // based on the how the scanning proceeded).
25396 bool fUnscannedPromotions = true;
25397
25398 // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
25399 // scan without performing any new promotions.
25400 while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
25401 {
25402 // On each iteration of the loop start with the assumption that no further objects have been promoted.
25403 fUnscannedPromotions = false;
25404
25405 // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25406 // being visible. If there was an overflow (background_process_mark_overflow returned true) then
25407 // additional objects now appear to be promoted and we should set the flag.
25408 if (background_process_mark_overflow (sc->concurrent))
25409 fUnscannedPromotions = true;
25410
25411 // Perform the scan and set the flag if any promotions resulted.
25412 if (GCScan::GcDhReScan (sc))
25413 fUnscannedPromotions = true;
25414 }
25415
25416 // Perform a last processing of any overflowed mark stack.
25417 background_process_mark_overflow (sc->concurrent);
25418}
25419#endif //MULTIPLE_HEAPS
25420
25421void gc_heap::recover_bgc_settings()
25422{
25423 if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
25424 {
25425 dprintf (2, ("restoring bgc settings"));
25426 settings = saved_bgc_settings;
25427 GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
25428 }
25429}
25430
25431void gc_heap::allow_fgc()
25432{
25433 assert (bgc_thread == GCToEEInterface::GetThread());
25434 bool bToggleGC = false;
25435
25436 if (g_fSuspensionPending > 0)
25437 {
25438 bToggleGC = GCToEEInterface::EnablePreemptiveGC();
25439 if (bToggleGC)
25440 {
25441 GCToEEInterface::DisablePreemptiveGC();
25442 }
25443 }
25444}
25445
25446BOOL gc_heap::should_commit_mark_array()
25447{
25448 return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
25449}
25450
25451void gc_heap::clear_commit_flag()
25452{
25453 generation* gen = generation_of (max_generation);
25454 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25455 while (1)
25456 {
25457 if (seg == 0)
25458 {
25459 if (gen != large_object_generation)
25460 {
25461 gen = large_object_generation;
25462 seg = heap_segment_in_range (generation_start_segment (gen));
25463 }
25464 else
25465 {
25466 break;
25467 }
25468 }
25469
25470 if (seg->flags & heap_segment_flags_ma_committed)
25471 {
25472 seg->flags &= ~heap_segment_flags_ma_committed;
25473 }
25474
25475 if (seg->flags & heap_segment_flags_ma_pcommitted)
25476 {
25477 seg->flags &= ~heap_segment_flags_ma_pcommitted;
25478 }
25479
25480 seg = heap_segment_next (seg);
25481 }
25482}
25483
25484void gc_heap::clear_commit_flag_global()
25485{
25486#ifdef MULTIPLE_HEAPS
25487 for (int i = 0; i < n_heaps; i++)
25488 {
25489 g_heaps[i]->clear_commit_flag();
25490 }
25491#else
25492 clear_commit_flag();
25493#endif //MULTIPLE_HEAPS
25494}
25495
25496void gc_heap::verify_mark_array_cleared (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25497{
25498#ifdef _DEBUG
25499 size_t markw = mark_word_of (begin);
25500 size_t markw_end = mark_word_of (end);
25501
25502 while (markw < markw_end)
25503 {
25504 if (mark_array_addr[markw])
25505 {
25506 dprintf (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
25507 markw, mark_array_addr[markw], mark_word_address (markw)));
25508 FATAL_GC_ERROR();
25509 }
25510 markw++;
25511 }
25512#else // _DEBUG
25513 UNREFERENCED_PARAMETER(begin);
25514 UNREFERENCED_PARAMETER(end);
25515 UNREFERENCED_PARAMETER(mark_array_addr);
25516#endif //_DEBUG
25517}
25518
25519void gc_heap::verify_mark_array_cleared (heap_segment* seg, uint32_t* mark_array_addr)
25520{
25521 verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
25522}
25523
25524BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp,
25525 heap_segment* seg,
25526 uint32_t* new_card_table,
25527 uint8_t* new_lowest_address)
25528{
25529 UNREFERENCED_PARAMETER(hp); // compiler bug? -- this *is*, indeed, referenced
25530
25531 uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25532 uint8_t* end = heap_segment_reserved (seg);
25533
25534 uint8_t* lowest = hp->background_saved_lowest_address;
25535 uint8_t* highest = hp->background_saved_highest_address;
25536
25537 uint8_t* commit_start = NULL;
25538 uint8_t* commit_end = NULL;
25539 size_t commit_flag = 0;
25540
25541 if ((highest >= start) &&
25542 (lowest <= end))
25543 {
25544 if ((start >= lowest) && (end <= highest))
25545 {
25546 dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25547 start, end, lowest, highest));
25548 commit_flag = heap_segment_flags_ma_committed;
25549 }
25550 else
25551 {
25552 dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25553 start, end, lowest, highest));
25554 commit_flag = heap_segment_flags_ma_pcommitted;
25555 }
25556
25557 commit_start = max (lowest, start);
25558 commit_end = min (highest, end);
25559
25560 if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
25561 {
25562 return FALSE;
25563 }
25564
25565 if (new_card_table == 0)
25566 {
25567 new_card_table = g_gc_card_table;
25568 }
25569
25570 if (hp->card_table != new_card_table)
25571 {
25572 if (new_lowest_address == 0)
25573 {
25574 new_lowest_address = g_gc_lowest_address;
25575 }
25576
25577 uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))];
25578 uint32_t* ma = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
25579
25580 dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix",
25581 hp->card_table, new_card_table,
25582 hp->mark_array, ma));
25583
25584 if (!commit_mark_array_by_range (commit_start, commit_end, ma))
25585 {
25586 return FALSE;
25587 }
25588 }
25589
25590 seg->flags |= commit_flag;
25591 }
25592
25593 return TRUE;
25594}
25595
25596BOOL gc_heap::commit_mark_array_by_range (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25597{
25598 size_t beg_word = mark_word_of (begin);
25599 size_t end_word = mark_word_of (align_on_mark_word (end));
25600 uint8_t* commit_start = align_lower_page ((uint8_t*)&mark_array_addr[beg_word]);
25601 uint8_t* commit_end = align_on_page ((uint8_t*)&mark_array_addr[end_word]);
25602 size_t size = (size_t)(commit_end - commit_start);
25603
25604#ifdef SIMPLE_DPRINTF
25605 dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
25606 begin, end,
25607 beg_word, end_word,
25608 (end_word - beg_word) * sizeof (uint32_t),
25609 &mark_array_addr[beg_word],
25610 &mark_array_addr[end_word],
25611 (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
25612 commit_start, commit_end,
25613 size));
25614#endif //SIMPLE_DPRINTF
25615
25616 if (GCToOSInterface::VirtualCommit (commit_start, size))
25617 {
25618 // We can only verify the mark array is cleared from begin to end, the first and the last
25619 // page aren't necessarily all cleared 'cause they could be used by other segments or
25620 // card bundle.
25621 verify_mark_array_cleared (begin, end, mark_array_addr);
25622 return TRUE;
25623 }
25624 else
25625 {
25626 dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (uint32_t)));
25627 return FALSE;
25628 }
25629}
25630
25631BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, uint32_t* new_mark_array_addr)
25632{
25633 uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25634 uint8_t* end = heap_segment_reserved (seg);
25635
25636#ifdef MULTIPLE_HEAPS
25637 uint8_t* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
25638 uint8_t* highest = heap_segment_heap (seg)->background_saved_highest_address;
25639#else
25640 uint8_t* lowest = background_saved_lowest_address;
25641 uint8_t* highest = background_saved_highest_address;
25642#endif //MULTIPLE_HEAPS
25643
25644 if ((highest >= start) &&
25645 (lowest <= end))
25646 {
25647 start = max (lowest, start);
25648 end = min (highest, end);
25649 if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
25650 {
25651 return FALSE;
25652 }
25653 }
25654
25655 return TRUE;
25656}
25657
25658BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, uint32_t* mark_array_addr)
25659{
25660 dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix",
25661 seg,
25662 heap_segment_reserved (seg),
25663 mark_array_addr));
25664 uint8_t* start = (heap_segment_read_only_p (seg) ? heap_segment_mem (seg) : (uint8_t*)seg);
25665
25666 return commit_mark_array_by_range (start, heap_segment_reserved (seg), mark_array_addr);
25667}
25668
25669BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr)
25670{
25671 UNREFERENCED_PARAMETER(mark_array_addr);
25672
25673 dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix",
25674 lowest_address, highest_address, mark_array));
25675
25676 generation* gen = generation_of (max_generation);
25677 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25678 while (1)
25679 {
25680 if (seg == 0)
25681 {
25682 if (gen != large_object_generation)
25683 {
25684 gen = large_object_generation;
25685 seg = heap_segment_in_range (generation_start_segment (gen));
25686 }
25687 else
25688 {
25689 break;
25690 }
25691 }
25692
25693 dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
25694
25695 if (!(seg->flags & heap_segment_flags_ma_committed))
25696 {
25697 // For ro segments they could always be only partially in range so we'd
25698 // be calling this at the beginning of every BGC. We are not making this
25699 // more efficient right now - ro segments are currently only used by redhawk.
25700 if (heap_segment_read_only_p (seg))
25701 {
25702 if ((heap_segment_mem (seg) >= lowest_address) &&
25703 (heap_segment_reserved (seg) <= highest_address))
25704 {
25705 if (commit_mark_array_by_seg (seg, mark_array))
25706 {
25707 seg->flags |= heap_segment_flags_ma_committed;
25708 }
25709 else
25710 {
25711 return FALSE;
25712 }
25713 }
25714 else
25715 {
25716 uint8_t* start = max (lowest_address, heap_segment_mem (seg));
25717 uint8_t* end = min (highest_address, heap_segment_reserved (seg));
25718 if (commit_mark_array_by_range (start, end, mark_array))
25719 {
25720 seg->flags |= heap_segment_flags_ma_pcommitted;
25721 }
25722 else
25723 {
25724 return FALSE;
25725 }
25726 }
25727 }
25728 else
25729 {
25730 // For normal segments they are by design completely in range so just
25731 // commit the whole mark array for each seg.
25732 if (commit_mark_array_by_seg (seg, mark_array))
25733 {
25734 if (seg->flags & heap_segment_flags_ma_pcommitted)
25735 {
25736 seg->flags &= ~heap_segment_flags_ma_pcommitted;
25737 }
25738 seg->flags |= heap_segment_flags_ma_committed;
25739 }
25740 else
25741 {
25742 return FALSE;
25743 }
25744 }
25745 }
25746
25747 seg = heap_segment_next (seg);
25748 }
25749
25750 return TRUE;
25751}
25752
25753// This function doesn't check the commit flag since it's for a new array -
25754// the mark_array flag for these segments will remain the same.
25755BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr)
25756{
25757 dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
25758 generation* gen = generation_of (max_generation);
25759 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25760 while (1)
25761 {
25762 if (seg == 0)
25763 {
25764 if (gen != large_object_generation)
25765 {
25766 gen = large_object_generation;
25767 seg = heap_segment_in_range (generation_start_segment (gen));
25768 }
25769 else
25770 {
25771 break;
25772 }
25773 }
25774
25775 if (!commit_mark_array_with_check (seg, new_mark_array_addr))
25776 {
25777 return FALSE;
25778 }
25779
25780 seg = heap_segment_next (seg);
25781 }
25782
25783#ifdef MULTIPLE_HEAPS
25784 if (new_heap_segment)
25785 {
25786 if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
25787 {
25788 return FALSE;
25789 }
25790 }
25791#endif //MULTIPLE_HEAPS
25792
25793 return TRUE;
25794}
25795
25796BOOL gc_heap::commit_new_mark_array_global (uint32_t* new_mark_array)
25797{
25798#ifdef MULTIPLE_HEAPS
25799 for (int i = 0; i < n_heaps; i++)
25800 {
25801 if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
25802 {
25803 return FALSE;
25804 }
25805 }
25806#else
25807 if (!commit_new_mark_array (new_mark_array))
25808 {
25809 return FALSE;
25810 }
25811#endif //MULTIPLE_HEAPS
25812
25813 return TRUE;
25814}
25815
25816void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
25817{
25818 // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
25819 // been set to NULL.
25820 if (mark_array == NULL)
25821 {
25822 return;
25823 }
25824
25825 dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
25826
25827 size_t flags = seg->flags;
25828
25829 if ((flags & heap_segment_flags_ma_committed) ||
25830 (flags & heap_segment_flags_ma_pcommitted))
25831 {
25832 uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25833 uint8_t* end = heap_segment_reserved (seg);
25834
25835 if (flags & heap_segment_flags_ma_pcommitted)
25836 {
25837 start = max (lowest_address, start);
25838 end = min (highest_address, end);
25839 }
25840
25841 size_t beg_word = mark_word_of (start);
25842 size_t end_word = mark_word_of (align_on_mark_word (end));
25843 uint8_t* decommit_start = align_on_page ((uint8_t*)&mark_array[beg_word]);
25844 uint8_t* decommit_end = align_lower_page ((uint8_t*)&mark_array[end_word]);
25845 size_t size = (size_t)(decommit_end - decommit_start);
25846
25847#ifdef SIMPLE_DPRINTF
25848 dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
25849 seg,
25850 beg_word, end_word,
25851 (end_word - beg_word) * sizeof (uint32_t),
25852 &mark_array[beg_word],
25853 &mark_array[end_word],
25854 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
25855 decommit_start, decommit_end,
25856 size));
25857#endif //SIMPLE_DPRINTF
25858
25859 if (decommit_start < decommit_end)
25860 {
25861 if (!GCToOSInterface::VirtualDecommit (decommit_start, size))
25862 {
25863 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualDecommit on %Ix for %Id bytes failed",
25864 decommit_start, size));
25865 assert (!"decommit failed");
25866 }
25867 }
25868
25869 dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
25870 }
25871}
25872
25873void gc_heap::background_mark_phase ()
25874{
25875 verify_mark_array_cleared();
25876
25877 ScanContext sc;
25878 sc.thread_number = heap_number;
25879 sc.promotion = TRUE;
25880 sc.concurrent = FALSE;
25881
25882 THREAD_FROM_HEAP;
25883 BOOL cooperative_mode = TRUE;
25884#ifndef MULTIPLE_HEAPS
25885 const int thread = heap_number;
25886#endif //!MULTIPLE_HEAPS
25887
25888 dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
25889
25890 assert (settings.concurrent);
25891
25892#ifdef TIME_GC
25893 unsigned start;
25894 unsigned finish;
25895 start = GetCycleCount32();
25896#endif //TIME_GC
25897
25898#ifdef FFIND_OBJECT
25899 if (gen0_must_clear_bricks > 0)
25900 gen0_must_clear_bricks--;
25901#endif //FFIND_OBJECT
25902
25903 background_soh_alloc_count = 0;
25904 background_loh_alloc_count = 0;
25905 bgc_overflow_count = 0;
25906
25907 bpromoted_bytes (heap_number) = 0;
25908 static uint32_t num_sizedrefs = 0;
25909
25910 background_min_overflow_address = MAX_PTR;
25911 background_max_overflow_address = 0;
25912 background_min_soh_overflow_address = MAX_PTR;
25913 background_max_soh_overflow_address = 0;
25914 processed_soh_overflow_p = FALSE;
25915
25916 {
25917 //set up the mark lists from g_mark_list
25918 assert (g_mark_list);
25919 mark_list = g_mark_list;
25920 //dont use the mark list for full gc
25921 //because multiple segments are more complex to handle and the list
25922 //is likely to overflow
25923 mark_list_end = &mark_list [0];
25924 mark_list_index = &mark_list [0];
25925
25926 c_mark_list_index = 0;
25927
25928#ifndef MULTIPLE_HEAPS
25929 shigh = (uint8_t*) 0;
25930 slow = MAX_PTR;
25931#endif //MULTIPLE_HEAPS
25932
25933 generation* gen = generation_of (max_generation);
25934
25935 dprintf(3,("BGC: stack marking"));
25936 sc.concurrent = TRUE;
25937
25938 GCScan::GcScanRoots(background_promote_callback,
25939 max_generation, max_generation,
25940 &sc);
25941 }
25942
25943 {
25944 dprintf(3,("BGC: finalization marking"));
25945 finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
25946 }
25947
25948 size_t total_loh_size = generation_size (max_generation + 1);
25949 bgc_begin_loh_size = total_loh_size;
25950 bgc_alloc_spin_loh = 0;
25951 bgc_loh_size_increased = 0;
25952 bgc_loh_allocated_in_free = 0;
25953 size_t total_soh_size = generation_sizes (generation_of (max_generation));
25954
25955 dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
25956
25957 {
25958 //concurrent_print_time_delta ("copying stack roots");
25959 concurrent_print_time_delta ("CS");
25960
25961 FIRE_EVENT(BGC1stNonConEnd);
25962
25963 expanded_in_fgc = FALSE;
25964 saved_overflow_ephemeral_seg = 0;
25965 current_bgc_state = bgc_reset_ww;
25966
25967 // we don't need a join here - just whichever thread that gets here
25968 // first can change the states and call restart_vm.
25969 // this is not true - we can't let the EE run when we are scanning stack.
25970 // since we now allow reset ww to run concurrently and have a join for it,
25971 // we can do restart ee on the 1st thread that got here. Make sure we handle the
25972 // sizedref handles correctly.
25973#ifdef MULTIPLE_HEAPS
25974 bgc_t_join.join(this, gc_join_restart_ee);
25975 if (bgc_t_join.joined())
25976#endif //MULTIPLE_HEAPS
25977 {
25978#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25979 // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset
25980 // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while
25981 // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below.
25982#ifdef WRITE_WATCH
25983 concurrent_print_time_delta ("CRWW begin");
25984
25985#ifdef MULTIPLE_HEAPS
25986 for (int i = 0; i < n_heaps; i++)
25987 {
25988 g_heaps[i]->reset_write_watch (FALSE);
25989 }
25990#else
25991 reset_write_watch (FALSE);
25992#endif //MULTIPLE_HEAPS
25993
25994 concurrent_print_time_delta ("CRWW");
25995#endif //WRITE_WATCH
25996#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25997
25998 num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
25999
26000 // this c_write is not really necessary because restart_vm
26001 // has an instruction that will flush the cpu cache (interlocked
26002 // or whatever) but we don't want to rely on that.
26003 dprintf (BGC_LOG, ("setting cm_in_progress"));
26004 c_write (cm_in_progress, TRUE);
26005
26006 //restart all thread, doing the marking from the array
26007 assert (dont_restart_ee_p);
26008 dont_restart_ee_p = FALSE;
26009
26010 restart_vm();
26011 GCToOSInterface::YieldThread (0);
26012#ifdef MULTIPLE_HEAPS
26013 dprintf(3, ("Starting all gc threads for gc"));
26014 bgc_t_join.restart();
26015#endif //MULTIPLE_HEAPS
26016 }
26017
26018#ifdef MULTIPLE_HEAPS
26019 bgc_t_join.join(this, gc_join_after_reset);
26020 if (bgc_t_join.joined())
26021#endif //MULTIPLE_HEAPS
26022 {
26023 disable_preemptive (true);
26024
26025#ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26026 // When software write watch is enabled, resetting write watch is done while the runtime is suspended above. The
26027 // post-reset call to revisit_written_pages is only necessary for concurrent reset_write_watch, to discard dirtied
26028 // pages during the concurrent reset.
26029
26030#ifdef WRITE_WATCH
26031 concurrent_print_time_delta ("CRWW begin");
26032
26033#ifdef MULTIPLE_HEAPS
26034 for (int i = 0; i < n_heaps; i++)
26035 {
26036 g_heaps[i]->reset_write_watch (TRUE);
26037 }
26038#else
26039 reset_write_watch (TRUE);
26040#endif //MULTIPLE_HEAPS
26041
26042 concurrent_print_time_delta ("CRWW");
26043#endif //WRITE_WATCH
26044
26045#ifdef MULTIPLE_HEAPS
26046 for (int i = 0; i < n_heaps; i++)
26047 {
26048 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
26049 }
26050#else
26051 revisit_written_pages (TRUE, TRUE);
26052#endif //MULTIPLE_HEAPS
26053
26054 concurrent_print_time_delta ("CRW");
26055#endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26056
26057#ifdef MULTIPLE_HEAPS
26058 for (int i = 0; i < n_heaps; i++)
26059 {
26060 g_heaps[i]->current_bgc_state = bgc_mark_handles;
26061 }
26062#else
26063 current_bgc_state = bgc_mark_handles;
26064#endif //MULTIPLE_HEAPS
26065
26066 current_c_gc_state = c_gc_state_marking;
26067
26068 enable_preemptive ();
26069
26070#ifdef MULTIPLE_HEAPS
26071 dprintf(3, ("Joining BGC threads after resetting writewatch"));
26072 bgc_t_join.restart();
26073#endif //MULTIPLE_HEAPS
26074 }
26075
26076 disable_preemptive (true);
26077
26078 if (num_sizedrefs > 0)
26079 {
26080 GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
26081
26082 enable_preemptive ();
26083
26084#ifdef MULTIPLE_HEAPS
26085 bgc_t_join.join(this, gc_join_scan_sizedref_done);
26086 if (bgc_t_join.joined())
26087 {
26088 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
26089 bgc_t_join.restart();
26090 }
26091#endif //MULTIPLE_HEAPS
26092
26093 disable_preemptive (true);
26094 }
26095
26096 dprintf (3,("BGC: handle table marking"));
26097 GCScan::GcScanHandles(background_promote,
26098 max_generation, max_generation,
26099 &sc);
26100 //concurrent_print_time_delta ("concurrent marking handle table");
26101 concurrent_print_time_delta ("CRH");
26102
26103 current_bgc_state = bgc_mark_stack;
26104 dprintf (2,("concurrent draining mark list"));
26105 background_drain_mark_list (thread);
26106 //concurrent_print_time_delta ("concurrent marking stack roots");
26107 concurrent_print_time_delta ("CRS");
26108
26109 dprintf (2,("concurrent revisiting dirtied pages"));
26110 revisit_written_pages (TRUE);
26111 revisit_written_pages (TRUE);
26112 //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
26113 concurrent_print_time_delta ("CRre");
26114
26115 enable_preemptive ();
26116
26117#ifdef MULTIPLE_HEAPS
26118 bgc_t_join.join(this, gc_join_concurrent_overflow);
26119 if (bgc_t_join.joined())
26120 {
26121 uint8_t* all_heaps_max = 0;
26122 uint8_t* all_heaps_min = MAX_PTR;
26123 int i;
26124 for (i = 0; i < n_heaps; i++)
26125 {
26126 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix",
26127 i,
26128 g_heaps[i]->background_max_overflow_address,
26129 g_heaps[i]->background_min_overflow_address));
26130 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
26131 all_heaps_max = g_heaps[i]->background_max_overflow_address;
26132 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
26133 all_heaps_min = g_heaps[i]->background_min_overflow_address;
26134 }
26135 for (i = 0; i < n_heaps; i++)
26136 {
26137 g_heaps[i]->background_max_overflow_address = all_heaps_max;
26138 g_heaps[i]->background_min_overflow_address = all_heaps_min;
26139 }
26140 dprintf(3, ("Starting all bgc threads after updating the overflow info"));
26141 bgc_t_join.restart();
26142 }
26143#endif //MULTIPLE_HEAPS
26144
26145 disable_preemptive (true);
26146
26147 dprintf (2, ("before CRov count: %d", bgc_overflow_count));
26148 bgc_overflow_count = 0;
26149 background_process_mark_overflow (TRUE);
26150 dprintf (2, ("after CRov count: %d", bgc_overflow_count));
26151 bgc_overflow_count = 0;
26152 //concurrent_print_time_delta ("concurrent processing mark overflow");
26153 concurrent_print_time_delta ("CRov");
26154
26155 // Stop all threads, crawl all stacks and revisit changed pages.
26156 FIRE_EVENT(BGC1stConEnd);
26157
26158 dprintf (2, ("Stopping the EE"));
26159
26160 enable_preemptive ();
26161
26162#ifdef MULTIPLE_HEAPS
26163 bgc_t_join.join(this, gc_join_suspend_ee);
26164 if (bgc_t_join.joined())
26165 {
26166 bgc_threads_sync_event.Reset();
26167
26168 dprintf(3, ("Joining BGC threads for non concurrent final marking"));
26169 bgc_t_join.restart();
26170 }
26171#endif //MULTIPLE_HEAPS
26172
26173 if (heap_number == 0)
26174 {
26175 enter_spin_lock (&gc_lock);
26176
26177 bgc_suspend_EE ();
26178 //suspend_EE ();
26179 bgc_threads_sync_event.Set();
26180 }
26181 else
26182 {
26183 bgc_threads_sync_event.Wait(INFINITE, FALSE);
26184 dprintf (2, ("bgc_threads_sync_event is signalled"));
26185 }
26186
26187 assert (settings.concurrent);
26188 assert (settings.condemned_generation == max_generation);
26189
26190 dprintf (2, ("clearing cm_in_progress"));
26191 c_write (cm_in_progress, FALSE);
26192
26193 bgc_alloc_lock->check();
26194
26195 current_bgc_state = bgc_final_marking;
26196
26197 //concurrent_print_time_delta ("concurrent marking ended");
26198 concurrent_print_time_delta ("CR");
26199
26200 FIRE_EVENT(BGC2ndNonConBegin);
26201
26202 mark_absorb_new_alloc();
26203
26204 // We need a join here 'cause find_object would complain if the gen0
26205 // bricks of another heap haven't been fixed up. So we need to make sure
26206 // that every heap's gen0 bricks are fixed up before we proceed.
26207#ifdef MULTIPLE_HEAPS
26208 bgc_t_join.join(this, gc_join_after_absorb);
26209 if (bgc_t_join.joined())
26210 {
26211 dprintf(3, ("Joining BGC threads after absorb"));
26212 bgc_t_join.restart();
26213 }
26214#endif //MULTIPLE_HEAPS
26215
26216 // give VM a chance to do work
26217 GCToEEInterface::GcBeforeBGCSweepWork();
26218
26219 //reset the flag, indicating that the EE no longer expect concurrent
26220 //marking
26221 sc.concurrent = FALSE;
26222
26223 total_loh_size = generation_size (max_generation + 1);
26224 total_soh_size = generation_sizes (generation_of (max_generation));
26225
26226 dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
26227
26228 dprintf (2, ("nonconcurrent marking stack roots"));
26229 GCScan::GcScanRoots(background_promote,
26230 max_generation, max_generation,
26231 &sc);
26232 //concurrent_print_time_delta ("nonconcurrent marking stack roots");
26233 concurrent_print_time_delta ("NRS");
26234
26235// finalize_queue->EnterFinalizeLock();
26236 finalize_queue->GcScanRoots(background_promote, heap_number, 0);
26237// finalize_queue->LeaveFinalizeLock();
26238
26239 dprintf (2, ("nonconcurrent marking handle table"));
26240 GCScan::GcScanHandles(background_promote,
26241 max_generation, max_generation,
26242 &sc);
26243 //concurrent_print_time_delta ("nonconcurrent marking handle table");
26244 concurrent_print_time_delta ("NRH");
26245
26246 dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
26247 revisit_written_pages (FALSE);
26248 //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
26249 concurrent_print_time_delta ("NRre LOH");
26250
26251#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26252#ifdef MULTIPLE_HEAPS
26253 bgc_t_join.join(this, gc_join_disable_software_write_watch);
26254 if (bgc_t_join.joined())
26255#endif // MULTIPLE_HEAPS
26256 {
26257 // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to
26258 // avoid further perf penalty after the runtime is restarted
26259 SoftwareWriteWatch::DisableForGCHeap();
26260
26261#ifdef MULTIPLE_HEAPS
26262 dprintf(3, ("Restarting BGC threads after disabling software write watch"));
26263 bgc_t_join.restart();
26264#endif // MULTIPLE_HEAPS
26265 }
26266#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26267
26268 dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
26269 bgc_overflow_count = 0;
26270
26271 // Dependent handles need to be scanned with a special algorithm (see the header comment on
26272 // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
26273 // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
26274 // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
26275 // The call to background_scan_dependent_handles is what will cycle through more iterations if
26276 // required and will also perform processing of any mark stack overflow once the dependent handle
26277 // table has been fully promoted.
26278 dprintf (2, ("1st dependent handle scan and process mark overflow"));
26279 GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
26280 background_scan_dependent_handles (&sc);
26281 //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
26282 concurrent_print_time_delta ("NR 1st Hov");
26283
26284 dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
26285 bgc_overflow_count = 0;
26286
26287#ifdef MULTIPLE_HEAPS
26288 bgc_t_join.join(this, gc_join_null_dead_short_weak);
26289 if (bgc_t_join.joined())
26290#endif //MULTIPLE_HEAPS
26291 {
26292 GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
26293
26294#ifdef MULTIPLE_HEAPS
26295 dprintf(3, ("Joining BGC threads for short weak handle scan"));
26296 bgc_t_join.restart();
26297#endif //MULTIPLE_HEAPS
26298 }
26299
26300 // null out the target of short weakref that were not promoted.
26301 GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
26302
26303 //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
26304 concurrent_print_time_delta ("NR GcShortWeakPtrScan");
26305 }
26306
26307 {
26308#ifdef MULTIPLE_HEAPS
26309 bgc_t_join.join(this, gc_join_scan_finalization);
26310 if (bgc_t_join.joined())
26311 {
26312 dprintf(3, ("Joining BGC threads for finalization"));
26313 bgc_t_join.restart();
26314 }
26315#endif //MULTIPLE_HEAPS
26316
26317 //Handle finalization.
26318 dprintf(3,("Marking finalization data"));
26319 //concurrent_print_time_delta ("bgc joined to mark finalization");
26320 concurrent_print_time_delta ("NRj");
26321
26322// finalize_queue->EnterFinalizeLock();
26323 finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
26324// finalize_queue->LeaveFinalizeLock();
26325
26326 concurrent_print_time_delta ("NRF");
26327 }
26328
26329 dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
26330 bgc_overflow_count = 0;
26331
26332 // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
26333 // for finalization. As before background_scan_dependent_handles will also process any mark stack
26334 // overflow.
26335 dprintf (2, ("2nd dependent handle scan and process mark overflow"));
26336 background_scan_dependent_handles (&sc);
26337 //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
26338 concurrent_print_time_delta ("NR 2nd Hov");
26339
26340#ifdef MULTIPLE_HEAPS
26341 bgc_t_join.join(this, gc_join_null_dead_long_weak);
26342 if (bgc_t_join.joined())
26343 {
26344 dprintf(2, ("Joining BGC threads for weak pointer deletion"));
26345 bgc_t_join.restart();
26346 }
26347#endif //MULTIPLE_HEAPS
26348
26349 // null out the target of long weakref that were not promoted.
26350 GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
26351 concurrent_print_time_delta ("NR GcWeakPtrScan");
26352
26353#ifdef MULTIPLE_HEAPS
26354 bgc_t_join.join(this, gc_join_null_dead_syncblk);
26355 if (bgc_t_join.joined())
26356#endif //MULTIPLE_HEAPS
26357 {
26358 dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
26359 // scan for deleted entries in the syncblk cache
26360 GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
26361 concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
26362#ifdef MULTIPLE_HEAPS
26363 dprintf(2, ("Starting BGC threads for end of background mark phase"));
26364 bgc_t_join.restart();
26365#endif //MULTIPLE_HEAPS
26366 }
26367
26368 gen0_bricks_cleared = FALSE;
26369
26370 dprintf (2, ("end of bgc mark: loh: %d, soh: %d",
26371 generation_size (max_generation + 1),
26372 generation_sizes (generation_of (max_generation))));
26373
26374 for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
26375 {
26376 generation* gen = generation_of (gen_idx);
26377 dynamic_data* dd = dynamic_data_of (gen_idx);
26378 dd_begin_data_size (dd) = generation_size (gen_idx) -
26379 (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
26380 Align (size (generation_allocation_start (gen)));
26381 dd_survived_size (dd) = 0;
26382 dd_pinned_survived_size (dd) = 0;
26383 dd_artificial_pinned_survived_size (dd) = 0;
26384 dd_added_pinned_size (dd) = 0;
26385 }
26386
26387 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26388 PREFIX_ASSUME(seg != NULL);
26389
26390 while (seg)
26391 {
26392 seg->flags &= ~heap_segment_flags_swept;
26393
26394 if (heap_segment_allocated (seg) == heap_segment_mem (seg))
26395 {
26396 // This can't happen...
26397 FATAL_GC_ERROR();
26398 }
26399
26400 if (seg == ephemeral_heap_segment)
26401 {
26402 heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
26403 }
26404 else
26405 {
26406 heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
26407 }
26408
26409 dprintf (2, ("seg %Ix background allocated is %Ix",
26410 heap_segment_mem (seg),
26411 heap_segment_background_allocated (seg)));
26412 seg = heap_segment_next_rw (seg);
26413 }
26414
26415 // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
26416 // we can't let the user code consume the left over parts in these alloc contexts.
26417 repair_allocation_contexts (FALSE);
26418
26419#ifdef TIME_GC
26420 finish = GetCycleCount32();
26421 mark_time = finish - start;
26422#endif //TIME_GC
26423
26424 dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d",
26425 generation_free_list_space (generation_of (max_generation)),
26426 generation_free_obj_space (generation_of (max_generation))));
26427
26428 dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
26429}
26430
26431void
26432gc_heap::suspend_EE ()
26433{
26434 dprintf (2, ("suspend_EE"));
26435#ifdef MULTIPLE_HEAPS
26436 gc_heap* hp = gc_heap::g_heaps[0];
26437 GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26438#else
26439 GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26440#endif //MULTIPLE_HEAPS
26441}
26442
26443#ifdef MULTIPLE_HEAPS
26444void
26445gc_heap::bgc_suspend_EE ()
26446{
26447 for (int i = 0; i < n_heaps; i++)
26448 {
26449 gc_heap::g_heaps[i]->reset_gc_done();
26450 }
26451 gc_started = TRUE;
26452 dprintf (2, ("bgc_suspend_EE"));
26453 GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26454
26455 gc_started = FALSE;
26456 for (int i = 0; i < n_heaps; i++)
26457 {
26458 gc_heap::g_heaps[i]->set_gc_done();
26459 }
26460}
26461#else
26462void
26463gc_heap::bgc_suspend_EE ()
26464{
26465 reset_gc_done();
26466 gc_started = TRUE;
26467 dprintf (2, ("bgc_suspend_EE"));
26468 GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26469 gc_started = FALSE;
26470 set_gc_done();
26471}
26472#endif //MULTIPLE_HEAPS
26473
26474void
26475gc_heap::restart_EE ()
26476{
26477 dprintf (2, ("restart_EE"));
26478#ifdef MULTIPLE_HEAPS
26479 GCToEEInterface::RestartEE(FALSE);
26480#else
26481 GCToEEInterface::RestartEE(FALSE);
26482#endif //MULTIPLE_HEAPS
26483}
26484
26485inline uint8_t* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
26486{
26487 if (concurrent_p)
26488 {
26489 uint8_t* end = ((seg == ephemeral_heap_segment) ?
26490 generation_allocation_start (generation_of (max_generation-1)) :
26491 heap_segment_allocated (seg));
26492 return align_lower_page (end);
26493 }
26494 else
26495 {
26496 return heap_segment_allocated (seg);
26497 }
26498}
26499
26500void gc_heap::revisit_written_page (uint8_t* page,
26501 uint8_t* end,
26502 BOOL concurrent_p,
26503 heap_segment* seg,
26504 uint8_t*& last_page,
26505 uint8_t*& last_object,
26506 BOOL large_objects_p,
26507 size_t& num_marked_objects)
26508{
26509 UNREFERENCED_PARAMETER(seg);
26510
26511 uint8_t* start_address = page;
26512 uint8_t* o = 0;
26513 int align_const = get_alignment_constant (!large_objects_p);
26514 uint8_t* high_address = end;
26515 uint8_t* current_lowest_address = background_saved_lowest_address;
26516 uint8_t* current_highest_address = background_saved_highest_address;
26517 BOOL no_more_loop_p = FALSE;
26518
26519 THREAD_FROM_HEAP;
26520#ifndef MULTIPLE_HEAPS
26521 const int thread = heap_number;
26522#endif //!MULTIPLE_HEAPS
26523
26524 if (large_objects_p)
26525 {
26526 o = last_object;
26527 }
26528 else
26529 {
26530 if (((last_page + WRITE_WATCH_UNIT_SIZE) == page)
26531 || (start_address <= last_object))
26532 {
26533 o = last_object;
26534 }
26535 else
26536 {
26537 o = find_first_object (start_address, last_object);
26538 // We can visit the same object again, but on a different page.
26539 assert (o >= last_object);
26540 }
26541 }
26542
26543 dprintf (3,("page %Ix start: %Ix, %Ix[ ",
26544 (size_t)page, (size_t)o,
26545 (size_t)(min (high_address, page + WRITE_WATCH_UNIT_SIZE))));
26546
26547 while (o < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26548 {
26549 size_t s;
26550
26551 if (concurrent_p && large_objects_p)
26552 {
26553 bgc_alloc_lock->bgc_mark_set (o);
26554
26555 if (((CObjectHeader*)o)->IsFree())
26556 {
26557 s = unused_array_size (o);
26558 }
26559 else
26560 {
26561 s = size (o);
26562 }
26563 }
26564 else
26565 {
26566 s = size (o);
26567 }
26568
26569 dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
26570
26571 assert (Align (s) >= Align (min_obj_size));
26572
26573 uint8_t* next_o = o + Align (s, align_const);
26574
26575 if (next_o >= start_address)
26576 {
26577#ifdef MULTIPLE_HEAPS
26578 if (concurrent_p)
26579 {
26580 // We set last_object here for SVR BGC here because SVR BGC has more than
26581 // one GC thread. When we have more than one GC thread we would run into this
26582 // situation if we skipped unmarked objects:
26583 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it
26584 // for revisit.
26585 // bgc thread 2 marks X and all its current children.
26586 // user thread comes along and dirties more (and later) pages in X.
26587 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
26588 // on them because it had already skipped X. We need to detect that this object is now
26589 // marked and mark the children on the dirtied pages.
26590 // In the future if we have less BGC threads than we have heaps we should add
26591 // the check to the number of BGC threads.
26592 last_object = o;
26593 }
26594#endif //MULTIPLE_HEAPS
26595
26596 if (contain_pointers (o) &&
26597 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
26598 background_marked (o)))
26599 {
26600 dprintf (3, ("going through %Ix", (size_t)o));
26601 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
26602 if ((uint8_t*)poo >= min (high_address, page + WRITE_WATCH_UNIT_SIZE))
26603 {
26604 no_more_loop_p = TRUE;
26605 goto end_limit;
26606 }
26607 uint8_t* oo = *poo;
26608
26609 num_marked_objects++;
26610 background_mark_object (oo THREAD_NUMBER_ARG);
26611 );
26612 }
26613 else if (
26614 concurrent_p &&
26615#ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // see comment below
26616 large_objects_p &&
26617#endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26618 ((CObjectHeader*)o)->IsFree() &&
26619 (next_o > min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26620 {
26621 // We need to not skip the object here because of this corner scenario:
26622 // A large object was being allocated during BGC mark so we first made it
26623 // into a free object, then cleared its memory. In this loop we would detect
26624 // that it's a free object which normally we would skip. But by the next time
26625 // we call GetWriteWatch we could still be on this object and the object had
26626 // been made into a valid object and some of its memory was changed. We need
26627 // to be sure to process those written pages so we can't skip the object just
26628 // yet.
26629 //
26630 // Similarly, when using software write watch, don't advance last_object when
26631 // the current object is a free object that spans beyond the current page or
26632 // high_address. Software write watch acquires gc_lock before the concurrent
26633 // GetWriteWatch() call during revisit_written_pages(). A foreground GC may
26634 // happen at that point and allocate from this free region, so when
26635 // revisit_written_pages() continues, it cannot skip now-valid objects in this
26636 // region.
26637 no_more_loop_p = TRUE;
26638 goto end_limit;
26639 }
26640 }
26641end_limit:
26642 if (concurrent_p && large_objects_p)
26643 {
26644 bgc_alloc_lock->bgc_mark_done ();
26645 }
26646 if (no_more_loop_p)
26647 {
26648 break;
26649 }
26650 o = next_o;
26651 }
26652
26653#ifdef MULTIPLE_HEAPS
26654 if (concurrent_p)
26655 {
26656 assert (last_object < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)));
26657 }
26658 else
26659#endif //MULTIPLE_HEAPS
26660 {
26661 last_object = o;
26662 }
26663
26664 dprintf (3,("Last object: %Ix", (size_t)last_object));
26665 last_page = align_write_watch_lower_page (o);
26666}
26667
26668// When reset_only_p is TRUE, we should only reset pages that are in range
26669// because we need to consider the segments or part of segments that were
26670// allocated out of range all live.
26671void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
26672{
26673#ifdef WRITE_WATCH
26674 if (concurrent_p && !reset_only_p)
26675 {
26676 current_bgc_state = bgc_revisit_soh;
26677 }
26678
26679 size_t total_dirtied_pages = 0;
26680 size_t total_marked_objects = 0;
26681
26682 heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26683
26684 PREFIX_ASSUME(seg != NULL);
26685
26686 bool reset_watch_state = !!concurrent_p;
26687 bool is_runtime_suspended = !concurrent_p;
26688 BOOL small_object_segments = TRUE;
26689 int align_const = get_alignment_constant (small_object_segments);
26690
26691 while (1)
26692 {
26693 if (seg == 0)
26694 {
26695 if (small_object_segments)
26696 {
26697 //switch to large segment
26698 if (concurrent_p && !reset_only_p)
26699 {
26700 current_bgc_state = bgc_revisit_loh;
26701 }
26702
26703 if (!reset_only_p)
26704 {
26705 dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26706 fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26707 concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
26708 total_dirtied_pages = 0;
26709 total_marked_objects = 0;
26710 }
26711
26712 small_object_segments = FALSE;
26713 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
26714
26715 dprintf (3, ("now revisiting large object segments"));
26716 align_const = get_alignment_constant (small_object_segments);
26717 seg = heap_segment_rw (generation_start_segment (large_object_generation));
26718
26719 PREFIX_ASSUME(seg != NULL);
26720
26721 continue;
26722 }
26723 else
26724 {
26725 if (reset_only_p)
26726 {
26727 dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
26728 }
26729 else
26730 {
26731 dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26732 fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26733 }
26734 break;
26735 }
26736 }
26737 uint8_t* base_address = (uint8_t*)heap_segment_mem (seg);
26738 //we need to truncate to the base of the page because
26739 //some newly allocated could exist beyond heap_segment_allocated
26740 //and if we reset the last page write watch status,
26741 // they wouldn't be guaranteed to be visited -> gc hole.
26742 uintptr_t bcount = array_size;
26743 uint8_t* last_page = 0;
26744 uint8_t* last_object = heap_segment_mem (seg);
26745 uint8_t* high_address = 0;
26746
26747 BOOL skip_seg_p = FALSE;
26748
26749 if (reset_only_p)
26750 {
26751 if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
26752 (heap_segment_reserved (seg) <= background_saved_highest_address))
26753 {
26754 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number,
26755 heap_segment_mem (seg), heap_segment_reserved (seg)));
26756 skip_seg_p = TRUE;
26757 }
26758 }
26759
26760 if (!skip_seg_p)
26761 {
26762 dprintf (3, ("looking at seg %Ix", (size_t)last_object));
26763
26764 if (reset_only_p)
26765 {
26766 base_address = max (base_address, background_saved_lowest_address);
26767 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
26768 }
26769
26770 dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address,
26771 heap_segment_mem (seg), heap_segment_reserved (seg)));
26772
26773
26774 while (1)
26775 {
26776 if (reset_only_p)
26777 {
26778 high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
26779 high_address = min (high_address, background_saved_highest_address);
26780 }
26781 else
26782 {
26783 high_address = high_page (seg, concurrent_p);
26784 }
26785
26786 if ((base_address < high_address) &&
26787 (bcount >= array_size))
26788 {
26789 ptrdiff_t region_size = high_address - base_address;
26790 dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
26791
26792#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26793 // When the runtime is not suspended, it's possible for the table to be resized concurrently with the scan
26794 // for dirty pages below. Prevent that by synchronizing with grow_brick_card_tables(). When the runtime is
26795 // suspended, it's ok to scan for dirty pages concurrently from multiple background GC threads for disjoint
26796 // memory regions.
26797 if (!is_runtime_suspended)
26798 {
26799 enter_spin_lock(&gc_lock);
26800 }
26801#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26802
26803 get_write_watch_for_gc_heap (reset_watch_state, base_address, region_size,
26804 (void**)background_written_addresses,
26805 &bcount, is_runtime_suspended);
26806
26807#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26808 if (!is_runtime_suspended)
26809 {
26810 leave_spin_lock(&gc_lock);
26811 }
26812#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26813
26814 if (bcount != 0)
26815 {
26816 total_dirtied_pages += bcount;
26817
26818 dprintf (3, ("Found %d pages [%Ix, %Ix[",
26819 bcount, (size_t)base_address, (size_t)high_address));
26820 }
26821
26822 if (!reset_only_p)
26823 {
26824 for (unsigned i = 0; i < bcount; i++)
26825 {
26826 uint8_t* page = (uint8_t*)background_written_addresses[i];
26827 dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i,
26828 (size_t)page, (size_t)high_address));
26829 if (page < high_address)
26830 {
26831 //search for marked objects in the page
26832 revisit_written_page (page, high_address, concurrent_p,
26833 seg, last_page, last_object,
26834 !small_object_segments,
26835 total_marked_objects);
26836 }
26837 else
26838 {
26839 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
26840 assert (!"page shouldn't have exceeded limit");
26841 }
26842 }
26843 }
26844
26845 if (bcount >= array_size){
26846 base_address = background_written_addresses [array_size-1] + WRITE_WATCH_UNIT_SIZE;
26847 bcount = array_size;
26848 }
26849 }
26850 else
26851 {
26852 break;
26853 }
26854 }
26855 }
26856
26857 seg = heap_segment_next_rw (seg);
26858 }
26859
26860#endif //WRITE_WATCH
26861}
26862
26863void gc_heap::background_grow_c_mark_list()
26864{
26865 assert (c_mark_list_index >= c_mark_list_length);
26866 BOOL should_drain_p = FALSE;
26867 THREAD_FROM_HEAP;
26868#ifndef MULTIPLE_HEAPS
26869 const int thread = heap_number;
26870#endif //!MULTIPLE_HEAPS
26871
26872 dprintf (2, ("stack copy buffer overflow"));
26873 uint8_t** new_c_mark_list = 0;
26874 {
26875 FAULT_NOT_FATAL();
26876 if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (uint8_t*))))
26877 {
26878 should_drain_p = TRUE;
26879 }
26880 else
26881 {
26882 new_c_mark_list = new (nothrow) uint8_t*[c_mark_list_length*2];
26883 if (new_c_mark_list == 0)
26884 {
26885 should_drain_p = TRUE;
26886 }
26887 }
26888 }
26889 if (should_drain_p)
26890
26891 {
26892 dprintf (2, ("No more memory for the stacks copy, draining.."));
26893 //drain the list by marking its elements
26894 background_drain_mark_list (thread);
26895 }
26896 else
26897 {
26898 assert (new_c_mark_list);
26899 memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(uint8_t*));
26900 c_mark_list_length = c_mark_list_length*2;
26901 delete c_mark_list;
26902 c_mark_list = new_c_mark_list;
26903 }
26904}
26905
26906void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
26907 uint32_t flags)
26908{
26909 UNREFERENCED_PARAMETER(sc);
26910 //in order to save space on the array, mark the object,
26911 //knowing that it will be visited later
26912 assert (settings.concurrent);
26913
26914 THREAD_NUMBER_FROM_CONTEXT;
26915#ifndef MULTIPLE_HEAPS
26916 const int thread = 0;
26917#endif //!MULTIPLE_HEAPS
26918
26919 uint8_t* o = (uint8_t*)*ppObject;
26920
26921 if (o == 0)
26922 return;
26923
26924 HEAP_FROM_THREAD;
26925
26926 gc_heap* hp = gc_heap::heap_of (o);
26927
26928 if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
26929 {
26930 return;
26931 }
26932
26933#ifdef INTERIOR_POINTERS
26934 if (flags & GC_CALL_INTERIOR)
26935 {
26936 o = hp->find_object (o, hp->background_saved_lowest_address);
26937 if (o == 0)
26938 return;
26939 }
26940#endif //INTERIOR_POINTERS
26941
26942#ifdef FEATURE_CONSERVATIVE_GC
26943 // For conservative GC, a value on stack may point to middle of a free object.
26944 // In this case, we don't need to promote the pointer.
26945 if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
26946 {
26947 return;
26948 }
26949#endif //FEATURE_CONSERVATIVE_GC
26950
26951#ifdef _DEBUG
26952 ((CObjectHeader*)o)->Validate();
26953#endif //_DEBUG
26954
26955 dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
26956 if (o && (size (o) > loh_size_threshold))
26957 {
26958 dprintf (3, ("Brc %Ix", (size_t)o));
26959 }
26960
26961 if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
26962 {
26963 hpt->background_grow_c_mark_list();
26964 }
26965 dprintf (3, ("pushing %08x into mark_list", (size_t)o));
26966 hpt->c_mark_list [hpt->c_mark_list_index++] = o;
26967
26968 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);
26969}
26970
26971void gc_heap::mark_absorb_new_alloc()
26972{
26973 fix_allocation_contexts (FALSE);
26974
26975 gen0_bricks_cleared = FALSE;
26976
26977 clear_gen0_bricks();
26978}
26979
26980BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
26981{
26982 BOOL success = FALSE;
26983 BOOL thread_created = FALSE;
26984 dprintf (2, ("Preparing gc thread"));
26985 gh->bgc_threads_timeout_cs.Enter();
26986 if (!(gh->bgc_thread_running))
26987 {
26988 dprintf (2, ("GC thread not runnning"));
26989 if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
26990 {
26991 success = TRUE;
26992 thread_created = TRUE;
26993 }
26994 }
26995 else
26996 {
26997 dprintf (3, ("GC thread already running"));
26998 success = TRUE;
26999 }
27000 gh->bgc_threads_timeout_cs.Leave();
27001
27002 if(thread_created)
27003 FIRE_EVENT(GCCreateConcurrentThread_V1);
27004
27005 return success;
27006}
27007
27008BOOL gc_heap::create_bgc_thread(gc_heap* gh)
27009{
27010 assert (background_gc_done_event.IsValid());
27011
27012 //dprintf (2, ("Creating BGC thread"));
27013
27014 gh->bgc_thread_running = GCToEEInterface::CreateThread(gh->bgc_thread_stub, gh, true, ".NET Background GC");
27015 return gh->bgc_thread_running;
27016}
27017
27018BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
27019{
27020 BOOL ret = FALSE;
27021 dprintf (3, ("Creating concurrent GC thread for the first time"));
27022 if (!background_gc_done_event.CreateManualEventNoThrow(TRUE))
27023 {
27024 goto cleanup;
27025 }
27026 if (!bgc_threads_sync_event.CreateManualEventNoThrow(FALSE))
27027 {
27028 goto cleanup;
27029 }
27030 if (!ee_proceed_event.CreateAutoEventNoThrow(FALSE))
27031 {
27032 goto cleanup;
27033 }
27034 if (!bgc_start_event.CreateManualEventNoThrow(FALSE))
27035 {
27036 goto cleanup;
27037 }
27038
27039#ifdef MULTIPLE_HEAPS
27040 bgc_t_join.init (number_of_heaps, join_flavor_bgc);
27041#else
27042 UNREFERENCED_PARAMETER(number_of_heaps);
27043#endif //MULTIPLE_HEAPS
27044
27045 ret = TRUE;
27046
27047cleanup:
27048
27049 if (!ret)
27050 {
27051 if (background_gc_done_event.IsValid())
27052 {
27053 background_gc_done_event.CloseEvent();
27054 }
27055 if (bgc_threads_sync_event.IsValid())
27056 {
27057 bgc_threads_sync_event.CloseEvent();
27058 }
27059 if (ee_proceed_event.IsValid())
27060 {
27061 ee_proceed_event.CloseEvent();
27062 }
27063 if (bgc_start_event.IsValid())
27064 {
27065 bgc_start_event.CloseEvent();
27066 }
27067 }
27068
27069 return ret;
27070}
27071
27072BOOL gc_heap::create_bgc_thread_support()
27073{
27074 BOOL ret = FALSE;
27075 uint8_t** parr;
27076
27077 if (!gc_lh_block_event.CreateManualEventNoThrow(FALSE))
27078 {
27079 goto cleanup;
27080 }
27081
27082 //needs to have room for enough smallest objects fitting on a page
27083 parr = new (nothrow) uint8_t*[1 + OS_PAGE_SIZE / MIN_OBJECT_SIZE];
27084 if (!parr)
27085 {
27086 goto cleanup;
27087 }
27088
27089 make_c_mark_list (parr);
27090
27091 ret = TRUE;
27092
27093cleanup:
27094
27095 if (!ret)
27096 {
27097 if (gc_lh_block_event.IsValid())
27098 {
27099 gc_lh_block_event.CloseEvent();
27100 }
27101 }
27102
27103 return ret;
27104}
27105
27106int gc_heap::check_for_ephemeral_alloc()
27107{
27108 int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
27109
27110 if (gen == -1)
27111 {
27112#ifdef MULTIPLE_HEAPS
27113 for (int heap_index = 0; heap_index < n_heaps; heap_index++)
27114#endif //MULTIPLE_HEAPS
27115 {
27116 for (int i = 0; i <= (max_generation - 1); i++)
27117 {
27118#ifdef MULTIPLE_HEAPS
27119 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
27120#else
27121 if (get_new_allocation (i) <= 0)
27122#endif //MULTIPLE_HEAPS
27123 {
27124 gen = max (gen, i);
27125 }
27126 else
27127 break;
27128 }
27129 }
27130 }
27131
27132 return gen;
27133}
27134
27135// Wait for gc to finish sequential part
27136void gc_heap::wait_to_proceed()
27137{
27138 assert (background_gc_done_event.IsValid());
27139 assert (bgc_start_event.IsValid());
27140
27141 user_thread_wait(&ee_proceed_event, FALSE);
27142}
27143
27144// Start a new concurrent gc
27145void gc_heap::start_c_gc()
27146{
27147 assert (background_gc_done_event.IsValid());
27148 assert (bgc_start_event.IsValid());
27149
27150//Need to make sure that the gc thread is in the right place.
27151 background_gc_done_event.Wait(INFINITE, FALSE);
27152 background_gc_done_event.Reset();
27153 bgc_start_event.Set();
27154}
27155
27156void gc_heap::do_background_gc()
27157{
27158 dprintf (2, ("starting a BGC"));
27159#ifdef MULTIPLE_HEAPS
27160 for (int i = 0; i < n_heaps; i++)
27161 {
27162 g_heaps[i]->init_background_gc();
27163 }
27164#else
27165 init_background_gc();
27166#endif //MULTIPLE_HEAPS
27167 //start the background gc
27168 start_c_gc ();
27169
27170 //wait until we get restarted by the BGC.
27171 wait_to_proceed();
27172}
27173
27174void gc_heap::kill_gc_thread()
27175{
27176 //assert (settings.concurrent == FALSE);
27177
27178 // We are doing a two-stage shutdown now.
27179 // In the first stage, we do minimum work, and call ExitProcess at the end.
27180 // In the secodn stage, we have the Loader lock and only one thread is
27181 // alive. Hence we do not need to kill gc thread.
27182 background_gc_done_event.CloseEvent();
27183 gc_lh_block_event.CloseEvent();
27184 bgc_start_event.CloseEvent();
27185 bgc_threads_timeout_cs.Destroy();
27186 bgc_thread = 0;
27187 recursive_gc_sync::shutdown();
27188}
27189
27190void gc_heap::bgc_thread_function()
27191{
27192 assert (background_gc_done_event.IsValid());
27193 assert (bgc_start_event.IsValid());
27194
27195 dprintf (3, ("gc_thread thread starting..."));
27196
27197 BOOL do_exit = FALSE;
27198
27199 bool cooperative_mode = true;
27200 bgc_thread_id.SetToCurrentThread();
27201 dprintf (1, ("bgc_thread_id is set to %x", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()));
27202 while (1)
27203 {
27204 // Wait for work to do...
27205 dprintf (3, ("bgc thread: waiting..."));
27206
27207 cooperative_mode = enable_preemptive ();
27208 //current_thread->m_fPreemptiveGCDisabled = 0;
27209
27210 uint32_t result = bgc_start_event.Wait(
27211#ifdef _DEBUG
27212#ifdef MULTIPLE_HEAPS
27213 INFINITE,
27214#else
27215 2000,
27216#endif //MULTIPLE_HEAPS
27217#else //_DEBUG
27218#ifdef MULTIPLE_HEAPS
27219 INFINITE,
27220#else
27221 20000,
27222#endif //MULTIPLE_HEAPS
27223#endif //_DEBUG
27224 FALSE);
27225 dprintf (2, ("gc thread: finished waiting"));
27226
27227 // not calling disable_preemptive here 'cause we
27228 // can't wait for GC complete here - RestartEE will be called
27229 // when we've done the init work.
27230
27231 if (result == WAIT_TIMEOUT)
27232 {
27233 // Should join the bgc threads and terminate all of them
27234 // at once.
27235 dprintf (1, ("GC thread timeout"));
27236 bgc_threads_timeout_cs.Enter();
27237 if (!keep_bgc_threads_p)
27238 {
27239 dprintf (2, ("GC thread exiting"));
27240 bgc_thread_running = FALSE;
27241 bgc_thread = 0;
27242 bgc_thread_id.Clear();
27243 do_exit = TRUE;
27244 }
27245 bgc_threads_timeout_cs.Leave();
27246 if (do_exit)
27247 break;
27248 else
27249 {
27250 dprintf (3, ("GC thread needed, not exiting"));
27251 continue;
27252 }
27253 }
27254 // if we signal the thread with no concurrent work to do -> exit
27255 if (!settings.concurrent)
27256 {
27257 dprintf (3, ("no concurrent GC needed, exiting"));
27258 break;
27259 }
27260#ifdef TRACE_GC
27261 //trace_gc = TRUE;
27262#endif //TRACE_GC
27263 recursive_gc_sync::begin_background();
27264 dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d",
27265 generation_free_list_space (generation_of (max_generation)),
27266 generation_free_obj_space (generation_of (max_generation)),
27267 dd_fragmentation (dynamic_data_of (max_generation))));
27268
27269 gc1();
27270
27271 current_bgc_state = bgc_not_in_process;
27272
27273#ifdef TRACE_GC
27274 //trace_gc = FALSE;
27275#endif //TRACE_GC
27276
27277 enable_preemptive ();
27278#ifdef MULTIPLE_HEAPS
27279 bgc_t_join.join(this, gc_join_done);
27280 if (bgc_t_join.joined())
27281#endif //MULTIPLE_HEAPS
27282 {
27283 enter_spin_lock (&gc_lock);
27284 dprintf (SPINLOCK_LOG, ("bgc Egc"));
27285
27286 bgc_start_event.Reset();
27287 do_post_gc();
27288#ifdef MULTIPLE_HEAPS
27289 for (int gen = max_generation; gen <= (max_generation + 1); gen++)
27290 {
27291 size_t desired_per_heap = 0;
27292 size_t total_desired = 0;
27293 gc_heap* hp = 0;
27294 dynamic_data* dd;
27295 for (int i = 0; i < n_heaps; i++)
27296 {
27297 hp = g_heaps[i];
27298 dd = hp->dynamic_data_of (gen);
27299 size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
27300 if (temp_total_desired < total_desired)
27301 {
27302 // we overflowed.
27303 total_desired = (size_t)MAX_PTR;
27304 break;
27305 }
27306 total_desired = temp_total_desired;
27307 }
27308
27309 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
27310
27311 for (int i = 0; i < n_heaps; i++)
27312 {
27313 hp = gc_heap::g_heaps[i];
27314 dd = hp->dynamic_data_of (gen);
27315 dd_desired_allocation (dd) = desired_per_heap;
27316 dd_gc_new_allocation (dd) = desired_per_heap;
27317 dd_new_allocation (dd) = desired_per_heap;
27318 }
27319 }
27320#endif //MULTIPLE_HEAPS
27321#ifdef MULTIPLE_HEAPS
27322 fire_pevents();
27323#endif //MULTIPLE_HEAPS
27324
27325 c_write (settings.concurrent, FALSE);
27326 recursive_gc_sync::end_background();
27327 keep_bgc_threads_p = FALSE;
27328 background_gc_done_event.Set();
27329
27330 dprintf (SPINLOCK_LOG, ("bgc Lgc"));
27331 leave_spin_lock (&gc_lock);
27332#ifdef MULTIPLE_HEAPS
27333 dprintf(1, ("End of BGC - starting all BGC threads"));
27334 bgc_t_join.restart();
27335#endif //MULTIPLE_HEAPS
27336 }
27337 // We can't disable preempt here because there might've been a GC already
27338 // started and decided to do a BGC and waiting for a BGC thread to restart
27339 // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
27340 // to restart the VM so we deadlock.
27341 //gc_heap::disable_preemptive (true);
27342 }
27343
27344 FIRE_EVENT(GCTerminateConcurrentThread_V1);
27345
27346 dprintf (3, ("bgc_thread thread exiting"));
27347 return;
27348}
27349
27350#endif //BACKGROUND_GC
27351
27352//Clear the cards [start_card, end_card[
27353void gc_heap::clear_cards (size_t start_card, size_t end_card)
27354{
27355 if (start_card < end_card)
27356 {
27357 size_t start_word = card_word (start_card);
27358 size_t end_word = card_word (end_card);
27359 if (start_word < end_word)
27360 {
27361 // Figure out the bit positions of the cards within their words
27362 unsigned bits = card_bit (start_card);
27363 card_table [start_word] &= lowbits (~0, bits);
27364 for (size_t i = start_word+1; i < end_word; i++)
27365 card_table [i] = 0;
27366 bits = card_bit (end_card);
27367 // Don't write beyond end_card (and possibly uncommitted card table space).
27368 if (bits != 0)
27369 {
27370 card_table [end_word] &= highbits (~0, bits);
27371 }
27372 }
27373 else
27374 {
27375 // If the start and end cards are in the same word, just clear the appropriate card
27376 // bits in that word.
27377 card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
27378 highbits (~0, card_bit (end_card)));
27379 }
27380#ifdef VERYSLOWDEBUG
27381 size_t card = start_card;
27382 while (card < end_card)
27383 {
27384 assert (! (card_set_p (card)));
27385 card++;
27386 }
27387#endif //VERYSLOWDEBUG
27388 dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
27389 start_card, (size_t)card_address (start_card),
27390 end_card, (size_t)card_address (end_card)));
27391 }
27392}
27393
27394void gc_heap::clear_card_for_addresses (uint8_t* start_address, uint8_t* end_address)
27395{
27396 size_t start_card = card_of (align_on_card (start_address));
27397 size_t end_card = card_of (align_lower_card (end_address));
27398 clear_cards (start_card, end_card);
27399}
27400
27401// copy [srccard, ...[ to [dst_card, end_card[
27402// This will set the same bit twice. Can be optimized.
27403inline
27404void gc_heap::copy_cards (size_t dst_card,
27405 size_t src_card,
27406 size_t end_card,
27407 BOOL nextp)
27408{
27409 // If the range is empty, this function is a no-op - with the subtlety that
27410 // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
27411 // outside the committed region. To avoid the access, leave early.
27412 if (!(dst_card < end_card))
27413 return;
27414
27415 unsigned int srcbit = card_bit (src_card);
27416 unsigned int dstbit = card_bit (dst_card);
27417 size_t srcwrd = card_word (src_card);
27418 size_t dstwrd = card_word (dst_card);
27419 unsigned int srctmp = card_table[srcwrd];
27420 unsigned int dsttmp = card_table[dstwrd];
27421
27422 for (size_t card = dst_card; card < end_card; card++)
27423 {
27424 if (srctmp & (1 << srcbit))
27425 dsttmp |= 1 << dstbit;
27426 else
27427 dsttmp &= ~(1 << dstbit);
27428 if (!(++srcbit % 32))
27429 {
27430 srctmp = card_table[++srcwrd];
27431 srcbit = 0;
27432 }
27433
27434 if (nextp)
27435 {
27436 if (srctmp & (1 << srcbit))
27437 dsttmp |= 1 << dstbit;
27438 }
27439
27440 if (!(++dstbit % 32))
27441 {
27442 card_table[dstwrd] = dsttmp;
27443
27444#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27445 if (dsttmp != 0)
27446 {
27447 card_bundle_set(cardw_card_bundle(dstwrd));
27448 }
27449#endif
27450
27451 dstwrd++;
27452 dsttmp = card_table[dstwrd];
27453 dstbit = 0;
27454 }
27455 }
27456
27457 card_table[dstwrd] = dsttmp;
27458
27459#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27460 if (dsttmp != 0)
27461 {
27462 card_bundle_set(cardw_card_bundle(dstwrd));
27463 }
27464#endif
27465}
27466
27467void gc_heap::copy_cards_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27468{
27469 ptrdiff_t relocation_distance = src - dest;
27470 size_t start_dest_card = card_of (align_on_card (dest));
27471 size_t end_dest_card = card_of (dest + len - 1);
27472 size_t dest_card = start_dest_card;
27473 size_t src_card = card_of (card_address (dest_card)+relocation_distance);
27474 dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
27475 src_card, (size_t)src, dest_card, (size_t)dest));
27476 dprintf (3,(" %Ix->%Ix:%Ix[",
27477 (size_t)src+len, end_dest_card, (size_t)dest+len));
27478
27479 dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
27480 dest, src, len, relocation_distance, (align_on_card (dest))));
27481
27482 dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
27483 start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
27484
27485 //First card has two boundaries
27486 if (start_dest_card != card_of (dest))
27487 {
27488 if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
27489 card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
27490 {
27491 dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
27492 (card_address (start_dest_card) + relocation_distance),
27493 card_of (card_address (start_dest_card) + relocation_distance),
27494 (src + len - 1),
27495 card_of (src + len - 1)));
27496
27497 dprintf (3, ("setting card: %Ix", card_of (dest)));
27498 set_card (card_of (dest));
27499 }
27500 }
27501
27502 if (card_set_p (card_of (src)))
27503 set_card (card_of (dest));
27504
27505
27506 copy_cards (dest_card, src_card, end_dest_card,
27507 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
27508
27509 //Last card has two boundaries.
27510 if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
27511 card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
27512 {
27513 dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
27514 (card_address (end_dest_card) + relocation_distance),
27515 card_of (card_address (end_dest_card) + relocation_distance),
27516 src,
27517 card_of (src)));
27518
27519 dprintf (3, ("setting card: %Ix", end_dest_card));
27520 set_card (end_dest_card);
27521 }
27522
27523 if (card_set_p (card_of (src + len - 1)))
27524 set_card (end_dest_card);
27525
27526#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27527 card_bundles_set(cardw_card_bundle(card_word(card_of(dest))), cardw_card_bundle(align_cardw_on_bundle(card_word(end_dest_card))));
27528#endif
27529}
27530
27531#ifdef BACKGROUND_GC
27532// this does not need the Interlocked version of mark_array_set_marked.
27533void gc_heap::copy_mark_bits_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27534{
27535 dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
27536 (size_t)src, (size_t)dest,
27537 (size_t)src+len, (size_t)dest+len));
27538
27539 uint8_t* src_o = src;
27540 uint8_t* dest_o;
27541 uint8_t* src_end = src + len;
27542 int align_const = get_alignment_constant (TRUE);
27543 ptrdiff_t reloc = dest - src;
27544
27545 while (src_o < src_end)
27546 {
27547 uint8_t* next_o = src_o + Align (size (src_o), align_const);
27548
27549 if (background_object_marked (src_o, TRUE))
27550 {
27551 dest_o = src_o + reloc;
27552
27553 //if (background_object_marked (dest_o, FALSE))
27554 //{
27555 // dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
27556 // FATAL_GC_ERROR();
27557 //}
27558
27559 background_mark (dest_o,
27560 background_saved_lowest_address,
27561 background_saved_highest_address);
27562 dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
27563 }
27564
27565 src_o = next_o;
27566 }
27567}
27568#endif //BACKGROUND_GC
27569
27570void gc_heap::fix_brick_to_highest (uint8_t* o, uint8_t* next_o)
27571{
27572 size_t new_current_brick = brick_of (o);
27573 set_brick (new_current_brick,
27574 (o - brick_address (new_current_brick)));
27575 size_t b = 1 + new_current_brick;
27576 size_t limit = brick_of (next_o);
27577 //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
27578 dprintf(3,("b:%Ix->%Ix-%Ix",
27579 new_current_brick, (size_t)o, (size_t)next_o));
27580 while (b < limit)
27581 {
27582 set_brick (b,(new_current_brick - b));
27583 b++;
27584 }
27585}
27586
27587// start can not be >= heap_segment_allocated for the segment.
27588uint8_t* gc_heap::find_first_object (uint8_t* start, uint8_t* first_object)
27589{
27590 size_t brick = brick_of (start);
27591 uint8_t* o = 0;
27592 //last_object == null -> no search shortcut needed
27593 if ((brick == brick_of (first_object) || (start <= first_object)))
27594 {
27595 o = first_object;
27596 }
27597 else
27598 {
27599 ptrdiff_t min_brick = (ptrdiff_t)brick_of (first_object);
27600 ptrdiff_t prev_brick = (ptrdiff_t)brick - 1;
27601 int brick_entry = 0;
27602 while (1)
27603 {
27604 if (prev_brick < min_brick)
27605 {
27606 break;
27607 }
27608 if ((brick_entry = get_brick_entry(prev_brick)) >= 0)
27609 {
27610 break;
27611 }
27612 assert (! ((brick_entry == 0)));
27613 prev_brick = (brick_entry + prev_brick);
27614
27615 }
27616 o = ((prev_brick < min_brick) ? first_object :
27617 brick_address (prev_brick) + brick_entry - 1);
27618 assert (o <= start);
27619 }
27620
27621 assert (Align (size (o)) >= Align (min_obj_size));
27622 uint8_t* next_o = o + Align (size (o));
27623 size_t curr_cl = (size_t)next_o / brick_size;
27624 size_t min_cl = (size_t)first_object / brick_size;
27625
27626 //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
27627#ifdef TRACE_GC
27628 unsigned int n_o = 1;
27629#endif //TRACE_GC
27630
27631 uint8_t* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27632
27633 while (next_o <= start)
27634 {
27635 do
27636 {
27637#ifdef TRACE_GC
27638 n_o++;
27639#endif //TRACE_GC
27640 o = next_o;
27641 assert (Align (size (o)) >= Align (min_obj_size));
27642 next_o = o + Align (size (o));
27643 Prefetch (next_o);
27644 }while (next_o < next_b);
27645
27646 if (((size_t)next_o / brick_size) != curr_cl)
27647 {
27648 if (curr_cl >= min_cl)
27649 {
27650 fix_brick_to_highest (o, next_o);
27651 }
27652 curr_cl = (size_t) next_o / brick_size;
27653 }
27654 next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27655 }
27656
27657 size_t bo = brick_of (o);
27658 //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix",
27659 dprintf (3, ("%Id o, [%Ix-[%Ix",
27660 n_o, bo, brick));
27661 if (bo < brick)
27662 {
27663 set_brick (bo, (o - brick_address(bo)));
27664 size_t b = 1 + bo;
27665 int x = -1;
27666 while (b < brick)
27667 {
27668 set_brick (b,x--);
27669 b++;
27670 }
27671 }
27672
27673 return o;
27674}
27675
27676#ifdef CARD_BUNDLE
27677
27678// Find the first non-zero card word between cardw and cardw_end.
27679// The index of the word we find is returned in cardw.
27680BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
27681{
27682 dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
27683 dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
27684
27685 if (card_bundles_enabled())
27686 {
27687 size_t cardb = cardw_card_bundle (cardw);
27688 size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
27689 while (1)
27690 {
27691 // Find a non-zero bundle
27692 while ((cardb < end_cardb) && (card_bundle_set_p (cardb) == 0))
27693 {
27694 cardb++;
27695 }
27696 if (cardb == end_cardb)
27697 return FALSE;
27698
27699 uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
27700 uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
27701 while ((card_word < card_word_end) && !(*card_word))
27702 {
27703 card_word++;
27704 }
27705
27706 if (card_word != card_word_end)
27707 {
27708 cardw = (card_word - &card_table[0]);
27709 return TRUE;
27710 }
27711 else if ((cardw <= card_bundle_cardw (cardb)) &&
27712 (card_word == &card_table [card_bundle_cardw (cardb+1)]))
27713 {
27714 // a whole bundle was explored and is empty
27715 dprintf (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
27716 dd_collection_count (dynamic_data_of (0)),
27717 cardb, card_bundle_cardw (cardb),
27718 card_bundle_cardw (cardb+1)));
27719 card_bundle_clear (cardb);
27720 }
27721
27722 cardb++;
27723 }
27724 }
27725 else
27726 {
27727 uint32_t* card_word = &card_table[cardw];
27728 uint32_t* card_word_end = &card_table [cardw_end];
27729
27730 while (card_word < card_word_end)
27731 {
27732 if ((*card_word) != 0)
27733 {
27734 cardw = (card_word - &card_table [0]);
27735 return TRUE;
27736 }
27737
27738 card_word++;
27739 }
27740 return FALSE;
27741
27742 }
27743
27744}
27745
27746#endif //CARD_BUNDLE
27747
27748// Find cards that are set between two points in a card table.
27749// Parameters
27750// card_table : The card table.
27751// card : [in/out] As input, the card to start searching from.
27752// As output, the first card that's set.
27753// card_word_end : The card word at which to stop looking.
27754// end_card : [out] The last card which is set.
27755BOOL gc_heap::find_card(uint32_t* card_table,
27756 size_t& card,
27757 size_t card_word_end,
27758 size_t& end_card)
27759{
27760 uint32_t* last_card_word;
27761 uint32_t card_word_value;
27762 uint32_t bit_position;
27763
27764 // Find the first card which is set
27765 last_card_word = &card_table [card_word (card)];
27766 bit_position = card_bit (card);
27767 card_word_value = (*last_card_word) >> bit_position;
27768 if (!card_word_value)
27769 {
27770 bit_position = 0;
27771#ifdef CARD_BUNDLE
27772 // Using the card bundle, go through the remaining card words between here and
27773 // card_word_end until we find one that is non-zero.
27774 size_t lcw = card_word(card) + 1;
27775 if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
27776 {
27777 return FALSE;
27778 }
27779 else
27780 {
27781 last_card_word = &card_table [lcw];
27782 card_word_value = *last_card_word;
27783 }
27784
27785#else //CARD_BUNDLE
27786 // Go through the remaining card words between here and card_word_end until we find
27787 // one that is non-zero.
27788 do
27789 {
27790 ++last_card_word;
27791 }
27792
27793 while ((last_card_word < &card_table [card_word_end]) && !(*last_card_word));
27794 if (last_card_word < &card_table [card_word_end])
27795 {
27796 card_word_value = *last_card_word;
27797 }
27798 else
27799 {
27800 // We failed to find any non-zero card words before we got to card_word_end
27801 return FALSE;
27802 }
27803#endif //CARD_BUNDLE
27804 }
27805
27806
27807 // Look for the lowest bit set
27808 if (card_word_value)
27809 {
27810 while (!(card_word_value & 1))
27811 {
27812 bit_position++;
27813 card_word_value = card_word_value / 2;
27814 }
27815 }
27816
27817 // card is the card word index * card size + the bit index within the card
27818 card = (last_card_word - &card_table[0]) * card_word_width + bit_position;
27819
27820 do
27821 {
27822 // Keep going until we get to an un-set card.
27823 bit_position++;
27824 card_word_value = card_word_value / 2;
27825
27826 // If we reach the end of the card word and haven't hit a 0 yet, start going
27827 // card word by card word until we get to one that's not fully set (0xFFFF...)
27828 // or we reach card_word_end.
27829 if ((bit_position == card_word_width) && (last_card_word < &card_table [card_word_end]))
27830 {
27831 do
27832 {
27833 card_word_value = *(++last_card_word);
27834 } while ((last_card_word < &card_table [card_word_end]) &&
27835
27836#ifdef _MSC_VER
27837 (card_word_value == (1 << card_word_width)-1)
27838#else
27839 // if left shift count >= width of type,
27840 // gcc reports error.
27841 (card_word_value == ~0u)
27842#endif // _MSC_VER
27843 );
27844 bit_position = 0;
27845 }
27846 } while (card_word_value & 1);
27847
27848 end_card = (last_card_word - &card_table [0])* card_word_width + bit_position;
27849
27850 //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
27851 dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
27852 return TRUE;
27853}
27854
27855
27856 //because of heap expansion, computing end is complicated.
27857uint8_t* compute_next_end (heap_segment* seg, uint8_t* low)
27858{
27859 if ((low >= heap_segment_mem (seg)) &&
27860 (low < heap_segment_allocated (seg)))
27861 return low;
27862 else
27863 return heap_segment_allocated (seg);
27864}
27865
27866uint8_t*
27867gc_heap::compute_next_boundary (uint8_t* low, int gen_number,
27868 BOOL relocating)
27869{
27870 UNREFERENCED_PARAMETER(low);
27871
27872 //when relocating, the fault line is the plan start of the younger
27873 //generation because the generation is promoted.
27874 if (relocating && (gen_number == (settings.condemned_generation + 1)))
27875 {
27876 generation* gen = generation_of (gen_number - 1);
27877 uint8_t* gen_alloc = generation_plan_allocation_start (gen);
27878 assert (gen_alloc);
27879 return gen_alloc;
27880 }
27881 else
27882 {
27883 assert (gen_number > settings.condemned_generation);
27884 return generation_allocation_start (generation_of (gen_number - 1 ));
27885 }
27886
27887}
27888
27889inline void
27890gc_heap::keep_card_live (uint8_t* o, size_t& n_gen,
27891 size_t& cg_pointers_found)
27892{
27893 THREAD_FROM_HEAP;
27894 if ((gc_low <= o) && (gc_high > o))
27895 {
27896 n_gen++;
27897 }
27898#ifdef MULTIPLE_HEAPS
27899 else if (o)
27900 {
27901 gc_heap* hp = heap_of (o);
27902 if (hp != this)
27903 {
27904 if ((hp->gc_low <= o) &&
27905 (hp->gc_high > o))
27906 {
27907 n_gen++;
27908 }
27909 }
27910 }
27911#endif //MULTIPLE_HEAPS
27912 cg_pointers_found ++;
27913 dprintf (4, ("keep card live for %Ix", o));
27914}
27915
27916inline void
27917gc_heap::mark_through_cards_helper (uint8_t** poo, size_t& n_gen,
27918 size_t& cg_pointers_found,
27919 card_fn fn, uint8_t* nhigh,
27920 uint8_t* next_boundary)
27921{
27922 THREAD_FROM_HEAP;
27923 if ((gc_low <= *poo) && (gc_high > *poo))
27924 {
27925 n_gen++;
27926 call_fn(fn) (poo THREAD_NUMBER_ARG);
27927 }
27928#ifdef MULTIPLE_HEAPS
27929 else if (*poo)
27930 {
27931 gc_heap* hp = heap_of_gc (*poo);
27932 if (hp != this)
27933 {
27934 if ((hp->gc_low <= *poo) &&
27935 (hp->gc_high > *poo))
27936 {
27937 n_gen++;
27938 call_fn(fn) (poo THREAD_NUMBER_ARG);
27939 }
27940 if ((fn == &gc_heap::relocate_address) ||
27941 ((hp->ephemeral_low <= *poo) &&
27942 (hp->ephemeral_high > *poo)))
27943 {
27944 cg_pointers_found++;
27945 }
27946 }
27947 }
27948#endif //MULTIPLE_HEAPS
27949 if ((next_boundary <= *poo) && (nhigh > *poo))
27950 {
27951 cg_pointers_found ++;
27952 dprintf (4, ("cg pointer %Ix found, %Id so far",
27953 (size_t)*poo, cg_pointers_found ));
27954
27955 }
27956}
27957
27958BOOL gc_heap::card_transition (uint8_t* po, uint8_t* end, size_t card_word_end,
27959 size_t& cg_pointers_found,
27960 size_t& n_eph, size_t& n_card_set,
27961 size_t& card, size_t& end_card,
27962 BOOL& foundp, uint8_t*& start_address,
27963 uint8_t*& limit, size_t& n_cards_cleared)
27964{
27965 dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
27966 dprintf (3, ("ct: %Id cg", cg_pointers_found));
27967 BOOL passed_end_card_p = FALSE;
27968 foundp = FALSE;
27969
27970 if (cg_pointers_found == 0)
27971 {
27972 //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
27973 dprintf(3,(" CC [%Ix, %Ix[ ",
27974 (size_t)card_address(card), (size_t)po));
27975 clear_cards (card, card_of(po));
27976 n_card_set -= (card_of (po) - card);
27977 n_cards_cleared += (card_of (po) - card);
27978
27979 }
27980 n_eph +=cg_pointers_found;
27981 cg_pointers_found = 0;
27982 card = card_of (po);
27983 if (card >= end_card)
27984 {
27985 passed_end_card_p = TRUE;
27986 dprintf (3, ("card %Ix exceeding end_card %Ix",
27987 (size_t)card, (size_t)end_card));
27988 foundp = find_card (card_table, card, card_word_end, end_card);
27989 if (foundp)
27990 {
27991 n_card_set+= end_card - card;
27992 start_address = card_address (card);
27993 dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
27994 (size_t)card, (size_t)start_address,
27995 (size_t)card_address (end_card)));
27996 }
27997 limit = min (end, card_address (end_card));
27998
27999 assert (!((limit == card_address (end_card))&&
28000 card_set_p (end_card)));
28001 }
28002
28003 return passed_end_card_p;
28004}
28005
28006void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
28007{
28008#ifdef BACKGROUND_GC
28009 dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
28010 current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
28011
28012 heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
28013 PREFIX_ASSUME(soh_seg != NULL);
28014
28015 while (soh_seg)
28016 {
28017 dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix",
28018 soh_seg,
28019 heap_segment_background_allocated (soh_seg),
28020 heap_segment_allocated (soh_seg)));
28021
28022 soh_seg = heap_segment_next_rw (soh_seg);
28023 }
28024#endif //BACKGROUND_GC
28025
28026 uint8_t* low = gc_low;
28027 uint8_t* high = gc_high;
28028 size_t end_card = 0;
28029
28030 generation* oldest_gen = generation_of (max_generation);
28031 int curr_gen_number = max_generation;
28032 uint8_t* gen_boundary = generation_allocation_start(generation_of(curr_gen_number - 1));
28033 uint8_t* next_boundary = compute_next_boundary(gc_low, curr_gen_number, relocating);
28034
28035 heap_segment* seg = heap_segment_rw (generation_start_segment (oldest_gen));
28036 PREFIX_ASSUME(seg != NULL);
28037
28038 uint8_t* beg = generation_allocation_start (oldest_gen);
28039 uint8_t* end = compute_next_end (seg, low);
28040 uint8_t* last_object = beg;
28041
28042 size_t cg_pointers_found = 0;
28043
28044 size_t card_word_end = (card_of (align_on_card_word (end)) / card_word_width);
28045
28046 size_t n_eph = 0;
28047 size_t n_gen = 0;
28048 size_t n_card_set = 0;
28049 uint8_t* nhigh = (relocating ?
28050 heap_segment_plan_allocated (ephemeral_heap_segment) : high);
28051
28052 BOOL foundp = FALSE;
28053 uint8_t* start_address = 0;
28054 uint8_t* limit = 0;
28055 size_t card = card_of (beg);
28056#ifdef BACKGROUND_GC
28057 BOOL consider_bgc_mark_p = FALSE;
28058 BOOL check_current_sweep_p = FALSE;
28059 BOOL check_saved_sweep_p = FALSE;
28060 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
28061#endif //BACKGROUND_GC
28062
28063 dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
28064 size_t total_cards_cleared = 0;
28065
28066 while (1)
28067 {
28068 if (card_of(last_object) > card)
28069 {
28070 dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
28071 if (cg_pointers_found == 0)
28072 {
28073 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
28074 clear_cards (card, card_of(last_object));
28075 n_card_set -= (card_of (last_object) - card);
28076 total_cards_cleared += (card_of (last_object) - card);
28077 }
28078
28079 n_eph += cg_pointers_found;
28080 cg_pointers_found = 0;
28081 card = card_of (last_object);
28082 }
28083
28084 if (card >= end_card)
28085 {
28086 foundp = find_card (card_table, card, card_word_end, end_card);
28087 if (foundp)
28088 {
28089 n_card_set += end_card - card;
28090 start_address = max (beg, card_address (card));
28091 }
28092 limit = min (end, card_address (end_card));
28093 }
28094 if (!foundp || (last_object >= end) || (card_address (card) >= end))
28095 {
28096 if (foundp && (cg_pointers_found == 0))
28097 {
28098 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
28099 (size_t)end));
28100 clear_cards (card, card_of (end));
28101 n_card_set -= (card_of (end) - card);
28102 total_cards_cleared += (card_of (end) - card);
28103 }
28104 n_eph += cg_pointers_found;
28105 cg_pointers_found = 0;
28106 if ((seg = heap_segment_next_in_range (seg)) != 0)
28107 {
28108#ifdef BACKGROUND_GC
28109 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
28110#endif //BACKGROUND_GC
28111 beg = heap_segment_mem (seg);
28112 end = compute_next_end (seg, low);
28113 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
28114 card = card_of (beg);
28115 last_object = beg;
28116 end_card = 0;
28117 continue;
28118 }
28119 else
28120 {
28121 break;
28122 }
28123 }
28124
28125 assert (card_set_p (card));
28126 {
28127 uint8_t* o = last_object;
28128
28129 o = find_first_object (start_address, last_object);
28130 // Never visit an object twice.
28131 assert (o >= last_object);
28132
28133 //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
28134 dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
28135 card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
28136
28137 while (o < limit)
28138 {
28139 assert (Align (size (o)) >= Align (min_obj_size));
28140 size_t s = size (o);
28141
28142 uint8_t* next_o = o + Align (s);
28143 Prefetch (next_o);
28144
28145 if ((o >= gen_boundary) &&
28146 (seg == ephemeral_heap_segment))
28147 {
28148 dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
28149 curr_gen_number--;
28150 assert ((curr_gen_number > 0));
28151 gen_boundary = generation_allocation_start
28152 (generation_of (curr_gen_number - 1));
28153 next_boundary = (compute_next_boundary
28154 (low, curr_gen_number, relocating));
28155 }
28156
28157 dprintf (4, ("|%Ix|", (size_t)o));
28158
28159 if (next_o < start_address)
28160 {
28161 goto end_object;
28162 }
28163
28164#ifdef BACKGROUND_GC
28165 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
28166 {
28167 goto end_object;
28168 }
28169#endif //BACKGROUND_GC
28170
28171#ifdef COLLECTIBLE_CLASS
28172 if (is_collectible(o))
28173 {
28174 BOOL passed_end_card_p = FALSE;
28175
28176 if (card_of (o) > card)
28177 {
28178 passed_end_card_p = card_transition (o, end, card_word_end,
28179 cg_pointers_found,
28180 n_eph, n_card_set,
28181 card, end_card,
28182 foundp, start_address,
28183 limit, total_cards_cleared);
28184 }
28185
28186 if ((!passed_end_card_p || foundp) && (card_of (o) == card))
28187 {
28188 // card is valid and it covers the head of the object
28189 if (fn == &gc_heap::relocate_address)
28190 {
28191 keep_card_live (o, n_gen, cg_pointers_found);
28192 }
28193 else
28194 {
28195 uint8_t* class_obj = get_class_object (o);
28196 mark_through_cards_helper (&class_obj, n_gen,
28197 cg_pointers_found, fn,
28198 nhigh, next_boundary);
28199 }
28200 }
28201
28202 if (passed_end_card_p)
28203 {
28204 if (foundp && (card_address (card) < next_o))
28205 {
28206 goto go_through_refs;
28207 }
28208 else if (foundp && (start_address < limit))
28209 {
28210 next_o = find_first_object (start_address, o);
28211 goto end_object;
28212 }
28213 else
28214 goto end_limit;
28215 }
28216 }
28217
28218go_through_refs:
28219#endif //COLLECTIBLE_CLASS
28220
28221 if (contain_pointers (o))
28222 {
28223 dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
28224
28225 {
28226 dprintf (4, ("normal object path"));
28227 go_through_object
28228 (method_table(o), o, s, poo,
28229 start_address, use_start, (o + s),
28230 {
28231 dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
28232 if (card_of ((uint8_t*)poo) > card)
28233 {
28234 BOOL passed_end_card_p = card_transition ((uint8_t*)poo, end,
28235 card_word_end,
28236 cg_pointers_found,
28237 n_eph, n_card_set,
28238 card, end_card,
28239 foundp, start_address,
28240 limit, total_cards_cleared);
28241
28242 if (passed_end_card_p)
28243 {
28244 if (foundp && (card_address (card) < next_o))
28245 {
28246 //new_start();
28247 {
28248 if (ppstop <= (uint8_t**)start_address)
28249 {break;}
28250 else if (poo < (uint8_t**)start_address)
28251 {poo = (uint8_t**)start_address;}
28252 }
28253 }
28254 else if (foundp && (start_address < limit))
28255 {
28256 next_o = find_first_object (start_address, o);
28257 goto end_object;
28258 }
28259 else
28260 goto end_limit;
28261 }
28262 }
28263
28264 mark_through_cards_helper (poo, n_gen,
28265 cg_pointers_found, fn,
28266 nhigh, next_boundary);
28267 }
28268 );
28269 }
28270 }
28271
28272 end_object:
28273 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
28274 {
28275 if (brick_table [brick_of (o)] <0)
28276 fix_brick_to_highest (o, next_o);
28277 }
28278 o = next_o;
28279 }
28280 end_limit:
28281 last_object = o;
28282 }
28283 }
28284 // compute the efficiency ratio of the card table
28285 if (!relocating)
28286 {
28287 generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
28288 dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d",
28289 n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
28290 }
28291 else
28292 {
28293 dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d",
28294 n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
28295 }
28296}
28297
28298#ifdef SEG_REUSE_STATS
28299size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
28300{
28301 size_t total_items = 0;
28302 *total_size = 0;
28303 for (int i = 0; i < count; i++)
28304 {
28305 total_items += ordered_indices[i];
28306 *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
28307 dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
28308 }
28309 dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
28310 return total_items;
28311}
28312#endif // SEG_REUSE_STATS
28313
28314void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug)
28315{
28316 // detect pinned plugs
28317 if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
28318 {
28319 deque_pinned_plug();
28320 update_oldest_pinned_plug();
28321 dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
28322 }
28323 else
28324 {
28325 size_t plug_size = last_plug_size + Align(min_obj_size);
28326 BOOL is_padded = FALSE;
28327
28328#ifdef SHORT_PLUGS
28329 plug_size += Align (min_obj_size);
28330 is_padded = TRUE;
28331#endif //SHORT_PLUGS
28332
28333#ifdef RESPECT_LARGE_ALIGNMENT
28334 plug_size += switch_alignment_size (is_padded);
28335#endif //RESPECT_LARGE_ALIGNMENT
28336
28337 total_ephemeral_plugs += plug_size;
28338 size_t plug_size_power2 = round_up_power2 (plug_size);
28339 ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
28340 dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array",
28341 heap_number,
28342 last_plug,
28343 plug_size,
28344 (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
28345 }
28346}
28347
28348void gc_heap::count_plugs_in_brick (uint8_t* tree, uint8_t*& last_plug)
28349{
28350 assert ((tree != NULL));
28351 if (node_left_child (tree))
28352 {
28353 count_plugs_in_brick (tree + node_left_child (tree), last_plug);
28354 }
28355
28356 if (last_plug != 0)
28357 {
28358 uint8_t* plug = tree;
28359 size_t gap_size = node_gap_size (plug);
28360 uint8_t* gap = (plug - gap_size);
28361 uint8_t* last_plug_end = gap;
28362 size_t last_plug_size = (last_plug_end - last_plug);
28363 dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
28364 tree, last_plug, gap_size, gap, last_plug_size));
28365
28366 if (tree == oldest_pinned_plug)
28367 {
28368 dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
28369 tree, last_plug, last_plug_size));
28370 mark* m = oldest_pin();
28371 if (m->has_pre_plug_info())
28372 {
28373 last_plug_size += sizeof (gap_reloc_pair);
28374 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
28375 }
28376 }
28377 // Can't assert here - if it's a pinned plug it can be less.
28378 //assert (last_plug_size >= Align (min_obj_size));
28379
28380 count_plug (last_plug_size, last_plug);
28381 }
28382
28383 last_plug = tree;
28384
28385 if (node_right_child (tree))
28386 {
28387 count_plugs_in_brick (tree + node_right_child (tree), last_plug);
28388 }
28389}
28390
28391void gc_heap::build_ordered_plug_indices ()
28392{
28393 memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
28394 memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
28395
28396 uint8_t* start_address = generation_limit (max_generation);
28397 uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment);
28398 size_t current_brick = brick_of (start_address);
28399 size_t end_brick = brick_of (end_address - 1);
28400 uint8_t* last_plug = 0;
28401
28402 //Look for the right pinned plug to start from.
28403 reset_pinned_queue_bos();
28404 while (!pinned_plug_que_empty_p())
28405 {
28406 mark* m = oldest_pin();
28407 if ((m->first >= start_address) && (m->first < end_address))
28408 {
28409 dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
28410
28411 break;
28412 }
28413 else
28414 deque_pinned_plug();
28415 }
28416
28417 update_oldest_pinned_plug();
28418
28419 while (current_brick <= end_brick)
28420 {
28421 int brick_entry = brick_table [ current_brick ];
28422 if (brick_entry >= 0)
28423 {
28424 count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
28425 }
28426
28427 current_brick++;
28428 }
28429
28430 if (last_plug !=0)
28431 {
28432 count_plug (end_address - last_plug, last_plug);
28433 }
28434
28435 // we need to make sure that after fitting all the existing plugs, we
28436 // have big enough free space left to guarantee that the next allocation
28437 // will succeed.
28438 size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
28439 total_ephemeral_plugs += extra_size;
28440 dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
28441 ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
28442
28443 memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
28444
28445#ifdef SEG_REUSE_STATS
28446 dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
28447 size_t total_plug_power2 = 0;
28448 dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
28449 dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))",
28450 total_ephemeral_plugs,
28451 total_plug_power2,
28452 (total_ephemeral_plugs ?
28453 (total_plug_power2 * 100 / total_ephemeral_plugs) :
28454 0)));
28455 dprintf (SEG_REUSE_LOG_0, ("-------------------"));
28456#endif // SEG_REUSE_STATS
28457}
28458
28459void gc_heap::init_ordered_free_space_indices ()
28460{
28461 memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
28462 memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
28463}
28464
28465void gc_heap::trim_free_spaces_indices ()
28466{
28467 trimmed_free_space_index = -1;
28468 size_t max_count = max_free_space_items - 1;
28469 size_t count = 0;
28470 int i = 0;
28471 for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
28472 {
28473 count += ordered_free_space_indices[i];
28474
28475 if (count >= max_count)
28476 {
28477 break;
28478 }
28479 }
28480
28481 ptrdiff_t extra_free_space_items = count - max_count;
28482
28483 if (extra_free_space_items > 0)
28484 {
28485 ordered_free_space_indices[i] -= extra_free_space_items;
28486 free_space_items = max_count;
28487 trimmed_free_space_index = i;
28488 }
28489 else
28490 {
28491 free_space_items = count;
28492 }
28493
28494 if (i == -1)
28495 {
28496 i = 0;
28497 }
28498
28499 free_space_buckets = MAX_NUM_BUCKETS - i;
28500
28501 for (--i; i >= 0; i--)
28502 {
28503 ordered_free_space_indices[i] = 0;
28504 }
28505
28506 memcpy (saved_ordered_free_space_indices,
28507 ordered_free_space_indices,
28508 sizeof(ordered_free_space_indices));
28509}
28510
28511// We fit as many plugs as we can and update the number of plugs left and the number
28512// of free spaces left.
28513BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
28514{
28515 assert (small_index <= big_index);
28516 assert (big_index < MAX_NUM_BUCKETS);
28517
28518 size_t small_blocks = ordered_blocks[small_index];
28519
28520 if (small_blocks == 0)
28521 {
28522 return TRUE;
28523 }
28524
28525 size_t big_spaces = ordered_spaces[big_index];
28526
28527 if (big_spaces == 0)
28528 {
28529 return FALSE;
28530 }
28531
28532 dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces",
28533 heap_number,
28534 small_blocks, (small_index + MIN_INDEX_POWER2),
28535 big_spaces, (big_index + MIN_INDEX_POWER2)));
28536
28537 size_t big_to_small = big_spaces << (big_index - small_index);
28538
28539 ptrdiff_t extra_small_spaces = big_to_small - small_blocks;
28540 dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks",
28541 heap_number,
28542 big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
28543 BOOL can_fit = (extra_small_spaces >= 0);
28544
28545 if (can_fit)
28546 {
28547 dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks",
28548 heap_number,
28549 extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
28550 }
28551
28552 int i = 0;
28553
28554 dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
28555 ordered_spaces[big_index] = 0;
28556 if (extra_small_spaces > 0)
28557 {
28558 dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
28559 ordered_blocks[small_index] = 0;
28560 for (i = small_index; i < big_index; i++)
28561 {
28562 if (extra_small_spaces & 1)
28563 {
28564 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d",
28565 heap_number,
28566 (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
28567 ordered_spaces[i] += 1;
28568 }
28569 extra_small_spaces >>= 1;
28570 }
28571
28572 dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d",
28573 heap_number,
28574 (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
28575 ordered_spaces[i] += extra_small_spaces;
28576 }
28577 else
28578 {
28579 dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d",
28580 heap_number,
28581 (small_index + MIN_INDEX_POWER2),
28582 ordered_blocks[small_index],
28583 (ordered_blocks[small_index] - big_to_small)));
28584 ordered_blocks[small_index] -= big_to_small;
28585 }
28586
28587#ifdef SEG_REUSE_STATS
28588 size_t temp;
28589 dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
28590 dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
28591
28592 dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
28593 dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
28594#endif //SEG_REUSE_STATS
28595
28596 return can_fit;
28597}
28598
28599// space_index gets updated to the biggest available space index.
28600BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
28601{
28602 assert (*space_index >= block_index);
28603
28604 while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
28605 {
28606 (*space_index)--;
28607 if (*space_index < block_index)
28608 {
28609 return FALSE;
28610 }
28611 }
28612
28613 return TRUE;
28614}
28615
28616BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
28617{
28618#ifdef FEATURE_STRUCTALIGN
28619 // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
28620 return FALSE;
28621#endif // FEATURE_STRUCTALIGN
28622 int space_index = count - 1;
28623 for (int block_index = (count - 1); block_index >= 0; block_index--)
28624 {
28625 if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
28626 {
28627 return FALSE;
28628 }
28629 }
28630
28631 return TRUE;
28632}
28633
28634void gc_heap::build_ordered_free_spaces (heap_segment* seg)
28635{
28636 assert (bestfit_seg);
28637
28638 //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2,
28639 // ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets),
28640 // free_space_buckets,
28641 // free_space_items);
28642
28643 bestfit_seg->add_buckets (MIN_INDEX_POWER2,
28644 ordered_free_space_indices,
28645 MAX_NUM_BUCKETS,
28646 free_space_items);
28647
28648 assert (settings.condemned_generation == max_generation);
28649
28650 uint8_t* first_address = heap_segment_mem (seg);
28651 uint8_t* end_address = heap_segment_reserved (seg);
28652 //look through the pinned plugs for relevant ones.
28653 //Look for the right pinned plug to start from.
28654 reset_pinned_queue_bos();
28655 mark* m = 0;
28656 // See comment in can_expand_into_p why we need (max_generation + 1).
28657 size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
28658 BOOL has_fit_gen_starts = FALSE;
28659
28660 while (!pinned_plug_que_empty_p())
28661 {
28662 m = oldest_pin();
28663 if ((pinned_plug (m) >= first_address) &&
28664 (pinned_plug (m) < end_address) &&
28665 (pinned_len (m) >= eph_gen_starts))
28666 {
28667
28668 assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
28669 break;
28670 }
28671 else
28672 {
28673 deque_pinned_plug();
28674 }
28675 }
28676
28677 if (!pinned_plug_que_empty_p())
28678 {
28679 bestfit_seg->add ((void*)m, TRUE, TRUE);
28680 deque_pinned_plug();
28681 m = oldest_pin();
28682 has_fit_gen_starts = TRUE;
28683 }
28684
28685 while (!pinned_plug_que_empty_p() &&
28686 ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28687 {
28688 bestfit_seg->add ((void*)m, TRUE, FALSE);
28689 deque_pinned_plug();
28690 m = oldest_pin();
28691 }
28692
28693 if (commit_end_of_seg)
28694 {
28695 if (!has_fit_gen_starts)
28696 {
28697 assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
28698 }
28699 bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
28700 }
28701
28702#ifdef _DEBUG
28703 bestfit_seg->check();
28704#endif //_DEBUG
28705}
28706
28707BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
28708{
28709 if (!end_of_segment_p)
28710 {
28711 trim_free_spaces_indices ();
28712 }
28713
28714 BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices,
28715 ordered_free_space_indices,
28716 MAX_NUM_BUCKETS);
28717
28718 return can_bestfit;
28719}
28720
28721BOOL gc_heap::best_fit (size_t free_space,
28722 size_t largest_free_space,
28723 size_t additional_space,
28724 BOOL* use_additional_space)
28725{
28726 dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
28727
28728 assert (!additional_space || (additional_space && use_additional_space));
28729 if (use_additional_space)
28730 {
28731 *use_additional_space = FALSE;
28732 }
28733
28734 if (ordered_plug_indices_init == FALSE)
28735 {
28736 total_ephemeral_plugs = 0;
28737 build_ordered_plug_indices();
28738 ordered_plug_indices_init = TRUE;
28739 }
28740 else
28741 {
28742 memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
28743 }
28744
28745 if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
28746 {
28747 dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
28748 size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
28749 BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
28750 if (!can_fit_empty_eph)
28751 {
28752 can_fit_empty_eph = (additional_space >= empty_eph);
28753
28754 if (can_fit_empty_eph)
28755 {
28756 *use_additional_space = TRUE;
28757 }
28758 }
28759
28760 return can_fit_empty_eph;
28761 }
28762
28763 if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
28764 {
28765 dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
28766 return FALSE;
28767 }
28768
28769 if ((free_space + additional_space) == 0)
28770 {
28771 dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
28772 return FALSE;
28773 }
28774
28775#ifdef SEG_REUSE_STATS
28776 dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
28777 size_t total_free_space_power2 = 0;
28778 size_t total_free_space_items =
28779 dump_buckets (ordered_free_space_indices,
28780 MAX_NUM_BUCKETS,
28781 &total_free_space_power2);
28782 dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
28783
28784 dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
28785 total_ephemeral_plugs,
28786 free_space,
28787 total_free_space_power2,
28788 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
28789 additional_space));
28790
28791 size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
28792 memcpy (saved_all_free_space_indices,
28793 ordered_free_space_indices,
28794 sizeof(saved_all_free_space_indices));
28795
28796#endif // SEG_REUSE_STATS
28797
28798 if (total_ephemeral_plugs > (free_space + additional_space))
28799 {
28800 return FALSE;
28801 }
28802
28803 use_bestfit = try_best_fit(FALSE);
28804
28805 if (!use_bestfit && additional_space)
28806 {
28807 int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
28808
28809 if (relative_free_space_index != -1)
28810 {
28811 int relative_plug_index = 0;
28812 size_t plugs_to_fit = 0;
28813
28814 for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
28815 {
28816 plugs_to_fit = ordered_plug_indices[relative_plug_index];
28817 if (plugs_to_fit != 0)
28818 {
28819 break;
28820 }
28821 }
28822
28823 if ((relative_plug_index > relative_free_space_index) ||
28824 ((relative_plug_index == relative_free_space_index) &&
28825 (plugs_to_fit > 1)))
28826 {
28827#ifdef SEG_REUSE_STATS
28828 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
28829 (relative_free_space_index + MIN_INDEX_POWER2),
28830 plugs_to_fit,
28831 (relative_plug_index + MIN_INDEX_POWER2)));
28832#endif // SEG_REUSE_STATS
28833 goto adjust;
28834 }
28835
28836 dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
28837 ordered_free_space_indices[relative_free_space_index]++;
28838 use_bestfit = try_best_fit(TRUE);
28839 if (use_bestfit)
28840 {
28841 free_space_items++;
28842 // Since we might've trimmed away some of the free spaces we had, we should see
28843 // if we really need to use end of seg space - if it's the same or smaller than
28844 // the largest space we trimmed we can just add that one back instead of
28845 // using end of seg.
28846 if (relative_free_space_index > trimmed_free_space_index)
28847 {
28848 *use_additional_space = TRUE;
28849 }
28850 else
28851 {
28852 // If the addition space is <= than the last trimmed space, we
28853 // should just use that last trimmed space instead.
28854 saved_ordered_free_space_indices[trimmed_free_space_index]++;
28855 }
28856 }
28857 }
28858 }
28859
28860adjust:
28861
28862 if (!use_bestfit)
28863 {
28864 dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
28865
28866#ifdef SEG_REUSE_STATS
28867 size_t saved_max = max_free_space_items;
28868 BOOL temp_bestfit = FALSE;
28869
28870 dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
28871 dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
28872
28873 // TODO: need to take the end of segment into consideration.
28874 while (max_free_space_items <= total_free_space_items)
28875 {
28876 max_free_space_items += max_free_space_items / 2;
28877 dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
28878 memcpy (ordered_free_space_indices,
28879 saved_all_free_space_indices,
28880 sizeof(ordered_free_space_indices));
28881 if (try_best_fit(FALSE))
28882 {
28883 temp_bestfit = TRUE;
28884 break;
28885 }
28886 }
28887
28888 if (temp_bestfit)
28889 {
28890 dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
28891 }
28892 else
28893 {
28894 dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
28895 }
28896
28897 dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
28898 max_free_space_items = saved_max;
28899#endif // SEG_REUSE_STATS
28900 if (free_space_items)
28901 {
28902 max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
28903 max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
28904 }
28905 else
28906 {
28907 max_free_space_items = MAX_NUM_FREE_SPACES;
28908 }
28909 }
28910
28911 dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
28912 dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
28913
28914 return use_bestfit;
28915}
28916
28917BOOL gc_heap::process_free_space (heap_segment* seg,
28918 size_t free_space,
28919 size_t min_free_size,
28920 size_t min_cont_size,
28921 size_t* total_free_space,
28922 size_t* largest_free_space)
28923{
28924 *total_free_space += free_space;
28925 *largest_free_space = max (*largest_free_space, free_space);
28926
28927#ifdef SIMPLE_DPRINTF
28928 dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix",
28929 free_space, *total_free_space, *largest_free_space));
28930#endif //SIMPLE_DPRINTF
28931
28932 if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
28933 {
28934#ifdef SIMPLE_DPRINTF
28935 dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit",
28936 settings.condemned_generation,
28937 *total_free_space, min_free_size, *largest_free_space, min_cont_size,
28938 (size_t)seg));
28939#else
28940 UNREFERENCED_PARAMETER(seg);
28941#endif //SIMPLE_DPRINTF
28942 return TRUE;
28943 }
28944
28945 int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
28946 if (free_space_index != -1)
28947 {
28948 ordered_free_space_indices[free_space_index]++;
28949 }
28950 return FALSE;
28951}
28952
28953BOOL gc_heap::expand_reused_seg_p()
28954{
28955 BOOL reused_seg = FALSE;
28956 int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
28957 if ((heap_expand_mechanism == expand_reuse_bestfit) ||
28958 (heap_expand_mechanism == expand_reuse_normal))
28959 {
28960 reused_seg = TRUE;
28961 }
28962
28963 return reused_seg;
28964}
28965
28966BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
28967 allocator* gen_allocator)
28968{
28969 min_cont_size += END_SPACE_AFTER_GC;
28970 use_bestfit = FALSE;
28971 commit_end_of_seg = FALSE;
28972 bestfit_first_pin = 0;
28973 uint8_t* first_address = heap_segment_mem (seg);
28974 uint8_t* end_address = heap_segment_reserved (seg);
28975 size_t end_extra_space = end_space_after_gc();
28976
28977 if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
28978 {
28979 dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
28980 first_address, end_address, end_extra_space));
28981 return FALSE;
28982 }
28983
28984 end_address -= end_extra_space;
28985
28986 dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix",
28987 settings.condemned_generation, min_free_size, min_cont_size));
28988 size_t eph_gen_starts = eph_gen_starts_size;
28989
28990 if (settings.condemned_generation == max_generation)
28991 {
28992 size_t free_space = 0;
28993 size_t largest_free_space = free_space;
28994 dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
28995 //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from.
28996 //We are going to allocate the generation starts in the 1st free space,
28997 //so start from the first free space that's big enough for gen starts and a min object size.
28998 // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it -
28999 // we could use it by allocating the last generation start a bit bigger but
29000 // the complexity isn't worth the effort (those plugs are from gen2
29001 // already anyway).
29002 reset_pinned_queue_bos();
29003 mark* m = 0;
29004 BOOL has_fit_gen_starts = FALSE;
29005
29006 init_ordered_free_space_indices ();
29007 while (!pinned_plug_que_empty_p())
29008 {
29009 m = oldest_pin();
29010 if ((pinned_plug (m) >= first_address) &&
29011 (pinned_plug (m) < end_address) &&
29012 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
29013 {
29014 break;
29015 }
29016 else
29017 {
29018 deque_pinned_plug();
29019 }
29020 }
29021
29022 if (!pinned_plug_que_empty_p())
29023 {
29024 bestfit_first_pin = pinned_plug (m) - pinned_len (m);
29025
29026 if (process_free_space (seg,
29027 pinned_len (m) - eph_gen_starts,
29028 min_free_size, min_cont_size,
29029 &free_space, &largest_free_space))
29030 {
29031 return TRUE;
29032 }
29033
29034 deque_pinned_plug();
29035 m = oldest_pin();
29036 has_fit_gen_starts = TRUE;
29037 }
29038
29039 dprintf (3, ("first pin is %Ix", pinned_plug (m)));
29040
29041 //tally up free space
29042 while (!pinned_plug_que_empty_p() &&
29043 ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
29044 {
29045 dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
29046 if (process_free_space (seg,
29047 pinned_len (m),
29048 min_free_size, min_cont_size,
29049 &free_space, &largest_free_space))
29050 {
29051 return TRUE;
29052 }
29053
29054 deque_pinned_plug();
29055 m = oldest_pin();
29056 }
29057
29058 //try to find space at the end of the segment.
29059 size_t end_space = (end_address - heap_segment_plan_allocated (seg));
29060 size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0);
29061 dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
29062 if (end_space >= additional_space)
29063 {
29064 BOOL can_fit = TRUE;
29065 commit_end_of_seg = TRUE;
29066
29067 if (largest_free_space < min_cont_size)
29068 {
29069 if (end_space >= min_cont_size)
29070 {
29071 additional_space = max (min_cont_size, additional_space);
29072 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph",
29073 seg));
29074 }
29075 else
29076 {
29077 if (settings.concurrent)
29078 {
29079 can_fit = FALSE;
29080 commit_end_of_seg = FALSE;
29081 }
29082 else
29083 {
29084 size_t additional_space_bestfit = additional_space;
29085 if (!has_fit_gen_starts)
29086 {
29087 if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
29088 {
29089 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
29090 additional_space_bestfit));
29091 return FALSE;
29092 }
29093
29094 bestfit_first_pin = heap_segment_plan_allocated (seg);
29095 additional_space_bestfit -= eph_gen_starts;
29096 }
29097
29098 can_fit = best_fit (free_space,
29099 largest_free_space,
29100 additional_space_bestfit,
29101 &commit_end_of_seg);
29102
29103 if (can_fit)
29104 {
29105 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg",
29106 seg, (commit_end_of_seg ? "with" : "without")));
29107 }
29108 else
29109 {
29110 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
29111 }
29112 }
29113 }
29114 }
29115 else
29116 {
29117 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
29118 }
29119
29120 assert (additional_space <= end_space);
29121 if (commit_end_of_seg)
29122 {
29123 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
29124 {
29125 dprintf (2, ("Couldn't commit end of segment?!"));
29126 use_bestfit = FALSE;
29127
29128 return FALSE;
29129 }
29130
29131 if (use_bestfit)
29132 {
29133 // We increase the index here because growing heap segment could create a discrepency with
29134 // the additional space we used (could be bigger).
29135 size_t free_space_end_of_seg =
29136 heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
29137 int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
29138 saved_ordered_free_space_indices[relative_free_space_index]++;
29139 }
29140 }
29141
29142 if (use_bestfit)
29143 {
29144 memcpy (ordered_free_space_indices,
29145 saved_ordered_free_space_indices,
29146 sizeof(ordered_free_space_indices));
29147 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
29148 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
29149 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
29150 }
29151
29152 return can_fit;
29153 }
29154
29155 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
29156 return FALSE;
29157 }
29158 else
29159 {
29160 assert (settings.condemned_generation == (max_generation-1));
29161 size_t free_space = (end_address - heap_segment_plan_allocated (seg));
29162 size_t largest_free_space = free_space;
29163 dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
29164 //find the first free list in range of the current segment
29165 size_t sz_list = gen_allocator->first_bucket_size();
29166 unsigned int a_l_idx = 0;
29167 uint8_t* free_list = 0;
29168 for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
29169 {
29170 if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
29171 {
29172 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
29173 while (free_list)
29174 {
29175 if ((free_list >= first_address) &&
29176 (free_list < end_address) &&
29177 (unused_array_size (free_list) >= eph_gen_starts))
29178 {
29179 goto next;
29180 }
29181 else
29182 {
29183 free_list = free_list_slot (free_list);
29184 }
29185 }
29186 }
29187 }
29188next:
29189 if (free_list)
29190 {
29191 init_ordered_free_space_indices ();
29192 if (process_free_space (seg,
29193 unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size),
29194 min_free_size, min_cont_size,
29195 &free_space, &largest_free_space))
29196 {
29197 return TRUE;
29198 }
29199
29200 free_list = free_list_slot (free_list);
29201 }
29202 else
29203 {
29204 dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
29205 return FALSE;
29206 }
29207
29208 //tally up free space
29209
29210 while (1)
29211 {
29212 while (free_list)
29213 {
29214 if ((free_list >= first_address) && (free_list < end_address) &&
29215 process_free_space (seg,
29216 unused_array_size (free_list),
29217 min_free_size, min_cont_size,
29218 &free_space, &largest_free_space))
29219 {
29220 return TRUE;
29221 }
29222
29223 free_list = free_list_slot (free_list);
29224 }
29225 a_l_idx++;
29226 if (a_l_idx < gen_allocator->number_of_buckets())
29227 {
29228 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
29229 }
29230 else
29231 break;
29232 }
29233
29234 dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
29235 return FALSE;
29236
29237 /*
29238 BOOL can_fit = best_fit (free_space, 0, NULL);
29239 if (can_fit)
29240 {
29241 dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
29242 }
29243 else
29244 {
29245 dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
29246 }
29247
29248 return can_fit;
29249 */
29250 }
29251}
29252
29253void gc_heap::realloc_plug (size_t last_plug_size, uint8_t*& last_plug,
29254 generation* gen, uint8_t* start_address,
29255 unsigned int& active_new_gen_number,
29256 uint8_t*& last_pinned_gap, BOOL& leftp,
29257 BOOL shortened_p
29258#ifdef SHORT_PLUGS
29259 , mark* pinned_plug_entry
29260#endif //SHORT_PLUGS
29261 )
29262{
29263 // detect generation boundaries
29264 // make sure that active_new_gen_number is not the youngest generation.
29265 // because the generation_limit wouldn't return the right thing in this case.
29266 if (!use_bestfit)
29267 {
29268 if ((active_new_gen_number > 1) &&
29269 (last_plug >= generation_limit (active_new_gen_number)))
29270 {
29271 assert (last_plug >= start_address);
29272 active_new_gen_number--;
29273 realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
29274 assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
29275 leftp = FALSE;
29276 }
29277 }
29278
29279 // detect pinned plugs
29280 if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
29281 {
29282 size_t entry = deque_pinned_plug();
29283 mark* m = pinned_plug_of (entry);
29284
29285 size_t saved_pinned_len = pinned_len(m);
29286 pinned_len(m) = last_plug - last_pinned_gap;
29287 //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
29288
29289 if (m->has_post_plug_info())
29290 {
29291 last_plug_size += sizeof (gap_reloc_pair);
29292 dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29293 }
29294
29295 last_pinned_gap = last_plug + last_plug_size;
29296 dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
29297 pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
29298 leftp = FALSE;
29299
29300 //we are creating a generation fault. set the cards.
29301 {
29302 size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
29303 size_t card = card_of (last_plug);
29304 while (card != end_card)
29305 {
29306 set_card (card);
29307 card++;
29308 }
29309 }
29310 }
29311 else if (last_plug >= start_address)
29312 {
29313#ifdef FEATURE_STRUCTALIGN
29314 int requiredAlignment;
29315 ptrdiff_t pad;
29316 node_aligninfo (last_plug, requiredAlignment, pad);
29317
29318 // from how we previously aligned the plug's destination address,
29319 // compute the actual alignment offset.
29320 uint8_t* reloc_plug = last_plug + node_relocation_distance (last_plug);
29321 ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
29322 if (!alignmentOffset)
29323 {
29324 // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
29325 alignmentOffset = requiredAlignment;
29326 }
29327
29328 //clear the alignment info because we are reallocating
29329 clear_node_aligninfo (last_plug);
29330#else // FEATURE_STRUCTALIGN
29331 //clear the realignment flag because we are reallocating
29332 clear_node_realigned (last_plug);
29333#endif // FEATURE_STRUCTALIGN
29334 BOOL adjacentp = FALSE;
29335 BOOL set_padding_on_saved_p = FALSE;
29336
29337 if (shortened_p)
29338 {
29339 last_plug_size += sizeof (gap_reloc_pair);
29340
29341#ifdef SHORT_PLUGS
29342 assert (pinned_plug_entry != NULL);
29343 if (last_plug_size <= sizeof (plug_and_gap))
29344 {
29345 set_padding_on_saved_p = TRUE;
29346 }
29347#endif //SHORT_PLUGS
29348
29349 dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29350 }
29351
29352#ifdef SHORT_PLUGS
29353 clear_padding_in_expand (last_plug, set_padding_on_saved_p, pinned_plug_entry);
29354#endif //SHORT_PLUGS
29355
29356 uint8_t* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug,
29357#ifdef SHORT_PLUGS
29358 set_padding_on_saved_p,
29359 pinned_plug_entry,
29360#endif //SHORT_PLUGS
29361 TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
29362
29363 dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
29364 assert (new_address);
29365 set_node_relocation_distance (last_plug, new_address - last_plug);
29366#ifdef FEATURE_STRUCTALIGN
29367 if (leftp && node_alignpad (last_plug) == 0)
29368#else // FEATURE_STRUCTALIGN
29369 if (leftp && !node_realigned (last_plug))
29370#endif // FEATURE_STRUCTALIGN
29371 {
29372 // TODO - temporarily disable L optimization because of a bug in it.
29373 //set_node_left (last_plug);
29374 }
29375 dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
29376 leftp = adjacentp;
29377 }
29378}
29379
29380void gc_heap::realloc_in_brick (uint8_t* tree, uint8_t*& last_plug,
29381 uint8_t* start_address,
29382 generation* gen,
29383 unsigned int& active_new_gen_number,
29384 uint8_t*& last_pinned_gap, BOOL& leftp)
29385{
29386 assert (tree != NULL);
29387 int left_node = node_left_child (tree);
29388 int right_node = node_right_child (tree);
29389
29390 dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d",
29391 tree, last_pinned_gap, last_plug, left_node, right_node));
29392
29393 if (left_node)
29394 {
29395 dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
29396 realloc_in_brick ((tree + left_node), last_plug, start_address,
29397 gen, active_new_gen_number, last_pinned_gap,
29398 leftp);
29399 }
29400
29401 if (last_plug != 0)
29402 {
29403 uint8_t* plug = tree;
29404
29405 BOOL has_pre_plug_info_p = FALSE;
29406 BOOL has_post_plug_info_p = FALSE;
29407 mark* pinned_plug_entry = get_next_pinned_entry (tree,
29408 &has_pre_plug_info_p,
29409 &has_post_plug_info_p,
29410 FALSE);
29411
29412 // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
29413 // The pinned plugs are handled in realloc_plug.
29414 size_t gap_size = node_gap_size (plug);
29415 uint8_t* gap = (plug - gap_size);
29416 uint8_t* last_plug_end = gap;
29417 size_t last_plug_size = (last_plug_end - last_plug);
29418 // Cannot assert this - a plug could be less than that due to the shortened ones.
29419 //assert (last_plug_size >= Align (min_obj_size));
29420 dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
29421 plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
29422 realloc_plug (last_plug_size, last_plug, gen, start_address,
29423 active_new_gen_number, last_pinned_gap,
29424 leftp, has_pre_plug_info_p
29425#ifdef SHORT_PLUGS
29426 , pinned_plug_entry
29427#endif //SHORT_PLUGS
29428 );
29429 }
29430
29431 last_plug = tree;
29432
29433 if (right_node)
29434 {
29435 dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
29436 realloc_in_brick ((tree + right_node), last_plug, start_address,
29437 gen, active_new_gen_number, last_pinned_gap,
29438 leftp);
29439 }
29440}
29441
29442void
29443gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
29444 uint8_t* start_address, uint8_t* end_address,
29445 unsigned active_new_gen_number)
29446{
29447 dprintf (3, ("--- Reallocing ---"));
29448
29449 if (use_bestfit)
29450 {
29451 //make sure that every generation has a planned allocation start
29452 int gen_number = max_generation - 1;
29453 while (gen_number >= 0)
29454 {
29455 generation* gen = generation_of (gen_number);
29456 if (0 == generation_plan_allocation_start (gen))
29457 {
29458 generation_plan_allocation_start (gen) =
29459 bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
29460 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
29461 assert (generation_plan_allocation_start (gen));
29462 }
29463 gen_number--;
29464 }
29465 }
29466
29467 uint8_t* first_address = start_address;
29468 //Look for the right pinned plug to start from.
29469 reset_pinned_queue_bos();
29470 uint8_t* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
29471 while (!pinned_plug_que_empty_p())
29472 {
29473 mark* m = oldest_pin();
29474 if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
29475 {
29476 if (pinned_plug (m) < first_address)
29477 {
29478 first_address = pinned_plug (m);
29479 }
29480 break;
29481 }
29482 else
29483 deque_pinned_plug();
29484 }
29485
29486 size_t current_brick = brick_of (first_address);
29487 size_t end_brick = brick_of (end_address-1);
29488 uint8_t* last_plug = 0;
29489
29490 uint8_t* last_pinned_gap = heap_segment_plan_allocated (seg);
29491 BOOL leftp = FALSE;
29492
29493 dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
29494 start_address, first_address, pinned_plug (oldest_pin())));
29495
29496 while (current_brick <= end_brick)
29497 {
29498 int brick_entry = brick_table [ current_brick ];
29499 if (brick_entry >= 0)
29500 {
29501 realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
29502 last_plug, start_address, consing_gen,
29503 active_new_gen_number, last_pinned_gap,
29504 leftp);
29505 }
29506 current_brick++;
29507 }
29508
29509 if (last_plug != 0)
29510 {
29511 realloc_plug (end_address - last_plug, last_plug, consing_gen,
29512 start_address,
29513 active_new_gen_number, last_pinned_gap,
29514 leftp, FALSE
29515#ifdef SHORT_PLUGS
29516 , NULL
29517#endif //SHORT_PLUGS
29518 );
29519 }
29520
29521 //Fix the old segment allocated size
29522 assert (last_pinned_gap >= heap_segment_mem (seg));
29523 assert (last_pinned_gap <= heap_segment_committed (seg));
29524 heap_segment_plan_allocated (seg) = last_pinned_gap;
29525}
29526
29527void gc_heap::verify_no_pins (uint8_t* start, uint8_t* end)
29528{
29529#ifdef VERIFY_HEAP
29530 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
29531 {
29532 BOOL contains_pinned_plugs = FALSE;
29533 size_t mi = 0;
29534 mark* m = 0;
29535 while (mi != mark_stack_tos)
29536 {
29537 m = pinned_plug_of (mi);
29538 if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
29539 {
29540 contains_pinned_plugs = TRUE;
29541 break;
29542 }
29543 else
29544 mi++;
29545 }
29546
29547 if (contains_pinned_plugs)
29548 {
29549 FATAL_GC_ERROR();
29550 }
29551 }
29552#endif //VERIFY_HEAP
29553}
29554
29555void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
29556{
29557 if (!should_expand_in_full_gc)
29558 {
29559 if ((condemned_gen_number != max_generation) &&
29560 (settings.pause_mode != pause_low_latency) &&
29561 (settings.pause_mode != pause_sustained_low_latency))
29562 {
29563 should_expand_in_full_gc = TRUE;
29564 }
29565 }
29566}
29567
29568void gc_heap::save_ephemeral_generation_starts()
29569{
29570 for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
29571 {
29572 saved_ephemeral_plan_start[ephemeral_generation] =
29573 generation_plan_allocation_start (generation_of (ephemeral_generation));
29574 saved_ephemeral_plan_start_size[ephemeral_generation] =
29575 generation_plan_allocation_start_size (generation_of (ephemeral_generation));
29576 }
29577}
29578
29579generation* gc_heap::expand_heap (int condemned_generation,
29580 generation* consing_gen,
29581 heap_segment* new_heap_segment)
29582{
29583 UNREFERENCED_PARAMETER(condemned_generation);
29584 assert (condemned_generation >= (max_generation -1));
29585 unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
29586 uint8_t* start_address = generation_limit (max_generation);
29587 uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment);
29588 BOOL should_promote_ephemeral = FALSE;
29589 ptrdiff_t eph_size = total_ephemeral_size;
29590#ifdef BACKGROUND_GC
29591 dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
29592#endif //BACKGROUND_GC
29593 settings.heap_expansion = TRUE;
29594
29595#ifdef BACKGROUND_GC
29596 if (cm_in_progress)
29597 {
29598 if (!expanded_in_fgc)
29599 {
29600 expanded_in_fgc = TRUE;
29601 }
29602 }
29603#endif //BACKGROUND_GC
29604
29605 //reset the elevation state for next time.
29606 dprintf (2, ("Elevation: elevation = el_none"));
29607 if (settings.should_lock_elevation && !expand_reused_seg_p())
29608 settings.should_lock_elevation = FALSE;
29609
29610 heap_segment* new_seg = new_heap_segment;
29611
29612 if (!new_seg)
29613 return consing_gen;
29614
29615 //copy the card and brick tables
29616 if (g_gc_card_table!= card_table)
29617 copy_brick_card_table();
29618
29619 BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
29620 dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
29621
29622 assert (generation_plan_allocation_start (generation_of (max_generation-1)));
29623 assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
29624 heap_segment_mem (ephemeral_heap_segment));
29625 assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
29626 heap_segment_committed (ephemeral_heap_segment));
29627
29628 assert (generation_plan_allocation_start (youngest_generation));
29629 assert (generation_plan_allocation_start (youngest_generation) <
29630 heap_segment_plan_allocated (ephemeral_heap_segment));
29631
29632 if (settings.pause_mode == pause_no_gc)
29633 {
29634 // We don't reuse for no gc, so the size used on the new eph seg is eph_size.
29635 if ((size_t)(heap_segment_reserved (new_seg) - heap_segment_mem (new_seg)) < (eph_size + soh_allocation_no_gc))
29636 should_promote_ephemeral = TRUE;
29637 }
29638 else
29639 {
29640 if (!use_bestfit)
29641 {
29642 should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
29643 }
29644 }
29645
29646 if (should_promote_ephemeral)
29647 {
29648 ephemeral_promotion = TRUE;
29649 get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_new_seg_ep);
29650 dprintf (2, ("promoting ephemeral"));
29651 save_ephemeral_generation_starts();
29652 }
29653 else
29654 {
29655 // commit the new ephemeral segment all at once if it is a new one.
29656 if ((eph_size > 0) && new_segment_p)
29657 {
29658#ifdef FEATURE_STRUCTALIGN
29659 // The destination may require a larger alignment padding than the source.
29660 // Assume the worst possible alignment padding.
29661 eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
29662#endif // FEATURE_STRUCTALIGN
29663#ifdef RESPECT_LARGE_ALIGNMENT
29664 //Since the generation start can be larger than min_obj_size
29665 //The alignment could be switched.
29666 eph_size += switch_alignment_size(FALSE);
29667#endif //RESPECT_LARGE_ALIGNMENT
29668 //Since the generation start can be larger than min_obj_size
29669 //Compare the alignment of the first object in gen1
29670 if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
29671 {
29672 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
29673 return consing_gen;
29674 }
29675 heap_segment_used (new_seg) = heap_segment_committed (new_seg);
29676 }
29677
29678 //Fix the end of the old ephemeral heap segment
29679 heap_segment_plan_allocated (ephemeral_heap_segment) =
29680 generation_plan_allocation_start (generation_of (max_generation-1));
29681
29682 dprintf (3, ("Old ephemeral allocated set to %Ix",
29683 (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
29684 }
29685
29686 if (new_segment_p)
29687 {
29688 // TODO - Is this really necessary? We should think about it.
29689 //initialize the first brick
29690 size_t first_brick = brick_of (heap_segment_mem (new_seg));
29691 set_brick (first_brick,
29692 heap_segment_mem (new_seg) - brick_address (first_brick));
29693 }
29694
29695 //From this point on, we cannot run out of memory
29696
29697 //reset the allocation of the consing generation back to the end of the
29698 //old ephemeral segment
29699 generation_allocation_limit (consing_gen) =
29700 heap_segment_plan_allocated (ephemeral_heap_segment);
29701 generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
29702 generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
29703
29704 //clear the generation gap for all of the ephemeral generations
29705 {
29706 int generation_num = max_generation-1;
29707 while (generation_num >= 0)
29708 {
29709 generation* gen = generation_of (generation_num);
29710 generation_plan_allocation_start (gen) = 0;
29711 generation_num--;
29712 }
29713 }
29714
29715 heap_segment* old_seg = ephemeral_heap_segment;
29716 ephemeral_heap_segment = new_seg;
29717
29718 //Note: the ephemeral segment shouldn't be threaded onto the segment chain
29719 //because the relocation and compact phases shouldn't see it
29720
29721 // set the generation members used by allocate_in_expanded_heap
29722 // and switch to ephemeral generation
29723 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
29724
29725 if (!should_promote_ephemeral)
29726 {
29727 realloc_plugs (consing_gen, old_seg, start_address, end_address,
29728 active_new_gen_number);
29729 }
29730
29731 if (!use_bestfit)
29732 {
29733 repair_allocation_in_expanded_heap (consing_gen);
29734 }
29735
29736 // assert that the generation gap for all of the ephemeral generations were allocated.
29737#ifdef _DEBUG
29738 {
29739 int generation_num = max_generation-1;
29740 while (generation_num >= 0)
29741 {
29742 generation* gen = generation_of (generation_num);
29743 assert (generation_plan_allocation_start (gen));
29744 generation_num--;
29745 }
29746 }
29747#endif // _DEBUG
29748
29749 if (!new_segment_p)
29750 {
29751 dprintf (2, ("Demoting ephemeral segment"));
29752 //demote the entire segment.
29753 settings.demotion = TRUE;
29754 get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
29755 demotion_low = heap_segment_mem (ephemeral_heap_segment);
29756 demotion_high = heap_segment_reserved (ephemeral_heap_segment);
29757 }
29758 else
29759 {
29760 demotion_low = MAX_PTR;
29761 demotion_high = 0;
29762#ifndef MULTIPLE_HEAPS
29763 settings.demotion = FALSE;
29764 get_gc_data_per_heap()->clear_mechanism_bit (gc_demotion_bit);
29765#endif //!MULTIPLE_HEAPS
29766 }
29767 ptrdiff_t eph_size1 = total_ephemeral_size;
29768 MAYBE_UNUSED_VAR(eph_size1);
29769
29770 if (!should_promote_ephemeral && new_segment_p)
29771 {
29772 assert (eph_size1 <= eph_size);
29773 }
29774
29775 if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
29776 {
29777 // This is to catch when we accidently delete a segment that has pins.
29778 verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
29779 }
29780
29781 verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
29782
29783 dprintf(2,("---- End of Heap Expansion ----"));
29784 return consing_gen;
29785}
29786
29787void gc_heap::set_static_data()
29788{
29789 static_data* pause_mode_sdata = static_data_table[latency_level];
29790 for (int i = 0; i < NUMBERGENERATIONS; i++)
29791 {
29792 dynamic_data* dd = dynamic_data_of (i);
29793 static_data* sdata = &pause_mode_sdata[i];
29794
29795 dd->sdata = sdata;
29796 dd->min_size = sdata->min_size;
29797
29798 dprintf (GTC_LOG, ("PM: %d - min: %Id, max: %Id, fr_l: %Id, fr_b: %d%%",
29799 settings.pause_mode,
29800 dd->min_size, dd_max_size,
29801 sdata->fragmentation_limit, (int)(sdata->fragmentation_burden_limit * 100)));
29802 }
29803}
29804
29805// Initialize the values that are not const.
29806void gc_heap::init_static_data()
29807{
29808 size_t gen0size = GCHeap::GetValidGen0MaxSize(get_valid_segment_size());
29809 size_t gen0_min_size = Align(gen0size / 8 * 5);
29810
29811 size_t gen0_max_size =
29812#ifdef MULTIPLE_HEAPS
29813 max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024));
29814#else //MULTIPLE_HEAPS
29815 (gc_can_use_concurrent ?
29816 6*1024*1024 :
29817 max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024)));
29818#endif //MULTIPLE_HEAPS
29819
29820 // TODO: gen0_max_size has a 200mb cap; gen1_max_size should also have a cap.
29821 size_t gen1_max_size =
29822#ifdef MULTIPLE_HEAPS
29823 max (6*1024*1024, Align(soh_segment_size/2));
29824#else //MULTIPLE_HEAPS
29825 (gc_can_use_concurrent ?
29826 6*1024*1024 :
29827 max (6*1024*1024, Align(soh_segment_size/2)));
29828#endif //MULTIPLE_HEAPS
29829
29830 dprintf (GTC_LOG, ("gen0size: %Id, gen0 min: %Id, max: %Id, gen1 max: %Id",
29831 gen0size, gen0_min_size, gen0_max_size, gen1_max_size));
29832
29833 for (int i = latency_level_first; i <= latency_level_last; i++)
29834 {
29835 static_data_table[i][0].min_size = gen0_min_size;
29836 static_data_table[i][0].max_size = gen0_max_size;
29837 static_data_table[i][1].max_size = gen1_max_size;
29838 }
29839}
29840
29841bool gc_heap::init_dynamic_data()
29842{
29843 qpf = GCToOSInterface::QueryPerformanceFrequency();
29844
29845 uint32_t now = (uint32_t)GetHighPrecisionTimeStamp();
29846
29847 set_static_data();
29848
29849 for (int i = 0; i <= max_generation+1; i++)
29850 {
29851 dynamic_data* dd = dynamic_data_of (i);
29852 dd->gc_clock = 0;
29853 dd->time_clock = now;
29854 dd->current_size = 0;
29855 dd->promoted_size = 0;
29856 dd->collection_count = 0;
29857 dd->new_allocation = dd->min_size;
29858 dd->gc_new_allocation = dd->new_allocation;
29859 dd->desired_allocation = dd->new_allocation;
29860 dd->fragmentation = 0;
29861 }
29862
29863#ifdef GC_CONFIG_DRIVEN
29864 if (heap_number == 0)
29865 time_init = now;
29866#endif //GC_CONFIG_DRIVEN
29867
29868 return true;
29869}
29870
29871float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
29872{
29873 if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
29874 return ((limit - limit*cst) / (1.0f - (cst * limit)));
29875 else
29876 return max_limit;
29877}
29878
29879
29880//if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may
29881//not be correct (collection happened too soon). Correct with a linear estimation based on the previous
29882//value of the budget
29883static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation,
29884 size_t previous_desired_allocation, size_t collection_count)
29885{
29886 if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
29887 {
29888 dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0)));
29889 new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
29890 }
29891#if 0
29892 size_t smoothing = 3; // exponential smoothing factor
29893 if (smoothing > collection_count)
29894 smoothing = collection_count;
29895 new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
29896#else
29897 UNREFERENCED_PARAMETER(collection_count);
29898#endif //0
29899 return new_allocation;
29900}
29901
29902size_t gc_heap::desired_new_allocation (dynamic_data* dd,
29903 size_t out, int gen_number,
29904 int pass)
29905{
29906 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
29907
29908 if (dd_begin_data_size (dd) == 0)
29909 {
29910 size_t new_allocation = dd_min_size (dd);
29911 current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;
29912 return new_allocation;
29913 }
29914 else
29915 {
29916 float cst;
29917 size_t previous_desired_allocation = dd_desired_allocation (dd);
29918 size_t current_size = dd_current_size (dd);
29919 float max_limit = dd_max_limit (dd);
29920 float limit = dd_limit (dd);
29921 size_t min_gc_size = dd_min_size (dd);
29922 float f = 0;
29923 size_t max_size = dd_max_size (dd);
29924 size_t new_allocation = 0;
29925 float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
29926 if (gen_number >= max_generation)
29927 {
29928 size_t new_size = 0;
29929
29930 cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
29931
29932 f = surv_to_growth (cst, limit, max_limit);
29933 size_t max_growth_size = (size_t)(max_size / f);
29934 if (current_size >= max_growth_size)
29935 {
29936 new_size = max_size;
29937 }
29938 else
29939 {
29940 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
29941 }
29942
29943 assert ((new_size >= current_size) || (new_size == max_size));
29944
29945 if (gen_number == max_generation)
29946 {
29947 new_allocation = max((new_size - current_size), min_gc_size);
29948
29949 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29950 dd_desired_allocation (dd), dd_collection_count (dd));
29951
29952 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
29953 {
29954 //reducing allocation in case of fragmentation
29955 size_t new_allocation1 = max (min_gc_size,
29956 // CAN OVERFLOW
29957 (size_t)((float)new_allocation * current_size /
29958 ((float)current_size + 2*dd_fragmentation (dd))));
29959 dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
29960 new_allocation, new_allocation1));
29961 new_allocation = new_allocation1;
29962 }
29963 }
29964 else //large object heap
29965 {
29966 uint32_t memory_load = 0;
29967 uint64_t available_physical = 0;
29968 get_memory_info (&memory_load, &available_physical);
29969 if (heap_number == 0)
29970 settings.exit_memory_load = memory_load;
29971 if (available_physical > 1024*1024)
29972 available_physical -= 1024*1024;
29973
29974 uint64_t available_free = available_physical + (uint64_t)generation_free_list_space (generation_of (gen_number));
29975 if (available_free > (uint64_t)MAX_PTR)
29976 {
29977 available_free = (uint64_t)MAX_PTR;
29978 }
29979
29980 //try to avoid OOM during large object allocation
29981 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))),
29982 (size_t)available_free),
29983 max ((current_size/4), min_gc_size));
29984
29985 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29986 dd_desired_allocation (dd), dd_collection_count (dd));
29987
29988 }
29989 }
29990 else
29991 {
29992 size_t survivors = out;
29993 cst = float (survivors) / float (dd_begin_data_size (dd));
29994 f = surv_to_growth (cst, limit, max_limit);
29995 new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
29996
29997 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
29998 dd_desired_allocation (dd), dd_collection_count (dd));
29999
30000 if (gen_number == 0)
30001 {
30002 if (pass == 0)
30003 {
30004
30005 //printf ("%f, %Id\n", cst, new_allocation);
30006 size_t free_space = generation_free_list_space (generation_of (gen_number));
30007 // DTREVIEW - is min_gc_size really a good choice?
30008 // on 64-bit this will almost always be true.
30009 dprintf (GTC_LOG, ("frag: %Id, min: %Id", free_space, min_gc_size));
30010 if (free_space > min_gc_size)
30011 {
30012 settings.gen0_reduction_count = 2;
30013 }
30014 else
30015 {
30016 if (settings.gen0_reduction_count > 0)
30017 settings.gen0_reduction_count--;
30018 }
30019 }
30020 if (settings.gen0_reduction_count > 0)
30021 {
30022 dprintf (2, ("Reducing new allocation based on fragmentation"));
30023 new_allocation = min (new_allocation,
30024 max (min_gc_size, (max_size/3)));
30025 }
30026 }
30027 }
30028
30029 size_t new_allocation_ret =
30030 Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
30031 int gen_data_index = gen_number;
30032 gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
30033 gen_data->new_allocation = new_allocation_ret;
30034
30035 dd_surv (dd) = cst;
30036
30037#ifdef SIMPLE_DPRINTF
30038 dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
30039 heap_number, gen_number, out, current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)),
30040 (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
30041#else
30042 dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
30043 dprintf (1,("current: %Id alloc: %Id ", current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd))));
30044 dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
30045 (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
30046#endif //SIMPLE_DPRINTF
30047
30048 return new_allocation_ret;
30049 }
30050}
30051
30052//returns the planned size of a generation (including free list element)
30053size_t gc_heap::generation_plan_size (int gen_number)
30054{
30055 if (0 == gen_number)
30056 return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
30057 generation_plan_allocation_start (generation_of (gen_number))),
30058 (int)Align (min_obj_size));
30059 else
30060 {
30061 generation* gen = generation_of (gen_number);
30062 if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
30063 return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
30064 generation_plan_allocation_start (generation_of (gen_number)));
30065 else
30066 {
30067 size_t gensize = 0;
30068 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30069
30070 PREFIX_ASSUME(seg != NULL);
30071
30072 while (seg && (seg != ephemeral_heap_segment))
30073 {
30074 gensize += heap_segment_plan_allocated (seg) -
30075 heap_segment_mem (seg);
30076 seg = heap_segment_next_rw (seg);
30077 }
30078 if (seg)
30079 {
30080 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
30081 heap_segment_mem (ephemeral_heap_segment));
30082 }
30083 return gensize;
30084 }
30085 }
30086
30087}
30088
30089//returns the size of a generation (including free list element)
30090size_t gc_heap::generation_size (int gen_number)
30091{
30092 if (0 == gen_number)
30093 return max((heap_segment_allocated (ephemeral_heap_segment) -
30094 generation_allocation_start (generation_of (gen_number))),
30095 (int)Align (min_obj_size));
30096 else
30097 {
30098 generation* gen = generation_of (gen_number);
30099 if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
30100 return (generation_allocation_start (generation_of (gen_number - 1)) -
30101 generation_allocation_start (generation_of (gen_number)));
30102 else
30103 {
30104 size_t gensize = 0;
30105 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30106
30107 PREFIX_ASSUME(seg != NULL);
30108
30109 while (seg && (seg != ephemeral_heap_segment))
30110 {
30111 gensize += heap_segment_allocated (seg) -
30112 heap_segment_mem (seg);
30113 seg = heap_segment_next_rw (seg);
30114 }
30115 if (seg)
30116 {
30117 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
30118 heap_segment_mem (ephemeral_heap_segment));
30119 }
30120
30121 return gensize;
30122 }
30123 }
30124
30125}
30126
30127size_t gc_heap::compute_in (int gen_number)
30128{
30129 assert (gen_number != 0);
30130 dynamic_data* dd = dynamic_data_of (gen_number);
30131
30132 size_t in = generation_allocation_size (generation_of (gen_number));
30133
30134 if (gen_number == max_generation && ephemeral_promotion)
30135 {
30136 in = 0;
30137 for (int i = 0; i <= max_generation; i++)
30138 {
30139 dynamic_data* dd = dynamic_data_of (i);
30140 in += dd_survived_size (dd);
30141 if (i != max_generation)
30142 {
30143 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
30144 }
30145 }
30146 }
30147
30148 dd_gc_new_allocation (dd) -= in;
30149 dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30150
30151 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30152 gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
30153 gen_data->in = in;
30154
30155 generation_allocation_size (generation_of (gen_number)) = 0;
30156 return in;
30157}
30158
30159void gc_heap::compute_promoted_allocation (int gen_number)
30160{
30161 compute_in (gen_number);
30162}
30163
30164#ifdef BIT64
30165inline
30166size_t gc_heap::trim_youngest_desired (uint32_t memory_load,
30167 size_t total_new_allocation,
30168 size_t total_min_allocation)
30169{
30170 if (memory_load < MAX_ALLOWED_MEM_LOAD)
30171 {
30172 // If the total of memory load and gen0 budget exceeds
30173 // our max memory load limit, trim the gen0 budget so the total
30174 // is the max memory load limit.
30175 size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
30176 return min (total_new_allocation, remain_memory_load);
30177 }
30178 else
30179 {
30180 return max (mem_one_percent, total_min_allocation);
30181 }
30182}
30183
30184size_t gc_heap::joined_youngest_desired (size_t new_allocation)
30185{
30186 dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
30187
30188 size_t final_new_allocation = new_allocation;
30189 if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
30190 {
30191 uint32_t num_heaps = 1;
30192
30193#ifdef MULTIPLE_HEAPS
30194 num_heaps = gc_heap::n_heaps;
30195#endif //MULTIPLE_HEAPS
30196
30197 size_t total_new_allocation = new_allocation * num_heaps;
30198 size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
30199
30200 if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
30201 (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
30202 {
30203 uint32_t memory_load = 0;
30204 get_memory_info (&memory_load);
30205 settings.exit_memory_load = memory_load;
30206 dprintf (2, ("Current emory load: %d", memory_load));
30207
30208 size_t final_total =
30209 trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation);
30210 size_t max_new_allocation =
30211#ifdef MULTIPLE_HEAPS
30212 dd_max_size (g_heaps[0]->dynamic_data_of (0));
30213#else //MULTIPLE_HEAPS
30214 dd_max_size (dynamic_data_of (0));
30215#endif //MULTIPLE_HEAPS
30216
30217 final_new_allocation = min (Align ((final_total / num_heaps), get_alignment_constant (TRUE)), max_new_allocation);
30218 }
30219 }
30220
30221 if (final_new_allocation < new_allocation)
30222 {
30223 settings.gen0_reduction_count = 2;
30224 }
30225
30226 return final_new_allocation;
30227}
30228#endif // BIT64
30229
30230inline
30231gc_history_per_heap* gc_heap::get_gc_data_per_heap()
30232{
30233#ifdef BACKGROUND_GC
30234 return (settings.concurrent ? &bgc_data_per_heap : &gc_data_per_heap);
30235#else
30236 return &gc_data_per_heap;
30237#endif //BACKGROUND_GC
30238}
30239
30240void gc_heap::compute_new_dynamic_data (int gen_number)
30241{
30242 PREFIX_ASSUME(gen_number >= 0);
30243 PREFIX_ASSUME(gen_number <= max_generation);
30244
30245 dynamic_data* dd = dynamic_data_of (gen_number);
30246 generation* gen = generation_of (gen_number);
30247 size_t in = (gen_number==0) ? 0 : compute_in (gen_number);
30248
30249 size_t total_gen_size = generation_size (gen_number);
30250 //keep track of fragmentation
30251 dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
30252 dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
30253
30254 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30255
30256 size_t out = dd_survived_size (dd);
30257
30258 gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
30259 gen_data->size_after = total_gen_size;
30260 gen_data->free_list_space_after = generation_free_list_space (gen);
30261 gen_data->free_obj_space_after = generation_free_obj_space (gen);
30262
30263 if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
30264 {
30265 // When we are in the low latency mode, we can still be
30266 // condemning more than gen1's 'cause of induced GCs.
30267 dd_desired_allocation (dd) = low_latency_alloc;
30268 }
30269 else
30270 {
30271 if (gen_number == 0)
30272 {
30273 //compensate for dead finalizable objects promotion.
30274 //they shoudn't be counted for growth.
30275 size_t final_promoted = 0;
30276 final_promoted = min (promoted_bytes (heap_number), out);
30277 // Prefast: this is clear from above but prefast needs to be told explicitly
30278 PREFIX_ASSUME(final_promoted <= out);
30279
30280 dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
30281 dd_freach_previous_promotion (dd) = final_promoted;
30282 size_t lower_bound = desired_new_allocation (dd, out-final_promoted, gen_number, 0);
30283
30284 if (settings.condemned_generation == 0)
30285 {
30286 //there is no noise.
30287 dd_desired_allocation (dd) = lower_bound;
30288 }
30289 else
30290 {
30291 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
30292
30293 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
30294 //assert ( lower_bound <= higher_bound);
30295
30296 //discount the noise. Change the desired allocation
30297 //only if the previous value is outside of the range.
30298 if (dd_desired_allocation (dd) < lower_bound)
30299 {
30300 dd_desired_allocation (dd) = lower_bound;
30301 }
30302 else if (dd_desired_allocation (dd) > higher_bound)
30303 {
30304 dd_desired_allocation (dd) = higher_bound;
30305 }
30306#if defined (BIT64) && !defined (MULTIPLE_HEAPS)
30307 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
30308#endif // BIT64 && !MULTIPLE_HEAPS
30309 trim_youngest_desired_low_memory();
30310 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
30311 }
30312 }
30313 else
30314 {
30315 dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
30316 }
30317 }
30318
30319 gen_data->pinned_surv = dd_pinned_survived_size (dd);
30320 gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd);
30321
30322 dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
30323 dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30324
30325 //update counter
30326 dd_promoted_size (dd) = out;
30327 if (gen_number == max_generation)
30328 {
30329 dd = dynamic_data_of (max_generation+1);
30330 total_gen_size = generation_size (max_generation + 1);
30331 dd_fragmentation (dd) = generation_free_list_space (large_object_generation) +
30332 generation_free_obj_space (large_object_generation);
30333 dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
30334 dd_survived_size (dd) = dd_current_size (dd);
30335 in = 0;
30336 out = dd_current_size (dd);
30337 dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
30338 dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
30339 get_alignment_constant (FALSE));
30340 dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30341
30342 gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
30343 gen_data->size_after = total_gen_size;
30344 gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
30345 gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
30346 gen_data->npinned_surv = out;
30347#ifdef BACKGROUND_GC
30348 end_loh_size = total_gen_size;
30349#endif //BACKGROUND_GC
30350 //update counter
30351 dd_promoted_size (dd) = out;
30352 }
30353}
30354
30355void gc_heap::trim_youngest_desired_low_memory()
30356{
30357 if (g_low_memory_status)
30358 {
30359 size_t committed_mem = 0;
30360 heap_segment* seg = generation_start_segment (generation_of (max_generation));
30361 while (seg)
30362 {
30363 committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30364 seg = heap_segment_next (seg);
30365 }
30366 seg = generation_start_segment (generation_of (max_generation + 1));
30367 while (seg)
30368 {
30369 committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30370 seg = heap_segment_next (seg);
30371 }
30372
30373 dynamic_data* dd = dynamic_data_of (0);
30374 size_t current = dd_desired_allocation (dd);
30375 size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_size (dd));
30376
30377 dd_desired_allocation (dd) = min (current, candidate);
30378 }
30379}
30380
30381void gc_heap::decommit_ephemeral_segment_pages()
30382{
30383 if (settings.concurrent)
30384 {
30385 return;
30386 }
30387
30388 size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30389 dynamic_data* dd = dynamic_data_of (0);
30390
30391#ifndef MULTIPLE_HEAPS
30392 size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
30393 size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
30394 size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
30395
30396 if (dd_desired_allocation (dd) > gc_gen0_desired_high)
30397 {
30398 gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space;
30399 }
30400
30401 if (ephemeral_elapsed >= decommit_timeout)
30402 {
30403 slack_space = min (slack_space, gc_gen0_desired_high);
30404
30405 gc_last_ephemeral_decommit_time = dd_time_clock(dd);
30406 gc_gen0_desired_high = 0;
30407 }
30408#endif //!MULTIPLE_HEAPS
30409
30410 if (settings.condemned_generation >= (max_generation-1))
30411 {
30412 size_t new_slack_space =
30413#ifdef BIT64
30414 max(min(min(soh_segment_size/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
30415#else
30416#ifdef FEATURE_CORECLR
30417 dd_desired_allocation (dd);
30418#else
30419 dd_max_size (dd);
30420#endif //FEATURE_CORECLR
30421#endif // BIT64
30422
30423 slack_space = min (slack_space, new_slack_space);
30424 }
30425
30426 decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);
30427
30428 gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30429 current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30430}
30431
30432//This is meant to be called by decide_on_compacting.
30433
30434size_t gc_heap::generation_fragmentation (generation* gen,
30435 generation* consing_gen,
30436 uint8_t* end)
30437{
30438 size_t frag;
30439 uint8_t* alloc = generation_allocation_pointer (consing_gen);
30440 // If the allocation pointer has reached the ephemeral segment
30441 // fine, otherwise the whole ephemeral segment is considered
30442 // fragmentation
30443 if (in_range_for_segment (alloc, ephemeral_heap_segment))
30444 {
30445 if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
30446 frag = end - alloc;
30447 else
30448 {
30449 // case when no survivors, allocated set to beginning
30450 frag = 0;
30451 }
30452 dprintf (3, ("ephemeral frag: %Id", frag));
30453 }
30454 else
30455 frag = (heap_segment_allocated (ephemeral_heap_segment) -
30456 heap_segment_mem (ephemeral_heap_segment));
30457 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30458
30459 PREFIX_ASSUME(seg != NULL);
30460
30461 while (seg != ephemeral_heap_segment)
30462 {
30463 frag += (heap_segment_allocated (seg) -
30464 heap_segment_plan_allocated (seg));
30465 dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
30466 (heap_segment_allocated (seg) -
30467 heap_segment_plan_allocated (seg))));
30468
30469 seg = heap_segment_next_rw (seg);
30470 assert (seg);
30471 }
30472 dprintf (3, ("frag: %Id discounting pinned plugs", frag));
30473 //add the length of the dequeued plug free space
30474 size_t bos = 0;
30475 while (bos < mark_stack_bos)
30476 {
30477 frag += (pinned_len (pinned_plug_of (bos)));
30478 bos++;
30479 }
30480
30481 return frag;
30482}
30483
30484// for SOH this returns the total sizes of the generation and its
30485// younger generation(s).
30486// for LOH this returns just LOH size.
30487size_t gc_heap::generation_sizes (generation* gen)
30488{
30489 size_t result = 0;
30490 if (generation_start_segment (gen ) == ephemeral_heap_segment)
30491 result = (heap_segment_allocated (ephemeral_heap_segment) -
30492 generation_allocation_start (gen));
30493 else
30494 {
30495 heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
30496
30497 PREFIX_ASSUME(seg != NULL);
30498
30499 while (seg)
30500 {
30501 result += (heap_segment_allocated (seg) -
30502 heap_segment_mem (seg));
30503 seg = heap_segment_next_in_range (seg);
30504 }
30505 }
30506
30507 return result;
30508}
30509
30510BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
30511 size_t fragmentation,
30512 BOOL& should_expand)
30513{
30514 BOOL should_compact = FALSE;
30515 should_expand = FALSE;
30516 generation* gen = generation_of (condemned_gen_number);
30517 dynamic_data* dd = dynamic_data_of (condemned_gen_number);
30518 size_t gen_sizes = generation_sizes(gen);
30519 float fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
30520 (float (fragmentation) / gen_sizes) );
30521
30522 dprintf (GTC_LOG, ("fragmentation: %Id (%d%%)", fragmentation, (int)(fragmentation_burden * 100.0)));
30523
30524#ifdef STRESS_HEAP
30525 // for pure GC stress runs we need compaction, for GC stress "mix"
30526 // we need to ensure a better mix of compacting and sweeping collections
30527 if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
30528 && !g_pConfig->IsGCStressMix())
30529 should_compact = TRUE;
30530
30531#ifdef GC_STATS
30532 // in GC stress "mix" mode, for stress induced collections make sure we
30533 // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
30534 // against the GC's determination, as it may lead to premature OOMs.
30535 if (g_pConfig->IsGCStressMix() && settings.stress_induced)
30536 {
30537 int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
30538 int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
30539 if (compactions < sweeps / 10)
30540 {
30541 should_compact = TRUE;
30542 }
30543 }
30544#endif // GC_STATS
30545#endif //STRESS_HEAP
30546
30547 if (GCConfig::GetForceCompact())
30548 should_compact = TRUE;
30549
30550 if ((condemned_gen_number == max_generation) && last_gc_before_oom)
30551 {
30552 should_compact = TRUE;
30553 last_gc_before_oom = FALSE;
30554 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc);
30555 }
30556
30557 if (settings.reason == reason_induced_compacting)
30558 {
30559 dprintf (2, ("induced compacting GC"));
30560 should_compact = TRUE;
30561 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
30562 }
30563
30564 if (settings.reason == reason_pm_full_gc)
30565 {
30566 assert (condemned_gen_number == max_generation);
30567 if (heap_number == 0)
30568 {
30569 dprintf (GTC_LOG, ("PM doing compacting full GC after a gen1"));
30570 }
30571 should_compact = TRUE;
30572 }
30573
30574 dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
30575 fragmentation, (int) (100*fragmentation_burden)));
30576
30577 if (provisional_mode_triggered && (condemned_gen_number == (max_generation - 1)))
30578 {
30579 dprintf (GTC_LOG, ("gen1 in PM always compact"));
30580 should_compact = TRUE;
30581 }
30582
30583 if (!should_compact)
30584 {
30585 if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
30586 {
30587 dprintf(GTC_LOG, ("compacting due to low ephemeral"));
30588 should_compact = TRUE;
30589 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral);
30590 }
30591 }
30592
30593 if (should_compact)
30594 {
30595 if ((condemned_gen_number >= (max_generation - 1)))
30596 {
30597 if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
30598 {
30599 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
30600 should_expand = TRUE;
30601 }
30602 }
30603 }
30604
30605#ifdef BIT64
30606 BOOL high_memory = FALSE;
30607#endif // BIT64
30608
30609 if (!should_compact)
30610 {
30611 // We are not putting this in dt_high_frag_p because it's not exactly
30612 // high fragmentation - it's just enough planned fragmentation for us to
30613 // want to compact. Also the "fragmentation" we are talking about here
30614 // is different from anywhere else.
30615 BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
30616 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
30617
30618 if (frag_exceeded)
30619 {
30620#ifdef BACKGROUND_GC
30621 // do not force compaction if this was a stress-induced GC
30622 IN_STRESS_HEAP(if (!settings.stress_induced))
30623 {
30624#endif // BACKGROUND_GC
30625 assert (settings.concurrent == FALSE);
30626 should_compact = TRUE;
30627 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag);
30628#ifdef BACKGROUND_GC
30629 }
30630#endif // BACKGROUND_GC
30631 }
30632
30633#ifdef BIT64
30634 // check for high memory situation
30635 if(!should_compact)
30636 {
30637 uint32_t num_heaps = 1;
30638#ifdef MULTIPLE_HEAPS
30639 num_heaps = gc_heap::n_heaps;
30640#endif // MULTIPLE_HEAPS
30641
30642 ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
30643 if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th))
30644 {
30645 if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps)))
30646 {
30647 dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
30648 should_compact = TRUE;
30649 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag);
30650 }
30651 high_memory = TRUE;
30652 }
30653 else if(settings.entry_memory_load >= v_high_memory_load_th)
30654 {
30655 if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps)))
30656 {
30657 dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
30658 should_compact = TRUE;
30659 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag);
30660 }
30661 high_memory = TRUE;
30662 }
30663 }
30664#endif // BIT64
30665 }
30666
30667 // The purpose of calling ensure_gap_allocation here is to make sure
30668 // that we actually are able to commit the memory to allocate generation
30669 // starts.
30670 if ((should_compact == FALSE) &&
30671 (ensure_gap_allocation (condemned_gen_number) == FALSE))
30672 {
30673 should_compact = TRUE;
30674 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps);
30675 }
30676
30677 if (settings.condemned_generation == max_generation)
30678 {
30679 //check the progress
30680 if (
30681#ifdef BIT64
30682 (high_memory && !should_compact) ||
30683#endif // BIT64
30684 (generation_plan_allocation_start (generation_of (max_generation - 1)) >=
30685 generation_allocation_start (generation_of (max_generation - 1))))
30686 {
30687 dprintf (2, (" Elevation: gen2 size: %d, gen2 plan size: %d, no progress, elevation = locked",
30688 generation_size (max_generation),
30689 generation_plan_size (max_generation)));
30690 //no progress -> lock
30691 settings.should_lock_elevation = TRUE;
30692 }
30693 }
30694
30695 if (settings.pause_mode == pause_no_gc)
30696 {
30697 should_compact = TRUE;
30698 if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment))
30699 < soh_allocation_no_gc)
30700 {
30701 should_expand = TRUE;
30702 }
30703 }
30704
30705 dprintf (2, ("will %s", (should_compact ? "compact" : "sweep")));
30706 return should_compact;
30707}
30708
30709size_t align_lower_good_size_allocation (size_t size)
30710{
30711 return (size/64)*64;
30712}
30713
30714size_t gc_heap::approximate_new_allocation()
30715{
30716 dynamic_data* dd0 = dynamic_data_of (0);
30717 return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
30718}
30719
30720// After we did a GC we expect to have at least this
30721// much space at the end of the segment to satisfy
30722// a reasonable amount of allocation requests.
30723size_t gc_heap::end_space_after_gc()
30724{
30725 return max ((dd_min_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
30726}
30727
30728BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
30729{
30730 uint8_t* start = 0;
30731
30732 if ((tp == tuning_deciding_condemned_gen) ||
30733 (tp == tuning_deciding_compaction))
30734 {
30735 start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
30736 if (settings.concurrent)
30737 {
30738 dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)",
30739 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30740 }
30741 else
30742 {
30743 dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)",
30744 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
30745 }
30746 }
30747 else if (tp == tuning_deciding_expansion)
30748 {
30749 start = heap_segment_plan_allocated (ephemeral_heap_segment);
30750 dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan",
30751 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
30752 }
30753 else
30754 {
30755 assert (tp == tuning_deciding_full_gc);
30756 dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)",
30757 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
30758 start = alloc_allocated;
30759 }
30760
30761 if (start == 0) // empty ephemeral generations
30762 {
30763 assert (tp == tuning_deciding_expansion);
30764 // if there are no survivors in the ephemeral segment,
30765 // this should be the beginning of ephemeral segment.
30766 start = generation_allocation_pointer (generation_of (max_generation));
30767 assert (start == heap_segment_mem (ephemeral_heap_segment));
30768 }
30769
30770 if (tp == tuning_deciding_expansion)
30771 {
30772 assert (settings.condemned_generation >= (max_generation-1));
30773 size_t gen0size = approximate_new_allocation();
30774 size_t eph_size = gen0size;
30775
30776 for (int j = 1; j <= max_generation-1; j++)
30777 {
30778 eph_size += 2*dd_min_size (dynamic_data_of(j));
30779 }
30780
30781 // We must find room for one large object and enough room for gen0size
30782 if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
30783 {
30784 dprintf (3, ("Enough room before end of segment"));
30785 return TRUE;
30786 }
30787 else
30788 {
30789 size_t room = align_lower_good_size_allocation
30790 (heap_segment_reserved (ephemeral_heap_segment) - start);
30791 size_t end_seg = room;
30792
30793 //look at the plug free space
30794 size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
30795 bool large_chunk_found = FALSE;
30796 size_t bos = 0;
30797 uint8_t* gen0start = generation_plan_allocation_start (youngest_generation);
30798 dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
30799 if (gen0start == 0)
30800 return FALSE;
30801 dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
30802 room, gen0size));
30803 while ((bos < mark_stack_bos) &&
30804 !((room >= gen0size) && large_chunk_found))
30805 {
30806 uint8_t* plug = pinned_plug (pinned_plug_of (bos));
30807 if (in_range_for_segment (plug, ephemeral_heap_segment))
30808 {
30809 if (plug >= gen0start)
30810 {
30811 size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
30812 room += chunk;
30813 if (!large_chunk_found)
30814 {
30815 large_chunk_found = (chunk >= largest_alloc);
30816 }
30817 dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
30818 room, large_chunk_found));
30819 }
30820 }
30821 bos++;
30822 }
30823
30824 if (room >= gen0size)
30825 {
30826 if (large_chunk_found)
30827 {
30828 sufficient_gen0_space_p = TRUE;
30829
30830 dprintf (3, ("Enough room"));
30831 return TRUE;
30832 }
30833 else
30834 {
30835 // now we need to find largest_alloc at the end of the segment.
30836 if (end_seg >= end_space_after_gc())
30837 {
30838 dprintf (3, ("Enough room (may need end of seg)"));
30839 return TRUE;
30840 }
30841 }
30842 }
30843
30844 dprintf (3, ("Not enough room"));
30845 return FALSE;
30846 }
30847 }
30848 else
30849 {
30850 size_t end_space = 0;
30851 dynamic_data* dd = dynamic_data_of (0);
30852 if ((tp == tuning_deciding_condemned_gen) ||
30853 (tp == tuning_deciding_full_gc))
30854 {
30855 end_space = max (2*dd_min_size (dd), end_space_after_gc());
30856 }
30857 else
30858 {
30859 assert (tp == tuning_deciding_compaction);
30860 end_space = approximate_new_allocation();
30861 }
30862
30863 if (!((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space))
30864 {
30865 dprintf (GTC_LOG, ("ephemeral_gen_fit_p: does not fit without compaction"));
30866 }
30867 return ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > end_space);
30868 }
30869}
30870
30871CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_bytes)
30872{
30873 //create a new alloc context because gen3context is shared.
30874 alloc_context acontext;
30875 acontext.alloc_ptr = 0;
30876 acontext.alloc_limit = 0;
30877 acontext.alloc_bytes = 0;
30878#ifdef MULTIPLE_HEAPS
30879 acontext.set_alloc_heap(vm_heap);
30880#endif //MULTIPLE_HEAPS
30881
30882#if BIT64
30883 size_t maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
30884#else
30885 size_t maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
30886#endif
30887
30888 if (jsize >= maxObjectSize)
30889 {
30890 if (GCConfig::GetBreakOnOOM())
30891 {
30892 GCToOSInterface::DebugBreak();
30893 }
30894 return NULL;
30895 }
30896
30897 size_t size = AlignQword (jsize);
30898 int align_const = get_alignment_constant (FALSE);
30899#ifdef FEATURE_LOH_COMPACTION
30900 size_t pad = Align (loh_padding_obj_size, align_const);
30901#else
30902 size_t pad = 0;
30903#endif //FEATURE_LOH_COMPACTION
30904
30905 assert (size >= Align (min_obj_size, align_const));
30906#ifdef _MSC_VER
30907#pragma inline_depth(0)
30908#endif //_MSC_VER
30909 if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
30910 {
30911 return 0;
30912 }
30913
30914#ifdef _MSC_VER
30915#pragma inline_depth(20)
30916#endif //_MSC_VER
30917
30918#ifdef MARK_ARRAY
30919 uint8_t* current_lowest_address = lowest_address;
30920 uint8_t* current_highest_address = highest_address;
30921#ifdef BACKGROUND_GC
30922 if (recursive_gc_sync::background_running_p())
30923 {
30924 current_lowest_address = background_saved_lowest_address;
30925 current_highest_address = background_saved_highest_address;
30926 }
30927#endif //BACKGROUND_GC
30928#endif // MARK_ARRAY
30929
30930#ifdef FEATURE_LOH_COMPACTION
30931 // The GC allocator made a free object already in this alloc context and
30932 // adjusted the alloc_ptr accordingly.
30933#endif //FEATURE_LOH_COMPACTION
30934
30935 uint8_t* result = acontext.alloc_ptr;
30936
30937 assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
30938 alloc_bytes += size;
30939
30940 CObjectHeader* obj = (CObjectHeader*)result;
30941
30942#ifdef MARK_ARRAY
30943 if (recursive_gc_sync::background_running_p())
30944 {
30945 if ((result < current_highest_address) && (result >= current_lowest_address))
30946 {
30947 dprintf (3, ("Clearing mark bit at address %Ix",
30948 (size_t)(&mark_array [mark_word_of (result)])));
30949
30950 mark_array_clear_marked (result);
30951 }
30952#ifdef BACKGROUND_GC
30953 //the object has to cover one full mark uint32_t
30954 assert (size > mark_word_size);
30955 if (current_c_gc_state != c_gc_state_free)
30956 {
30957 dprintf (3, ("Concurrent allocation of a large object %Ix",
30958 (size_t)obj));
30959 //mark the new block specially so we know it is a new object
30960 if ((result < current_highest_address) && (result >= current_lowest_address))
30961 {
30962 dprintf (3, ("Setting mark bit at address %Ix",
30963 (size_t)(&mark_array [mark_word_of (result)])));
30964
30965 mark_array_set_marked (result);
30966 }
30967 }
30968#endif //BACKGROUND_GC
30969 }
30970#endif //MARK_ARRAY
30971
30972 assert (obj != 0);
30973 assert ((size_t)obj == Align ((size_t)obj, align_const));
30974
30975 return obj;
30976}
30977
30978void reset_memory (uint8_t* o, size_t sizeo)
30979{
30980 if (sizeo > 128 * 1024)
30981 {
30982 // We cannot reset the memory for the useful part of a free object.
30983 size_t size_to_skip = min_free_list - plug_skew;
30984
30985 size_t page_start = align_on_page ((size_t)(o + size_to_skip));
30986 size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
30987 // Note we need to compensate for an OS bug here. This bug would cause the MEM_RESET to fail
30988 // on write watched memory.
30989 if (reset_mm_p)
30990 {
30991#ifdef MULTIPLE_HEAPS
30992 bool unlock_p = true;
30993#else
30994 // We don't do unlock because there could be many processes using workstation GC and it's
30995 // bad perf to have many threads doing unlock at the same time.
30996 bool unlock_p = false;
30997#endif //MULTIPLE_HEAPS
30998
30999 reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, unlock_p);
31000 }
31001 }
31002}
31003
31004void gc_heap::reset_large_object (uint8_t* o)
31005{
31006 // If it's a large object, allow the O/S to discard the backing store for these pages.
31007 reset_memory (o, size(o));
31008}
31009
31010BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp)
31011{
31012 BOOL m = FALSE;
31013 // It shouldn't be necessary to do these comparisons because this is only used for blocking
31014 // GCs and LOH segments cannot be out of range.
31015 if ((o >= lowest_address) && (o < highest_address))
31016 {
31017 if (marked (o))
31018 {
31019 if (clearp)
31020 {
31021 clear_marked (o);
31022 if (pinned (o))
31023 clear_pinned(o);
31024 }
31025 m = TRUE;
31026 }
31027 else
31028 m = FALSE;
31029 }
31030 else
31031 m = TRUE;
31032 return m;
31033}
31034
31035void gc_heap::walk_survivors_relocation (void* profiling_context, record_surv_fn fn)
31036{
31037 // Now walk the portion of memory that is actually being relocated.
31038 walk_relocation (profiling_context, fn);
31039
31040#ifdef FEATURE_LOH_COMPACTION
31041 if (loh_compacted_p)
31042 {
31043 walk_relocation_for_loh (profiling_context, fn);
31044 }
31045#endif //FEATURE_LOH_COMPACTION
31046}
31047
31048void gc_heap::walk_survivors_for_loh (void* profiling_context, record_surv_fn fn)
31049{
31050 generation* gen = large_object_generation;
31051 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));;
31052
31053 PREFIX_ASSUME(seg != NULL);
31054
31055 uint8_t* o = generation_allocation_start (gen);
31056 uint8_t* plug_end = o;
31057 uint8_t* plug_start = o;
31058
31059 while (1)
31060 {
31061 if (o >= heap_segment_allocated (seg))
31062 {
31063 seg = heap_segment_next (seg);
31064 if (seg == 0)
31065 break;
31066 else
31067 o = heap_segment_mem (seg);
31068 }
31069 if (large_object_marked(o, FALSE))
31070 {
31071 plug_start = o;
31072
31073 BOOL m = TRUE;
31074 while (m)
31075 {
31076 o = o + AlignQword (size (o));
31077 if (o >= heap_segment_allocated (seg))
31078 {
31079 break;
31080 }
31081 m = large_object_marked (o, FALSE);
31082 }
31083
31084 plug_end = o;
31085
31086 fn (plug_start, plug_end, 0, profiling_context, false, false);
31087 }
31088 else
31089 {
31090 while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
31091 {
31092 o = o + AlignQword (size (o));
31093 }
31094 }
31095 }
31096}
31097
31098#ifdef BACKGROUND_GC
31099
31100BOOL gc_heap::background_object_marked (uint8_t* o, BOOL clearp)
31101{
31102 BOOL m = FALSE;
31103 if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
31104 {
31105 if (mark_array_marked (o))
31106 {
31107 if (clearp)
31108 {
31109 mark_array_clear_marked (o);
31110 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
31111 dprintf (3, ("CM: %Ix", o));
31112 }
31113 m = TRUE;
31114 }
31115 else
31116 m = FALSE;
31117 }
31118 else
31119 m = TRUE;
31120
31121 dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
31122 return m;
31123}
31124
31125void gc_heap::background_delay_delete_loh_segments()
31126{
31127 generation* gen = large_object_generation;
31128 heap_segment* seg = heap_segment_rw (generation_start_segment (large_object_generation));
31129 heap_segment* prev_seg = 0;
31130
31131 while (seg)
31132 {
31133 heap_segment* next_seg = heap_segment_next (seg);
31134 if (seg->flags & heap_segment_flags_loh_delete)
31135 {
31136 dprintf (3, ("deleting %Ix-%Ix-%Ix", (size_t)seg, heap_segment_allocated (seg), heap_segment_reserved (seg)));
31137 delete_heap_segment (seg, (GCConfig::GetRetainVM() != 0));
31138 heap_segment_next (prev_seg) = next_seg;
31139 }
31140 else
31141 {
31142 prev_seg = seg;
31143 }
31144
31145 seg = next_seg;
31146 }
31147}
31148
31149uint8_t* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
31150{
31151 return
31152 (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
31153}
31154
31155void gc_heap::set_mem_verify (uint8_t* start, uint8_t* end, uint8_t b)
31156{
31157#ifdef VERIFY_HEAP
31158 if (end > start)
31159 {
31160 if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
31161 !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_MEM_FILL))
31162 {
31163 dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
31164 memset (start, b, (end - start));
31165 }
31166 }
31167#endif //VERIFY_HEAP
31168}
31169
31170void gc_heap::generation_delete_heap_segment (generation* gen,
31171 heap_segment* seg,
31172 heap_segment* prev_seg,
31173 heap_segment* next_seg)
31174{
31175 dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
31176 if (gen == large_object_generation)
31177 {
31178 dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
31179
31180 // We cannot thread segs in here onto freeable_large_heap_segment because
31181 // grow_brick_card_tables could be committing mark array which needs to read
31182 // the seg list. So we delay it till next time we suspend EE.
31183 seg->flags |= heap_segment_flags_loh_delete;
31184 // Since we will be decommitting the seg, we need to prevent heap verification
31185 // to verify this segment.
31186 heap_segment_allocated (seg) = heap_segment_mem (seg);
31187 }
31188 else
31189 {
31190 if (seg == ephemeral_heap_segment)
31191 {
31192 FATAL_GC_ERROR();
31193 }
31194
31195 heap_segment_next (next_seg) = prev_seg;
31196
31197 dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
31198 heap_segment_next (seg) = freeable_small_heap_segment;
31199 freeable_small_heap_segment = seg;
31200 }
31201
31202 decommit_heap_segment (seg);
31203 seg->flags |= heap_segment_flags_decommitted;
31204
31205 set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
31206}
31207
31208void gc_heap::process_background_segment_end (heap_segment* seg,
31209 generation* gen,
31210 uint8_t* last_plug_end,
31211 heap_segment* start_seg,
31212 BOOL* delete_p)
31213{
31214 *delete_p = FALSE;
31215 uint8_t* allocated = heap_segment_allocated (seg);
31216 uint8_t* background_allocated = heap_segment_background_allocated (seg);
31217 BOOL loh_p = heap_segment_loh_p (seg);
31218
31219 dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)",
31220 (size_t)heap_segment_mem (seg), background_allocated, allocated));
31221
31222 if (!loh_p && (allocated != background_allocated))
31223 {
31224 assert (gen != large_object_generation);
31225
31226 dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[",
31227 (size_t)last_plug_end, background_allocated));
31228 thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
31229
31230
31231 fix_brick_to_highest (last_plug_end, background_allocated);
31232
31233 // When we allowed fgc's during going through gaps, we could have erased the brick
31234 // that corresponds to bgc_allocated 'cause we had to update the brick there,
31235 // recover it here.
31236 fix_brick_to_highest (background_allocated, background_allocated);
31237 }
31238 else
31239 {
31240 // by default, if allocated == background_allocated, it can't
31241 // be the ephemeral segment.
31242 if (seg == ephemeral_heap_segment)
31243 {
31244 FATAL_GC_ERROR();
31245 }
31246
31247 if (allocated == heap_segment_mem (seg))
31248 {
31249 // this can happen with LOH segments when multiple threads
31250 // allocate new segments and not all of them were needed to
31251 // satisfy allocation requests.
31252 assert (gen == large_object_generation);
31253 }
31254
31255 if (last_plug_end == heap_segment_mem (seg))
31256 {
31257 dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
31258 (size_t)allocated, (*delete_p ? "should" : "should not")));
31259
31260 if (seg != start_seg)
31261 {
31262 *delete_p = TRUE;
31263 }
31264 }
31265 else
31266 {
31267 dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
31268 heap_segment_allocated (seg) = last_plug_end;
31269 set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
31270
31271 decommit_heap_segment_pages (seg, 0);
31272 }
31273 }
31274
31275 dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
31276 bgc_verify_mark_array_cleared (seg);
31277}
31278
31279void gc_heap::process_n_background_segments (heap_segment* seg,
31280 heap_segment* prev_seg,
31281 generation* gen)
31282{
31283 assert (gen != large_object_generation);
31284
31285 while (seg)
31286 {
31287 dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
31288 heap_segment* next_seg = heap_segment_next (seg);
31289
31290 if (heap_segment_read_only_p (seg))
31291 {
31292 prev_seg = seg;
31293 }
31294 else
31295 {
31296 if (heap_segment_allocated (seg) == heap_segment_mem (seg))
31297 {
31298 // This can happen - if we have a LOH segment where nothing survived
31299 // or a SOH segment allocated by a gen1 GC when BGC was going where
31300 // nothing survived last time we did a gen1 GC.
31301 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
31302 }
31303 else
31304 {
31305 prev_seg = seg;
31306 }
31307 }
31308
31309 verify_soh_segment_list();
31310 seg = next_seg;
31311 }
31312}
31313
31314inline
31315BOOL gc_heap::fgc_should_consider_object (uint8_t* o,
31316 heap_segment* seg,
31317 BOOL consider_bgc_mark_p,
31318 BOOL check_current_sweep_p,
31319 BOOL check_saved_sweep_p)
31320{
31321 // the logic for this function must be kept in sync with the analogous function
31322 // in ToolBox\SOS\Strike\gc.cpp
31323
31324 // TRUE means we don't need to check the bgc mark bit
31325 // FALSE means we do.
31326 BOOL no_bgc_mark_p = FALSE;
31327
31328 if (consider_bgc_mark_p)
31329 {
31330 if (check_current_sweep_p && (o < current_sweep_pos))
31331 {
31332 dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
31333 no_bgc_mark_p = TRUE;
31334 }
31335
31336 if (!no_bgc_mark_p)
31337 {
31338 if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
31339 {
31340 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
31341 no_bgc_mark_p = TRUE;
31342 }
31343
31344 if (!check_saved_sweep_p)
31345 {
31346 uint8_t* background_allocated = heap_segment_background_allocated (seg);
31347 // if this was the saved ephemeral segment, check_saved_sweep_p
31348 // would've been true.
31349 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
31350 // background_allocated could be 0 for the new segments acquired during bgc
31351 // sweep and we still want no_bgc_mark_p to be true.
31352 if (o >= background_allocated)
31353 {
31354 dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
31355 no_bgc_mark_p = TRUE;
31356 }
31357 }
31358 }
31359 }
31360 else
31361 {
31362 no_bgc_mark_p = TRUE;
31363 }
31364
31365 dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
31366 return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
31367}
31368
31369// consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
31370// if it's TRUE, check_current_sweep_p tells you if you should consider the
31371// current sweep position or not.
31372void gc_heap::should_check_bgc_mark (heap_segment* seg,
31373 BOOL* consider_bgc_mark_p,
31374 BOOL* check_current_sweep_p,
31375 BOOL* check_saved_sweep_p)
31376{
31377 // the logic for this function must be kept in sync with the analogous function
31378 // in ToolBox\SOS\Strike\gc.cpp
31379 *consider_bgc_mark_p = FALSE;
31380 *check_current_sweep_p = FALSE;
31381 *check_saved_sweep_p = FALSE;
31382
31383 if (current_c_gc_state == c_gc_state_planning)
31384 {
31385 // We are doing the current_sweep_pos comparison here because we have yet to
31386 // turn on the swept flag for the segment but in_range_for_segment will return
31387 // FALSE if the address is the same as reserved.
31388 if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
31389 {
31390 dprintf (3, ("seg %Ix is already swept by bgc", seg));
31391 }
31392 else
31393 {
31394 *consider_bgc_mark_p = TRUE;
31395
31396 dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
31397
31398 if (seg == saved_sweep_ephemeral_seg)
31399 {
31400 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
31401 *check_saved_sweep_p = TRUE;
31402 }
31403
31404 if (in_range_for_segment (current_sweep_pos, seg))
31405 {
31406 dprintf (3, ("current sweep pos is %Ix and within seg %Ix",
31407 current_sweep_pos, seg));
31408 *check_current_sweep_p = TRUE;
31409 }
31410 }
31411 }
31412}
31413
31414void gc_heap::background_ephemeral_sweep()
31415{
31416 dprintf (3, ("bgc ephemeral sweep"));
31417
31418 int align_const = get_alignment_constant (TRUE);
31419
31420 saved_sweep_ephemeral_seg = ephemeral_heap_segment;
31421 saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
31422
31423 // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
31424 // we thread onto a list first then publish it when we are done.
31425 allocator youngest_free_list;
31426 size_t youngest_free_list_space = 0;
31427 size_t youngest_free_obj_space = 0;
31428
31429 youngest_free_list.clear();
31430
31431 for (int i = 0; i <= (max_generation - 1); i++)
31432 {
31433 generation* gen_to_reset = generation_of (i);
31434 assert (generation_free_list_space (gen_to_reset) == 0);
31435 // Can only assert free_list_space is 0, not free_obj_space as the allocator could have added
31436 // something there.
31437 }
31438
31439 for (int i = (max_generation - 1); i >= 0; i--)
31440 {
31441 generation* current_gen = generation_of (i);
31442 uint8_t* o = generation_allocation_start (current_gen);
31443 //Skip the generation gap object
31444 o = o + Align(size (o), align_const);
31445 uint8_t* end = ((i > 0) ?
31446 generation_allocation_start (generation_of (i - 1)) :
31447 heap_segment_allocated (ephemeral_heap_segment));
31448
31449 uint8_t* plug_end = o;
31450 uint8_t* plug_start = o;
31451 BOOL marked_p = FALSE;
31452
31453 while (o < end)
31454 {
31455 marked_p = background_object_marked (o, TRUE);
31456 if (marked_p)
31457 {
31458 plug_start = o;
31459 size_t plug_size = plug_start - plug_end;
31460
31461 if (i >= 1)
31462 {
31463 thread_gap (plug_end, plug_size, current_gen);
31464 }
31465 else
31466 {
31467 if (plug_size > 0)
31468 {
31469 make_unused_array (plug_end, plug_size);
31470 if (plug_size >= min_free_list)
31471 {
31472 youngest_free_list_space += plug_size;
31473 youngest_free_list.thread_item (plug_end, plug_size);
31474 }
31475 else
31476 {
31477 youngest_free_obj_space += plug_size;
31478 }
31479 }
31480 }
31481
31482 fix_brick_to_highest (plug_end, plug_start);
31483 fix_brick_to_highest (plug_start, plug_start);
31484
31485 BOOL m = TRUE;
31486 while (m)
31487 {
31488 o = o + Align (size (o), align_const);
31489 if (o >= end)
31490 {
31491 break;
31492 }
31493
31494 m = background_object_marked (o, TRUE);
31495 }
31496 plug_end = o;
31497 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31498 }
31499 else
31500 {
31501 while ((o < end) && !background_object_marked (o, FALSE))
31502 {
31503 o = o + Align (size (o), align_const);
31504 }
31505 }
31506 }
31507
31508 if (plug_end != end)
31509 {
31510 if (i >= 1)
31511 {
31512 thread_gap (plug_end, end - plug_end, current_gen);
31513 fix_brick_to_highest (plug_end, end);
31514 }
31515 else
31516 {
31517 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
31518 // the following line is temporary.
31519 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
31520#ifdef VERIFY_HEAP
31521 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
31522 {
31523 make_unused_array (plug_end, (end - plug_end));
31524 }
31525#endif //VERIFY_HEAP
31526 }
31527 }
31528
31529 dd_fragmentation (dynamic_data_of (i)) =
31530 generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
31531 }
31532
31533 generation* youngest_gen = generation_of (0);
31534 generation_free_list_space (youngest_gen) = youngest_free_list_space;
31535 generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
31536 dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
31537 generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
31538}
31539
31540void gc_heap::background_sweep()
31541{
31542 generation* gen = generation_of (max_generation);
31543 dynamic_data* dd = dynamic_data_of (max_generation);
31544 // For SOH segments we go backwards.
31545 heap_segment* start_seg = ephemeral_heap_segment;
31546 PREFIX_ASSUME(start_seg != NULL);
31547 heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31548 heap_segment* seg = start_seg;
31549 uint8_t* o = heap_segment_mem (seg);
31550
31551 heap_segment* prev_seg = heap_segment_next (seg);
31552 int align_const = get_alignment_constant (TRUE);
31553 if (seg == fseg)
31554 {
31555 assert (o == generation_allocation_start (generation_of (max_generation)));
31556 o = o + Align(size (o), align_const);
31557 }
31558
31559 uint8_t* plug_end = o;
31560 uint8_t* plug_start = o;
31561 next_sweep_obj = o;
31562 current_sweep_pos = o;
31563
31564 //uint8_t* end = background_next_end (seg, (gen == large_object_generation));
31565 uint8_t* end = heap_segment_background_allocated (seg);
31566 BOOL delete_p = FALSE;
31567
31568 //concurrent_print_time_delta ("finished with mark and start with sweep");
31569 concurrent_print_time_delta ("Sw");
31570 dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31571
31572 //block concurrent allocation for large objects
31573 dprintf (3, ("lh state: planning"));
31574 if (gc_lh_block_event.IsValid())
31575 {
31576 gc_lh_block_event.Reset();
31577 }
31578
31579 for (int i = 0; i <= (max_generation + 1); i++)
31580 {
31581 generation* gen_to_reset = generation_of (i);
31582 generation_allocator (gen_to_reset)->clear();
31583 generation_free_list_space (gen_to_reset) = 0;
31584 generation_free_obj_space (gen_to_reset) = 0;
31585 generation_free_list_allocated (gen_to_reset) = 0;
31586 generation_end_seg_allocated (gen_to_reset) = 0;
31587 generation_condemned_allocated (gen_to_reset) = 0;
31588 //reset the allocation so foreground gc can allocate into older generation
31589 generation_allocation_pointer (gen_to_reset)= 0;
31590 generation_allocation_limit (gen_to_reset) = 0;
31591 generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
31592 }
31593
31594 FIRE_EVENT(BGC2ndNonConEnd);
31595
31596 loh_alloc_thread_count = 0;
31597 current_bgc_state = bgc_sweep_soh;
31598 verify_soh_segment_list();
31599
31600#ifdef FEATURE_BASICFREEZE
31601 if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
31602 ro_segments_in_range)
31603 {
31604 sweep_ro_segments (generation_start_segment (gen));
31605 }
31606#endif // FEATURE_BASICFREEZE
31607
31608 //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
31609 if (current_c_gc_state != c_gc_state_planning)
31610 {
31611 current_c_gc_state = c_gc_state_planning;
31612 }
31613
31614 concurrent_print_time_delta ("Swe");
31615
31616 heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
31617 PREFIX_ASSUME(loh_seg != NULL);
31618 while (loh_seg )
31619 {
31620 loh_seg->flags &= ~heap_segment_flags_swept;
31621 heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
31622 loh_seg = heap_segment_next_rw (loh_seg);
31623 }
31624
31625#ifdef MULTIPLE_HEAPS
31626 bgc_t_join.join(this, gc_join_restart_ee);
31627 if (bgc_t_join.joined())
31628#endif //MULTIPLE_HEAPS
31629 {
31630#ifdef MULTIPLE_HEAPS
31631 dprintf(2, ("Starting BGC threads for resuming EE"));
31632 bgc_t_join.restart();
31633#endif //MULTIPLE_HEAPS
31634 }
31635
31636 if (heap_number == 0)
31637 {
31638 restart_EE ();
31639 }
31640
31641 FIRE_EVENT(BGC2ndConBegin);
31642
31643 background_ephemeral_sweep();
31644
31645 concurrent_print_time_delta ("Swe eph");
31646
31647#ifdef MULTIPLE_HEAPS
31648 bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
31649 if (bgc_t_join.joined())
31650#endif //MULTIPLE_HEAPS
31651 {
31652#ifdef FEATURE_EVENT_TRACE
31653 bgc_heap_walk_for_etw_p = GCEventStatus::IsEnabled(GCEventProvider_Default,
31654 GCEventKeyword_GCHeapSurvivalAndMovement,
31655 GCEventLevel_Information);
31656#endif //FEATURE_EVENT_TRACE
31657
31658 leave_spin_lock (&gc_lock);
31659
31660#ifdef MULTIPLE_HEAPS
31661 dprintf(2, ("Starting BGC threads for BGC sweeping"));
31662 bgc_t_join.restart();
31663#endif //MULTIPLE_HEAPS
31664 }
31665
31666 disable_preemptive (true);
31667
31668 dprintf (2, ("bgs: sweeping gen2 objects"));
31669 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31670 (size_t)heap_segment_mem (seg),
31671 (size_t)heap_segment_allocated (seg),
31672 (size_t)heap_segment_background_allocated (seg)));
31673
31674 int num_objs = 256;
31675 int current_num_objs = 0;
31676 heap_segment* next_seg = 0;
31677
31678 while (1)
31679 {
31680 if (o >= end)
31681 {
31682 if (gen == large_object_generation)
31683 {
31684 next_seg = heap_segment_next (seg);
31685 }
31686 else
31687 {
31688 next_seg = heap_segment_prev (fseg, seg);
31689 }
31690
31691 delete_p = FALSE;
31692
31693 if (!heap_segment_read_only_p (seg))
31694 {
31695 if (gen == large_object_generation)
31696 {
31697 // we can treat all LOH segments as in the bgc domain
31698 // regardless of whether we saw in bgc mark or not
31699 // because we don't allow LOH allocations during bgc
31700 // sweep anyway - the LOH segments can't change.
31701 process_background_segment_end (seg, gen, plug_end,
31702 start_seg, &delete_p);
31703 }
31704 else
31705 {
31706 assert (heap_segment_background_allocated (seg) != 0);
31707 process_background_segment_end (seg, gen, plug_end,
31708 start_seg, &delete_p);
31709
31710 assert (next_seg || !delete_p);
31711 }
31712 }
31713
31714 if (delete_p)
31715 {
31716 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
31717 }
31718 else
31719 {
31720 prev_seg = seg;
31721 dprintf (2, ("seg %Ix has been swept", seg));
31722 seg->flags |= heap_segment_flags_swept;
31723 }
31724
31725 verify_soh_segment_list();
31726
31727 seg = next_seg;
31728
31729 dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
31730
31731 if (seg == 0)
31732 {
31733 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
31734
31735 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
31736
31737 if (gen != large_object_generation)
31738 {
31739 dprintf (2, ("bgs: sweeping gen3 objects"));
31740 concurrent_print_time_delta ("Swe SOH");
31741 FIRE_EVENT(BGC1stSweepEnd, 0);
31742
31743 enter_spin_lock (&more_space_lock_loh);
31744 add_saved_spinlock_info (true, me_acquire, mt_bgc_loh_sweep);
31745
31746 concurrent_print_time_delta ("Swe LOH took msl");
31747
31748 // We wait till all allocating threads are completely done.
31749 int spin_count = yp_spin_count_unit;
31750 while (loh_alloc_thread_count)
31751 {
31752 spin_and_switch (spin_count, (loh_alloc_thread_count == 0));
31753 }
31754
31755 current_bgc_state = bgc_sweep_loh;
31756 gen = generation_of (max_generation+1);
31757 start_seg = heap_segment_rw (generation_start_segment (gen));
31758
31759 PREFIX_ASSUME(start_seg != NULL);
31760
31761 seg = start_seg;
31762 prev_seg = 0;
31763 o = generation_allocation_start (gen);
31764 assert (method_table (o) == g_gc_pFreeObjectMethodTable);
31765 align_const = get_alignment_constant (FALSE);
31766 o = o + Align(size (o), align_const);
31767 plug_end = o;
31768 end = heap_segment_allocated (seg);
31769 dprintf (2, ("sweeping gen3 objects"));
31770 generation_free_obj_space (gen) = 0;
31771 generation_allocator (gen)->clear();
31772 generation_free_list_space (gen) = 0;
31773
31774 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31775 (size_t)heap_segment_mem (seg),
31776 (size_t)heap_segment_allocated (seg),
31777 (size_t)heap_segment_background_allocated (seg)));
31778 }
31779 else
31780 break;
31781 }
31782 else
31783 {
31784 o = heap_segment_mem (seg);
31785 if (seg == fseg)
31786 {
31787 assert (gen != large_object_generation);
31788 assert (o == generation_allocation_start (generation_of (max_generation)));
31789 align_const = get_alignment_constant (TRUE);
31790 o = o + Align(size (o), align_const);
31791 }
31792
31793 plug_end = o;
31794 current_sweep_pos = o;
31795 next_sweep_obj = o;
31796
31797 allow_fgc();
31798 end = background_next_end (seg, (gen == large_object_generation));
31799 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
31800 (size_t)heap_segment_mem (seg),
31801 (size_t)heap_segment_allocated (seg),
31802 (size_t)heap_segment_background_allocated (seg)));
31803 }
31804 }
31805
31806 if ((o < end) && background_object_marked (o, TRUE))
31807 {
31808 plug_start = o;
31809 if (gen == large_object_generation)
31810 {
31811 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
31812 }
31813
31814 thread_gap (plug_end, plug_start-plug_end, gen);
31815 if (gen != large_object_generation)
31816 {
31817 add_gen_free (max_generation, plug_start-plug_end);
31818 fix_brick_to_highest (plug_end, plug_start);
31819 // we need to fix the brick for the next plug here 'cause an FGC can
31820 // happen and can't read a stale brick.
31821 fix_brick_to_highest (plug_start, plug_start);
31822 }
31823
31824 BOOL m = TRUE;
31825
31826 while (m)
31827 {
31828 next_sweep_obj = o + Align(size (o), align_const);
31829 current_num_objs++;
31830 if (current_num_objs >= num_objs)
31831 {
31832 current_sweep_pos = next_sweep_obj;
31833
31834 allow_fgc();
31835 current_num_objs = 0;
31836 }
31837
31838 o = next_sweep_obj;
31839 if (o >= end)
31840 {
31841 break;
31842 }
31843
31844 m = background_object_marked (o, TRUE);
31845 }
31846 plug_end = o;
31847 if (gen != large_object_generation)
31848 {
31849 add_gen_plug (max_generation, plug_end-plug_start);
31850 dd_survived_size (dd) += (plug_end - plug_start);
31851 }
31852 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31853 }
31854 else
31855 {
31856 while ((o < end) && !background_object_marked (o, FALSE))
31857 {
31858 next_sweep_obj = o + Align(size (o), align_const);;
31859 current_num_objs++;
31860 if (current_num_objs >= num_objs)
31861 {
31862 current_sweep_pos = plug_end;
31863 dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
31864 allow_fgc();
31865 current_num_objs = 0;
31866 }
31867
31868 o = next_sweep_obj;
31869 }
31870 }
31871 }
31872
31873 size_t total_loh_size = generation_size (max_generation + 1);
31874 size_t total_soh_size = generation_sizes (generation_of (max_generation));
31875
31876 dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
31877
31878 dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id",
31879 generation_free_list_space (generation_of (max_generation)),
31880 generation_free_obj_space (generation_of (max_generation))));
31881 dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id",
31882 heap_number,
31883 generation_free_list_space (generation_of (max_generation + 1)),
31884 generation_free_obj_space (generation_of (max_generation + 1))));
31885
31886 FIRE_EVENT(BGC2ndConEnd);
31887 concurrent_print_time_delta ("background sweep");
31888
31889 heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31890 PREFIX_ASSUME(reset_seg != NULL);
31891
31892 while (reset_seg)
31893 {
31894 heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
31895 heap_segment_background_allocated (reset_seg) = 0;
31896 reset_seg = heap_segment_next_rw (reset_seg);
31897 }
31898
31899 generation* loh_gen = generation_of (max_generation + 1);
31900 generation_allocation_segment (loh_gen) = heap_segment_rw (generation_start_segment (loh_gen));
31901
31902 // We calculate dynamic data here because if we wait till we signal the lh event,
31903 // the allocation thread can change the fragmentation and we may read an intermediate
31904 // value (which can be greater than the generation size). Plus by that time it won't
31905 // be accurate.
31906 compute_new_dynamic_data (max_generation);
31907
31908 enable_preemptive ();
31909
31910#ifdef MULTIPLE_HEAPS
31911 bgc_t_join.join(this, gc_join_set_state_free);
31912 if (bgc_t_join.joined())
31913#endif //MULTIPLE_HEAPS
31914 {
31915 // TODO: We are using this join just to set the state. Should
31916 // look into eliminating it - check to make sure things that use
31917 // this state can live with per heap state like should_check_bgc_mark.
31918 current_c_gc_state = c_gc_state_free;
31919
31920#ifdef MULTIPLE_HEAPS
31921 dprintf(2, ("Starting BGC threads after background sweep phase"));
31922 bgc_t_join.restart();
31923#endif //MULTIPLE_HEAPS
31924 }
31925
31926 disable_preemptive (true);
31927
31928 if (gc_lh_block_event.IsValid())
31929 {
31930 gc_lh_block_event.Set();
31931 }
31932
31933 add_saved_spinlock_info (true, me_release, mt_bgc_loh_sweep);
31934 leave_spin_lock (&more_space_lock_loh);
31935
31936 //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31937 dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
31938}
31939#endif //BACKGROUND_GC
31940
31941void gc_heap::sweep_large_objects ()
31942{
31943 //this min value is for the sake of the dynamic tuning.
31944 //so we know that we are not starting even if we have no
31945 //survivors.
31946 generation* gen = large_object_generation;
31947 heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
31948
31949 PREFIX_ASSUME(start_seg != NULL);
31950
31951 heap_segment* seg = start_seg;
31952 heap_segment* prev_seg = 0;
31953 uint8_t* o = generation_allocation_start (gen);
31954 int align_const = get_alignment_constant (FALSE);
31955
31956 //Skip the generation gap object
31957 o = o + Align(size (o), align_const);
31958
31959 uint8_t* plug_end = o;
31960 uint8_t* plug_start = o;
31961
31962 generation_allocator (gen)->clear();
31963 generation_free_list_space (gen) = 0;
31964 generation_free_obj_space (gen) = 0;
31965
31966
31967 dprintf (3, ("sweeping large objects"));
31968 dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix",
31969 (size_t)seg,
31970 (size_t)heap_segment_mem (seg),
31971 (size_t)heap_segment_allocated (seg),
31972 o));
31973
31974 while (1)
31975 {
31976 if (o >= heap_segment_allocated (seg))
31977 {
31978 heap_segment* next_seg = heap_segment_next (seg);
31979 //delete the empty segment if not the only one
31980 if ((plug_end == heap_segment_mem (seg)) &&
31981 (seg != start_seg) && !heap_segment_read_only_p (seg))
31982 {
31983 //prepare for deletion
31984 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
31985 assert (prev_seg);
31986 heap_segment_next (prev_seg) = next_seg;
31987 heap_segment_next (seg) = freeable_large_heap_segment;
31988 freeable_large_heap_segment = seg;
31989 }
31990 else
31991 {
31992 if (!heap_segment_read_only_p (seg))
31993 {
31994 dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
31995 heap_segment_allocated (seg) = plug_end;
31996 decommit_heap_segment_pages (seg, 0);
31997 }
31998 prev_seg = seg;
31999 }
32000 seg = next_seg;
32001 if (seg == 0)
32002 break;
32003 else
32004 {
32005 o = heap_segment_mem (seg);
32006 plug_end = o;
32007 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
32008 (size_t)heap_segment_mem (seg),
32009 (size_t)heap_segment_allocated (seg)));
32010 }
32011 }
32012 if (large_object_marked(o, TRUE))
32013 {
32014 plug_start = o;
32015 //everything between plug_end and plug_start is free
32016 thread_gap (plug_end, plug_start-plug_end, gen);
32017
32018 BOOL m = TRUE;
32019 while (m)
32020 {
32021 o = o + AlignQword (size (o));
32022 if (o >= heap_segment_allocated (seg))
32023 {
32024 break;
32025 }
32026 m = large_object_marked (o, TRUE);
32027 }
32028 plug_end = o;
32029 dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
32030 }
32031 else
32032 {
32033 while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
32034 {
32035 o = o + AlignQword (size (o));
32036 }
32037 }
32038 }
32039
32040 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
32041
32042 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
32043}
32044
32045void gc_heap::relocate_in_large_objects ()
32046{
32047 relocate_args args;
32048 args.low = gc_low;
32049 args.high = gc_high;
32050 args.last_plug = 0;
32051
32052 generation* gen = large_object_generation;
32053
32054 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32055
32056 PREFIX_ASSUME(seg != NULL);
32057
32058 uint8_t* o = generation_allocation_start (gen);
32059
32060 while (1)
32061 {
32062 if (o >= heap_segment_allocated (seg))
32063 {
32064 seg = heap_segment_next_rw (seg);
32065 if (seg == 0)
32066 break;
32067 else
32068 {
32069 o = heap_segment_mem (seg);
32070 }
32071 }
32072 while (o < heap_segment_allocated (seg))
32073 {
32074 check_class_object_demotion (o);
32075 if (contain_pointers (o))
32076 {
32077 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
32078 go_through_object_nostart (method_table (o), o, size(o), pval,
32079 {
32080 reloc_survivor_helper (pval);
32081 });
32082 }
32083 o = o + AlignQword (size (o));
32084 }
32085 }
32086}
32087
32088void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
32089 BOOL relocating)
32090{
32091 uint8_t* low = gc_low;
32092 size_t end_card = 0;
32093 generation* oldest_gen = generation_of (max_generation+1);
32094 heap_segment* seg = heap_segment_rw (generation_start_segment (oldest_gen));
32095
32096 PREFIX_ASSUME(seg != NULL);
32097
32098 uint8_t* beg = generation_allocation_start (oldest_gen);
32099 uint8_t* end = heap_segment_allocated (seg);
32100
32101 size_t cg_pointers_found = 0;
32102
32103 size_t card_word_end = (card_of (align_on_card_word (end)) /
32104 card_word_width);
32105
32106 size_t n_eph = 0;
32107 size_t n_gen = 0;
32108 size_t n_card_set = 0;
32109 uint8_t* next_boundary = (relocating ?
32110 generation_plan_allocation_start (generation_of (max_generation -1)) :
32111 ephemeral_low);
32112
32113 uint8_t* nhigh = (relocating ?
32114 heap_segment_plan_allocated (ephemeral_heap_segment) :
32115 ephemeral_high);
32116
32117 BOOL foundp = FALSE;
32118 uint8_t* start_address = 0;
32119 uint8_t* limit = 0;
32120 size_t card = card_of (beg);
32121 uint8_t* o = beg;
32122#ifdef BACKGROUND_GC
32123 BOOL consider_bgc_mark_p = FALSE;
32124 BOOL check_current_sweep_p = FALSE;
32125 BOOL check_saved_sweep_p = FALSE;
32126 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32127#endif //BACKGROUND_GC
32128
32129 size_t total_cards_cleared = 0;
32130
32131 //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
32132 dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
32133 while (1)
32134 {
32135 if ((o < end) && (card_of(o) > card))
32136 {
32137 dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
32138 if (cg_pointers_found == 0)
32139 {
32140 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
32141 clear_cards (card, card_of((uint8_t*)o));
32142 total_cards_cleared += (card_of((uint8_t*)o) - card);
32143 }
32144 n_eph +=cg_pointers_found;
32145 cg_pointers_found = 0;
32146 card = card_of ((uint8_t*)o);
32147 }
32148 if ((o < end) &&(card >= end_card))
32149 {
32150 foundp = find_card (card_table, card, card_word_end, end_card);
32151 if (foundp)
32152 {
32153 n_card_set+= end_card - card;
32154 start_address = max (beg, card_address (card));
32155 }
32156 limit = min (end, card_address (end_card));
32157 }
32158 if ((!foundp) || (o >= end) || (card_address (card) >= end))
32159 {
32160 if ((foundp) && (cg_pointers_found == 0))
32161 {
32162 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
32163 (size_t)card_address(card+1)));
32164 clear_cards (card, card+1);
32165 total_cards_cleared += 1;
32166 }
32167 n_eph +=cg_pointers_found;
32168 cg_pointers_found = 0;
32169 if ((seg = heap_segment_next_rw (seg)) != 0)
32170 {
32171#ifdef BACKGROUND_GC
32172 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32173#endif //BACKGROUND_GC
32174 beg = heap_segment_mem (seg);
32175 end = compute_next_end (seg, low);
32176 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
32177 card = card_of (beg);
32178 o = beg;
32179 end_card = 0;
32180 continue;
32181 }
32182 else
32183 {
32184 break;
32185 }
32186 }
32187
32188 assert (card_set_p (card));
32189 {
32190 dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
32191 card, (size_t)o, (size_t)limit));
32192
32193 assert (Align (size (o)) >= Align (min_obj_size));
32194 size_t s = size (o);
32195 uint8_t* next_o = o + AlignQword (s);
32196 Prefetch (next_o);
32197
32198 while (o < limit)
32199 {
32200 s = size (o);
32201 assert (Align (s) >= Align (min_obj_size));
32202 next_o = o + AlignQword (s);
32203 Prefetch (next_o);
32204
32205 dprintf (4, ("|%Ix|", (size_t)o));
32206 if (next_o < start_address)
32207 {
32208 goto end_object;
32209 }
32210
32211#ifdef BACKGROUND_GC
32212 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
32213 {
32214 goto end_object;
32215 }
32216#endif //BACKGROUND_GC
32217
32218#ifdef COLLECTIBLE_CLASS
32219 if (is_collectible(o))
32220 {
32221 BOOL passed_end_card_p = FALSE;
32222
32223 if (card_of (o) > card)
32224 {
32225 passed_end_card_p = card_transition (o, end, card_word_end,
32226 cg_pointers_found,
32227 n_eph, n_card_set,
32228 card, end_card,
32229 foundp, start_address,
32230 limit, total_cards_cleared);
32231 }
32232
32233 if ((!passed_end_card_p || foundp) && (card_of (o) == card))
32234 {
32235 // card is valid and it covers the head of the object
32236 if (fn == &gc_heap::relocate_address)
32237 {
32238 keep_card_live (o, n_gen, cg_pointers_found);
32239 }
32240 else
32241 {
32242 uint8_t* class_obj = get_class_object (o);
32243 mark_through_cards_helper (&class_obj, n_gen,
32244 cg_pointers_found, fn,
32245 nhigh, next_boundary);
32246 }
32247 }
32248
32249 if (passed_end_card_p)
32250 {
32251 if (foundp && (card_address (card) < next_o))
32252 {
32253 goto go_through_refs;
32254 }
32255 else
32256 {
32257 goto end_object;
32258 }
32259 }
32260 }
32261
32262go_through_refs:
32263#endif //COLLECTIBLE_CLASS
32264
32265 if (contain_pointers (o))
32266 {
32267 dprintf(3,("Going through %Ix", (size_t)o));
32268
32269 go_through_object (method_table(o), o, s, poo,
32270 start_address, use_start, (o + s),
32271 {
32272 if (card_of ((uint8_t*)poo) > card)
32273 {
32274 BOOL passed_end_card_p = card_transition ((uint8_t*)poo, end,
32275 card_word_end,
32276 cg_pointers_found,
32277 n_eph, n_card_set,
32278 card, end_card,
32279 foundp, start_address,
32280 limit, total_cards_cleared);
32281
32282 if (passed_end_card_p)
32283 {
32284 if (foundp && (card_address (card) < next_o))
32285 {
32286 //new_start();
32287 {
32288 if (ppstop <= (uint8_t**)start_address)
32289 {break;}
32290 else if (poo < (uint8_t**)start_address)
32291 {poo = (uint8_t**)start_address;}
32292 }
32293 }
32294 else
32295 {
32296 goto end_object;
32297 }
32298 }
32299 }
32300
32301 mark_through_cards_helper (poo, n_gen,
32302 cg_pointers_found, fn,
32303 nhigh, next_boundary);
32304 }
32305 );
32306 }
32307
32308 end_object:
32309 o = next_o;
32310 }
32311
32312 }
32313 }
32314
32315 // compute the efficiency ratio of the card table
32316 if (!relocating)
32317 {
32318 generation_skip_ratio = min (((n_eph > 800) ?
32319 (int)(((float)n_gen / (float)n_eph) * 100) : 100),
32320 generation_skip_ratio);
32321
32322 dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d",
32323 n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
32324 }
32325 else
32326 {
32327 dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d",
32328 n_eph, n_gen, n_card_set, generation_skip_ratio));
32329 }
32330}
32331
32332void gc_heap::descr_segment (heap_segment* seg )
32333{
32334#ifdef TRACE_GC
32335 uint8_t* x = heap_segment_mem (seg);
32336 while (x < heap_segment_allocated (seg))
32337 {
32338 dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
32339 x = x + Align(size (x));
32340 }
32341#else // TRACE_GC
32342 UNREFERENCED_PARAMETER(seg);
32343#endif // TRACE_GC
32344}
32345
32346void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
32347{
32348#ifdef MULTIPLE_HEAPS
32349 int n_heaps = g_theGCHeap->GetNumberOfHeaps ();
32350 for (int i = 0; i < n_heaps; i++)
32351 {
32352 gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
32353#else //MULTIPLE_HEAPS
32354 {
32355 gc_heap* hp = NULL;
32356#ifdef _PREFAST_
32357 // prefix complains about us dereferencing hp in wks build even though we only access static members
32358 // this way. not sure how to shut it up except for this ugly workaround:
32359 PREFIX_ASSUME(hp != NULL);
32360#endif // _PREFAST_
32361#endif //MULTIPLE_HEAPS
32362
32363 int curr_gen_number0 = max_generation+1;
32364 while (curr_gen_number0 >= 0)
32365 {
32366 generation* gen = hp->generation_of (curr_gen_number0);
32367 heap_segment* seg = generation_start_segment (gen);
32368 while (seg && (seg != hp->ephemeral_heap_segment))
32369 {
32370 assert (curr_gen_number0 > 0);
32371
32372 // report bounds from heap_segment_mem (seg) to
32373 // heap_segment_allocated (seg);
32374 // for generation # curr_gen_number0
32375 // for heap # heap_no
32376
32377 fn(context, curr_gen_number0, heap_segment_mem (seg),
32378 heap_segment_allocated (seg),
32379 curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
32380
32381 seg = heap_segment_next (seg);
32382 }
32383 if (seg)
32384 {
32385 assert (seg == hp->ephemeral_heap_segment);
32386 assert (curr_gen_number0 <= max_generation);
32387 //
32388 if (curr_gen_number0 == max_generation)
32389 {
32390 if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
32391 {
32392 // report bounds from heap_segment_mem (seg) to
32393 // generation_allocation_start (generation_of (max_generation-1))
32394 // for heap # heap_number
32395
32396 fn(context, curr_gen_number0, heap_segment_mem (seg),
32397 generation_allocation_start (hp->generation_of (max_generation-1)),
32398 generation_allocation_start (hp->generation_of (max_generation-1)) );
32399 }
32400 }
32401 else if (curr_gen_number0 != 0)
32402 {
32403 //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32404 // to generation_allocation_start (generation_of (curr_gen_number0-1))
32405 // for heap # heap_number
32406
32407 fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32408 generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
32409 generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
32410 }
32411 else
32412 {
32413 //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32414 // to heap_segment_allocated (ephemeral_heap_segment);
32415 // for heap # heap_number
32416
32417 fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32418 heap_segment_allocated (hp->ephemeral_heap_segment),
32419 heap_segment_reserved (hp->ephemeral_heap_segment) );
32420 }
32421 }
32422 curr_gen_number0--;
32423 }
32424 }
32425}
32426
32427#ifdef TRACE_GC
32428// Note that when logging is on it can take a long time to go through the free items.
32429void gc_heap::print_free_list (int gen, heap_segment* seg)
32430{
32431 UNREFERENCED_PARAMETER(gen);
32432 UNREFERENCED_PARAMETER(seg);
32433/*
32434 if (settings.concurrent == FALSE)
32435 {
32436 uint8_t* seg_start = heap_segment_mem (seg);
32437 uint8_t* seg_end = heap_segment_allocated (seg);
32438
32439 dprintf (3, ("Free list in seg %Ix:", seg_start));
32440
32441 size_t total_free_item = 0;
32442
32443 allocator* gen_allocator = generation_allocator (generation_of (gen));
32444 for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
32445 {
32446 uint8_t* fo = gen_allocator->alloc_list_head_of (b);
32447 while (fo)
32448 {
32449 if (fo >= seg_start && fo < seg_end)
32450 {
32451 total_free_item++;
32452
32453 size_t free_item_len = size(fo);
32454
32455 dprintf (3, ("[%Ix, %Ix[:%Id",
32456 (size_t)fo,
32457 (size_t)(fo + free_item_len),
32458 free_item_len));
32459 }
32460
32461 fo = free_list_slot (fo);
32462 }
32463 }
32464
32465 dprintf (3, ("total %Id free items", total_free_item));
32466 }
32467*/
32468}
32469#endif //TRACE_GC
32470
32471void gc_heap::descr_generations (BOOL begin_gc_p)
32472{
32473 UNREFERENCED_PARAMETER(begin_gc_p);
32474#ifdef STRESS_LOG
32475 if (StressLog::StressLogOn(LF_GC, LL_INFO10))
32476 {
32477 gc_heap* hp = 0;
32478#ifdef MULTIPLE_HEAPS
32479 hp= this;
32480#endif //MULTIPLE_HEAPS
32481
32482 STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
32483 for (int n = max_generation; n >= 0; --n)
32484 {
32485 STRESS_LOG4(LF_GC, LL_INFO10, " Generation %d [%p, %p] cur = %p\n",
32486 n,
32487 generation_allocation_start(generation_of(n)),
32488 generation_allocation_limit(generation_of(n)),
32489 generation_allocation_pointer(generation_of(n)));
32490
32491 heap_segment* seg = generation_start_segment(generation_of(n));
32492 while (seg)
32493 {
32494 STRESS_LOG4(LF_GC, LL_INFO10, " Segment mem %p alloc = %p used %p committed %p\n",
32495 heap_segment_mem(seg),
32496 heap_segment_allocated(seg),
32497 heap_segment_used(seg),
32498 heap_segment_committed(seg));
32499 seg = heap_segment_next(seg);
32500 }
32501 }
32502 }
32503#endif // STRESS_LOG
32504
32505#ifdef TRACE_GC
32506 dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
32507 (size_t) lowest_address, (size_t) highest_address));
32508#ifdef BACKGROUND_GC
32509 dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
32510 (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
32511#endif //BACKGROUND_GC
32512
32513 if (heap_number == 0)
32514 {
32515 dprintf (1, ("total heap size: %Id, commit size: %Id", get_total_heap_size(), get_total_committed_size()));
32516 }
32517
32518 int curr_gen_number = max_generation+1;
32519 while (curr_gen_number >= 0)
32520 {
32521 size_t total_gen_size = generation_size (curr_gen_number);
32522#ifdef SIMPLE_DPRINTF
32523 dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
32524 (begin_gc_p ? "BEG" : "END"),
32525 settings.condemned_generation,
32526 curr_gen_number,
32527 total_gen_size,
32528 dd_fragmentation (dynamic_data_of (curr_gen_number)),
32529 generation_free_list_space (generation_of (curr_gen_number)),
32530 generation_free_obj_space (generation_of (curr_gen_number)),
32531 (total_gen_size ?
32532 (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
32533 0),
32534 (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
32535 (settings.heap_expansion ? "(EX)" : " "),
32536 (settings.promotion ? "Promotion" : "NoPromotion")));
32537#else
32538 dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
32539 curr_gen_number,
32540 size (generation_allocation_start (generation_of (curr_gen_number))),
32541 total_gen_size,
32542 dd_fragmentation (dynamic_data_of (curr_gen_number))));
32543#endif //SIMPLE_DPRINTF
32544
32545 generation* gen = generation_of (curr_gen_number);
32546 heap_segment* seg = generation_start_segment (gen);
32547 while (seg && (seg != ephemeral_heap_segment))
32548 {
32549 dprintf (GTC_LOG, ("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
32550 curr_gen_number,
32551 (size_t)heap_segment_mem (seg),
32552 (size_t)heap_segment_allocated (seg),
32553 (size_t)heap_segment_committed (seg),
32554 (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
32555 (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
32556 print_free_list (curr_gen_number, seg);
32557 seg = heap_segment_next (seg);
32558 }
32559 if (seg && (seg != generation_start_segment (gen)))
32560 {
32561 dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32562 curr_gen_number,
32563 (size_t)heap_segment_mem (seg),
32564 (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
32565 print_free_list (curr_gen_number, seg);
32566
32567 }
32568 else if (seg)
32569 {
32570 dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32571 curr_gen_number,
32572 (size_t)generation_allocation_start (generation_of (curr_gen_number)),
32573 (size_t)(((curr_gen_number == 0)) ?
32574 (heap_segment_allocated
32575 (generation_start_segment
32576 (generation_of (curr_gen_number)))) :
32577 (generation_allocation_start
32578 (generation_of (curr_gen_number - 1))))
32579 ));
32580 print_free_list (curr_gen_number, seg);
32581 }
32582 curr_gen_number--;
32583 }
32584
32585#endif //TRACE_GC
32586}
32587
32588#undef TRACE_GC
32589
32590//#define TRACE_GC
32591
32592//-----------------------------------------------------------------------------
32593//
32594// VM Specific support
32595//
32596//-----------------------------------------------------------------------------
32597
32598
32599#ifdef TRACE_GC
32600
32601 unsigned int PromotedObjectCount = 0;
32602 unsigned int CreatedObjectCount = 0;
32603 unsigned int AllocDuration = 0;
32604 unsigned int AllocCount = 0;
32605 unsigned int AllocBigCount = 0;
32606 unsigned int AllocSmallCount = 0;
32607 unsigned int AllocStart = 0;
32608#endif //TRACE_GC
32609
32610//Static member variables.
32611VOLATILE(BOOL) GCHeap::GcInProgress = FALSE;
32612//GCTODO
32613//CMCSafeLock* GCHeap::fGcLock;
32614GCEvent *GCHeap::WaitForGCEvent = NULL;
32615//GCTODO
32616#ifdef TRACE_GC
32617unsigned int GCHeap::GcDuration;
32618#endif //TRACE_GC
32619unsigned GCHeap::GcCondemnedGeneration = 0;
32620size_t GCHeap::totalSurvivedSize = 0;
32621#ifdef FEATURE_PREMORTEM_FINALIZATION
32622CFinalize* GCHeap::m_Finalize = 0;
32623BOOL GCHeap::GcCollectClasses = FALSE;
32624VOLATILE(int32_t) GCHeap::m_GCFLock = 0;
32625
32626#ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32627#ifdef STRESS_HEAP
32628#ifdef BACKGROUND_GC
32629int GCHeap::gc_stress_fgcs_in_bgc = 0;
32630#endif // BACKGROUND_GC
32631#ifndef MULTIPLE_HEAPS
32632OBJECTHANDLE GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
32633int GCHeap::m_CurStressObj = 0;
32634#endif // !MULTIPLE_HEAPS
32635#endif // STRESS_HEAP
32636#endif // FEATURE_REDHAWK
32637
32638#endif //FEATURE_PREMORTEM_FINALIZATION
32639
32640class NoGCRegionLockHolder
32641{
32642public:
32643 NoGCRegionLockHolder()
32644 {
32645 enter_spin_lock_noinstru(&g_no_gc_lock);
32646 }
32647
32648 ~NoGCRegionLockHolder()
32649 {
32650 leave_spin_lock_noinstru(&g_no_gc_lock);
32651 }
32652};
32653
32654// An explanation of locking for finalization:
32655//
32656// Multiple threads allocate objects. During the allocation, they are serialized by
32657// the AllocLock above. But they release that lock before they register the object
32658// for finalization. That's because there is much contention for the alloc lock, but
32659// finalization is presumed to be a rare case.
32660//
32661// So registering an object for finalization must be protected by the FinalizeLock.
32662//
32663// There is another logical queue that involves finalization. When objects registered
32664// for finalization become unreachable, they are moved from the "registered" queue to
32665// the "unreachable" queue. Note that this only happens inside a GC, so no other
32666// threads can be manipulating either queue at that time. Once the GC is over and
32667// threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
32668// queue and call their finalizers. This dequeue operation is also protected with
32669// the finalize lock.
32670//
32671// At first, this seems unnecessary. Only one thread is ever enqueuing or dequeuing
32672// on the unreachable queue (either the GC thread during a GC or the finalizer thread
32673// when a GC is not in progress). The reason we share a lock with threads enqueuing
32674// on the "registered" queue is that the "registered" and "unreachable" queues are
32675// interrelated.
32676//
32677// They are actually two regions of a longer list, which can only grow at one end.
32678// So to enqueue an object to the "registered" list, you actually rotate an unreachable
32679// object at the boundary between the logical queues, out to the other end of the
32680// unreachable queue -- where all growing takes place. Then you move the boundary
32681// pointer so that the gap we created at the boundary is now on the "registered"
32682// side rather than the "unreachable" side. Now the object can be placed into the
32683// "registered" side at that point. This is much more efficient than doing moves
32684// of arbitrarily long regions, but it causes the two queues to require a shared lock.
32685//
32686// Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock. Instead, it relies
32687// on the fact that the lock will only be taken for a brief period and that it will
32688// never provoke or allow a GC while the lock is held. This is critical. If the
32689// FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
32690// allow a GC), then the Alloc client would have to GC protect a finalizable object
32691// to protect against that eventuality. That is too slow!
32692
32693
32694
32695BOOL IsValidObject99(uint8_t *pObject)
32696{
32697#ifdef VERIFY_HEAP
32698 if (!((CObjectHeader*)pObject)->IsFree())
32699 ((CObjectHeader *) pObject)->Validate();
32700#endif //VERIFY_HEAP
32701 return(TRUE);
32702}
32703
32704#ifdef BACKGROUND_GC
32705BOOL gc_heap::bgc_mark_array_range (heap_segment* seg,
32706 BOOL whole_seg_p,
32707 uint8_t** range_beg,
32708 uint8_t** range_end)
32709{
32710 uint8_t* seg_start = heap_segment_mem (seg);
32711 uint8_t* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
32712
32713 if ((seg_start < background_saved_highest_address) &&
32714 (seg_end > background_saved_lowest_address))
32715 {
32716 *range_beg = max (seg_start, background_saved_lowest_address);
32717 *range_end = min (seg_end, background_saved_highest_address);
32718 return TRUE;
32719 }
32720 else
32721 {
32722 return FALSE;
32723 }
32724}
32725
32726void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
32727{
32728#if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32729 if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32730 {
32731 uint8_t* range_beg = 0;
32732 uint8_t* range_end = 0;
32733
32734 if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
32735 {
32736 size_t markw = mark_word_of (range_beg);
32737 size_t markw_end = mark_word_of (range_end);
32738 while (markw < markw_end)
32739 {
32740 if (mark_array [markw])
32741 {
32742 dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
32743 markw, mark_array [markw], mark_word_address (markw)));
32744 FATAL_GC_ERROR();
32745 }
32746 markw++;
32747 }
32748 uint8_t* p = mark_word_address (markw_end);
32749 while (p < range_end)
32750 {
32751 assert (!(mark_array_marked (p)));
32752 p++;
32753 }
32754 }
32755 }
32756#endif //VERIFY_HEAP && MARK_ARRAY
32757}
32758
32759void gc_heap::verify_mark_bits_cleared (uint8_t* obj, size_t s)
32760{
32761#if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32762 size_t start_mark_bit = mark_bit_of (obj) + 1;
32763 size_t end_mark_bit = mark_bit_of (obj + s);
32764 unsigned int startbit = mark_bit_bit (start_mark_bit);
32765 unsigned int endbit = mark_bit_bit (end_mark_bit);
32766 size_t startwrd = mark_bit_word (start_mark_bit);
32767 size_t endwrd = mark_bit_word (end_mark_bit);
32768 unsigned int result = 0;
32769
32770 unsigned int firstwrd = ~(lowbits (~0, startbit));
32771 unsigned int lastwrd = ~(highbits (~0, endbit));
32772
32773 if (startwrd == endwrd)
32774 {
32775 unsigned int wrd = firstwrd & lastwrd;
32776 result = mark_array[startwrd] & wrd;
32777 if (result)
32778 {
32779 FATAL_GC_ERROR();
32780 }
32781 return;
32782 }
32783
32784 // verify the first mark word is cleared.
32785 if (startbit)
32786 {
32787 result = mark_array[startwrd] & firstwrd;
32788 if (result)
32789 {
32790 FATAL_GC_ERROR();
32791 }
32792 startwrd++;
32793 }
32794
32795 for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
32796 {
32797 result = mark_array[wrdtmp];
32798 if (result)
32799 {
32800 FATAL_GC_ERROR();
32801 }
32802 }
32803
32804 // set the last mark word.
32805 if (endbit)
32806 {
32807 result = mark_array[endwrd] & lastwrd;
32808 if (result)
32809 {
32810 FATAL_GC_ERROR();
32811 }
32812 }
32813#endif //VERIFY_HEAP && MARK_ARRAY
32814}
32815
32816void gc_heap::clear_all_mark_array()
32817{
32818#ifdef MARK_ARRAY
32819 //size_t num_dwords_written = 0;
32820 //size_t begin_time = GetHighPrecisionTimeStamp();
32821
32822 generation* gen = generation_of (max_generation);
32823 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32824
32825 while (1)
32826 {
32827 if (seg == 0)
32828 {
32829 if (gen != large_object_generation)
32830 {
32831 gen = generation_of (max_generation+1);
32832 seg = heap_segment_rw (generation_start_segment (gen));
32833 }
32834 else
32835 {
32836 break;
32837 }
32838 }
32839
32840 uint8_t* range_beg = 0;
32841 uint8_t* range_end = 0;
32842
32843 if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
32844 {
32845 size_t markw = mark_word_of (range_beg);
32846 size_t markw_end = mark_word_of (range_end);
32847 size_t size_total = (markw_end - markw) * sizeof (uint32_t);
32848 //num_dwords_written = markw_end - markw;
32849 size_t size = 0;
32850 size_t size_left = 0;
32851
32852 assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
32853
32854 if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
32855 {
32856 size = (size_total & ~(sizeof(PTR_PTR) - 1));
32857 size_left = size_total - size;
32858 assert ((size_left & (sizeof (uint32_t) - 1)) == 0);
32859 }
32860 else
32861 {
32862 size = size_total;
32863 }
32864
32865 memclr ((uint8_t*)&mark_array[markw], size);
32866
32867 if (size_left != 0)
32868 {
32869 uint32_t* markw_to_clear = &mark_array[markw + size / sizeof (uint32_t)];
32870 for (size_t i = 0; i < (size_left / sizeof (uint32_t)); i++)
32871 {
32872 *markw_to_clear = 0;
32873 markw_to_clear++;
32874 }
32875 }
32876 }
32877
32878 seg = heap_segment_next_rw (seg);
32879 }
32880
32881 //size_t end_time = GetHighPrecisionTimeStamp() - begin_time;
32882
32883 //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(uint32_t));
32884
32885#endif //MARK_ARRAY
32886}
32887
32888#endif //BACKGROUND_GC
32889
32890void gc_heap::verify_mark_array_cleared (heap_segment* seg)
32891{
32892#if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32893 assert (card_table == g_gc_card_table);
32894 size_t markw = mark_word_of (heap_segment_mem (seg));
32895 size_t markw_end = mark_word_of (heap_segment_reserved (seg));
32896
32897 while (markw < markw_end)
32898 {
32899 if (mark_array [markw])
32900 {
32901 dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
32902 markw, mark_array [markw], mark_word_address (markw)));
32903 FATAL_GC_ERROR();
32904 }
32905 markw++;
32906 }
32907#endif //VERIFY_HEAP && MARK_ARRAY
32908}
32909
32910void gc_heap::verify_mark_array_cleared ()
32911{
32912#if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32913 if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32914 {
32915 generation* gen = generation_of (max_generation);
32916 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32917
32918 while (1)
32919 {
32920 if (seg == 0)
32921 {
32922 if (gen != large_object_generation)
32923 {
32924 gen = generation_of (max_generation+1);
32925 seg = heap_segment_rw (generation_start_segment (gen));
32926 }
32927 else
32928 {
32929 break;
32930 }
32931 }
32932
32933 bgc_verify_mark_array_cleared (seg);
32934 seg = heap_segment_next_rw (seg);
32935 }
32936 }
32937#endif //VERIFY_HEAP && MARK_ARRAY
32938}
32939
32940void gc_heap::verify_seg_end_mark_array_cleared()
32941{
32942#if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
32943 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
32944 {
32945 generation* gen = generation_of (max_generation);
32946 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32947
32948 while (1)
32949 {
32950 if (seg == 0)
32951 {
32952 if (gen != large_object_generation)
32953 {
32954 gen = generation_of (max_generation+1);
32955 seg = heap_segment_rw (generation_start_segment (gen));
32956 }
32957 else
32958 {
32959 break;
32960 }
32961 }
32962
32963 // We already cleared all mark array bits for ephemeral generations
32964 // at the beginning of bgc sweep
32965 uint8_t* from = ((seg == ephemeral_heap_segment) ?
32966 generation_allocation_start (generation_of (max_generation - 1)) :
32967 heap_segment_allocated (seg));
32968 size_t markw = mark_word_of (align_on_mark_word (from));
32969 size_t markw_end = mark_word_of (heap_segment_reserved (seg));
32970
32971 while (from < mark_word_address (markw))
32972 {
32973 if (is_mark_bit_set (from))
32974 {
32975 dprintf (3, ("mark bit for %Ix was not cleared", from));
32976 FATAL_GC_ERROR();
32977 }
32978
32979 from += mark_bit_pitch;
32980 }
32981
32982 while (markw < markw_end)
32983 {
32984 if (mark_array [markw])
32985 {
32986 dprintf (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared",
32987 markw, mark_array [markw], mark_word_address (markw)));
32988 FATAL_GC_ERROR();
32989 }
32990 markw++;
32991 }
32992 seg = heap_segment_next_rw (seg);
32993 }
32994 }
32995#endif //VERIFY_HEAP && MARK_ARRAY
32996}
32997
32998// This function is called to make sure we don't mess up the segment list
32999// in SOH. It's called by:
33000// 1) begin and end of ephemeral GCs
33001// 2) during bgc sweep when we switch segments.
33002void gc_heap::verify_soh_segment_list()
33003{
33004#ifdef VERIFY_HEAP
33005 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33006 {
33007 generation* gen = generation_of (max_generation);
33008 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33009 heap_segment* last_seg = 0;
33010 while (seg)
33011 {
33012 last_seg = seg;
33013 seg = heap_segment_next_rw (seg);
33014 }
33015 if (last_seg != ephemeral_heap_segment)
33016 {
33017 FATAL_GC_ERROR();
33018 }
33019 }
33020#endif //VERIFY_HEAP
33021}
33022
33023// This function can be called at any foreground GCs or blocking GCs. For background GCs,
33024// it can be called at the end of the final marking; and at any point during background
33025// sweep.
33026// NOTE - to be able to call this function during background sweep, we need to temporarily
33027// NOT clear the mark array bits as we go.
33028void gc_heap::verify_partial ()
33029{
33030#ifdef BACKGROUND_GC
33031 //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
33032 //generation* gen = large_object_generation;
33033 generation* gen = generation_of (max_generation);
33034 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33035 int align_const = get_alignment_constant (gen != large_object_generation);
33036
33037 uint8_t* o = 0;
33038 uint8_t* end = 0;
33039 size_t s = 0;
33040
33041 // Different ways to fail.
33042 BOOL mark_missed_p = FALSE;
33043 BOOL bad_ref_p = FALSE;
33044 BOOL free_ref_p = FALSE;
33045
33046 while (1)
33047 {
33048 if (seg == 0)
33049 {
33050 if (gen != large_object_generation)
33051 {
33052 //switch to LOH
33053 gen = large_object_generation;
33054 align_const = get_alignment_constant (gen != large_object_generation);
33055 seg = heap_segment_rw (generation_start_segment (gen));
33056 continue;
33057 }
33058 else
33059 {
33060 break;
33061 }
33062 }
33063
33064 o = heap_segment_mem (seg);
33065 end = heap_segment_allocated (seg);
33066 //printf ("validating [%Ix-[%Ix\n", o, end);
33067 while (o < end)
33068 {
33069 s = size (o);
33070
33071 BOOL marked_p = background_object_marked (o, FALSE);
33072
33073 if (marked_p)
33074 {
33075 go_through_object_cl (method_table (o), o, s, oo,
33076 {
33077 if (*oo)
33078 {
33079 //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
33080 MethodTable *pMT = method_table (*oo);
33081
33082 if (pMT == g_gc_pFreeObjectMethodTable)
33083 {
33084 free_ref_p = TRUE;
33085 FATAL_GC_ERROR();
33086 }
33087
33088 if (!pMT->SanityCheck())
33089 {
33090 bad_ref_p = TRUE;
33091 dprintf (3, ("Bad member of %Ix %Ix",
33092 (size_t)oo, (size_t)*oo));
33093 FATAL_GC_ERROR();
33094 }
33095
33096 if (current_bgc_state == bgc_final_marking)
33097 {
33098 if (marked_p && !background_object_marked (*oo, FALSE))
33099 {
33100 mark_missed_p = TRUE;
33101 FATAL_GC_ERROR();
33102 }
33103 }
33104 }
33105 }
33106 );
33107 }
33108
33109 o = o + Align(s, align_const);
33110 }
33111 seg = heap_segment_next_rw (seg);
33112 }
33113
33114 //printf ("didn't find any large object large enough...\n");
33115 //printf ("finished verifying loh\n");
33116#endif //BACKGROUND_GC
33117}
33118
33119#ifdef VERIFY_HEAP
33120
33121void
33122gc_heap::verify_free_lists ()
33123{
33124 for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
33125 {
33126 dprintf (3, ("Verifying free list for gen:%d", gen_num));
33127 allocator* gen_alloc = generation_allocator (generation_of (gen_num));
33128 size_t sz = gen_alloc->first_bucket_size();
33129 bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
33130
33131 for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
33132 {
33133 uint8_t* free_list = gen_alloc->alloc_list_head_of (a_l_number);
33134 uint8_t* prev = 0;
33135 while (free_list)
33136 {
33137 if (!((CObjectHeader*)free_list)->IsFree())
33138 {
33139 dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
33140 (size_t)free_list));
33141 FATAL_GC_ERROR();
33142 }
33143 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
33144 || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
33145 {
33146 dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
33147 (size_t)free_list));
33148 FATAL_GC_ERROR();
33149 }
33150 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
33151 {
33152 dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
33153 (size_t)free_list));
33154 FATAL_GC_ERROR();
33155 }
33156 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
33157 {
33158 dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
33159 (size_t)free_list));
33160 FATAL_GC_ERROR();
33161 }
33162
33163 prev = free_list;
33164 free_list = free_list_slot (free_list);
33165 }
33166 //verify the sanity of the tail
33167 uint8_t* tail = gen_alloc->alloc_list_tail_of (a_l_number);
33168 if (!((tail == 0) || (tail == prev)))
33169 {
33170 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
33171 FATAL_GC_ERROR();
33172 }
33173 if (tail == 0)
33174 {
33175 uint8_t* head = gen_alloc->alloc_list_head_of (a_l_number);
33176 if ((head != 0) && (free_list_slot (head) != 0))
33177 {
33178 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
33179 FATAL_GC_ERROR();
33180 }
33181 }
33182
33183 sz *=2;
33184 }
33185 }
33186}
33187
33188void
33189gc_heap::verify_heap (BOOL begin_gc_p)
33190{
33191 int heap_verify_level = static_cast<int>(GCConfig::GetHeapVerifyLevel());
33192 size_t last_valid_brick = 0;
33193 BOOL bCurrentBrickInvalid = FALSE;
33194 BOOL large_brick_p = TRUE;
33195 size_t curr_brick = 0;
33196 size_t prev_brick = (size_t)-1;
33197 int curr_gen_num = max_generation+1;
33198 heap_segment* seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
33199
33200 PREFIX_ASSUME(seg != NULL);
33201
33202 uint8_t* curr_object = heap_segment_mem (seg);
33203 uint8_t* prev_object = 0;
33204 uint8_t* begin_youngest = generation_allocation_start(generation_of(0));
33205 uint8_t* end_youngest = heap_segment_allocated (ephemeral_heap_segment);
33206 uint8_t* next_boundary = generation_allocation_start (generation_of (max_generation - 1));
33207 int align_const = get_alignment_constant (FALSE);
33208 size_t total_objects_verified = 0;
33209 size_t total_objects_verified_deep = 0;
33210
33211#ifdef BACKGROUND_GC
33212 BOOL consider_bgc_mark_p = FALSE;
33213 BOOL check_current_sweep_p = FALSE;
33214 BOOL check_saved_sweep_p = FALSE;
33215 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33216#endif //BACKGROUND_GC
33217
33218#ifdef MULTIPLE_HEAPS
33219 t_join* current_join = &gc_t_join;
33220#ifdef BACKGROUND_GC
33221 if (settings.concurrent && (bgc_thread_id.IsCurrentThread()))
33222 {
33223 // We always call verify_heap on entry of GC on the SVR GC threads.
33224 current_join = &bgc_t_join;
33225 }
33226#endif //BACKGROUND_GC
33227#endif //MULTIPLE_HEAPS
33228
33229 UNREFERENCED_PARAMETER(begin_gc_p);
33230#ifdef BACKGROUND_GC
33231 dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin",
33232 (begin_gc_p ? "BEG" : "END"),
33233 VolatileLoad(&settings.gc_index),
33234 (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
33235#else
33236 dprintf (2,("[%s]GC#%d: Verifying heap - begin",
33237 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
33238#endif //BACKGROUND_GC
33239
33240#ifndef MULTIPLE_HEAPS
33241 if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
33242 (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
33243 {
33244 FATAL_GC_ERROR();
33245 }
33246#endif //MULTIPLE_HEAPS
33247
33248#ifdef BACKGROUND_GC
33249 //don't touch the memory because the program is allocating from it.
33250 if (!settings.concurrent)
33251#endif //BACKGROUND_GC
33252 {
33253 if (!(heap_verify_level & GCConfig::HEAPVERIFY_NO_MEM_FILL))
33254 {
33255 //uninit the unused portions of segments.
33256 generation* gen1 = large_object_generation;
33257 heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
33258 PREFIX_ASSUME(seg1 != NULL);
33259
33260 while (1)
33261 {
33262 if (seg1)
33263 {
33264 uint8_t* clear_start = heap_segment_allocated (seg1) - plug_skew;
33265 if (heap_segment_used (seg1) > clear_start)
33266 {
33267 dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa",
33268 heap_segment_mem (seg1),
33269 clear_start ,
33270 heap_segment_used (seg1)));
33271 memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
33272 (heap_segment_used (seg1) - clear_start));
33273 }
33274 seg1 = heap_segment_next_rw (seg1);
33275 }
33276 else
33277 {
33278 if (gen1 == large_object_generation)
33279 {
33280 gen1 = generation_of (max_generation);
33281 seg1 = heap_segment_rw (generation_start_segment (gen1));
33282 PREFIX_ASSUME(seg1 != NULL);
33283 }
33284 else
33285 {
33286 break;
33287 }
33288 }
33289 }
33290 }
33291 }
33292
33293#ifdef MULTIPLE_HEAPS
33294 current_join->join(this, gc_join_verify_copy_table);
33295 if (current_join->joined())
33296 {
33297 // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
33298 for (int i = 0; i < n_heaps; i++)
33299 {
33300 //copy the card and brick tables
33301 if (g_gc_card_table != g_heaps[i]->card_table)
33302 {
33303 g_heaps[i]->copy_brick_card_table();
33304 }
33305 }
33306
33307 current_join->restart();
33308 }
33309#else
33310 if (g_gc_card_table != card_table)
33311 copy_brick_card_table();
33312#endif //MULTIPLE_HEAPS
33313
33314 //verify that the generation structures makes sense
33315 {
33316 generation* gen = generation_of (max_generation);
33317
33318 assert (generation_allocation_start (gen) ==
33319 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
33320 int gen_num = max_generation-1;
33321 generation* prev_gen = gen;
33322 while (gen_num >= 0)
33323 {
33324 gen = generation_of (gen_num);
33325 assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
33326 assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
33327 assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
33328
33329 if (generation_start_segment (prev_gen ) ==
33330 generation_start_segment (gen))
33331 {
33332 assert (generation_allocation_start (prev_gen) <
33333 generation_allocation_start (gen));
33334 }
33335 prev_gen = gen;
33336 gen_num--;
33337 }
33338 }
33339
33340 while (1)
33341 {
33342 // Handle segment transitions
33343 if (curr_object >= heap_segment_allocated (seg))
33344 {
33345 if (curr_object > heap_segment_allocated(seg))
33346 {
33347 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
33348 (size_t)curr_object, (size_t)seg));
33349 FATAL_GC_ERROR();
33350 }
33351 seg = heap_segment_next_in_range (seg);
33352 if (seg)
33353 {
33354#ifdef BACKGROUND_GC
33355 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33356#endif //BACKGROUND_GC
33357 curr_object = heap_segment_mem(seg);
33358 prev_object = 0;
33359 continue;
33360 }
33361 else
33362 {
33363 if (curr_gen_num == (max_generation+1))
33364 {
33365 curr_gen_num--;
33366 seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
33367
33368 PREFIX_ASSUME(seg != NULL);
33369
33370#ifdef BACKGROUND_GC
33371 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33372#endif //BACKGROUND_GC
33373 curr_object = heap_segment_mem (seg);
33374 prev_object = 0;
33375 large_brick_p = FALSE;
33376 align_const = get_alignment_constant (TRUE);
33377 }
33378 else
33379 break; // Done Verifying Heap -- no more segments
33380 }
33381 }
33382
33383 // Are we at the end of the youngest_generation?
33384 if (seg == ephemeral_heap_segment)
33385 {
33386 if (curr_object >= end_youngest)
33387 {
33388 // prev_object length is too long if we hit this int3
33389 if (curr_object > end_youngest)
33390 {
33391 dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
33392 (size_t)curr_object, (size_t)end_youngest));
33393 FATAL_GC_ERROR();
33394 }
33395 break;
33396 }
33397
33398 if ((curr_object >= next_boundary) && (curr_gen_num > 0))
33399 {
33400 curr_gen_num--;
33401 if (curr_gen_num > 0)
33402 {
33403 next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
33404 }
33405 }
33406 }
33407
33408 //if (is_mark_set (curr_object))
33409 //{
33410 // printf ("curr_object: %Ix is marked!",(size_t)curr_object);
33411 // FATAL_GC_ERROR();
33412 //}
33413
33414 size_t s = size (curr_object);
33415 dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
33416 if (s == 0)
33417 {
33418 dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
33419 FATAL_GC_ERROR();
33420 }
33421
33422 // If object is not in the youngest generation, then lets
33423 // verify that the brick table is correct....
33424 if (((seg != ephemeral_heap_segment) ||
33425 (brick_of(curr_object) < brick_of(begin_youngest))))
33426 {
33427 curr_brick = brick_of(curr_object);
33428
33429 // Brick Table Verification...
33430 //
33431 // On brick transition
33432 // if brick is negative
33433 // verify that brick indirects to previous valid brick
33434 // else
33435 // set current brick invalid flag to be flipped if we
33436 // encounter an object at the correct place
33437 //
33438 if (curr_brick != prev_brick)
33439 {
33440 // If the last brick we were examining had positive
33441 // entry but we never found the matching object, then
33442 // we have a problem
33443 // If prev_brick was the last one of the segment
33444 // it's ok for it to be invalid because it is never looked at
33445 if (bCurrentBrickInvalid &&
33446 (curr_brick != brick_of (heap_segment_mem (seg))) &&
33447 !heap_segment_read_only_p (seg))
33448 {
33449 dprintf (3, ("curr brick %Ix invalid", curr_brick));
33450 FATAL_GC_ERROR();
33451 }
33452
33453 if (large_brick_p)
33454 {
33455 //large objects verify the table only if they are in
33456 //range.
33457 if ((heap_segment_reserved (seg) <= highest_address) &&
33458 (heap_segment_mem (seg) >= lowest_address) &&
33459 brick_table [curr_brick] != 0)
33460 {
33461 dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
33462 curr_brick, (size_t)curr_object));
33463 FATAL_GC_ERROR();
33464 }
33465 else
33466 {
33467 bCurrentBrickInvalid = FALSE;
33468 }
33469 }
33470 else
33471 {
33472 // If the current brick contains a negative value make sure
33473 // that the indirection terminates at the last valid brick
33474 if (brick_table [curr_brick] <= 0)
33475 {
33476 if (brick_table [curr_brick] == 0)
33477 {
33478 dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
33479 curr_brick, (size_t)curr_object));
33480 FATAL_GC_ERROR();
33481 }
33482 ptrdiff_t i = curr_brick;
33483 while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
33484 (brick_table[i] < 0))
33485 {
33486 i = i + brick_table[i];
33487 }
33488 if (i < ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
33489 {
33490 dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
33491 i, brick_of (heap_segment_mem (seg)),
33492 curr_brick));
33493 FATAL_GC_ERROR();
33494 }
33495 // if (i != last_valid_brick)
33496 // FATAL_GC_ERROR();
33497 bCurrentBrickInvalid = FALSE;
33498 }
33499 else if (!heap_segment_read_only_p (seg))
33500 {
33501 bCurrentBrickInvalid = TRUE;
33502 }
33503 }
33504 }
33505
33506 if (bCurrentBrickInvalid)
33507 {
33508 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
33509 {
33510 bCurrentBrickInvalid = FALSE;
33511 last_valid_brick = curr_brick;
33512 }
33513 }
33514 }
33515
33516 if (*((uint8_t**)curr_object) != (uint8_t *) g_gc_pFreeObjectMethodTable)
33517 {
33518#ifdef FEATURE_LOH_COMPACTION
33519 if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
33520 {
33521 assert (method_table (prev_object) == g_gc_pFreeObjectMethodTable);
33522 }
33523#endif //FEATURE_LOH_COMPACTION
33524
33525 total_objects_verified++;
33526
33527 BOOL can_verify_deep = TRUE;
33528#ifdef BACKGROUND_GC
33529 can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
33530#endif //BACKGROUND_GC
33531
33532 BOOL deep_verify_obj = can_verify_deep;
33533 if ((heap_verify_level & GCConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
33534 deep_verify_obj = FALSE;
33535
33536 ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
33537
33538 if (can_verify_deep)
33539 {
33540 if (curr_gen_num > 0)
33541 {
33542 BOOL need_card_p = FALSE;
33543 if (contain_pointers_or_collectible (curr_object))
33544 {
33545 dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
33546 size_t crd = card_of (curr_object);
33547 BOOL found_card_p = card_set_p (crd);
33548
33549#ifdef COLLECTIBLE_CLASS
33550 if (is_collectible(curr_object))
33551 {
33552 uint8_t* class_obj = get_class_object (curr_object);
33553 if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
33554 {
33555 if (!found_card_p)
33556 {
33557 dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
33558 card_of (curr_object), (size_t)curr_object, class_obj));
33559
33560 FATAL_GC_ERROR();
33561 }
33562 }
33563 }
33564#endif //COLLECTIBLE_CLASS
33565
33566 if (contain_pointers(curr_object))
33567 {
33568 go_through_object_nostart
33569 (method_table(curr_object), curr_object, s, oo,
33570 {
33571 if ((crd != card_of ((uint8_t*)oo)) && !found_card_p)
33572 {
33573 crd = card_of ((uint8_t*)oo);
33574 found_card_p = card_set_p (crd);
33575 need_card_p = FALSE;
33576 }
33577 if ((*oo < ephemeral_high) && (*oo >= next_boundary))
33578 {
33579 need_card_p = TRUE;
33580 }
33581
33582 if (need_card_p && !found_card_p)
33583 {
33584
33585 dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33586 card_of (curr_object), (size_t)curr_object,
33587 card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33588 FATAL_GC_ERROR();
33589 }
33590 }
33591 );
33592 }
33593 if (need_card_p && !found_card_p)
33594 {
33595 dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33596 card_of (curr_object), (size_t)curr_object,
33597 card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33598 FATAL_GC_ERROR();
33599 }
33600 }
33601 }
33602 total_objects_verified_deep++;
33603 }
33604 }
33605
33606 prev_object = curr_object;
33607 prev_brick = curr_brick;
33608 curr_object = curr_object + Align(s, align_const);
33609 if (curr_object < prev_object)
33610 {
33611 dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
33612 FATAL_GC_ERROR();
33613 }
33614 }
33615
33616#ifdef BACKGROUND_GC
33617 dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id",
33618 (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
33619 (begin_gc_p ? "BEG" : "END"),
33620 ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
33621 total_objects_verified, total_objects_verified_deep));
33622 if (current_c_gc_state != c_gc_state_planning)
33623 {
33624 assert (total_objects_verified == total_objects_verified_deep);
33625 }
33626#endif //BACKGROUND_GC
33627
33628 verify_free_lists();
33629
33630#ifdef FEATURE_PREMORTEM_FINALIZATION
33631 finalize_queue->CheckFinalizerObjects();
33632#endif // FEATURE_PREMORTEM_FINALIZATION
33633
33634 {
33635 // to be consistent with handle table APIs pass a ScanContext*
33636 // to provide the heap number. the SC isn't complete though so
33637 // limit its scope to handle table verification.
33638 ScanContext sc;
33639 sc.thread_number = heap_number;
33640 GCScan::VerifyHandleTable(max_generation, max_generation, &sc);
33641 }
33642
33643#ifdef MULTIPLE_HEAPS
33644 current_join->join(this, gc_join_verify_objects_done);
33645 if (current_join->joined())
33646#endif //MULTIPLE_HEAPS
33647 {
33648 GCToEEInterface::VerifySyncTableEntry();
33649#ifdef MULTIPLE_HEAPS
33650 current_join->restart();
33651#endif //MULTIPLE_HEAPS
33652 }
33653
33654#ifdef BACKGROUND_GC
33655 if (!settings.concurrent)
33656 {
33657 if (current_c_gc_state == c_gc_state_planning)
33658 {
33659 // temporarily commenting this out 'cause an FGC
33660 // could be triggered before we sweep ephemeral.
33661 //verify_seg_end_mark_array_cleared();
33662 }
33663 }
33664
33665 if (settings.concurrent)
33666 {
33667 verify_mark_array_cleared();
33668 }
33669 dprintf (2,("GC%d(%s): Verifying heap - end",
33670 VolatileLoad(&settings.gc_index),
33671 (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
33672#else
33673 dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
33674#endif //BACKGROUND_GC
33675}
33676
33677#endif //VERIFY_HEAP
33678
33679
33680void GCHeap::ValidateObjectMember (Object* obj)
33681{
33682#ifdef VERIFY_HEAP
33683 size_t s = size (obj);
33684 uint8_t* o = (uint8_t*)obj;
33685
33686 go_through_object_cl (method_table (obj), o, s, oo,
33687 {
33688 uint8_t* child_o = *oo;
33689 if (child_o)
33690 {
33691 dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
33692 MethodTable *pMT = method_table (child_o);
33693 assert(pMT);
33694 if (!pMT->SanityCheck()) {
33695 dprintf (3, ("Bad member of %Ix %Ix",
33696 (size_t)oo, (size_t)child_o));
33697 FATAL_GC_ERROR();
33698 }
33699 }
33700 } );
33701#endif // VERIFY_HEAP
33702}
33703
33704void DestructObject (CObjectHeader* hdr)
33705{
33706 UNREFERENCED_PARAMETER(hdr); // compiler bug? -- this *is*, indeed, referenced
33707 hdr->~CObjectHeader();
33708}
33709
33710HRESULT GCHeap::Shutdown ()
33711{
33712 deleteGCShadow();
33713
33714 GCScan::GcRuntimeStructuresValid (FALSE);
33715
33716 // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
33717 // threads except the one performing the shutdown.
33718 // ASSERT( !GcInProgress );
33719
33720 // Guard against any more GC occurring and against any threads blocking
33721 // for GC to complete when the GC heap is gone. This fixes a race condition
33722 // where a thread in GC is destroyed as part of process destruction and
33723 // the remaining threads block for GC complete.
33724
33725 //GCTODO
33726 //EnterAllocLock();
33727 //Enter();
33728 //EnterFinalizeLock();
33729 //SetGCDone();
33730
33731 // during shutdown lot of threads are suspended
33732 // on this even, we don't want to wake them up just yet
33733 //CloseHandle (WaitForGCEvent);
33734
33735 //find out if the global card table hasn't been used yet
33736 uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
33737 if (card_table_refcount (ct) == 0)
33738 {
33739 destroy_card_table (ct);
33740 g_gc_card_table = nullptr;
33741
33742#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
33743 g_gc_card_bundle_table = nullptr;
33744#endif
33745#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33746 SoftwareWriteWatch::StaticClose();
33747#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
33748 }
33749
33750 //destroy all segments on the standby list
33751 while(gc_heap::segment_standby_list != 0)
33752 {
33753 heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
33754#ifdef MULTIPLE_HEAPS
33755 (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33756#else //MULTIPLE_HEAPS
33757 pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
33758#endif //MULTIPLE_HEAPS
33759 gc_heap::segment_standby_list = next_seg;
33760 }
33761
33762
33763#ifdef MULTIPLE_HEAPS
33764
33765 for (int i = 0; i < gc_heap::n_heaps; i ++)
33766 {
33767 delete gc_heap::g_heaps[i]->vm_heap;
33768 //destroy pure GC stuff
33769 gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
33770 }
33771#else
33772 gc_heap::destroy_gc_heap (pGenGCHeap);
33773
33774#endif //MULTIPLE_HEAPS
33775 gc_heap::shutdown_gc();
33776
33777 return S_OK;
33778}
33779
33780// Wait until a garbage collection is complete
33781// returns NOERROR if wait was OK, other error code if failure.
33782// WARNING: This will not undo the must complete state. If you are
33783// in a must complete when you call this, you'd better know what you're
33784// doing.
33785
33786#ifdef FEATURE_PREMORTEM_FINALIZATION
33787static
33788HRESULT AllocateCFinalize(CFinalize **pCFinalize)
33789{
33790 *pCFinalize = new (nothrow) CFinalize();
33791 if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
33792 return E_OUTOFMEMORY;
33793
33794 return S_OK;
33795}
33796#endif // FEATURE_PREMORTEM_FINALIZATION
33797
33798// init the instance heap
33799HRESULT GCHeap::Init(size_t hn)
33800{
33801 HRESULT hres = S_OK;
33802
33803#ifdef MULTIPLE_HEAPS
33804 if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
33805 hres = E_OUTOFMEMORY;
33806#else
33807 UNREFERENCED_PARAMETER(hn);
33808 if (!gc_heap::make_gc_heap())
33809 hres = E_OUTOFMEMORY;
33810#endif //MULTIPLE_HEAPS
33811
33812 // Failed.
33813 return hres;
33814}
33815
33816//System wide initialization
33817HRESULT GCHeap::Initialize ()
33818{
33819 HRESULT hr = S_OK;
33820
33821 g_gc_pFreeObjectMethodTable = GCToEEInterface::GetFreeObjectMethodTable();
33822 g_num_processors = GCToOSInterface::GetTotalProcessorCount();
33823 assert(g_num_processors != 0);
33824
33825//Initialize the static members.
33826#ifdef TRACE_GC
33827 GcDuration = 0;
33828 CreatedObjectCount = 0;
33829#endif //TRACE_GC
33830
33831 size_t seg_size = get_valid_segment_size();
33832 gc_heap::soh_segment_size = seg_size;
33833 size_t large_seg_size = get_valid_segment_size(TRUE);
33834 gc_heap::min_loh_segment_size = large_seg_size;
33835 gc_heap::min_segment_size = min (seg_size, large_seg_size);
33836#ifdef SEG_MAPPING_TABLE
33837 gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_heap::min_segment_size);
33838#endif //SEG_MAPPING_TABLE
33839
33840#ifdef MULTIPLE_HEAPS
33841 uint32_t nhp_from_config = static_cast<uint32_t>(GCConfig::GetHeapCount());
33842
33843 // GetGCProcessCpuCount only returns up to 64 procs.
33844 unsigned int nhp_from_process = GCToOSInterface::CanEnableGCCPUGroups() ?
33845 GCToOSInterface::GetTotalProcessorCount():
33846 GCToOSInterface::GetCurrentProcessCpuCount();
33847
33848 unsigned int nhp = ((nhp_from_config == 0) ? nhp_from_process :
33849 (min (nhp_from_config, nhp_from_process)));
33850
33851
33852 nhp = min (nhp, MAX_SUPPORTED_CPUS);
33853
33854 if (GCConfig::GetNoAffinitize())
33855 gc_heap::gc_thread_no_affinitize_p = true;
33856
33857#if !defined(FEATURE_REDHAWK) && !defined(FEATURE_CORECLR)
33858 if (!(gc_heap::gc_thread_no_affinitize_p))
33859 {
33860 if (!(GCToOSInterface::CanEnableGCCPUGroups()))
33861 {
33862 size_t gc_thread_affinity_mask = static_cast<size_t>(GCConfig::GetGCHeapAffinitizeMask());
33863
33864 uintptr_t pmask, smask;
33865 if (GCToOSInterface::GetCurrentProcessAffinityMask(&pmask, &smask))
33866 {
33867 pmask &= smask;
33868
33869 if (gc_thread_affinity_mask)
33870 {
33871 pmask &= gc_thread_affinity_mask;
33872 }
33873
33874 process_mask = pmask;
33875
33876 unsigned int set_bits_in_pmask = 0;
33877 while (pmask)
33878 {
33879 if (pmask & 1)
33880 set_bits_in_pmask++;
33881 pmask >>= 1;
33882 }
33883
33884 nhp = min (nhp, set_bits_in_pmask);
33885 }
33886 else
33887 {
33888 gc_heap::gc_thread_no_affinitize_p = true;
33889 }
33890 }
33891 }
33892#endif //!FEATURE_REDHAWK && !FEATURE_CORECLR
33893
33894 hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
33895#else
33896 hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
33897#endif //MULTIPLE_HEAPS
33898
33899 if (hr != S_OK)
33900 return hr;
33901
33902 gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit();
33903
33904 gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
33905#ifndef MULTIPLE_HEAPS
33906 gc_heap::mem_one_percent /= g_num_processors;
33907#endif //!MULTIPLE_HEAPS
33908
33909 uint32_t highmem_th_from_config = (uint32_t)GCConfig::GetGCHighMemPercent();
33910 if (highmem_th_from_config)
33911 {
33912 gc_heap::high_memory_load_th = min (99, highmem_th_from_config);
33913 gc_heap::v_high_memory_load_th = min (99, (highmem_th_from_config + 7));
33914 }
33915 else
33916 {
33917 // We should only use this if we are in the "many process" mode which really is only applicable
33918 // to very powerful machines - before that's implemented, temporarily I am only enabling this for 80GB+ memory.
33919 // For now I am using an estimate to calculate these numbers but this should really be obtained
33920 // programmatically going forward.
33921 // I am assuming 47 processes using WKS GC and 3 using SVR GC.
33922 // I am assuming 3 in part due to the "very high memory load" is 97%.
33923 int available_mem_th = 10;
33924 if (gc_heap::total_physical_mem >= ((uint64_t)80 * 1024 * 1024 * 1024))
33925 {
33926 int adjusted_available_mem_th = 3 + (int)((float)47 / (float)(GCToOSInterface::GetTotalProcessorCount()));
33927 available_mem_th = min (available_mem_th, adjusted_available_mem_th);
33928 }
33929
33930 gc_heap::high_memory_load_th = 100 - available_mem_th;
33931 gc_heap::v_high_memory_load_th = 97;
33932 }
33933
33934 gc_heap::m_high_memory_load_th = min ((gc_heap::high_memory_load_th + 5), gc_heap::v_high_memory_load_th);
33935
33936 gc_heap::pm_stress_on = (GCConfig::GetGCProvModeStress() != 0);
33937
33938#if defined(BIT64)
33939 gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
33940#endif // BIT64
33941
33942 WaitForGCEvent = new (nothrow) GCEvent;
33943
33944 if (!WaitForGCEvent)
33945 {
33946 return E_OUTOFMEMORY;
33947 }
33948
33949 if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE))
33950 {
33951 return E_FAIL;
33952 }
33953
33954#ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
33955#if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
33956 if (GCStress<cfg_any>::IsEnabled()) {
33957 for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
33958 {
33959 m_StressObjs[i] = CreateGlobalHandle(0);
33960 }
33961 m_CurStressObj = 0;
33962 }
33963#endif //STRESS_HEAP && !MULTIPLE_HEAPS
33964#endif // FEATURE_REDHAWK
33965
33966 initGCShadow(); // If we are debugging write barriers, initialize heap shadow
33967
33968#ifdef MULTIPLE_HEAPS
33969
33970 for (unsigned i = 0; i < nhp; i++)
33971 {
33972 GCHeap* Hp = new (nothrow) GCHeap();
33973 if (!Hp)
33974 return E_OUTOFMEMORY;
33975
33976 if ((hr = Hp->Init (i))!= S_OK)
33977 {
33978 return hr;
33979 }
33980 }
33981 // initialize numa node to heap map
33982 heap_select::init_numa_node_to_heap_map(nhp);
33983#else
33984 hr = Init (0);
33985#endif //MULTIPLE_HEAPS
33986
33987 if (hr == S_OK)
33988 {
33989 GCScan::GcRuntimeStructuresValid (TRUE);
33990
33991 GCToEEInterface::DiagUpdateGenerationBounds();
33992 }
33993
33994 return hr;
33995};
33996
33997////
33998// GC callback functions
33999bool GCHeap::IsPromoted(Object* object)
34000{
34001#ifdef _DEBUG
34002 ((CObjectHeader*)object)->Validate();
34003#endif //_DEBUG
34004
34005 uint8_t* o = (uint8_t*)object;
34006
34007 if (gc_heap::settings.condemned_generation == max_generation)
34008 {
34009#ifdef MULTIPLE_HEAPS
34010 gc_heap* hp = gc_heap::g_heaps[0];
34011#else
34012 gc_heap* hp = pGenGCHeap;
34013#endif //MULTIPLE_HEAPS
34014
34015#ifdef BACKGROUND_GC
34016 if (gc_heap::settings.concurrent)
34017 {
34018 bool is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
34019 hp->background_marked (o));
34020 return is_marked;
34021 }
34022 else
34023#endif //BACKGROUND_GC
34024 {
34025 return (!((o < hp->highest_address) && (o >= hp->lowest_address))
34026 || hp->is_mark_set (o));
34027 }
34028 }
34029 else
34030 {
34031 gc_heap* hp = gc_heap::heap_of (o);
34032 return (!((o < hp->gc_high) && (o >= hp->gc_low))
34033 || hp->is_mark_set (o));
34034 }
34035}
34036
34037size_t GCHeap::GetPromotedBytes(int heap_index)
34038{
34039#ifdef BACKGROUND_GC
34040 if (gc_heap::settings.concurrent)
34041 {
34042 return gc_heap::bpromoted_bytes (heap_index);
34043 }
34044 else
34045#endif //BACKGROUND_GC
34046 {
34047 return gc_heap::promoted_bytes (heap_index);
34048 }
34049}
34050
34051void GCHeap::SetYieldProcessorScalingFactor (float scalingFactor)
34052{
34053 assert (yp_spin_count_unit != 0);
34054 int saved_yp_spin_count_unit = yp_spin_count_unit;
34055 yp_spin_count_unit = (int)((float)yp_spin_count_unit * scalingFactor / (float)9);
34056
34057 // It's very suspicious if it becomes 0
34058 if (yp_spin_count_unit == 0)
34059 {
34060 yp_spin_count_unit = saved_yp_spin_count_unit;
34061 }
34062}
34063
34064unsigned int GCHeap::WhichGeneration (Object* object)
34065{
34066 gc_heap* hp = gc_heap::heap_of ((uint8_t*)object);
34067 unsigned int g = hp->object_gennum ((uint8_t*)object);
34068 dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
34069 return g;
34070}
34071
34072bool GCHeap::IsEphemeral (Object* object)
34073{
34074 uint8_t* o = (uint8_t*)object;
34075 gc_heap* hp = gc_heap::heap_of (o);
34076 return !!hp->ephemeral_pointer_p (o);
34077}
34078
34079// Return NULL if can't find next object. When EE is not suspended,
34080// the result is not accurate: if the input arg is in gen0, the function could
34081// return zeroed out memory as next object
34082Object * GCHeap::NextObj (Object * object)
34083{
34084#ifdef VERIFY_HEAP
34085 uint8_t* o = (uint8_t*)object;
34086
34087#ifndef FEATURE_BASICFREEZE
34088 if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)))
34089 {
34090 return NULL;
34091 }
34092#endif //!FEATURE_BASICFREEZE
34093
34094 heap_segment * hs = gc_heap::find_segment (o, FALSE);
34095 if (!hs)
34096 {
34097 return NULL;
34098 }
34099
34100 BOOL large_object_p = heap_segment_loh_p (hs);
34101 if (large_object_p)
34102 return NULL; //could be racing with another core allocating.
34103#ifdef MULTIPLE_HEAPS
34104 gc_heap* hp = heap_segment_heap (hs);
34105#else //MULTIPLE_HEAPS
34106 gc_heap* hp = 0;
34107#endif //MULTIPLE_HEAPS
34108 unsigned int g = hp->object_gennum ((uint8_t*)object);
34109 if ((g == 0) && hp->settings.demotion)
34110 return NULL;//could be racing with another core allocating.
34111 int align_const = get_alignment_constant (!large_object_p);
34112 uint8_t* nextobj = o + Align (size (o), align_const);
34113 if (nextobj <= o) // either overflow or 0 sized object.
34114 {
34115 return NULL;
34116 }
34117
34118 if ((nextobj < heap_segment_mem(hs)) ||
34119 (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) ||
34120 (nextobj >= hp->alloc_allocated))
34121 {
34122 return NULL;
34123 }
34124
34125 return (Object *)nextobj;
34126#else
34127 return nullptr;
34128#endif // VERIFY_HEAP
34129}
34130
34131#ifdef VERIFY_HEAP
34132
34133#ifdef FEATURE_BASICFREEZE
34134BOOL GCHeap::IsInFrozenSegment (Object * object)
34135{
34136 uint8_t* o = (uint8_t*)object;
34137 heap_segment * hs = gc_heap::find_segment (o, FALSE);
34138 //We create a frozen object for each frozen segment before the segment is inserted
34139 //to segment list; during ngen, we could also create frozen objects in segments which
34140 //don't belong to current GC heap.
34141 //So we return true if hs is NULL. It might create a hole about detecting invalidate
34142 //object. But given all other checks present, the hole should be very small
34143 return !hs || heap_segment_read_only_p (hs);
34144}
34145#endif //FEATURE_BASICFREEZE
34146
34147#endif //VERIFY_HEAP
34148
34149// returns TRUE if the pointer is in one of the GC heaps.
34150bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only)
34151{
34152 STATIC_CONTRACT_SO_TOLERANT;
34153
34154 // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment
34155 // no longer calls GCEvent::Wait which eventually takes a lock.
34156
34157 uint8_t* object = (uint8_t*) vpObject;
34158#ifndef FEATURE_BASICFREEZE
34159 if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address)))
34160 return FALSE;
34161#endif //!FEATURE_BASICFREEZE
34162
34163 heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
34164 return !!hs;
34165}
34166
34167#ifdef STRESS_PINNING
34168static n_promote = 0;
34169#endif //STRESS_PINNING
34170// promote an object
34171void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
34172{
34173 THREAD_NUMBER_FROM_CONTEXT;
34174#ifndef MULTIPLE_HEAPS
34175 const int thread = 0;
34176#endif //!MULTIPLE_HEAPS
34177
34178 uint8_t* o = (uint8_t*)*ppObject;
34179
34180 if (o == 0)
34181 return;
34182
34183#ifdef DEBUG_DestroyedHandleValue
34184 // we can race with destroy handle during concurrent scan
34185 if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
34186 return;
34187#endif //DEBUG_DestroyedHandleValue
34188
34189 HEAP_FROM_THREAD;
34190
34191 gc_heap* hp = gc_heap::heap_of (o);
34192
34193 dprintf (3, ("Promote %Ix", (size_t)o));
34194
34195#ifdef INTERIOR_POINTERS
34196 if (flags & GC_CALL_INTERIOR)
34197 {
34198 if ((o < hp->gc_low) || (o >= hp->gc_high))
34199 {
34200 return;
34201 }
34202 if ( (o = hp->find_object (o, hp->gc_low)) == 0)
34203 {
34204 return;
34205 }
34206
34207 }
34208#endif //INTERIOR_POINTERS
34209
34210#ifdef FEATURE_CONSERVATIVE_GC
34211 // For conservative GC, a value on stack may point to middle of a free object.
34212 // In this case, we don't need to promote the pointer.
34213 if (GCConfig::GetConservativeGC()
34214 && ((CObjectHeader*)o)->IsFree())
34215 {
34216 return;
34217 }
34218#endif
34219
34220#ifdef _DEBUG
34221 ((CObjectHeader*)o)->ValidatePromote(sc, flags);
34222#else
34223 UNREFERENCED_PARAMETER(sc);
34224#endif //_DEBUG
34225
34226 if (flags & GC_CALL_PINNED)
34227 hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
34228
34229#ifdef STRESS_PINNING
34230 if ((++n_promote % 20) == 1)
34231 hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
34232#endif //STRESS_PINNING
34233
34234#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34235 size_t promoted_size_begin = hp->promoted_bytes (thread);
34236#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34237
34238 if ((o >= hp->gc_low) && (o < hp->gc_high))
34239 {
34240 hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
34241 }
34242
34243#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34244 size_t promoted_size_end = hp->promoted_bytes (thread);
34245 if (g_fEnableAppDomainMonitoring)
34246 {
34247 if (sc->pCurrentDomain)
34248 {
34249 GCToEEInterface::RecordSurvivedBytesForHeap((promoted_size_end - promoted_size_begin), thread, sc->pCurrentDomain);
34250 }
34251 }
34252#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34253
34254 STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
34255}
34256
34257void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
34258 uint32_t flags)
34259{
34260 UNREFERENCED_PARAMETER(sc);
34261
34262 uint8_t* object = (uint8_t*)(Object*)(*ppObject);
34263
34264 THREAD_NUMBER_FROM_CONTEXT;
34265
34266 //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
34267 dprintf (3, ("R: %Ix", (size_t)ppObject));
34268
34269 if (object == 0)
34270 return;
34271
34272 gc_heap* hp = gc_heap::heap_of (object);
34273
34274#ifdef _DEBUG
34275 if (!(flags & GC_CALL_INTERIOR))
34276 {
34277 // We cannot validate this object if it's in the condemned gen because it could
34278 // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
34279 if (!((object >= hp->gc_low) && (object < hp->gc_high)))
34280 {
34281 ((CObjectHeader*)object)->Validate(FALSE);
34282 }
34283 }
34284#endif //_DEBUG
34285
34286 dprintf (3, ("Relocate %Ix\n", (size_t)object));
34287
34288 uint8_t* pheader;
34289
34290 if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
34291 {
34292 if (!((object >= hp->gc_low) && (object < hp->gc_high)))
34293 {
34294 return;
34295 }
34296
34297 if (gc_heap::loh_object_p (object))
34298 {
34299 pheader = hp->find_object (object, 0);
34300 if (pheader == 0)
34301 {
34302 return;
34303 }
34304
34305 ptrdiff_t ref_offset = object - pheader;
34306 hp->relocate_address(&pheader THREAD_NUMBER_ARG);
34307 *ppObject = (Object*)(pheader + ref_offset);
34308 return;
34309 }
34310 }
34311
34312 {
34313 pheader = object;
34314 hp->relocate_address(&pheader THREAD_NUMBER_ARG);
34315 *ppObject = (Object*)pheader;
34316 }
34317
34318 STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
34319}
34320
34321/*static*/ bool GCHeap::IsObjectInFixedHeap(Object *pObj)
34322{
34323 // For now we simply look at the size of the object to determine if it in the
34324 // fixed heap or not. If the bit indicating this gets set at some point
34325 // we should key off that instead.
34326 return size( pObj ) >= loh_size_threshold;
34327}
34328
34329#ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
34330#ifdef STRESS_HEAP
34331
34332void StressHeapDummy ();
34333
34334static int32_t GCStressStartCount = -1;
34335static int32_t GCStressCurCount = 0;
34336static int32_t GCStressStartAtJit = -1;
34337
34338// the maximum number of foreground GCs we'll induce during one BGC
34339// (this number does not include "naturally" occuring GCs).
34340static int32_t GCStressMaxFGCsPerBGC = -1;
34341
34342// CLRRandom implementation can produce FPU exceptions if
34343// the test/application run by CLR is enabling any FPU exceptions.
34344// We want to avoid any unexpected exception coming from stress
34345// infrastructure, so CLRRandom is not an option.
34346// The code below is a replicate of CRT rand() implementation.
34347// Using CRT rand() is not an option because we will interfere with the user application
34348// that may also use it.
34349int StressRNG(int iMaxValue)
34350{
34351 static BOOL bisRandInit = FALSE;
34352 static int lHoldrand = 1L;
34353
34354 if (!bisRandInit)
34355 {
34356 lHoldrand = (int)time(NULL);
34357 bisRandInit = TRUE;
34358 }
34359 int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
34360 return randValue % iMaxValue;
34361}
34362#endif // STRESS_HEAP
34363#endif // !FEATURE_REDHAWK
34364
34365// free up object so that things will move and then do a GC
34366//return TRUE if GC actually happens, otherwise FALSE
34367bool GCHeap::StressHeap(gc_alloc_context * context)
34368{
34369#if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34370 alloc_context* acontext = static_cast<alloc_context*>(context);
34371 assert(context != nullptr);
34372
34373 // if GC stress was dynamically disabled during this run we return FALSE
34374 if (!GCStressPolicy::IsEnabled())
34375 return FALSE;
34376
34377#ifdef _DEBUG
34378 if (g_pConfig->FastGCStressLevel() && !GCToEEInterface::GetThread()->StressHeapIsEnabled()) {
34379 return FALSE;
34380 }
34381
34382#endif //_DEBUG
34383
34384 if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
34385#ifdef _DEBUG
34386 || g_pConfig->FastGCStressLevel() > 1
34387#endif //_DEBUG
34388 ) {
34389 if (!Thread::UniqueStack(&acontext)) {
34390 return FALSE;
34391 }
34392 }
34393
34394#ifdef BACKGROUND_GC
34395 // don't trigger a GC from the GC threads but still trigger GCs from user threads.
34396 if (GCToEEInterface::WasCurrentThreadCreatedByGC())
34397 {
34398 return FALSE;
34399 }
34400#endif //BACKGROUND_GC
34401
34402 if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
34403 {
34404 GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
34405 GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
34406 }
34407
34408 if (GCStressMaxFGCsPerBGC == -1)
34409 {
34410 GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
34411 if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
34412 GCStressMaxFGCsPerBGC = 6;
34413 }
34414
34415#ifdef _DEBUG
34416 if (g_JitCount < GCStressStartAtJit)
34417 return FALSE;
34418#endif //_DEBUG
34419
34420 // Allow programmer to skip the first N Stress GCs so that you can
34421 // get to the interesting ones faster.
34422 Interlocked::Increment(&GCStressCurCount);
34423 if (GCStressCurCount < GCStressStartCount)
34424 return FALSE;
34425
34426 // throttle the number of stress-induced GCs by a factor given by GCStressStep
34427 if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
34428 {
34429 return FALSE;
34430 }
34431
34432#ifdef BACKGROUND_GC
34433 if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
34434 {
34435 // allow a maximum number of stress induced FGCs during one BGC
34436 if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
34437 return FALSE;
34438 ++gc_stress_fgcs_in_bgc;
34439 }
34440#endif // BACKGROUND_GC
34441
34442 if (g_pStringClass == 0)
34443 {
34444 // If the String class has not been loaded, dont do any stressing. This should
34445 // be kept to a minimum to get as complete coverage as possible.
34446 _ASSERTE(g_fEEInit);
34447 return FALSE;
34448 }
34449
34450#ifndef MULTIPLE_HEAPS
34451 static int32_t OneAtATime = -1;
34452
34453 // Only bother with this if the stress level is big enough and if nobody else is
34454 // doing it right now. Note that some callers are inside the AllocLock and are
34455 // guaranteed synchronized. But others are using AllocationContexts and have no
34456 // particular synchronization.
34457 //
34458 // For this latter case, we want a very high-speed way of limiting this to one
34459 // at a time. A secondary advantage is that we release part of our StressObjs
34460 // buffer sparingly but just as effectively.
34461
34462 if (Interlocked::Increment(&OneAtATime) == 0 &&
34463 !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
34464 {
34465 StringObject* str;
34466
34467 // If the current string is used up
34468 if (HndFetchHandle(m_StressObjs[m_CurStressObj]) == 0)
34469 {
34470 // Populate handles with strings
34471 int i = m_CurStressObj;
34472 while(HndFetchHandle(m_StressObjs[i]) == 0)
34473 {
34474 _ASSERTE(m_StressObjs[i] != 0);
34475 unsigned strLen = ((unsigned)loh_size_threshold - 32) / sizeof(WCHAR);
34476 unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
34477
34478 // update the cached type handle before allocating
34479 SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
34480 str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
34481 if (str)
34482 {
34483 str->SetMethodTable (g_pStringClass);
34484 str->SetStringLength (strLen);
34485 HndAssignHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
34486 }
34487 i = (i + 1) % NUM_HEAP_STRESS_OBJS;
34488 if (i == m_CurStressObj) break;
34489 }
34490
34491 // advance the current handle to the next string
34492 m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
34493 }
34494
34495 // Get the current string
34496 str = (StringObject*) OBJECTREFToObject(HndFetchHandle(m_StressObjs[m_CurStressObj]));
34497 if (str)
34498 {
34499 // Chop off the end of the string and form a new object out of it.
34500 // This will 'free' an object at the begining of the heap, which will
34501 // force data movement. Note that we can only do this so many times.
34502 // before we have to move on to the next string.
34503 unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
34504 if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
34505 {
34506 unsigned sizeToNextObj = (unsigned)Align(size(str));
34507 uint8_t* freeObj = ((uint8_t*) str) + sizeToNextObj - sizeOfNewObj;
34508 pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);
34509 str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
34510 }
34511 else
34512 {
34513 // Let the string itself become garbage.
34514 // will be realloced next time around
34515 HndAssignHandle(m_StressObjs[m_CurStressObj], 0);
34516 }
34517 }
34518 }
34519 Interlocked::Decrement(&OneAtATime);
34520#endif // !MULTIPLE_HEAPS
34521 if (IsConcurrentGCEnabled())
34522 {
34523 int rgen = StressRNG(10);
34524
34525 // gen0:gen1:gen2 distribution: 40:40:20
34526 if (rgen >= 8)
34527 rgen = 2;
34528 else if (rgen >= 4)
34529 rgen = 1;
34530 else
34531 rgen = 0;
34532
34533 GarbageCollectTry (rgen, FALSE, collection_gcstress);
34534 }
34535 else
34536 {
34537 GarbageCollect(max_generation, FALSE, collection_gcstress);
34538 }
34539
34540 return TRUE;
34541#else
34542 UNREFERENCED_PARAMETER(context);
34543 return FALSE;
34544#endif // defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34545}
34546
34547
34548#ifdef FEATURE_PREMORTEM_FINALIZATION
34549#define REGISTER_FOR_FINALIZATION(_object, _size) \
34550 hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
34551#else // FEATURE_PREMORTEM_FINALIZATION
34552#define REGISTER_FOR_FINALIZATION(_object, _size) true
34553#endif // FEATURE_PREMORTEM_FINALIZATION
34554
34555#define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do { \
34556 if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size))) \
34557 { \
34558 STRESS_LOG_OOM_STACK(_size); \
34559 return NULL; \
34560 } \
34561} while (false)
34562
34563//
34564// Small Object Allocator
34565//
34566//
34567// Allocate small object with an alignment requirement of 8-bytes.
34568Object*
34569GCHeap::AllocAlign8(gc_alloc_context* ctx, size_t size, uint32_t flags )
34570{
34571#ifdef FEATURE_64BIT_ALIGNMENT
34572 CONTRACTL {
34573 NOTHROW;
34574 GC_TRIGGERS;
34575 } CONTRACTL_END;
34576
34577 alloc_context* acontext = static_cast<alloc_context*>(ctx);
34578
34579#ifdef MULTIPLE_HEAPS
34580 if (acontext->get_alloc_heap() == 0)
34581 {
34582 AssignHeap (acontext);
34583 assert (acontext->get_alloc_heap());
34584 }
34585
34586 gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34587#else
34588 gc_heap* hp = pGenGCHeap;
34589#endif //MULTIPLE_HEAPS
34590
34591 return AllocAlign8Common(hp, acontext, size, flags);
34592#else
34593 UNREFERENCED_PARAMETER(ctx);
34594 UNREFERENCED_PARAMETER(size);
34595 UNREFERENCED_PARAMETER(flags);
34596 assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!");
34597 return nullptr;
34598#endif //FEATURE_64BIT_ALIGNMENT
34599}
34600
34601// Common code used by both variants of AllocAlign8 above.
34602Object*
34603GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, uint32_t flags)
34604{
34605#ifdef FEATURE_64BIT_ALIGNMENT
34606 CONTRACTL {
34607 NOTHROW;
34608 GC_TRIGGERS;
34609 } CONTRACTL_END;
34610
34611 gc_heap* hp = (gc_heap*)_hp;
34612
34613 TRIGGERSGC();
34614
34615 Object* newAlloc = NULL;
34616
34617#ifdef TRACE_GC
34618#ifdef COUNT_CYCLES
34619 AllocStart = GetCycleCount32();
34620 unsigned finish;
34621#elif defined(ENABLE_INSTRUMENTATION)
34622 unsigned AllocStart = GetInstLogTime();
34623 unsigned finish;
34624#endif //COUNT_CYCLES
34625#endif //TRACE_GC
34626
34627 if (size < loh_size_threshold)
34628 {
34629#ifdef TRACE_GC
34630 AllocSmallCount++;
34631#endif //TRACE_GC
34632
34633 // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
34634 // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
34635 // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
34636 size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
34637
34638 // Retrieve the address of the next allocation from the context (note that we're inside the alloc
34639 // lock at this point).
34640 uint8_t* result = acontext->alloc_ptr;
34641
34642 // Will an allocation at this point yield the correct alignment and fit into the remainder of the
34643 // context?
34644 if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
34645 {
34646 // Yes, we can just go ahead and make the allocation.
34647 newAlloc = (Object*) hp->allocate (size, acontext);
34648 ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34649 }
34650 else
34651 {
34652 // No, either the next available address is not aligned in the way we require it or there's
34653 // not enough space to allocate an object of the required size. In both cases we allocate a
34654 // padding object (marked as a free object). This object's size is such that it will reverse
34655 // the alignment of the next header (asserted below).
34656 //
34657 // We allocate both together then decide based on the result whether we'll format the space as
34658 // free object + real object or real object + free object.
34659 ASSERT((Align(min_obj_size) & 7) == 4);
34660 CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
34661 if (freeobj)
34662 {
34663 if (((size_t)freeobj & 7) == desiredAlignment)
34664 {
34665 // New allocation has desired alignment, return this one and place the free object at the
34666 // end of the allocated space.
34667 newAlloc = (Object*)freeobj;
34668 freeobj = (CObjectHeader*)((uint8_t*)freeobj + Align(size));
34669 }
34670 else
34671 {
34672 // New allocation is still mis-aligned, format the initial space as a free object and the
34673 // rest of the space should be correctly aligned for the real object.
34674 newAlloc = (Object*)((uint8_t*)freeobj + Align(min_obj_size));
34675 ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
34676 }
34677 freeobj->SetFree(min_obj_size);
34678 }
34679 }
34680 }
34681 else
34682 {
34683 // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
34684 // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
34685 // we've managed to arrange things so the only case where we see a bias is for boxed value types and
34686 // these can never get large enough to be allocated on the LOH.
34687 ASSERT(65536 < loh_size_threshold);
34688 ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
34689
34690 alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34691
34692 newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
34693 ASSERT(((size_t)newAlloc & 7) == 0);
34694 }
34695
34696 CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34697
34698#ifdef TRACE_GC
34699#ifdef COUNT_CYCLES
34700 finish = GetCycleCount32();
34701#elif defined(ENABLE_INSTRUMENTATION)
34702 finish = GetInstLogTime();
34703#endif //COUNT_CYCLES
34704 AllocDuration += finish - AllocStart;
34705 AllocCount++;
34706#endif //TRACE_GC
34707 return newAlloc;
34708#else
34709 UNREFERENCED_PARAMETER(_hp);
34710 UNREFERENCED_PARAMETER(acontext);
34711 UNREFERENCED_PARAMETER(size);
34712 UNREFERENCED_PARAMETER(flags);
34713 assert(!"Should not call GCHeap::AllocAlign8Common without FEATURE_64BIT_ALIGNMENT defined!");
34714 return nullptr;
34715#endif // FEATURE_64BIT_ALIGNMENT
34716}
34717
34718Object *
34719GCHeap::AllocLHeap( size_t size, uint32_t flags REQD_ALIGN_DCL)
34720{
34721 CONTRACTL {
34722 NOTHROW;
34723 GC_TRIGGERS;
34724 } CONTRACTL_END;
34725
34726 TRIGGERSGC();
34727
34728 Object* newAlloc = NULL;
34729
34730#ifdef TRACE_GC
34731#ifdef COUNT_CYCLES
34732 AllocStart = GetCycleCount32();
34733 unsigned finish;
34734#elif defined(ENABLE_INSTRUMENTATION)
34735 unsigned AllocStart = GetInstLogTime();
34736 unsigned finish;
34737#endif //COUNT_CYCLES
34738#endif //TRACE_GC
34739
34740#ifdef MULTIPLE_HEAPS
34741 //take the first heap....
34742 gc_heap* hp = gc_heap::g_heaps[0];
34743#else
34744 gc_heap* hp = pGenGCHeap;
34745#ifdef _PREFAST_
34746 // prefix complains about us dereferencing hp in wks build even though we only access static members
34747 // this way. not sure how to shut it up except for this ugly workaround:
34748 PREFIX_ASSUME(hp != NULL);
34749#endif //_PREFAST_
34750#endif //MULTIPLE_HEAPS
34751
34752 alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
34753
34754 newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34755#ifdef FEATURE_STRUCTALIGN
34756 newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34757#endif // FEATURE_STRUCTALIGN
34758 CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34759
34760#ifdef TRACE_GC
34761#ifdef COUNT_CYCLES
34762 finish = GetCycleCount32();
34763#elif defined(ENABLE_INSTRUMENTATION)
34764 finish = GetInstLogTime();
34765#endif //COUNT_CYCLES
34766 AllocDuration += finish - AllocStart;
34767 AllocCount++;
34768#endif //TRACE_GC
34769 return newAlloc;
34770}
34771
34772Object*
34773GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_DCL)
34774{
34775 CONTRACTL {
34776 NOTHROW;
34777 GC_TRIGGERS;
34778 } CONTRACTL_END;
34779
34780 TRIGGERSGC();
34781
34782 Object* newAlloc = NULL;
34783 alloc_context* acontext = static_cast<alloc_context*>(context);
34784
34785#ifdef TRACE_GC
34786#ifdef COUNT_CYCLES
34787 AllocStart = GetCycleCount32();
34788 unsigned finish;
34789#elif defined(ENABLE_INSTRUMENTATION)
34790 unsigned AllocStart = GetInstLogTime();
34791 unsigned finish;
34792#endif //COUNT_CYCLES
34793#endif //TRACE_GC
34794
34795#ifdef MULTIPLE_HEAPS
34796 if (acontext->get_alloc_heap() == 0)
34797 {
34798 AssignHeap (acontext);
34799 assert (acontext->get_alloc_heap());
34800 }
34801#endif //MULTIPLE_HEAPS
34802
34803#ifdef MULTIPLE_HEAPS
34804 gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34805#else
34806 gc_heap* hp = pGenGCHeap;
34807#ifdef _PREFAST_
34808 // prefix complains about us dereferencing hp in wks build even though we only access static members
34809 // this way. not sure how to shut it up except for this ugly workaround:
34810 PREFIX_ASSUME(hp != NULL);
34811#endif //_PREFAST_
34812#endif //MULTIPLE_HEAPS
34813
34814 if (size < loh_size_threshold)
34815 {
34816
34817#ifdef TRACE_GC
34818 AllocSmallCount++;
34819#endif //TRACE_GC
34820 newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
34821#ifdef FEATURE_STRUCTALIGN
34822 newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
34823#endif // FEATURE_STRUCTALIGN
34824// ASSERT (newAlloc);
34825 }
34826 else
34827 {
34828 newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
34829#ifdef FEATURE_STRUCTALIGN
34830 newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
34831#endif // FEATURE_STRUCTALIGN
34832 }
34833
34834 CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
34835
34836#ifdef TRACE_GC
34837#ifdef COUNT_CYCLES
34838 finish = GetCycleCount32();
34839#elif defined(ENABLE_INSTRUMENTATION)
34840 finish = GetInstLogTime();
34841#endif //COUNT_CYCLES
34842 AllocDuration += finish - AllocStart;
34843 AllocCount++;
34844#endif //TRACE_GC
34845 return newAlloc;
34846}
34847
34848void
34849GCHeap::FixAllocContext (gc_alloc_context* context, void* arg, void *heap)
34850{
34851 alloc_context* acontext = static_cast<alloc_context*>(context);
34852#ifdef MULTIPLE_HEAPS
34853
34854 if (arg != 0)
34855 acontext->alloc_count = 0;
34856
34857 uint8_t * alloc_ptr = acontext->alloc_ptr;
34858
34859 if (!alloc_ptr)
34860 return;
34861
34862 // The acontext->alloc_heap can be out of sync with the ptrs because
34863 // of heap re-assignment in allocate
34864 gc_heap* hp = gc_heap::heap_of (alloc_ptr);
34865#else
34866 gc_heap* hp = pGenGCHeap;
34867#endif //MULTIPLE_HEAPS
34868
34869 if (heap == NULL || heap == hp)
34870 {
34871 hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
34872 get_alignment_constant(TRUE));
34873 }
34874}
34875
34876Object*
34877GCHeap::GetContainingObject (void *pInteriorPtr, bool fCollectedGenOnly)
34878{
34879 uint8_t *o = (uint8_t*)pInteriorPtr;
34880
34881 gc_heap* hp = gc_heap::heap_of (o);
34882
34883 uint8_t* lowest = (fCollectedGenOnly ? hp->gc_low : hp->lowest_address);
34884 uint8_t* highest = (fCollectedGenOnly ? hp->gc_high : hp->highest_address);
34885
34886 if (o >= lowest && o < highest)
34887 {
34888 o = hp->find_object (o, lowest);
34889 }
34890 else
34891 {
34892 o = NULL;
34893 }
34894
34895 return (Object *)o;
34896}
34897
34898BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
34899{
34900 if (dd_new_allocation (dd) < 0)
34901 {
34902 return TRUE;
34903 }
34904
34905 if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
34906 {
34907 return TRUE;
34908 }
34909
34910 return FALSE;
34911}
34912
34913//----------------------------------------------------------------------------
34914// #GarbageCollector
34915//
34916// API to ensure that a complete new garbage collection takes place
34917//
34918HRESULT
34919GCHeap::GarbageCollect (int generation, bool low_memory_p, int mode)
34920{
34921#if defined(BIT64)
34922 if (low_memory_p)
34923 {
34924 size_t total_allocated = 0;
34925 size_t total_desired = 0;
34926#ifdef MULTIPLE_HEAPS
34927 int hn = 0;
34928 for (hn = 0; hn < gc_heap::n_heaps; hn++)
34929 {
34930 gc_heap* hp = gc_heap::g_heaps [hn];
34931 total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
34932 total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
34933 dd_new_allocation (hp->dynamic_data_of (0));
34934 }
34935#else
34936 gc_heap* hp = pGenGCHeap;
34937 total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
34938 total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
34939 dd_new_allocation (hp->dynamic_data_of (0));
34940#endif //MULTIPLE_HEAPS
34941
34942 if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
34943 {
34944 dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
34945 total_allocated, total_desired));
34946
34947 return S_OK;
34948 }
34949 }
34950#endif // BIT64
34951
34952#ifdef MULTIPLE_HEAPS
34953 gc_heap* hpt = gc_heap::g_heaps[0];
34954#else
34955 gc_heap* hpt = 0;
34956#endif //MULTIPLE_HEAPS
34957
34958 generation = (generation < 0) ? max_generation : min (generation, max_generation);
34959 dynamic_data* dd = hpt->dynamic_data_of (generation);
34960
34961#ifdef BACKGROUND_GC
34962 if (recursive_gc_sync::background_running_p())
34963 {
34964 if ((mode == collection_optimized) || (mode & collection_non_blocking))
34965 {
34966 return S_OK;
34967 }
34968 if (mode & collection_blocking)
34969 {
34970 pGenGCHeap->background_gc_wait();
34971 if (mode & collection_optimized)
34972 {
34973 return S_OK;
34974 }
34975 }
34976 }
34977#endif //BACKGROUND_GC
34978
34979 if (mode & collection_optimized)
34980 {
34981 if (pGenGCHeap->gc_started)
34982 {
34983 return S_OK;
34984 }
34985 else
34986 {
34987 BOOL should_collect = FALSE;
34988 BOOL should_check_loh = (generation == max_generation);
34989#ifdef MULTIPLE_HEAPS
34990 for (int i = 0; i < gc_heap::n_heaps; i++)
34991 {
34992 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
34993 dynamic_data* dd2 = (should_check_loh ?
34994 (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
34995 0);
34996
34997 if (should_collect_optimized (dd1, low_memory_p))
34998 {
34999 should_collect = TRUE;
35000 break;
35001 }
35002 if (dd2 && should_collect_optimized (dd2, low_memory_p))
35003 {
35004 should_collect = TRUE;
35005 break;
35006 }
35007 }
35008#else
35009 should_collect = should_collect_optimized (dd, low_memory_p);
35010 if (!should_collect && should_check_loh)
35011 {
35012 should_collect =
35013 should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
35014 }
35015#endif //MULTIPLE_HEAPS
35016 if (!should_collect)
35017 {
35018 return S_OK;
35019 }
35020 }
35021 }
35022
35023 size_t CollectionCountAtEntry = dd_collection_count (dd);
35024 size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
35025 size_t CurrentCollectionCount = 0;
35026
35027retry:
35028
35029 CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
35030
35031 if ((mode & collection_blocking) &&
35032 (generation == max_generation) &&
35033 (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
35034 {
35035#ifdef BACKGROUND_GC
35036 if (recursive_gc_sync::background_running_p())
35037 {
35038 pGenGCHeap->background_gc_wait();
35039 }
35040#endif //BACKGROUND_GC
35041
35042 goto retry;
35043 }
35044
35045 if (CollectionCountAtEntry == CurrentCollectionCount)
35046 {
35047 goto retry;
35048 }
35049
35050 return S_OK;
35051}
35052
35053size_t
35054GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
35055{
35056 int gen = (generation < 0) ?
35057 max_generation : min (generation, max_generation);
35058
35059 gc_reason reason = reason_empty;
35060
35061 if (low_memory_p)
35062 {
35063 if (mode & collection_blocking)
35064 {
35065 reason = reason_lowmemory_blocking;
35066 }
35067 else
35068 {
35069 reason = reason_lowmemory;
35070 }
35071 }
35072 else
35073 {
35074 reason = reason_induced;
35075 }
35076
35077 if (reason == reason_induced)
35078 {
35079 if (mode & collection_compacting)
35080 {
35081 reason = reason_induced_compacting;
35082 }
35083 else if (mode & collection_non_blocking)
35084 {
35085 reason = reason_induced_noforce;
35086 }
35087#ifdef STRESS_HEAP
35088 else if (mode & collection_gcstress)
35089 {
35090 reason = reason_gcstress;
35091 }
35092#endif
35093 }
35094
35095 return GarbageCollectGeneration (gen, reason);
35096}
35097
35098void gc_heap::do_pre_gc()
35099{
35100 STRESS_LOG_GC_STACK;
35101
35102#ifdef STRESS_LOG
35103 STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
35104 (uint32_t)settings.condemned_generation,
35105 (uint32_t)settings.reason);
35106#endif // STRESS_LOG
35107
35108#ifdef MULTIPLE_HEAPS
35109 gc_heap* hp = g_heaps[0];
35110#else
35111 gc_heap* hp = 0;
35112#endif //MULTIPLE_HEAPS
35113
35114#ifdef BACKGROUND_GC
35115 settings.b_state = hp->current_bgc_state;
35116#endif //BACKGROUND_GC
35117
35118#ifdef BACKGROUND_GC
35119 dprintf (1, ("*GC* %d(gen0:%d)(%d)(%s)(%d)",
35120 VolatileLoad(&settings.gc_index),
35121 dd_collection_count (hp->dynamic_data_of (0)),
35122 settings.condemned_generation,
35123 (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
35124 settings.b_state));
35125#else
35126 dprintf (1, ("*GC* %d(gen0:%d)(%d)",
35127 VolatileLoad(&settings.gc_index),
35128 dd_collection_count(hp->dynamic_data_of(0)),
35129 settings.condemned_generation));
35130#endif //BACKGROUND_GC
35131
35132 // TODO: this can happen...it's because of the way we are calling
35133 // do_pre_gc, will fix later.
35134 //if (last_gc_index > VolatileLoad(&settings.gc_index))
35135 //{
35136 // FATAL_GC_ERROR();
35137 //}
35138
35139 last_gc_index = VolatileLoad(&settings.gc_index);
35140 GCHeap::UpdatePreGCCounters();
35141
35142 if (settings.concurrent)
35143 {
35144#ifdef BACKGROUND_GC
35145 full_gc_counts[gc_type_background]++;
35146#if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
35147 GCHeap::gc_stress_fgcs_in_bgc = 0;
35148#endif // STRESS_HEAP && !FEATURE_REDHAWK
35149#endif // BACKGROUND_GC
35150 }
35151 else
35152 {
35153 if (settings.condemned_generation == max_generation)
35154 {
35155 full_gc_counts[gc_type_blocking]++;
35156 }
35157 else
35158 {
35159#ifdef BACKGROUND_GC
35160 if (settings.background_p)
35161 {
35162 ephemeral_fgc_counts[settings.condemned_generation]++;
35163 }
35164#endif //BACKGROUND_GC
35165 }
35166 }
35167
35168#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
35169 if (g_fEnableAppDomainMonitoring)
35170 {
35171 GCToEEInterface::ResetTotalSurvivedBytes();
35172 }
35173#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
35174}
35175
35176#ifdef GC_CONFIG_DRIVEN
35177void gc_heap::record_interesting_info_per_heap()
35178{
35179 // datapoints are always from the last blocking GC so don't record again
35180 // for BGCs.
35181 if (!(settings.concurrent))
35182 {
35183 for (int i = 0; i < max_idp_count; i++)
35184 {
35185 interesting_data_per_heap[i] += interesting_data_per_gc[i];
35186 }
35187 }
35188
35189 int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
35190 if (compact_reason >= 0)
35191 (compact_reasons_per_heap[compact_reason])++;
35192 int expand_mechanism = get_gc_data_per_heap()->get_mechanism (gc_heap_expand);
35193 if (expand_mechanism >= 0)
35194 (expand_mechanisms_per_heap[expand_mechanism])++;
35195
35196 for (int i = 0; i < max_gc_mechanism_bits_count; i++)
35197 {
35198 if (get_gc_data_per_heap()->is_mechanism_bit_set ((gc_mechanism_bit_per_heap)i))
35199 (interesting_mechanism_bits_per_heap[i])++;
35200 }
35201
35202 // h# | GC | gen | C | EX | NF | BF | ML | DM || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
35203 cprintf (("%2d | %6d | %1d | %1s | %2s | %2s | %2s | %2s | %2s || %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id |",
35204 heap_number,
35205 (size_t)settings.gc_index,
35206 settings.condemned_generation,
35207 // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs.
35208 (settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W") : ""), // compaction
35209 ((expand_mechanism >= 0)? "X" : ""), // EX
35210 ((expand_mechanism == expand_reuse_normal) ? "X" : ""), // NF
35211 ((expand_mechanism == expand_reuse_bestfit) ? "X" : ""), // BF
35212 (get_gc_data_per_heap()->is_mechanism_bit_set (gc_mark_list_bit) ? "X" : ""), // ML
35213 (get_gc_data_per_heap()->is_mechanism_bit_set (gc_demotion_bit) ? "X" : ""), // DM
35214 interesting_data_per_gc[idp_pre_short],
35215 interesting_data_per_gc[idp_post_short],
35216 interesting_data_per_gc[idp_merged_pin],
35217 interesting_data_per_gc[idp_converted_pin],
35218 interesting_data_per_gc[idp_pre_pin],
35219 interesting_data_per_gc[idp_post_pin],
35220 interesting_data_per_gc[idp_pre_and_post_pin],
35221 interesting_data_per_gc[idp_pre_short_padded],
35222 interesting_data_per_gc[idp_post_short_padded]));
35223}
35224
35225void gc_heap::record_global_mechanisms()
35226{
35227 for (int i = 0; i < max_global_mechanisms_count; i++)
35228 {
35229 if (gc_data_global.get_mechanism_p ((gc_global_mechanism_p)i))
35230 {
35231 ::record_global_mechanism (i);
35232 }
35233 }
35234}
35235
35236BOOL gc_heap::should_do_sweeping_gc (BOOL compact_p)
35237{
35238 if (!compact_ratio)
35239 return (!compact_p);
35240
35241 size_t compact_count = compact_or_sweep_gcs[0];
35242 size_t sweep_count = compact_or_sweep_gcs[1];
35243
35244 size_t total_count = compact_count + sweep_count;
35245 BOOL should_compact = compact_p;
35246 if (total_count > 3)
35247 {
35248 if (compact_p)
35249 {
35250 int temp_ratio = (int)((compact_count + 1) * 100 / (total_count + 1));
35251 if (temp_ratio > compact_ratio)
35252 {
35253 // cprintf (("compact would be: %d, total_count: %d, ratio would be %d%% > target\n",
35254 // (compact_count + 1), (total_count + 1), temp_ratio));
35255 should_compact = FALSE;
35256 }
35257 }
35258 else
35259 {
35260 int temp_ratio = (int)((sweep_count + 1) * 100 / (total_count + 1));
35261 if (temp_ratio > (100 - compact_ratio))
35262 {
35263 // cprintf (("sweep would be: %d, total_count: %d, ratio would be %d%% > target\n",
35264 // (sweep_count + 1), (total_count + 1), temp_ratio));
35265 should_compact = TRUE;
35266 }
35267 }
35268 }
35269
35270 return !should_compact;
35271}
35272#endif //GC_CONFIG_DRIVEN
35273
35274bool gc_heap::is_pm_ratio_exceeded()
35275{
35276 size_t maxgen_frag = 0;
35277 size_t maxgen_size = 0;
35278 size_t total_heap_size = get_total_heap_size();
35279
35280#ifdef MULTIPLE_HEAPS
35281 for (int i = 0; i < gc_heap::n_heaps; i++)
35282 {
35283 gc_heap* hp = gc_heap::g_heaps[i];
35284#else //MULTIPLE_HEAPS
35285 {
35286 gc_heap* hp = pGenGCHeap;
35287#endif //MULTIPLE_HEAPS
35288
35289 maxgen_frag += dd_fragmentation (hp->dynamic_data_of (max_generation));
35290 maxgen_size += hp->generation_size (max_generation);
35291 }
35292
35293 double maxgen_ratio = (double)maxgen_size / (double)total_heap_size;
35294 double maxgen_frag_ratio = (double)maxgen_frag / (double)maxgen_size;
35295 dprintf (GTC_LOG, ("maxgen %Id(%d%% total heap), frag: %Id (%d%% maxgen)",
35296 maxgen_size, (int)(maxgen_ratio * 100.0),
35297 maxgen_frag, (int)(maxgen_frag_ratio * 100.0)));
35298
35299 bool maxgen_highfrag_p = ((maxgen_ratio > 0.5) && (maxgen_frag_ratio > 0.1));
35300
35301 // We need to adjust elevation here because if there's enough fragmentation it's not
35302 // unproductive.
35303 if (maxgen_highfrag_p)
35304 {
35305 settings.should_lock_elevation = FALSE;
35306 dprintf (GTC_LOG, ("high frag gen2, turn off elevation"));
35307 }
35308
35309 return maxgen_highfrag_p;
35310}
35311
35312void gc_heap::do_post_gc()
35313{
35314 if (!settings.concurrent)
35315 {
35316 initGCShadow();
35317 }
35318
35319#ifdef TRACE_GC
35320#ifdef COUNT_CYCLES
35321 AllocStart = GetCycleCount32();
35322#else
35323 AllocStart = clock();
35324#endif //COUNT_CYCLES
35325#endif //TRACE_GC
35326
35327#ifdef MULTIPLE_HEAPS
35328 gc_heap* hp = g_heaps[0];
35329#else
35330 gc_heap* hp = 0;
35331#endif //MULTIPLE_HEAPS
35332
35333 GCToEEInterface::GcDone(settings.condemned_generation);
35334
35335 GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index),
35336 (uint32_t)settings.condemned_generation,
35337 (uint32_t)settings.reason,
35338 !!settings.concurrent);
35339
35340 //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)",
35341 dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)",
35342 VolatileLoad(&settings.gc_index),
35343 dd_collection_count(hp->dynamic_data_of(0)),
35344 settings.condemned_generation,
35345 (settings.concurrent ? "BGC" : "GC")));
35346
35347 if (settings.exit_memory_load != 0)
35348 last_gc_memory_load = settings.exit_memory_load;
35349 else if (settings.entry_memory_load != 0)
35350 last_gc_memory_load = settings.entry_memory_load;
35351
35352 last_gc_heap_size = get_total_heap_size();
35353 last_gc_fragmentation = get_total_fragmentation();
35354
35355 // Note we only do this at the end of full blocking GCs because we do not want
35356 // to turn on this provisional mode during the middle of a BGC.
35357 if ((settings.condemned_generation == max_generation) && (!settings.concurrent))
35358 {
35359 if (pm_stress_on)
35360 {
35361 size_t full_compacting_gc_count = full_gc_counts[gc_type_compacting];
35362 if (provisional_mode_triggered)
35363 {
35364 uint64_t r = gc_rand::get_rand(10);
35365 if ((full_compacting_gc_count - provisional_triggered_gc_count) >= r)
35366 {
35367 provisional_mode_triggered = false;
35368 provisional_off_gc_count = full_compacting_gc_count;
35369 dprintf (GTC_LOG, ("%Id NGC2s when turned on, %Id NGCs since(%Id)",
35370 provisional_triggered_gc_count, (full_compacting_gc_count - provisional_triggered_gc_count),
35371 num_provisional_triggered));
35372 }
35373 }
35374 else
35375 {
35376 uint64_t r = gc_rand::get_rand(5);
35377 if ((full_compacting_gc_count - provisional_off_gc_count) >= r)
35378 {
35379 provisional_mode_triggered = true;
35380 provisional_triggered_gc_count = full_compacting_gc_count;
35381 num_provisional_triggered++;
35382 dprintf (GTC_LOG, ("%Id NGC2s when turned off, %Id NGCs since(%Id)",
35383 provisional_off_gc_count, (full_compacting_gc_count - provisional_off_gc_count),
35384 num_provisional_triggered));
35385 }
35386 }
35387 }
35388 else
35389 {
35390 if (provisional_mode_triggered)
35391 {
35392 if ((settings.entry_memory_load < high_memory_load_th) ||
35393 !is_pm_ratio_exceeded())
35394 {
35395 dprintf (GTC_LOG, ("turning off PM"));
35396 provisional_mode_triggered = false;
35397 }
35398 }
35399 else if ((settings.entry_memory_load >= high_memory_load_th) && is_pm_ratio_exceeded())
35400 {
35401 dprintf (GTC_LOG, ("highmem && highfrag - turning on PM"));
35402 provisional_mode_triggered = true;
35403 num_provisional_triggered++;
35404 }
35405 }
35406 }
35407
35408 GCHeap::UpdatePostGCCounters();
35409#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
35410 //if (g_fEnableARM)
35411 //{
35412 // SystemDomain::GetADSurvivedBytes();
35413 //}
35414#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
35415
35416#ifdef STRESS_LOG
35417 STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
35418 (uint32_t)settings.condemned_generation,
35419 (uint32_t)settings.reason);
35420#endif // STRESS_LOG
35421
35422#ifdef GC_CONFIG_DRIVEN
35423 if (!settings.concurrent)
35424 {
35425 if (settings.compaction)
35426 (compact_or_sweep_gcs[0])++;
35427 else
35428 (compact_or_sweep_gcs[1])++;
35429 }
35430
35431#ifdef MULTIPLE_HEAPS
35432 for (int i = 0; i < n_heaps; i++)
35433 g_heaps[i]->record_interesting_info_per_heap();
35434#else
35435 record_interesting_info_per_heap();
35436#endif //MULTIPLE_HEAPS
35437 record_global_mechanisms();
35438#endif //GC_CONFIG_DRIVEN
35439}
35440
35441unsigned GCHeap::GetGcCount()
35442{
35443 return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
35444}
35445
35446size_t
35447GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
35448{
35449 dprintf (2, ("triggered a GC!"));
35450
35451#ifdef MULTIPLE_HEAPS
35452 gc_heap* hpt = gc_heap::g_heaps[0];
35453#else
35454 gc_heap* hpt = 0;
35455#endif //MULTIPLE_HEAPS
35456 bool cooperative_mode = true;
35457 dynamic_data* dd = hpt->dynamic_data_of (gen);
35458 size_t localCount = dd_collection_count (dd);
35459
35460 enter_spin_lock (&gc_heap::gc_lock);
35461 dprintf (SPINLOCK_LOG, ("GC Egc"));
35462 ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
35463
35464 //don't trigger another GC if one was already in progress
35465 //while waiting for the lock
35466 {
35467 size_t col_count = dd_collection_count (dd);
35468
35469 if (localCount != col_count)
35470 {
35471#ifdef SYNCHRONIZATION_STATS
35472 gc_lock_contended++;
35473#endif //SYNCHRONIZATION_STATS
35474 dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
35475 leave_spin_lock (&gc_heap::gc_lock);
35476
35477 // We don't need to release msl here 'cause this means a GC
35478 // has happened and would have release all msl's.
35479 return col_count;
35480 }
35481 }
35482
35483#ifdef COUNT_CYCLES
35484 int gc_start = GetCycleCount32();
35485#endif //COUNT_CYCLES
35486
35487#ifdef TRACE_GC
35488#ifdef COUNT_CYCLES
35489 AllocDuration += GetCycleCount32() - AllocStart;
35490#else
35491 AllocDuration += clock() - AllocStart;
35492#endif //COUNT_CYCLES
35493#endif //TRACE_GC
35494
35495 gc_heap::g_low_memory_status = (reason == reason_lowmemory) ||
35496 (reason == reason_lowmemory_blocking) ||
35497 (gc_heap::latency_level == latency_level_memory_footprint);
35498
35499 gc_trigger_reason = reason;
35500
35501#ifdef MULTIPLE_HEAPS
35502 for (int i = 0; i < gc_heap::n_heaps; i++)
35503 {
35504 gc_heap::g_heaps[i]->reset_gc_done();
35505 }
35506#else
35507 gc_heap::reset_gc_done();
35508#endif //MULTIPLE_HEAPS
35509
35510 gc_heap::gc_started = TRUE;
35511
35512 {
35513 init_sync_log_stats();
35514
35515#ifndef MULTIPLE_HEAPS
35516 cooperative_mode = gc_heap::enable_preemptive ();
35517
35518 dprintf (2, ("Suspending EE"));
35519 BEGIN_TIMING(suspend_ee_during_log);
35520 GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
35521 END_TIMING(suspend_ee_during_log);
35522 gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();
35523 gc_heap::disable_preemptive (cooperative_mode);
35524 if (gc_heap::proceed_with_gc_p)
35525 pGenGCHeap->settings.init_mechanisms();
35526 else
35527 gc_heap::update_collection_counts_for_no_gc();
35528
35529#endif //!MULTIPLE_HEAPS
35530 }
35531
35532// MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
35533
35534#ifdef TRACE_GC
35535#ifdef COUNT_CYCLES
35536 unsigned start;
35537 unsigned finish;
35538 start = GetCycleCount32();
35539#else
35540 clock_t start;
35541 clock_t finish;
35542 start = clock();
35543#endif //COUNT_CYCLES
35544 PromotedObjectCount = 0;
35545#endif //TRACE_GC
35546
35547 unsigned int condemned_generation_number = gen;
35548
35549 // We want to get a stack from the user thread that triggered the GC
35550 // instead of on the GC thread which is the case for Server GC.
35551 // But we are doing it for Workstation GC as well to be uniform.
35552 FIRE_EVENT(GCTriggered, static_cast<uint32_t>(reason));
35553
35554#ifdef MULTIPLE_HEAPS
35555 GcCondemnedGeneration = condemned_generation_number;
35556
35557 cooperative_mode = gc_heap::enable_preemptive ();
35558
35559 BEGIN_TIMING(gc_during_log);
35560 gc_heap::ee_suspend_event.Set();
35561 gc_heap::wait_for_gc_done();
35562 END_TIMING(gc_during_log);
35563
35564 gc_heap::disable_preemptive (cooperative_mode);
35565
35566 condemned_generation_number = GcCondemnedGeneration;
35567#else
35568 if (gc_heap::proceed_with_gc_p)
35569 {
35570 BEGIN_TIMING(gc_during_log);
35571 pGenGCHeap->garbage_collect (condemned_generation_number);
35572 if (gc_heap::pm_trigger_full_gc)
35573 {
35574 pGenGCHeap->garbage_collect_pm_full_gc();
35575 }
35576 END_TIMING(gc_during_log);
35577 }
35578#endif //MULTIPLE_HEAPS
35579
35580#ifdef TRACE_GC
35581#ifdef COUNT_CYCLES
35582 finish = GetCycleCount32();
35583#else
35584 finish = clock();
35585#endif //COUNT_CYCLES
35586 GcDuration += finish - start;
35587 dprintf (3,
35588 ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
35589 VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
35590 finish - start, GcDuration,
35591 AllocCount ? (AllocDuration / AllocCount) : 0,
35592 AllocSmallCount, AllocBigCount));
35593 AllocCount = 0;
35594 AllocDuration = 0;
35595#endif // TRACE_GC
35596
35597#ifdef BACKGROUND_GC
35598 // We are deciding whether we should fire the alloc wait end event here
35599 // because in begin_foreground we could be calling end_foreground
35600 // if we need to retry.
35601 if (gc_heap::alloc_wait_event_p)
35602 {
35603 hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
35604 gc_heap::alloc_wait_event_p = FALSE;
35605 }
35606#endif //BACKGROUND_GC
35607
35608#ifndef MULTIPLE_HEAPS
35609#ifdef BACKGROUND_GC
35610 if (!gc_heap::dont_restart_ee_p)
35611 {
35612#endif //BACKGROUND_GC
35613 BEGIN_TIMING(restart_ee_during_log);
35614 GCToEEInterface::RestartEE(TRUE);
35615 END_TIMING(restart_ee_during_log);
35616#ifdef BACKGROUND_GC
35617 }
35618#endif //BACKGROUND_GC
35619#endif //!MULTIPLE_HEAPS
35620
35621#ifdef COUNT_CYCLES
35622 printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
35623 GetCycleCount32() - gc_start);
35624#endif //COUNT_CYCLES
35625
35626#ifndef MULTIPLE_HEAPS
35627 process_sync_log_stats();
35628 gc_heap::gc_started = FALSE;
35629 gc_heap::set_gc_done();
35630 dprintf (SPINLOCK_LOG, ("GC Lgc"));
35631 leave_spin_lock (&gc_heap::gc_lock);
35632#endif //!MULTIPLE_HEAPS
35633
35634#ifdef FEATURE_PREMORTEM_FINALIZATION
35635 GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers);
35636#endif // FEATURE_PREMORTEM_FINALIZATION
35637
35638 return dd_collection_count (dd);
35639}
35640
35641size_t GCHeap::GetTotalBytesInUse ()
35642{
35643#ifdef MULTIPLE_HEAPS
35644 //enumarate all the heaps and get their size.
35645 size_t tot_size = 0;
35646 for (int i = 0; i < gc_heap::n_heaps; i++)
35647 {
35648 GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
35649 tot_size += Hp->ApproxTotalBytesInUse (FALSE);
35650 }
35651 return tot_size;
35652#else
35653 return ApproxTotalBytesInUse ();
35654#endif //MULTIPLE_HEAPS
35655}
35656
35657int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
35658{
35659 if (get_bgc_fgc_count != 0)
35660 {
35661#ifdef BACKGROUND_GC
35662 if (generation == max_generation)
35663 {
35664 return (int)(gc_heap::full_gc_counts[gc_type_background]);
35665 }
35666 else
35667 {
35668 return (int)(gc_heap::ephemeral_fgc_counts[generation]);
35669 }
35670#else
35671 return 0;
35672#endif //BACKGROUND_GC
35673 }
35674
35675#ifdef MULTIPLE_HEAPS
35676 gc_heap* hp = gc_heap::g_heaps [0];
35677#else //MULTIPLE_HEAPS
35678 gc_heap* hp = pGenGCHeap;
35679#endif //MULTIPLE_HEAPS
35680 if (generation > max_generation)
35681 return 0;
35682 else
35683 return (int)dd_collection_count (hp->dynamic_data_of (generation));
35684}
35685
35686size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
35687{
35688 size_t totsize = 0;
35689 //GCTODO
35690 //ASSERT(InMustComplete());
35691 enter_spin_lock (&pGenGCHeap->gc_lock);
35692
35693 heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
35694 // Get small block heap size info
35695 totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
35696 heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
35697 while (seg1 != eph_seg)
35698 {
35699 totsize += heap_segment_allocated (seg1) -
35700 heap_segment_mem (seg1);
35701 seg1 = heap_segment_next (seg1);
35702 }
35703
35704 //discount the fragmentation
35705 for (int i = 0; i <= max_generation; i++)
35706 {
35707 generation* gen = pGenGCHeap->generation_of (i);
35708 totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
35709 }
35710
35711 if (!small_heap_only)
35712 {
35713 heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
35714
35715 while (seg2 != 0)
35716 {
35717 totsize += heap_segment_allocated (seg2) -
35718 heap_segment_mem (seg2);
35719 seg2 = heap_segment_next (seg2);
35720 }
35721
35722 //discount the fragmentation
35723 generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
35724 size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
35725 totsize -= frag;
35726 }
35727 leave_spin_lock (&pGenGCHeap->gc_lock);
35728 return totsize;
35729}
35730
35731#ifdef MULTIPLE_HEAPS
35732void GCHeap::AssignHeap (alloc_context* acontext)
35733{
35734 // Assign heap based on processor
35735 acontext->set_alloc_heap(GetHeap(heap_select::select_heap(acontext, 0)));
35736 acontext->set_home_heap(acontext->get_alloc_heap());
35737}
35738GCHeap* GCHeap::GetHeap (int n)
35739{
35740 assert (n < gc_heap::n_heaps);
35741 return gc_heap::g_heaps [n]->vm_heap;
35742}
35743#endif //MULTIPLE_HEAPS
35744
35745bool GCHeap::IsThreadUsingAllocationContextHeap(gc_alloc_context* context, int thread_number)
35746{
35747 alloc_context* acontext = static_cast<alloc_context*>(context);
35748#ifdef MULTIPLE_HEAPS
35749 return ((acontext->get_home_heap() == GetHeap(thread_number)) ||
35750 ((acontext->get_home_heap() == 0) && (thread_number == 0)));
35751#else
35752 UNREFERENCED_PARAMETER(acontext);
35753 UNREFERENCED_PARAMETER(thread_number);
35754 return true;
35755#endif //MULTIPLE_HEAPS
35756}
35757
35758// Returns the number of processors required to trigger the use of thread based allocation contexts
35759int GCHeap::GetNumberOfHeaps ()
35760{
35761#ifdef MULTIPLE_HEAPS
35762 return gc_heap::n_heaps;
35763#else
35764 return 1;
35765#endif //MULTIPLE_HEAPS
35766}
35767
35768/*
35769 in this way we spend extra time cycling through all the heaps while create the handle
35770 it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
35771*/
35772int GCHeap::GetHomeHeapNumber ()
35773{
35774#ifdef MULTIPLE_HEAPS
35775 gc_alloc_context* ctx = GCToEEInterface::GetAllocContext();
35776 if (!ctx)
35777 {
35778 return 0;
35779 }
35780
35781 GCHeap *hp = static_cast<alloc_context*>(ctx)->get_home_heap();
35782 return (hp ? hp->pGenGCHeap->heap_number : 0);
35783#else
35784 return 0;
35785#endif //MULTIPLE_HEAPS
35786}
35787
35788unsigned int GCHeap::GetCondemnedGeneration()
35789{
35790 return gc_heap::settings.condemned_generation;
35791}
35792
35793void GCHeap::GetMemoryInfo(uint32_t* highMemLoadThreshold,
35794 uint64_t* totalPhysicalMem,
35795 uint32_t* lastRecordedMemLoad,
35796 size_t* lastRecordedHeapSize,
35797 size_t* lastRecordedFragmentation)
35798{
35799 *highMemLoadThreshold = gc_heap::high_memory_load_th;
35800 *totalPhysicalMem = gc_heap::total_physical_mem;
35801 *lastRecordedMemLoad = gc_heap::last_gc_memory_load;
35802 *lastRecordedHeapSize = gc_heap::last_gc_heap_size;
35803 *lastRecordedFragmentation = gc_heap::last_gc_fragmentation;
35804}
35805
35806int GCHeap::GetGcLatencyMode()
35807{
35808 return (int)(pGenGCHeap->settings.pause_mode);
35809}
35810
35811int GCHeap::SetGcLatencyMode (int newLatencyMode)
35812{
35813 if (gc_heap::settings.pause_mode == pause_no_gc)
35814 return (int)set_pause_mode_no_gc;
35815
35816 gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
35817
35818 if (new_mode == pause_low_latency)
35819 {
35820#ifndef MULTIPLE_HEAPS
35821 pGenGCHeap->settings.pause_mode = new_mode;
35822#endif //!MULTIPLE_HEAPS
35823 }
35824 else if (new_mode == pause_sustained_low_latency)
35825 {
35826#ifdef BACKGROUND_GC
35827 if (gc_heap::gc_can_use_concurrent)
35828 {
35829 pGenGCHeap->settings.pause_mode = new_mode;
35830 }
35831#endif //BACKGROUND_GC
35832 }
35833 else
35834 {
35835 pGenGCHeap->settings.pause_mode = new_mode;
35836 }
35837
35838#ifdef BACKGROUND_GC
35839 if (recursive_gc_sync::background_running_p())
35840 {
35841 // If we get here, it means we are doing an FGC. If the pause
35842 // mode was altered we will need to save it in the BGC settings.
35843 if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
35844 {
35845 gc_heap::saved_bgc_settings.pause_mode = new_mode;
35846 }
35847 }
35848#endif //BACKGROUND_GC
35849
35850 return (int)set_pause_mode_success;
35851}
35852
35853int GCHeap::GetLOHCompactionMode()
35854{
35855 return pGenGCHeap->loh_compaction_mode;
35856}
35857
35858void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
35859{
35860#ifdef FEATURE_LOH_COMPACTION
35861 pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
35862#endif //FEATURE_LOH_COMPACTION
35863}
35864
35865bool GCHeap::RegisterForFullGCNotification(uint32_t gen2Percentage,
35866 uint32_t lohPercentage)
35867{
35868#ifdef MULTIPLE_HEAPS
35869 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
35870 {
35871 gc_heap* hp = gc_heap::g_heaps [hn];
35872 hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
35873 }
35874#else //MULTIPLE_HEAPS
35875 pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
35876#endif //MULTIPLE_HEAPS
35877
35878 pGenGCHeap->full_gc_approach_event.Reset();
35879 pGenGCHeap->full_gc_end_event.Reset();
35880 pGenGCHeap->full_gc_approach_event_set = false;
35881
35882 pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
35883 pGenGCHeap->fgn_loh_percent = lohPercentage;
35884
35885 return TRUE;
35886}
35887
35888bool GCHeap::CancelFullGCNotification()
35889{
35890 pGenGCHeap->fgn_maxgen_percent = 0;
35891 pGenGCHeap->fgn_loh_percent = 0;
35892
35893 pGenGCHeap->full_gc_approach_event.Set();
35894 pGenGCHeap->full_gc_end_event.Set();
35895
35896 return TRUE;
35897}
35898
35899int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
35900{
35901 dprintf (2, ("WFGA: Begin wait"));
35902 int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
35903 dprintf (2, ("WFGA: End wait"));
35904 return result;
35905}
35906
35907int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
35908{
35909 dprintf (2, ("WFGE: Begin wait"));
35910 int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
35911 dprintf (2, ("WFGE: End wait"));
35912 return result;
35913}
35914
35915int GCHeap::StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC)
35916{
35917 NoGCRegionLockHolder lh;
35918
35919 dprintf (1, ("begin no gc called"));
35920 start_no_gc_region_status status = gc_heap::prepare_for_no_gc_region (totalSize, lohSizeKnown, lohSize, disallowFullBlockingGC);
35921 if (status == start_no_gc_success)
35922 {
35923 GarbageCollect (max_generation);
35924 status = gc_heap::get_start_no_gc_region_status();
35925 }
35926
35927 if (status != start_no_gc_success)
35928 gc_heap::handle_failure_for_no_gc();
35929
35930 return (int)status;
35931}
35932
35933int GCHeap::EndNoGCRegion()
35934{
35935 NoGCRegionLockHolder lh;
35936 return (int)gc_heap::end_no_gc_region();
35937}
35938
35939void GCHeap::PublishObject (uint8_t* Obj)
35940{
35941#ifdef BACKGROUND_GC
35942 gc_heap* hp = gc_heap::heap_of (Obj);
35943 hp->bgc_alloc_lock->loh_alloc_done (Obj);
35944 hp->bgc_untrack_loh_alloc();
35945#endif //BACKGROUND_GC
35946}
35947
35948// The spec for this one isn't clear. This function
35949// returns the size that can be allocated without
35950// triggering a GC of any kind.
35951size_t GCHeap::ApproxFreeBytes()
35952{
35953 //GCTODO
35954 //ASSERT(InMustComplete());
35955 enter_spin_lock (&pGenGCHeap->gc_lock);
35956
35957 generation* gen = pGenGCHeap->generation_of (0);
35958 size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
35959
35960 leave_spin_lock (&pGenGCHeap->gc_lock);
35961
35962 return res;
35963}
35964
35965HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
35966{
35967 if ((gen < 0) || (gen > max_generation))
35968 return E_FAIL;
35969#ifdef MULTIPLE_HEAPS
35970 counters->current_size = 0;
35971 counters->promoted_size = 0;
35972 counters->collection_count = 0;
35973
35974 //enumarate all the heaps and get their counters.
35975 for (int i = 0; i < gc_heap::n_heaps; i++)
35976 {
35977 dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
35978
35979 counters->current_size += dd_current_size (dd);
35980 counters->promoted_size += dd_promoted_size (dd);
35981 if (i == 0)
35982 counters->collection_count += dd_collection_count (dd);
35983 }
35984#else
35985 dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
35986 counters->current_size = dd_current_size (dd);
35987 counters->promoted_size = dd_promoted_size (dd);
35988 counters->collection_count = dd_collection_count (dd);
35989#endif //MULTIPLE_HEAPS
35990 return S_OK;
35991}
35992
35993// Get the segment size to use, making sure it conforms.
35994size_t GCHeap::GetValidSegmentSize(bool large_seg)
35995{
35996 return get_valid_segment_size (large_seg);
35997}
35998
35999// Get the max gen0 heap size, making sure it conforms.
36000size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
36001{
36002 size_t gen0size = static_cast<size_t>(GCConfig::GetGen0Size());
36003
36004 if ((gen0size == 0) || !g_theGCHeap->IsValidGen0MaxSize(gen0size))
36005 {
36006#ifdef SERVER_GC
36007 // performance data seems to indicate halving the size results
36008 // in optimal perf. Ask for adjusted gen0 size.
36009 gen0size = max(GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),(256*1024));
36010
36011 // if gen0 size is too large given the available memory, reduce it.
36012 // Get true cache size, as we don't want to reduce below this.
36013 size_t trueSize = max(GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE),(256*1024));
36014 dprintf (2, ("cache: %Id-%Id, cpu: %Id",
36015 GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),
36016 GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)));
36017
36018 int n_heaps = gc_heap::n_heaps;
36019#else //SERVER_GC
36020 size_t trueSize = GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE);
36021 gen0size = max((4*trueSize/5),(256*1024));
36022 trueSize = max(trueSize, (256*1024));
36023 int n_heaps = 1;
36024#endif //SERVER_GC
36025
36026 // if the total min GC across heaps will exceed 1/6th of available memory,
36027 // then reduce the min GC size until it either fits or has been reduced to cache size.
36028 while ((gen0size * n_heaps) > GCToOSInterface::GetPhysicalMemoryLimit() / 6)
36029 {
36030 gen0size = gen0size / 2;
36031 if (gen0size <= trueSize)
36032 {
36033 gen0size = trueSize;
36034 break;
36035 }
36036 }
36037 }
36038
36039 // Generation 0 must never be more than 1/2 the segment size.
36040 if (gen0size >= (seg_size / 2))
36041 gen0size = seg_size / 2;
36042
36043 return (gen0size);
36044}
36045
36046void GCHeap::SetReservedVMLimit (size_t vmlimit)
36047{
36048 gc_heap::reserved_memory_limit = vmlimit;
36049}
36050
36051
36052//versions of same method on each heap
36053
36054#ifdef FEATURE_PREMORTEM_FINALIZATION
36055
36056Object* GCHeap::GetNextFinalizableObject()
36057{
36058
36059#ifdef MULTIPLE_HEAPS
36060
36061 //return the first non critical one in the first queue.
36062 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36063 {
36064 gc_heap* hp = gc_heap::g_heaps [hn];
36065 Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
36066 if (O)
36067 return O;
36068 }
36069 //return the first non crtitical/critical one in the first queue.
36070 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36071 {
36072 gc_heap* hp = gc_heap::g_heaps [hn];
36073 Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
36074 if (O)
36075 return O;
36076 }
36077 return 0;
36078
36079
36080#else //MULTIPLE_HEAPS
36081 return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
36082#endif //MULTIPLE_HEAPS
36083
36084}
36085
36086size_t GCHeap::GetNumberFinalizableObjects()
36087{
36088#ifdef MULTIPLE_HEAPS
36089 size_t cnt = 0;
36090 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36091 {
36092 gc_heap* hp = gc_heap::g_heaps [hn];
36093 cnt += hp->finalize_queue->GetNumberFinalizableObjects();
36094 }
36095 return cnt;
36096
36097
36098#else //MULTIPLE_HEAPS
36099 return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
36100#endif //MULTIPLE_HEAPS
36101}
36102
36103size_t GCHeap::GetFinalizablePromotedCount()
36104{
36105#ifdef MULTIPLE_HEAPS
36106 size_t cnt = 0;
36107
36108 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36109 {
36110 gc_heap* hp = gc_heap::g_heaps [hn];
36111 cnt += hp->finalize_queue->GetPromotedCount();
36112 }
36113 return cnt;
36114
36115#else //MULTIPLE_HEAPS
36116 return pGenGCHeap->finalize_queue->GetPromotedCount();
36117#endif //MULTIPLE_HEAPS
36118}
36119
36120bool GCHeap::FinalizeAppDomain(void *pDomain, bool fRunFinalizers)
36121{
36122#ifdef MULTIPLE_HEAPS
36123 bool foundp = false;
36124 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36125 {
36126 gc_heap* hp = gc_heap::g_heaps [hn];
36127 if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
36128 foundp = true;
36129 }
36130 return foundp;
36131
36132#else //MULTIPLE_HEAPS
36133 return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
36134#endif //MULTIPLE_HEAPS
36135}
36136
36137bool GCHeap::ShouldRestartFinalizerWatchDog()
36138{
36139 // This condition was historically used as part of the condition to detect finalizer thread timeouts
36140 return gc_heap::gc_lock.lock != -1;
36141}
36142
36143void GCHeap::SetFinalizeQueueForShutdown(bool fHasLock)
36144{
36145#ifdef MULTIPLE_HEAPS
36146 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36147 {
36148 gc_heap* hp = gc_heap::g_heaps [hn];
36149 hp->finalize_queue->SetSegForShutDown(fHasLock);
36150 }
36151
36152#else //MULTIPLE_HEAPS
36153 pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
36154#endif //MULTIPLE_HEAPS
36155}
36156
36157//---------------------------------------------------------------------------
36158// Finalized class tracking
36159//---------------------------------------------------------------------------
36160
36161bool GCHeap::RegisterForFinalization (int gen, Object* obj)
36162{
36163 if (gen == -1)
36164 gen = 0;
36165 if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
36166 {
36167 //just reset the bit
36168 ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
36169 return true;
36170 }
36171 else
36172 {
36173 gc_heap* hp = gc_heap::heap_of ((uint8_t*)obj);
36174 return hp->finalize_queue->RegisterForFinalization (gen, obj);
36175 }
36176}
36177
36178void GCHeap::SetFinalizationRun (Object* obj)
36179{
36180 ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
36181}
36182
36183
36184//--------------------------------------------------------------------
36185//
36186// Support for finalization
36187//
36188//--------------------------------------------------------------------
36189
36190inline
36191unsigned int gen_segment (int gen)
36192{
36193 assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
36194 return (NUMBERGENERATIONS - gen - 1);
36195}
36196
36197bool CFinalize::Initialize()
36198{
36199 CONTRACTL {
36200 NOTHROW;
36201 GC_NOTRIGGER;
36202 } CONTRACTL_END;
36203
36204 m_Array = new (nothrow)(Object*[100]);
36205
36206 if (!m_Array)
36207 {
36208 ASSERT (m_Array);
36209 STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
36210 if (GCConfig::GetBreakOnOOM())
36211 {
36212 GCToOSInterface::DebugBreak();
36213 }
36214 return false;
36215 }
36216 m_EndArray = &m_Array[100];
36217
36218 for (int i =0; i < FreeList; i++)
36219 {
36220 SegQueueLimit (i) = m_Array;
36221 }
36222 m_PromotedCount = 0;
36223 lock = -1;
36224#ifdef _DEBUG
36225 lockowner_threadid.Clear();
36226#endif // _DEBUG
36227
36228 return true;
36229}
36230
36231CFinalize::~CFinalize()
36232{
36233 delete m_Array;
36234}
36235
36236size_t CFinalize::GetPromotedCount ()
36237{
36238 return m_PromotedCount;
36239}
36240
36241inline
36242void CFinalize::EnterFinalizeLock()
36243{
36244 _ASSERTE(dbgOnly_IsSpecialEEThread() ||
36245 GCToEEInterface::GetThread() == 0 ||
36246 GCToEEInterface::IsPreemptiveGCDisabled());
36247
36248retry:
36249 if (Interlocked::CompareExchange(&lock, 0, -1) >= 0)
36250 {
36251 unsigned int i = 0;
36252 while (lock >= 0)
36253 {
36254 YieldProcessor(); // indicate to the processor that we are spining
36255 if (++i & 7)
36256 GCToOSInterface::YieldThread (0);
36257 else
36258 GCToOSInterface::Sleep (5);
36259 }
36260 goto retry;
36261 }
36262
36263#ifdef _DEBUG
36264 lockowner_threadid.SetToCurrentThread();
36265#endif // _DEBUG
36266}
36267
36268inline
36269void CFinalize::LeaveFinalizeLock()
36270{
36271 _ASSERTE(dbgOnly_IsSpecialEEThread() ||
36272 GCToEEInterface::GetThread() == 0 ||
36273 GCToEEInterface::IsPreemptiveGCDisabled());
36274
36275#ifdef _DEBUG
36276 lockowner_threadid.Clear();
36277#endif // _DEBUG
36278 lock = -1;
36279}
36280
36281bool
36282CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
36283{
36284 CONTRACTL {
36285 NOTHROW;
36286 GC_NOTRIGGER;
36287 } CONTRACTL_END;
36288
36289 EnterFinalizeLock();
36290 // Adjust gen
36291 unsigned int dest = 0;
36292
36293 if (g_fFinalizerRunOnShutDown)
36294 {
36295 //no method table available yet,
36296 //put it in the finalizer queue and sort out when
36297 //dequeueing
36298 dest = FinalizerListSeg;
36299 }
36300
36301 else
36302 dest = gen_segment (gen);
36303
36304 // Adjust boundary for segments so that GC will keep objects alive.
36305 Object*** s_i = &SegQueue (FreeList);
36306 if ((*s_i) == m_EndArray)
36307 {
36308 if (!GrowArray())
36309 {
36310 LeaveFinalizeLock();
36311 if (method_table(obj) == NULL)
36312 {
36313 // If the object is uninitialized, a valid size should have been passed.
36314 assert (size >= Align (min_obj_size));
36315 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
36316 ((CObjectHeader*)obj)->SetFree(size);
36317 }
36318 STRESS_LOG_OOM_STACK(0);
36319 if (GCConfig::GetBreakOnOOM())
36320 {
36321 GCToOSInterface::DebugBreak();
36322 }
36323 return false;
36324 }
36325 }
36326 Object*** end_si = &SegQueueLimit (dest);
36327 do
36328 {
36329 //is the segment empty?
36330 if (!(*s_i == *(s_i-1)))
36331 {
36332 //no, swap the end elements.
36333 *(*s_i) = *(*(s_i-1));
36334 }
36335 //increment the fill pointer
36336 (*s_i)++;
36337 //go to the next segment.
36338 s_i--;
36339 } while (s_i > end_si);
36340
36341 // We have reached the destination segment
36342 // store the object
36343 **s_i = obj;
36344 // increment the fill pointer
36345 (*s_i)++;
36346
36347 LeaveFinalizeLock();
36348
36349 return true;
36350}
36351
36352Object*
36353CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
36354{
36355 Object* obj = 0;
36356 //serialize
36357 EnterFinalizeLock();
36358
36359retry:
36360 if (!IsSegEmpty(FinalizerListSeg))
36361 {
36362 if (g_fFinalizerRunOnShutDown)
36363 {
36364 obj = *(SegQueueLimit (FinalizerListSeg)-1);
36365 if (method_table(obj)->HasCriticalFinalizer())
36366 {
36367 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
36368 FinalizerListSeg, CriticalFinalizerListSeg);
36369 goto retry;
36370 }
36371 else
36372 --SegQueueLimit (FinalizerListSeg);
36373 }
36374 else
36375 obj = *(--SegQueueLimit (FinalizerListSeg));
36376
36377 }
36378 else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
36379 {
36380 //the FinalizerList is empty, we can adjust both
36381 // limit instead of moving the object to the free list
36382 obj = *(--SegQueueLimit (CriticalFinalizerListSeg));
36383 --SegQueueLimit (FinalizerListSeg);
36384 }
36385 if (obj)
36386 {
36387 dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
36388 }
36389 LeaveFinalizeLock();
36390 return obj;
36391}
36392
36393void
36394CFinalize::SetSegForShutDown(BOOL fHasLock)
36395{
36396 int i;
36397
36398 if (!fHasLock)
36399 EnterFinalizeLock();
36400 for (i = 0; i <= max_generation; i++)
36401 {
36402 unsigned int seg = gen_segment (i);
36403 Object** startIndex = SegQueueLimit (seg)-1;
36404 Object** stopIndex = SegQueue (seg);
36405 for (Object** po = startIndex; po >= stopIndex; po--)
36406 {
36407 Object* obj = *po;
36408 if (method_table(obj)->HasCriticalFinalizer())
36409 {
36410 MoveItem (po, seg, CriticalFinalizerListSeg);
36411 }
36412 else
36413 {
36414 MoveItem (po, seg, FinalizerListSeg);
36415 }
36416 }
36417 }
36418 if (!fHasLock)
36419 LeaveFinalizeLock();
36420}
36421
36422void
36423CFinalize::DiscardNonCriticalObjects()
36424{
36425 //empty the finalization queue
36426 Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
36427 Object** stopIndex = SegQueue (FinalizerListSeg);
36428 for (Object** po = startIndex; po >= stopIndex; po--)
36429 {
36430 MoveItem (po, FinalizerListSeg, FreeList);
36431 }
36432}
36433
36434size_t
36435CFinalize::GetNumberFinalizableObjects()
36436{
36437 return SegQueueLimit (FinalizerListSeg) -
36438 (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
36439}
36440
36441BOOL
36442CFinalize::FinalizeSegForAppDomain (void *pDomain,
36443 BOOL fRunFinalizers,
36444 unsigned int Seg)
36445{
36446 BOOL finalizedFound = FALSE;
36447 Object** endIndex = SegQueue (Seg);
36448 for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36449 {
36450 CObjectHeader* obj = (CObjectHeader*)*i;
36451
36452 // Objects are put into the finalization queue before they are complete (ie their methodtable
36453 // may be null) so we must check that the object we found has a method table before checking
36454 // if it has the index we are looking for. If the methodtable is null, it can't be from the
36455 // unloading domain, so skip it.
36456 if (method_table(obj) == NULL)
36457 {
36458 continue;
36459 }
36460
36461 // does the EE actually want us to finalize this object?
36462 if (!GCToEEInterface::ShouldFinalizeObjectForUnload(pDomain, obj))
36463 {
36464 continue;
36465 }
36466
36467 if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36468 {
36469 //remove the object because we don't want to
36470 //run the finalizer
36471 MoveItem (i, Seg, FreeList);
36472 //Reset the bit so it will be put back on the queue
36473 //if resurrected and re-registered.
36474 obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36475 }
36476 else
36477 {
36478 if (method_table(obj)->HasCriticalFinalizer())
36479 {
36480 finalizedFound = TRUE;
36481 MoveItem (i, Seg, CriticalFinalizerListSeg);
36482 }
36483 else
36484 {
36485 if (GCToEEInterface::AppDomainIsRudeUnload(pDomain))
36486 {
36487 MoveItem (i, Seg, FreeList);
36488 }
36489 else
36490 {
36491 finalizedFound = TRUE;
36492 MoveItem (i, Seg, FinalizerListSeg);
36493 }
36494 }
36495 }
36496 }
36497
36498 return finalizedFound;
36499}
36500
36501bool
36502CFinalize::FinalizeAppDomain (void *pDomain, bool fRunFinalizers)
36503{
36504 bool finalizedFound = false;
36505
36506 unsigned int startSeg = gen_segment (max_generation);
36507
36508 EnterFinalizeLock();
36509
36510 for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
36511 {
36512 if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
36513 {
36514 finalizedFound = true;
36515 }
36516 }
36517
36518 LeaveFinalizeLock();
36519
36520 return finalizedFound;
36521}
36522
36523void
36524CFinalize::MoveItem (Object** fromIndex,
36525 unsigned int fromSeg,
36526 unsigned int toSeg)
36527{
36528
36529 int step;
36530 ASSERT (fromSeg != toSeg);
36531 if (fromSeg > toSeg)
36532 step = -1;
36533 else
36534 step = +1;
36535 // Place the element at the boundary closest to dest
36536 Object** srcIndex = fromIndex;
36537 for (unsigned int i = fromSeg; i != toSeg; i+= step)
36538 {
36539 Object**& destFill = m_FillPointers[i+(step - 1 )/2];
36540 Object** destIndex = destFill - (step + 1)/2;
36541 if (srcIndex != destIndex)
36542 {
36543 Object* tmp = *srcIndex;
36544 *srcIndex = *destIndex;
36545 *destIndex = tmp;
36546 }
36547 destFill -= step;
36548 srcIndex = destIndex;
36549 }
36550}
36551
36552void
36553CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
36554{
36555 ScanContext sc;
36556 if (pSC == 0)
36557 pSC = &sc;
36558
36559 pSC->thread_number = hn;
36560
36561 //scan the finalization queue
36562 Object** startIndex = SegQueue (CriticalFinalizerListSeg);
36563 Object** stopIndex = SegQueueLimit (FinalizerListSeg);
36564
36565 for (Object** po = startIndex; po < stopIndex; po++)
36566 {
36567 Object* o = *po;
36568 //dprintf (3, ("scan freacheable %Ix", (size_t)o));
36569 dprintf (3, ("scan f %Ix", (size_t)o));
36570#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
36571 if (g_fEnableAppDomainMonitoring)
36572 {
36573 pSC->pCurrentDomain = GCToEEInterface::GetAppDomainForObject(o);
36574 }
36575#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
36576
36577 (*fn)(po, pSC, 0);
36578 }
36579}
36580
36581void CFinalize::WalkFReachableObjects (fq_walk_fn fn)
36582{
36583 Object** startIndex = SegQueue (CriticalFinalizerListSeg);
36584 Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
36585 Object** stopIndex = SegQueueLimit (FinalizerListSeg);
36586 for (Object** po = startIndex; po < stopIndex; po++)
36587 {
36588 //report *po
36589 fn(po < stopCriticalIndex, *po);
36590 }
36591}
36592
36593BOOL
36594CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
36595 gc_heap* hp)
36596{
36597 ScanContext sc;
36598 sc.promotion = TRUE;
36599#ifdef MULTIPLE_HEAPS
36600 sc.thread_number = hp->heap_number;
36601#else
36602 UNREFERENCED_PARAMETER(hp);
36603#endif //MULTIPLE_HEAPS
36604
36605 BOOL finalizedFound = FALSE;
36606
36607 //start with gen and explore all the younger generations.
36608 unsigned int startSeg = gen_segment (gen);
36609 {
36610 m_PromotedCount = 0;
36611 for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
36612 {
36613 Object** endIndex = SegQueue (Seg);
36614 for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36615 {
36616 CObjectHeader* obj = (CObjectHeader*)*i;
36617 dprintf (3, ("scanning: %Ix", (size_t)obj));
36618 if (!g_theGCHeap->IsPromoted (obj))
36619 {
36620 dprintf (3, ("freacheable: %Ix", (size_t)obj));
36621
36622 assert (method_table(obj)->HasFinalizer());
36623
36624 if (GCToEEInterface::EagerFinalized(obj))
36625 {
36626 MoveItem (i, Seg, FreeList);
36627 }
36628 else if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36629 {
36630 //remove the object because we don't want to
36631 //run the finalizer
36632
36633 MoveItem (i, Seg, FreeList);
36634
36635 //Reset the bit so it will be put back on the queue
36636 //if resurrected and re-registered.
36637 obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36638
36639 }
36640 else
36641 {
36642 m_PromotedCount++;
36643
36644 if (method_table(obj)->HasCriticalFinalizer())
36645 {
36646 MoveItem (i, Seg, CriticalFinalizerListSeg);
36647 }
36648 else
36649 {
36650 MoveItem (i, Seg, FinalizerListSeg);
36651 }
36652 }
36653 }
36654#ifdef BACKGROUND_GC
36655 else
36656 {
36657 if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
36658 {
36659 // TODO - fix the following line.
36660 //assert (gc_heap::background_object_marked ((uint8_t*)obj, FALSE));
36661 dprintf (3, ("%Ix is marked", (size_t)obj));
36662 }
36663 }
36664#endif //BACKGROUND_GC
36665 }
36666 }
36667 }
36668 finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
36669 !IsSegEmpty(CriticalFinalizerListSeg);
36670
36671 if (finalizedFound)
36672 {
36673 //Promote the f-reachable objects
36674 GcScanRoots (pfn,
36675#ifdef MULTIPLE_HEAPS
36676 hp->heap_number
36677#else
36678 0
36679#endif //MULTIPLE_HEAPS
36680 , 0);
36681
36682 hp->settings.found_finalizers = TRUE;
36683
36684#ifdef BACKGROUND_GC
36685 if (hp->settings.concurrent)
36686 {
36687 hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
36688 }
36689#endif //BACKGROUND_GC
36690 if (hp->settings.concurrent && hp->settings.found_finalizers)
36691 {
36692 if (!mark_only_p)
36693 GCToEEInterface::EnableFinalization(true);
36694 }
36695 }
36696
36697 return finalizedFound;
36698}
36699
36700//Relocates all of the objects in the finalization array
36701void
36702CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
36703{
36704 ScanContext sc;
36705 sc.promotion = FALSE;
36706#ifdef MULTIPLE_HEAPS
36707 sc.thread_number = hp->heap_number;
36708#else
36709 UNREFERENCED_PARAMETER(hp);
36710#endif //MULTIPLE_HEAPS
36711
36712 unsigned int Seg = gen_segment (gen);
36713
36714 Object** startIndex = SegQueue (Seg);
36715 for (Object** po = startIndex; po < SegQueue (FreeList);po++)
36716 {
36717 GCHeap::Relocate (po, &sc);
36718 }
36719}
36720
36721void
36722CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
36723{
36724 // update the generation fill pointers.
36725 // if gen_0_empty is FALSE, test each object to find out if
36726 // it was promoted or not
36727 if (gen_0_empty_p)
36728 {
36729 for (int i = min (gen+1, max_generation); i > 0; i--)
36730 {
36731 m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
36732 }
36733 }
36734 else
36735 {
36736 //Look for demoted or promoted plugs
36737
36738 for (int i = gen; i >= 0; i--)
36739 {
36740 unsigned int Seg = gen_segment (i);
36741 Object** startIndex = SegQueue (Seg);
36742
36743 for (Object** po = startIndex;
36744 po < SegQueueLimit (gen_segment(i)); po++)
36745 {
36746 int new_gen = g_theGCHeap->WhichGeneration (*po);
36747 if (new_gen != i)
36748 {
36749 if (new_gen > i)
36750 {
36751 //promotion
36752 MoveItem (po, gen_segment (i), gen_segment (new_gen));
36753 }
36754 else
36755 {
36756 //demotion
36757 MoveItem (po, gen_segment (i), gen_segment (new_gen));
36758 //back down in order to see all objects.
36759 po--;
36760 }
36761 }
36762
36763 }
36764 }
36765 }
36766}
36767
36768BOOL
36769CFinalize::GrowArray()
36770{
36771 size_t oldArraySize = (m_EndArray - m_Array);
36772 size_t newArraySize = (size_t)(((float)oldArraySize / 10) * 12);
36773
36774 Object** newArray = new (nothrow) Object*[newArraySize];
36775 if (!newArray)
36776 {
36777 // It's not safe to throw here, because of the FinalizeLock. Tell our caller
36778 // to throw for us.
36779// ASSERT (newArray);
36780 return FALSE;
36781 }
36782 memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
36783
36784 //adjust the fill pointers
36785 for (int i = 0; i < FreeList; i++)
36786 {
36787 m_FillPointers [i] += (newArray - m_Array);
36788 }
36789 delete m_Array;
36790 m_Array = newArray;
36791 m_EndArray = &m_Array [newArraySize];
36792
36793 return TRUE;
36794}
36795
36796#ifdef VERIFY_HEAP
36797void CFinalize::CheckFinalizerObjects()
36798{
36799 for (int i = 0; i <= max_generation; i++)
36800 {
36801 Object **startIndex = SegQueue (gen_segment (i));
36802 Object **stopIndex = SegQueueLimit (gen_segment (i));
36803
36804 for (Object **po = startIndex; po < stopIndex; po++)
36805 {
36806 if ((int)g_theGCHeap->WhichGeneration (*po) < i)
36807 FATAL_GC_ERROR ();
36808 ((CObjectHeader*)*po)->Validate();
36809 }
36810 }
36811}
36812#endif //VERIFY_HEAP
36813
36814#endif // FEATURE_PREMORTEM_FINALIZATION
36815
36816
36817//------------------------------------------------------------------------------
36818//
36819// End of VM specific support
36820//
36821//------------------------------------------------------------------------------
36822void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36823{
36824 generation* gen = gc_heap::generation_of (gen_number);
36825 heap_segment* seg = generation_start_segment (gen);
36826 uint8_t* x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
36827 generation_allocation_start (gen));
36828
36829 uint8_t* end = heap_segment_allocated (seg);
36830 BOOL small_object_segments = TRUE;
36831 int align_const = get_alignment_constant (small_object_segments);
36832
36833 while (1)
36834
36835 {
36836 if (x >= end)
36837 {
36838 if ((seg = heap_segment_next (seg)) != 0)
36839 {
36840 x = heap_segment_mem (seg);
36841 end = heap_segment_allocated (seg);
36842 continue;
36843 }
36844 else
36845 {
36846 if (small_object_segments && walk_large_object_heap_p)
36847
36848 {
36849 small_object_segments = FALSE;
36850 align_const = get_alignment_constant (small_object_segments);
36851 seg = generation_start_segment (large_object_generation);
36852 x = heap_segment_mem (seg);
36853 end = heap_segment_allocated (seg);
36854 continue;
36855 }
36856 else
36857 {
36858 break;
36859 }
36860 }
36861 }
36862
36863 size_t s = size (x);
36864 CObjectHeader* o = (CObjectHeader*)x;
36865
36866 if (!o->IsFree())
36867
36868 {
36869 _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
36870
36871 if (!fn (o->GetObjectBase(), context))
36872 return;
36873 }
36874 x = x + Align (s, align_const);
36875 }
36876}
36877
36878void gc_heap::walk_finalize_queue (fq_walk_fn fn)
36879{
36880#ifdef FEATURE_PREMORTEM_FINALIZATION
36881 finalize_queue->WalkFReachableObjects (fn);
36882#endif //FEATURE_PREMORTEM_FINALIZATION
36883}
36884
36885void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
36886{
36887#ifdef MULTIPLE_HEAPS
36888 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36889 {
36890 gc_heap* hp = gc_heap::g_heaps [hn];
36891
36892 hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p);
36893 }
36894#else
36895 walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p);
36896#endif //MULTIPLE_HEAPS
36897}
36898
36899void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context)
36900{
36901 uint8_t* o = (uint8_t*)obj;
36902 if (o)
36903 {
36904 go_through_object_cl (method_table (o), o, size(o), oo,
36905 {
36906 if (*oo)
36907 {
36908 Object *oh = (Object*)*oo;
36909 if (!fn (oh, context))
36910 return;
36911 }
36912 }
36913 );
36914 }
36915}
36916
36917void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type)
36918{
36919 gc_heap* hp = (gc_heap*)gc_context;
36920 hp->walk_survivors (fn, diag_context, type);
36921}
36922
36923void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p)
36924{
36925 gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
36926}
36927
36928void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
36929{
36930 gc_heap* hp = (gc_heap*)gc_context;
36931 hp->walk_finalize_queue (fn);
36932}
36933
36934void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc)
36935{
36936#ifdef MULTIPLE_HEAPS
36937 for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36938 {
36939 gc_heap* hp = gc_heap::g_heaps [hn];
36940 hp->finalize_queue->GcScanRoots(fn, hn, sc);
36941 }
36942#else
36943 pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc);
36944#endif //MULTIPLE_HEAPS
36945}
36946
36947void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36948{
36949 UNREFERENCED_PARAMETER(gen_number);
36950 GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn);
36951}
36952
36953void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
36954{
36955 UNREFERENCED_PARAMETER(gen_number);
36956 GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn);
36957}
36958
36959// Go through and touch (read) each page straddled by a memory block.
36960void TouchPages(void * pStart, size_t cb)
36961{
36962 const uint32_t pagesize = OS_PAGE_SIZE;
36963 _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
36964 if (cb)
36965 {
36966 VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
36967 VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) - (((size_t)pStart) & (pagesize-1)));
36968 while (p < pEnd)
36969 {
36970 char a;
36971 a = VolatileLoad(p);
36972 //printf("Touching page %lxh\n", (uint32_t)p);
36973 p += pagesize;
36974 }
36975 }
36976}
36977
36978#if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
36979 // This code is designed to catch the failure to update the write barrier
36980 // The way it works is to copy the whole heap right after every GC. The write
36981 // barrier code has been modified so that it updates the shadow as well as the
36982 // real GC heap. Before doing the next GC, we walk the heap, looking for pointers
36983 // that were updated in the real heap, but not the shadow. A mismatch indicates
36984 // an error. The offending code can be found by breaking after the correct GC,
36985 // and then placing a data breakpoint on the Heap location that was updated without
36986 // going through the write barrier.
36987
36988 // Called at process shutdown
36989void deleteGCShadow()
36990{
36991 if (g_GCShadow != 0)
36992 GCToOSInterface::VirtualRelease (g_GCShadow, g_GCShadowEnd - g_GCShadow);
36993 g_GCShadow = 0;
36994 g_GCShadowEnd = 0;
36995}
36996
36997 // Called at startup and right after a GC, get a snapshot of the GC Heap
36998void initGCShadow()
36999{
37000 if (!(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK))
37001 return;
37002
37003 size_t len = g_gc_highest_address - g_gc_lowest_address;
37004 if (len > (size_t)(g_GCShadowEnd - g_GCShadow))
37005 {
37006 deleteGCShadow();
37007 g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None);
37008 if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len))
37009 {
37010 _ASSERTE(!"Not enough memory to run HeapVerify level 2");
37011 // If after the assert we decide to allow the program to continue
37012 // running we need to be in a state that will not trigger any
37013 // additional AVs while we fail to allocate a shadow segment, i.e.
37014 // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
37015 deleteGCShadow();
37016 return;
37017 }
37018
37019 g_GCShadowEnd += len;
37020 }
37021
37022 // save the value of g_gc_lowest_address at this time. If this value changes before
37023 // the next call to checkGCWriteBarrier() it means we extended the heap (with a
37024 // large object segment most probably), and the whole shadow segment is inconsistent.
37025 g_shadow_lowest_address = g_gc_lowest_address;
37026
37027 //****** Copy the whole GC heap ******
37028 //
37029 // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
37030 // can produce a NULL result. This is because the initialization has not completed.
37031 //
37032 generation* gen = gc_heap::generation_of (max_generation);
37033 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37034
37035 ptrdiff_t delta = g_GCShadow - g_gc_lowest_address;
37036 BOOL small_object_segments = TRUE;
37037 while(1)
37038 {
37039 if (!seg)
37040 {
37041 if (small_object_segments)
37042 {
37043 small_object_segments = FALSE;
37044 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
37045 continue;
37046 }
37047 else
37048 break;
37049 }
37050 // Copy the segment
37051 uint8_t* start = heap_segment_mem(seg);
37052 uint8_t* end = heap_segment_allocated (seg);
37053 memcpy(start + delta, start, end - start);
37054 seg = heap_segment_next_rw (seg);
37055 }
37056}
37057
37058#define INVALIDGCVALUE (void*)((size_t)0xcccccccd)
37059
37060 // test to see if 'ptr' was only updated via the write barrier.
37061inline void testGCShadow(Object** ptr)
37062{
37063 Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)];
37064 if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow)
37065 {
37066
37067 // If you get this assertion, someone updated a GC pointer in the heap without
37068 // using the write barrier. To find out who, check the value of
37069 // dd_collection_count (dynamic_data_of (0)). Also
37070 // note the value of 'ptr'. Rerun the App that the previous GC just occurred.
37071 // Then put a data breakpoint for the value of 'ptr' Then check every write
37072 // to pointer between the two GCs. The last one is not using the write barrier.
37073
37074 // If the memory of interest does not exist at system startup,
37075 // you need to set the data breakpoint right after the memory gets committed
37076 // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
37077 // in the memory window. run until the memory gets mapped. Then you can set
37078 // your breakpoint
37079
37080 // Note a recent change, we've identified race conditions when updating the gc shadow.
37081 // Throughout the runtime, code will update an address in the gc heap, then erect the
37082 // write barrier, which calls updateGCShadow. With an app that pounds one heap location
37083 // from multiple threads, you can hit this assert even though all involved are using the
37084 // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
37085 // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
37086 // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
37087 // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
37088 // TODO: erroneous asserts in here.
37089
37090 if(*shadow!=INVALIDGCVALUE)
37091 {
37092#ifdef FEATURE_BASICFREEZE
37093 // Write barriers for stores of references to frozen objects may be optimized away.
37094 if (!gc_heap::frozen_object_p(*ptr))
37095#endif // FEATURE_BASICFREEZE
37096 {
37097 _ASSERTE(!"Pointer updated without using write barrier");
37098 }
37099 }
37100 /*
37101 else
37102 {
37103 printf("saw a INVALIDGCVALUE. (just to let you know)\n");
37104 }
37105 */
37106 }
37107}
37108
37109void testGCShadowHelper (uint8_t* x)
37110{
37111 size_t s = size (x);
37112 if (contain_pointers (x))
37113 {
37114 go_through_object_nostart (method_table(x), x, s, oo,
37115 { testGCShadow((Object**) oo); });
37116 }
37117}
37118
37119 // Walk the whole heap, looking for pointers that were not updated with the write barrier.
37120void checkGCWriteBarrier()
37121{
37122 // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment
37123 // and the GC shadow segment did not track that change!
37124 if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address)
37125 {
37126 // No shadow stack, nothing to check.
37127 return;
37128 }
37129
37130 {
37131 generation* gen = gc_heap::generation_of (max_generation);
37132 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37133
37134 PREFIX_ASSUME(seg != NULL);
37135
37136 while(seg)
37137 {
37138 uint8_t* x = heap_segment_mem(seg);
37139 while (x < heap_segment_allocated (seg))
37140 {
37141 size_t s = size (x);
37142 testGCShadowHelper (x);
37143 x = x + Align (s);
37144 }
37145 seg = heap_segment_next_rw (seg);
37146 }
37147 }
37148
37149 {
37150 // go through large object heap
37151 int alignment = get_alignment_constant(FALSE);
37152 generation* gen = gc_heap::generation_of (max_generation+1);
37153 heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37154
37155 PREFIX_ASSUME(seg != NULL);
37156
37157 while(seg)
37158 {
37159 uint8_t* x = heap_segment_mem(seg);
37160 while (x < heap_segment_allocated (seg))
37161 {
37162 size_t s = size (x);
37163 testGCShadowHelper (x);
37164 x = x + Align (s, alignment);
37165 }
37166 seg = heap_segment_next_rw (seg);
37167 }
37168 }
37169}
37170#endif //WRITE_BARRIER_CHECK && !SERVER_GC
37171
37172#endif // !DACCESS_COMPILE
37173
37174#ifdef FEATURE_BASICFREEZE
37175void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
37176{
37177#ifdef DACCESS_COMPILE
37178 UNREFERENCED_PARAMETER(seg);
37179 UNREFERENCED_PARAMETER(pvContext);
37180 UNREFERENCED_PARAMETER(pfnMethodTable);
37181 UNREFERENCED_PARAMETER(pfnObjRef);
37182#else
37183 uint8_t *o = heap_segment_mem(seg);
37184
37185 // small heap alignment constant
37186 int alignment = get_alignment_constant(TRUE);
37187
37188 while (o < heap_segment_allocated(seg))
37189 {
37190 pfnMethodTable(pvContext, o);
37191
37192 if (contain_pointers (o))
37193 {
37194 go_through_object_nostart (method_table (o), o, size(o), oo,
37195 {
37196 if (*oo)
37197 pfnObjRef(pvContext, oo);
37198 }
37199 );
37200 }
37201
37202 o += Align(size(o), alignment);
37203 }
37204#endif //!DACCESS_COMPILE
37205}
37206#endif // FEATURE_BASICFREEZE
37207
37208#ifndef DACCESS_COMPILE
37209HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
37210{
37211#ifdef BACKGROUND_GC
37212 if (recursive_gc_sync::background_running_p())
37213 {
37214 uint32_t dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
37215 if (dwRet == WAIT_OBJECT_0)
37216 return S_OK;
37217 else if (dwRet == WAIT_TIMEOUT)
37218 return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
37219 else
37220 return E_FAIL; // It is not clear if what the last error would be if the wait failed,
37221 // as there are too many layers in between. The best we can do is to return E_FAIL;
37222 }
37223#endif
37224
37225 return S_OK;
37226}
37227#endif // !DACCESS_COMPILE
37228
37229void GCHeap::TemporaryEnableConcurrentGC()
37230{
37231#ifdef BACKGROUND_GC
37232 gc_heap::temp_disable_concurrent_p = false;
37233#endif //BACKGROUND_GC
37234}
37235
37236void GCHeap::TemporaryDisableConcurrentGC()
37237{
37238#ifdef BACKGROUND_GC
37239 gc_heap::temp_disable_concurrent_p = true;
37240#endif //BACKGROUND_GC
37241}
37242
37243bool GCHeap::IsConcurrentGCEnabled()
37244{
37245#ifdef BACKGROUND_GC
37246 return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
37247#else
37248 return FALSE;
37249#endif //BACKGROUND_GC
37250}
37251
37252void GCHeap::SetFinalizeRunOnShutdown(bool value)
37253{
37254 g_fFinalizerRunOnShutDown = value;
37255}
37256
37257void PopulateDacVars(GcDacVars *gcDacVars)
37258{
37259#ifndef DACCESS_COMPILE
37260 assert(gcDacVars != nullptr);
37261 *gcDacVars = {};
37262 gcDacVars->major_version_number = 1;
37263 gcDacVars->minor_version_number = 0;
37264 gcDacVars->built_with_svr = &g_built_with_svr_gc;
37265 gcDacVars->build_variant = &g_build_variant;
37266 gcDacVars->gc_structures_invalid_cnt = const_cast<int32_t*>(&GCScan::m_GcStructuresInvalidCnt);
37267 gcDacVars->generation_size = sizeof(generation);
37268 gcDacVars->max_gen = &g_max_generation;
37269#ifndef MULTIPLE_HEAPS
37270 gcDacVars->mark_array = &gc_heap::mark_array;
37271 gcDacVars->ephemeral_heap_segment = reinterpret_cast<dac_heap_segment**>(&gc_heap::ephemeral_heap_segment);
37272 gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state);
37273 gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast<dac_heap_segment**>(&gc_heap::saved_sweep_ephemeral_seg);
37274 gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start;
37275 gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address;
37276 gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address;
37277 gcDacVars->alloc_allocated = &gc_heap::alloc_allocated;
37278 gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj;
37279 gcDacVars->oom_info = &gc_heap::oom_info;
37280 gcDacVars->finalize_queue = reinterpret_cast<dac_finalize_queue**>(&gc_heap::finalize_queue);
37281 gcDacVars->generation_table = reinterpret_cast<dac_generation**>(&gc_heap::generation_table);
37282#ifdef GC_CONFIG_DRIVEN
37283 gcDacVars->gc_global_mechanisms = reinterpret_cast<size_t**>(&gc_global_mechanisms);
37284 gcDacVars->interesting_data_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_data_per_heap);
37285 gcDacVars->compact_reasons_per_heap = reinterpret_cast<size_t**>(&gc_heap::compact_reasons_per_heap);
37286 gcDacVars->expand_mechanisms_per_heap = reinterpret_cast<size_t**>(&gc_heap::expand_mechanisms_per_heap);
37287 gcDacVars->interesting_mechanism_bits_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_mechanism_bits_per_heap);
37288#endif // GC_CONFIG_DRIVEN
37289#ifdef HEAP_ANALYZE
37290 gcDacVars->internal_root_array = &gc_heap::internal_root_array;
37291 gcDacVars->internal_root_array_index = &gc_heap::internal_root_array_index;
37292 gcDacVars->heap_analyze_success = &gc_heap::heap_analyze_success;
37293#endif // HEAP_ANALYZE
37294#else
37295 gcDacVars->n_heaps = &gc_heap::n_heaps;
37296 gcDacVars->g_heaps = reinterpret_cast<dac_gc_heap***>(&gc_heap::g_heaps);
37297#endif // MULTIPLE_HEAPS
37298#endif // DACCESS_COMPILE
37299}
37300