summaryrefslogtreecommitdiff
path: root/src/gc
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2017-02-10 20:35:12 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2017-02-10 20:35:12 +0900
commit4b11dc566a5bbfa1378d6266525c281b028abcc8 (patch)
treeb48831a898906734f8884d08b6e18f1144ee2b82 /src/gc
parentdb20f3f1bb8595633a7e16c8900fd401a453a6b5 (diff)
downloadcoreclr-4b11dc566a5bbfa1378d6266525c281b028abcc8.tar.gz
coreclr-4b11dc566a5bbfa1378d6266525c281b028abcc8.tar.bz2
coreclr-4b11dc566a5bbfa1378d6266525c281b028abcc8.zip
Imported Upstream version 1.0.0.9910upstream/1.0.0.9910
Diffstat (limited to 'src/gc')
-rw-r--r--src/gc/CMakeLists.txt10
-rw-r--r--src/gc/env/gcenv.base.h21
-rw-r--r--src/gc/env/gcenv.ee.h2
-rw-r--r--src/gc/gc.cpp168
-rw-r--r--src/gc/gc.h8
-rw-r--r--src/gc/gccommon.cpp3
-rw-r--r--src/gc/gcenv.ee.standalone.inl88
-rw-r--r--src/gc/gcenv.unix.cpp308
-rw-r--r--src/gc/gcimpl.h1
-rw-r--r--src/gc/gcinterface.ee.h5
-rw-r--r--src/gc/gcinterface.h23
-rw-r--r--src/gc/gcpriv.h4
-rw-r--r--src/gc/gcsvr.cpp1
-rw-r--r--src/gc/gcwks.cpp1
-rw-r--r--src/gc/sample/CMakeLists.txt2
-rw-r--r--src/gc/sample/GCSample.cpp13
-rw-r--r--src/gc/sample/GCSample.vcxproj2
-rw-r--r--src/gc/sample/gcenv.ee.cpp9
-rw-r--r--src/gc/softwarewritewatch.cpp11
-rw-r--r--src/gc/softwarewritewatch.h40
-rw-r--r--src/gc/unix/CMakeLists.txt10
-rw-r--r--src/gc/unix/config.h.in14
-rw-r--r--src/gc/unix/configure.cmake40
-rw-r--r--src/gc/unix/gcenv.unix.cpp627
-rw-r--r--src/gc/windows/gcenv.windows.cpp (renamed from src/gc/gcenv.windows.cpp)0
25 files changed, 876 insertions, 535 deletions
diff --git a/src/gc/CMakeLists.txt b/src/gc/CMakeLists.txt
index d32d1c2dfb..cba1aa9778 100644
--- a/src/gc/CMakeLists.txt
+++ b/src/gc/CMakeLists.txt
@@ -39,15 +39,11 @@ set( GC_SOURCES_DAC
${GC_SOURCES_DAC_AND_WKS_COMMON})
if(FEATURE_STANDALONE_GC)
- if(CLR_CMAKE_PLATFORM_UNIX)
+ if(NOT CLR_CMAKE_PLATFORM_UNIX)
set ( GC_SOURCES_WKS
${GC_SOURCES_WKS}
- gcenv.unix.cpp)
- else()
- set ( GC_SOURCES_WKS
- ${GC_SOURCES_WKS}
- gcenv.windows.cpp)
- endif(CLR_CMAKE_PLATFORM_UNIX)
+ windows/gcenv.windows.cpp)
+ endif(NOT CLR_CMAKE_PLATFORM_UNIX)
endif(FEATURE_STANDALONE_GC)
convert_to_absolute_path(GC_SOURCES_WKS ${GC_SOURCES_WKS})
diff --git a/src/gc/env/gcenv.base.h b/src/gc/env/gcenv.base.h
index 94f73762f8..9fe583f9a6 100644
--- a/src/gc/env/gcenv.base.h
+++ b/src/gc/env/gcenv.base.h
@@ -96,7 +96,7 @@ inline HRESULT HRESULT_FROM_WIN32(unsigned long x)
#define UNREFERENCED_PARAMETER(P) (void)(P)
#ifdef PLATFORM_UNIX
-#define _vsnprintf vsnprintf
+#define _vsnprintf_s(string, sizeInBytes, count, format, args) vsnprintf(string, sizeInBytes, format, args)
#define sprintf_s snprintf
#define swprintf_s swprintf
#endif
@@ -441,8 +441,6 @@ extern MethodTable * g_pFreeObjectMethodTable;
extern int32_t g_TrapReturningThreads;
-extern bool g_fFinalizerRunOnShutDown;
-
//
// Locks
//
@@ -454,21 +452,6 @@ Thread * GetThread();
typedef void (CALLBACK *HANDLESCANPROC)(PTR_UNCHECKED_OBJECTREF pref, uintptr_t *pExtraInfo, uintptr_t param1, uintptr_t param2);
-class FinalizerThread
-{
-public:
- static bool Initialize();
- static void EnableFinalization();
-
- static bool HaveExtraWorkForFinalizer();
-
- static bool IsCurrentThreadFinalizer();
- static void Wait(DWORD timeout, bool allowReentrantWait = false);
- static void SignalFinalizationDone(bool fFinalizer);
- static void SetFinalizerThread(Thread * pThread);
- static HANDLE GetFinalizerEvent();
-};
-
bool IsGCSpecialThread();
inline bool dbgOnly_IsSpecialEEThread()
@@ -509,8 +492,6 @@ void LogSpewAlways(const char *fmt, ...);
// -----------------------------------------------------------------------------------------------------------
-void StompWriteBarrierEphemeral(bool isRuntimeSuspended);
-void StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck);
bool IsGCThread();
class CLRConfig
diff --git a/src/gc/env/gcenv.ee.h b/src/gc/env/gcenv.ee.h
index beb0c1a98f..9f7f266a89 100644
--- a/src/gc/env/gcenv.ee.h
+++ b/src/gc/env/gcenv.ee.h
@@ -66,6 +66,8 @@ public:
static void DiagWalkLOHSurvivors(void* gcContext);
static void DiagWalkBGCSurvivors(void* gcContext);
static void StompWriteBarrier(WriteBarrierParameters* args);
+
+ static void EnableFinalization(bool foundFinalizers);
};
#endif // __GCENV_EE_H__
diff --git a/src/gc/gc.cpp b/src/gc/gc.cpp
index 6187938ff8..66c8b6afbc 100644
--- a/src/gc/gc.cpp
+++ b/src/gc/gc.cpp
@@ -389,7 +389,7 @@ void log_va_msg(const char *fmt, va_list args)
int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging());
buffer_start += pid_len;
memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start);
- int msg_len = _vsnprintf(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, fmt, args );
+ int msg_len = _vsnprintf_s(&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
if (msg_len == -1)
{
msg_len = BUFFERSIZE - buffer_start;
@@ -1402,9 +1402,6 @@ int mark_time, plan_time, sweep_time, reloc_time, compact_time;
#ifndef MULTIPLE_HEAPS
-#define ephemeral_low g_gc_ephemeral_low
-#define ephemeral_high g_gc_ephemeral_high
-
#endif // MULTIPLE_HEAPS
#ifdef TRACE_GC
@@ -2187,27 +2184,22 @@ void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_b
args.card_table = g_gc_card_table;
args.lowest_address = g_gc_lowest_address;
args.highest_address = g_gc_highest_address;
+#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
+ if (SoftwareWriteWatch::IsEnabledForGCHeap())
+ {
+ args.write_watch_table = g_gc_sw_ww_table;
+ }
+#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
GCToEEInterface::StompWriteBarrier(&args);
}
-void stomp_write_barrier_ephemeral(bool is_runtime_suspended, uint8_t* ephemeral_lo, uint8_t* ephemeral_hi)
+void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
{
WriteBarrierParameters args = {};
args.operation = WriteBarrierOp::StompEphemeral;
- args.is_runtime_suspended = is_runtime_suspended;
- args.ephemeral_lo = g_gc_ephemeral_low;
- args.ephemeral_hi = g_gc_ephemeral_high;
-#ifdef MULTIPLE_HEAPS
- // It is not correct to update the EE's g_ephemeral_low and g_ephemeral_high
- // to anything other than their default values when using Server GC, since
- // there is no single ephemeral generation across all of the heaps.
- // Server GC write barriers do not reference these two globals, but ErectWriteBarrier does.
- //
- // When MULTIPLE_HEAPS is defined, g_gc_ephemeral_low and g_gc_ephemeral_high should
- // always have their default values.
- assert(args.ephemeral_lo == (uint8_t*)1);
- assert(args.ephemeral_hi == (uint8_t*)~0);
-#endif // MULTIPLE_HEAPS
+ args.is_runtime_suspended = true;
+ args.ephemeral_low = ephemeral_low;
+ args.ephemeral_high = ephemeral_high;
GCToEEInterface::StompWriteBarrier(&args);
}
@@ -2220,6 +2212,8 @@ void stomp_write_barrier_initialize()
args.card_table = g_gc_card_table;
args.lowest_address = g_gc_lowest_address;
args.highest_address = g_gc_highest_address;
+ args.ephemeral_low = reinterpret_cast<uint8_t*>(1);
+ args.ephemeral_high = reinterpret_cast<uint8_t*>(~0);
GCToEEInterface::StompWriteBarrier(&args);
}
@@ -2430,6 +2424,10 @@ BOOL gc_heap::ro_segments_in_range;
size_t gc_heap::gen0_big_free_spaces = 0;
+uint8_t* gc_heap::ephemeral_low;
+
+uint8_t* gc_heap::ephemeral_high;
+
uint8_t* gc_heap::lowest_address;
uint8_t* gc_heap::highest_address;
@@ -7044,6 +7042,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
uint8_t* ha = g_gc_highest_address;
uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address);
uint8_t* saved_g_highest_address = max (end, g_gc_highest_address);
+ seg_mapping* new_seg_mapping_table = nullptr;
#ifdef BACKGROUND_GC
// This value is only for logging purpose - it's not necessarily exactly what we
// would commit for mark array but close enough for diagnostics purpose.
@@ -7204,14 +7203,18 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
#ifdef GROWABLE_SEG_MAPPING_TABLE
{
- seg_mapping* new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
+ new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table -
size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address))));
memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
&seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address));
- seg_mapping_table = new_seg_mapping_table;
+ // new_seg_mapping_table gets assigned to seg_mapping_table at the bottom of this function,
+ // not here. The reason for this is that, if we fail at mark array committing (OOM) and we've
+ // already switched seg_mapping_table to point to the new mapping table, we'll decommit it and
+ // run into trouble. By not assigning here, we're making sure that we will not change seg_mapping_table
+ // if an OOM occurs.
}
#endif //GROWABLE_SEG_MAPPING_TABLE
@@ -7225,7 +7228,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
translated_ct = translate_card_table (ct);
dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix",
- (size_t)ct, (size_t)translated_ct, (size_t)seg_mapping_table, (size_t)card_table_mark_array (ct)));
+ (size_t)ct, (size_t)translated_ct, (size_t)new_seg_mapping_table, (size_t)card_table_mark_array (ct)));
#ifdef BACKGROUND_GC
if (hp->should_commit_mark_array())
@@ -7277,9 +7280,6 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
}
g_gc_card_table = translated_ct;
- g_gc_lowest_address = saved_g_lowest_address;
- g_gc_highest_address = saved_g_highest_address;
-
SoftwareWriteWatch::SetResizedUntranslatedTable(
mem + sw_ww_table_offset,
saved_g_lowest_address,
@@ -7290,6 +7290,8 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
// grow version of the write barrier. This test tells us if the new
// segment was allocated at a lower address than the old, requiring
// that we start doing an upper bounds check in the write barrier.
+ g_gc_lowest_address = saved_g_lowest_address;
+ g_gc_highest_address = saved_g_highest_address;
stomp_write_barrier_resize(true, la != saved_g_lowest_address);
write_barrier_updated = true;
@@ -7304,6 +7306,9 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
g_gc_card_table = translated_ct;
}
+ seg_mapping_table = new_seg_mapping_table;
+
+ GCToOSInterface::FlushProcessWriteBuffers();
g_gc_lowest_address = saved_g_lowest_address;
g_gc_highest_address = saved_g_highest_address;
@@ -9662,7 +9667,7 @@ void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* star
#endif //FREE_USAGE_STATS
}
-void gc_heap::adjust_ephemeral_limits (bool is_runtime_suspended)
+void gc_heap::adjust_ephemeral_limits ()
{
ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);
@@ -9670,8 +9675,10 @@ void gc_heap::adjust_ephemeral_limits (bool is_runtime_suspended)
dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
(size_t)ephemeral_low, (size_t)ephemeral_high))
+#ifndef MULTIPLE_HEAPS
// This updates the write barrier helpers with the new info.
- stomp_write_barrier_ephemeral(is_runtime_suspended, ephemeral_low, ephemeral_high);
+ stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high);
+#endif // MULTIPLE_HEAPS
}
#if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN)
@@ -10466,7 +10473,7 @@ gc_heap::init_gc_heap (int h_number)
make_background_mark_stack (b_arr);
#endif //BACKGROUND_GC
- adjust_ephemeral_limits(true);
+ adjust_ephemeral_limits();
#ifdef MARK_ARRAY
// why would we clear the mark array for this page? it should be cleared..
@@ -15364,7 +15371,11 @@ void gc_heap::gc1()
if (!settings.concurrent)
#endif //BACKGROUND_GC
{
- adjust_ephemeral_limits(!!IsGCThread());
+#ifndef FEATURE_REDHAWK
+ // IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
+ assert(!!IsGCThread());
+#endif // FEATURE_REDHAWK
+ adjust_ephemeral_limits();
}
#ifdef BACKGROUND_GC
@@ -16204,7 +16215,11 @@ BOOL gc_heap::expand_soh_with_minimal_gc()
dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));
- adjust_ephemeral_limits(!!IsGCThread());
+#ifndef FEATURE_REDHAWK
+ // IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
+ assert(!!IsGCThread());
+#endif // FEATURE_REDHAWK
+ adjust_ephemeral_limits();
return TRUE;
}
else
@@ -32778,8 +32793,8 @@ gc_heap::verify_heap (BOOL begin_gc_p)
#endif //BACKGROUND_GC
#ifndef MULTIPLE_HEAPS
- if ((g_gc_ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
- (g_gc_ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
+ if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
+ (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
{
FATAL_GC_ERROR();
}
@@ -35140,11 +35155,7 @@ GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
#endif //!MULTIPLE_HEAPS
#ifdef FEATURE_PREMORTEM_FINALIZATION
- if ((!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers) ||
- FinalizerThread::HaveExtraWorkForFinalizer())
- {
- FinalizerThread::EnableFinalization();
- }
+ GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers);
#endif // FEATURE_PREMORTEM_FINALIZATION
return dd_collection_count (dd);
@@ -35681,85 +35692,6 @@ void GCHeap::SetFinalizationRun (Object* obj)
#endif // FEATURE_PREMORTEM_FINALIZATION
-//----------------------------------------------------------------------------
-//
-// Write Barrier Support for bulk copy ("Clone") operations
-//
-// StartPoint is the target bulk copy start point
-// len is the length of the bulk copy (in bytes)
-//
-//
-// Performance Note:
-//
-// This is implemented somewhat "conservatively", that is we
-// assume that all the contents of the bulk copy are object
-// references. If they are not, and the value lies in the
-// ephemeral range, we will set false positives in the card table.
-//
-// We could use the pointer maps and do this more accurately if necessary
-
-#if defined(_MSC_VER) && defined(_TARGET_X86_)
-#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
-#endif //_MSC_VER && _TARGET_X86_
-
-void
-GCHeap::SetCardsAfterBulkCopy( Object **StartPoint, size_t len )
-{
- Object **rover;
- Object **end;
-
- // Target should aligned
- assert(Aligned ((size_t)StartPoint));
-
-
- // Don't optimize the Generation 0 case if we are checking for write barrier voilations
- // since we need to update the shadow heap even in the generation 0 case.
-#if defined (WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
- if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)
- for(unsigned i=0; i < len / sizeof(Object*); i++)
- updateGCShadow(&StartPoint[i], StartPoint[i]);
-#endif //WRITE_BARRIER_CHECK && !SERVER_GC
-
-#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
- if (SoftwareWriteWatch::IsEnabledForGCHeap())
- {
- SoftwareWriteWatch::SetDirtyRegion(StartPoint, len);
- }
-#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
-
- // If destination is in Gen 0 don't bother
- if (
-#ifdef BACKGROUND_GC
- (!gc_heap::settings.concurrent) &&
-#endif //BACKGROUND_GC
- (g_theGCHeap->WhichGeneration( (Object*) StartPoint ) == 0))
- return;
-
- rover = StartPoint;
- end = StartPoint + (len/sizeof(Object*));
- while (rover < end)
- {
- if ( (((uint8_t*)*rover) >= g_gc_ephemeral_low) && (((uint8_t*)*rover) < g_gc_ephemeral_high) )
- {
- // Set Bit For Card and advance to next card
- size_t card = gcard_of ((uint8_t*)rover);
-
- Interlocked::Or (&g_gc_card_table[card/card_word_width], (1U << (card % card_word_width)));
- // Skip to next card for the object
- rover = (Object**)align_on_card ((uint8_t*)(rover+1));
- }
- else
- {
- rover++;
- }
- }
-}
-
-#if defined(_MSC_VER) && defined(_TARGET_X86_)
-#pragma optimize("", on) // Go back to command line default optimizations
-#endif //_MSC_VER && _TARGET_X86_
-
-
#ifdef FEATURE_PREMORTEM_FINALIZATION
//--------------------------------------------------------------------
@@ -36304,7 +36236,7 @@ CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
if (hp->settings.concurrent && hp->settings.found_finalizers)
{
if (!mark_only_p)
- FinalizerThread::EnableFinalization();
+ GCToEEInterface::EnableFinalization(true);
}
}
@@ -36560,11 +36492,13 @@ void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc)
void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
{
+ UNREFERENCED_PARAMETER(gen_number);
GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn);
}
void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
{
+ UNREFERENCED_PARAMETER(gen_number);
GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn);
}
diff --git a/src/gc/gc.h b/src/gc/gc.h
index b7f1e956b6..7332e42885 100644
--- a/src/gc/gc.h
+++ b/src/gc/gc.h
@@ -140,8 +140,7 @@ class DacHeapWalker;
extern "C" uint32_t* g_gc_card_table;
extern "C" uint8_t* g_gc_lowest_address;
extern "C" uint8_t* g_gc_highest_address;
-extern "C" uint8_t* g_gc_ephemeral_low;
-extern "C" uint8_t* g_gc_ephemeral_high;
+extern "C" bool g_fFinalizerRunOnShutDown;
namespace WKS {
::IGCHeapInternal* CreateGCHeap();
@@ -270,6 +269,11 @@ public:
return mt->GetBaseSize() >= LARGE_OBJECT_SIZE;
}
+ void SetFinalizeRunOnShutdown(bool value)
+ {
+ g_fFinalizerRunOnShutDown = value;
+ }
+
protected:
public:
#if defined(FEATURE_BASICFREEZE) && defined(VERIFY_HEAP)
diff --git a/src/gc/gccommon.cpp b/src/gc/gccommon.cpp
index d1ccddd205..133f05e490 100644
--- a/src/gc/gccommon.cpp
+++ b/src/gc/gccommon.cpp
@@ -41,8 +41,7 @@ uint8_t* g_shadow_lowest_address = NULL;
uint32_t* g_gc_card_table;
uint8_t* g_gc_lowest_address = 0;
uint8_t* g_gc_highest_address = 0;
-uint8_t* g_gc_ephemeral_low = (uint8_t*)1;
-uint8_t* g_gc_ephemeral_high = (uint8_t*)~0;
+bool g_fFinalizerRunOnShutDown = false;
VOLATILE(int32_t) m_GCLock = -1;
diff --git a/src/gc/gcenv.ee.standalone.inl b/src/gc/gcenv.ee.standalone.inl
index 3b64586d70..31f3d1d8da 100644
--- a/src/gc/gcenv.ee.standalone.inl
+++ b/src/gc/gcenv.ee.standalone.inl
@@ -11,166 +11,202 @@
// will be fowarded to this interface instance.
extern IGCToCLR* g_theGCToCLR;
+// A note about this:
+// In general, we don't want to pretend to be smarter than the compiler
+// and force it to inline things. However, inlining is here is required
+// for correctness as it stands today (though it will not always be required).
+//
+// The reason for this is because:
+// 1) This file (and the GCToEEInterface class) define symbols that are inline
+// and static, so the symbol GCToEEInterface::XYZ defines a symbol with weak
+// linkage when the function is not inlined,
+// 2) src/vm/gcenv.ee.cpp all define symbols that are not inline and instance methods
+// of GCToEEInterface, with external linkage.
+// 3) When it comes time to link the GC and the VM, the linker observes the duplicate
+// symbols and discards the one with weak linkage.
+// 4) All of the calls within the GC to the functions in this file are replaced by
+// the linker to calls to the implementation of a pure virtual IGCToCLR. The
+// functions implementing IGCToCLR have an extra argument (this).
+// 5) Now, all calls to these functions from within the GC are doomed because of the
+// functions that actually get called expect this to be in rdi, where the compiler
+// has placed the first argument instead.
+//
+// For now, by forcing the compiler to inline these functions, the compiler won't actually
+// emit symbols for them and we'll avoid the linker havoc.
+#ifdef _MSC_VER
+ #define ALWAYS_INLINE __forceinline
+#else
+ #define ALWAYS_INLINE __attribute__((always_inline)) inline
+#endif
+
// When we are building the GC in a standalone environment, we
// will be dispatching virtually against g_theGCToCLR to call
// into the EE. This class provides an identical API to the existing
// GCToEEInterface, but only forwards the call onto the global
// g_theGCToCLR instance.
-inline void GCToEEInterface::SuspendEE(SUSPEND_REASON reason)
+ALWAYS_INLINE void GCToEEInterface::SuspendEE(SUSPEND_REASON reason)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->SuspendEE(reason);
}
-inline void GCToEEInterface::RestartEE(bool bFinishedGC)
+ALWAYS_INLINE void GCToEEInterface::RestartEE(bool bFinishedGC)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->RestartEE(bFinishedGC);
}
-inline void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc)
+ALWAYS_INLINE void GCToEEInterface::GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcScanRoots(fn, condemned, max_gen, sc);
}
-inline void GCToEEInterface::GcStartWork(int condemned, int max_gen)
+ALWAYS_INLINE void GCToEEInterface::GcStartWork(int condemned, int max_gen)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcStartWork(condemned, max_gen);
}
-inline void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc)
+ALWAYS_INLINE void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->AfterGcScanRoots(condemned, max_gen, sc);
}
-inline void GCToEEInterface::GcBeforeBGCSweepWork()
+ALWAYS_INLINE void GCToEEInterface::GcBeforeBGCSweepWork()
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcBeforeBGCSweepWork();
}
-inline void GCToEEInterface::GcDone(int condemned)
+ALWAYS_INLINE void GCToEEInterface::GcDone(int condemned)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcDone(condemned);
}
-inline bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject)
+ALWAYS_INLINE bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->RefCountedHandleCallbacks(pObject);
}
-inline void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
+ALWAYS_INLINE void GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->SyncBlockCacheWeakPtrScan(scanProc, lp1, lp2);
}
-inline void GCToEEInterface::SyncBlockCacheDemote(int max_gen)
+ALWAYS_INLINE void GCToEEInterface::SyncBlockCacheDemote(int max_gen)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->SyncBlockCacheDemote(max_gen);
}
-inline void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen)
+ALWAYS_INLINE void GCToEEInterface::SyncBlockCachePromotionsGranted(int max_gen)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->SyncBlockCachePromotionsGranted(max_gen);
}
-inline bool GCToEEInterface::IsPreemptiveGCDisabled(Thread * pThread)
+ALWAYS_INLINE bool GCToEEInterface::IsPreemptiveGCDisabled(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->IsPreemptiveGCDisabled(pThread);
}
-inline void GCToEEInterface::EnablePreemptiveGC(Thread * pThread)
+ALWAYS_INLINE void GCToEEInterface::EnablePreemptiveGC(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->EnablePreemptiveGC(pThread);
}
-inline void GCToEEInterface::DisablePreemptiveGC(Thread * pThread)
+ALWAYS_INLINE void GCToEEInterface::DisablePreemptiveGC(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DisablePreemptiveGC(pThread);
}
-inline gc_alloc_context * GCToEEInterface::GetAllocContext(Thread * pThread)
+ALWAYS_INLINE gc_alloc_context * GCToEEInterface::GetAllocContext(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->GetAllocContext(pThread);
}
-inline bool GCToEEInterface::CatchAtSafePoint(Thread * pThread)
+ALWAYS_INLINE bool GCToEEInterface::CatchAtSafePoint(Thread * pThread)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->CatchAtSafePoint(pThread);
}
-inline void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, void* param)
+ALWAYS_INLINE void GCToEEInterface::GcEnumAllocContexts(enum_alloc_context_func* fn, void* param)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->GcEnumAllocContexts(fn, param);
}
-inline Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg)
+ALWAYS_INLINE Thread* GCToEEInterface::CreateBackgroundThread(GCBackgroundThreadFunction threadStart, void* arg)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->CreateBackgroundThread(threadStart, arg);
}
-inline void GCToEEInterface::DiagGCStart(int gen, bool isInduced)
+ALWAYS_INLINE void GCToEEInterface::DiagGCStart(int gen, bool isInduced)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagGCStart(gen, isInduced);
}
-inline void GCToEEInterface::DiagUpdateGenerationBounds()
+ALWAYS_INLINE void GCToEEInterface::DiagUpdateGenerationBounds()
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagUpdateGenerationBounds();
}
-inline void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent)
+ALWAYS_INLINE void GCToEEInterface::DiagGCEnd(size_t index, int gen, int reason, bool fConcurrent)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagGCEnd(index, gen, reason, fConcurrent);
}
-inline void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext)
+ALWAYS_INLINE void GCToEEInterface::DiagWalkFReachableObjects(void* gcContext)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagWalkFReachableObjects(gcContext);
}
-inline void GCToEEInterface::DiagWalkSurvivors(void* gcContext)
+ALWAYS_INLINE void GCToEEInterface::DiagWalkSurvivors(void* gcContext)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagWalkSurvivors(gcContext);
}
-inline void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext)
+ALWAYS_INLINE void GCToEEInterface::DiagWalkLOHSurvivors(void* gcContext)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->DiagWalkLOHSurvivors(gcContext);
}
-inline void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext)
+ALWAYS_INLINE void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext)
{
assert(g_theGCToCLR != nullptr);
return g_theGCToCLR->DiagWalkBGCSurvivors(gcContext);
}
-inline void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
+ALWAYS_INLINE void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
{
assert(g_theGCToCLR != nullptr);
g_theGCToCLR->StompWriteBarrier(args);
}
+ALWAYS_INLINE void GCToEEInterface::EnableFinalization(bool foundFinalizers)
+{
+ assert(g_theGCToCLR != nullptr);
+ g_theGCToCLR->EnableFinalization(foundFinalizers);
+}
+
+#undef ALWAYS_INLINE
+
#endif // __GCTOENV_EE_STANDALONE_INL__
diff --git a/src/gc/gcenv.unix.cpp b/src/gc/gcenv.unix.cpp
deleted file mode 100644
index 0235952e28..0000000000
--- a/src/gc/gcenv.unix.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-#include "env/gcenv.structs.h"
-#include "env/gcenv.base.h"
-#include "env/gcenv.os.h"
-
-// Initialize the interface implementation
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::Initialize()
-{
- throw nullptr;
-}
-
-// Shutdown the interface implementation
-void GCToOSInterface::Shutdown()
-{
- throw nullptr;
-}
-
-// Get numeric id of the current thread if possible on the
-// current platform. It is indended for logging purposes only.
-// Return:
-// Numeric id of the current thread or 0 if the
-uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
-{
- throw nullptr;
-}
-
-// Get id of the process
-uint32_t GCToOSInterface::GetCurrentProcessId()
-{
- throw nullptr;
-}
-
-// Set ideal affinity for the current thread
-// Parameters:
-// affinity - ideal processor affinity for the thread
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity)
-{
- throw nullptr;
-}
-
-// Get the number of the current processor
-uint32_t GCToOSInterface::GetCurrentProcessorNumber()
-{
- throw nullptr;
-}
-
-// Check if the OS supports getting current processor number
-bool GCToOSInterface::CanGetCurrentProcessorNumber()
-{
- throw nullptr;
-}
-
-// Flush write buffers of processors that are executing threads of the current process
-void GCToOSInterface::FlushProcessWriteBuffers()
-{
- throw nullptr;
-}
-
-// Break into a debugger
-void GCToOSInterface::DebugBreak()
-{
- throw nullptr;
-}
-
-// Get number of logical processors
-uint32_t GCToOSInterface::GetLogicalCpuCount()
-{
- throw nullptr;
-}
-
-// Causes the calling thread to sleep for the specified number of milliseconds
-// Parameters:
-// sleepMSec - time to sleep before switching to another thread
-void GCToOSInterface::Sleep(uint32_t sleepMSec)
-{
- throw nullptr;
-}
-
-// Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
-// Parameters:
-// switchCount - number of times the YieldThread was called in a loop
-void GCToOSInterface::YieldThread(uint32_t switchCount)
-{
- throw nullptr;
-}
-
-// Reserve virtual memory range.
-// Parameters:
-// size - size of the virtual memory range
-// alignment - requested memory alignment, 0 means no specific alignment requested
-// flags - flags to control special settings like write watching
-// Return:
-// Starting virtual address of the reserved range
-void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
-{
- throw nullptr;
-}
-
-// Release virtual memory range previously reserved using VirtualReserve
-// Parameters:
-// address - starting virtual address
-// size - size of the virtual memory range
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::VirtualRelease(void* address, size_t size)
-{
- throw nullptr;
-}
-
-// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
-// Parameters:
-// address - starting virtual address
-// size - size of the virtual memory range
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::VirtualCommit(void* address, size_t size)
-{
- throw nullptr;
-}
-
-// Decomit virtual memory range.
-// Parameters:
-// address - starting virtual address
-// size - size of the virtual memory range
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
-{
- throw nullptr;
-}
-
-// Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
-// longer of interest, but it should not be decommitted.
-// Parameters:
-// address - starting virtual address
-// size - size of the virtual memory range
-// unlock - true if the memory range should also be unlocked
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
-{
- throw nullptr;
-}
-
-// Check if the OS supports write watching
-bool GCToOSInterface::SupportsWriteWatch()
-{
- throw nullptr;
-}
-
-// Reset the write tracking state for the specified virtual memory range.
-// Parameters:
-// address - starting virtual address
-// size - size of the virtual memory range
-void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
-{
- throw nullptr;
-}
-
-// Retrieve addresses of the pages that are written to in a region of virtual memory
-// Parameters:
-// resetState - true indicates to reset the write tracking state
-// address - starting virtual address
-// size - size of the virtual memory range
-// pageAddresses - buffer that receives an array of page addresses in the memory region
-// pageAddressesCount - on input, size of the lpAddresses array, in array elements
-// on output, the number of page addresses that are returned in the array.
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
-{
- throw nullptr;
-}
-
-// Get size of the largest cache on the processor die
-// Parameters:
-// trueSize - true to return true cache size, false to return scaled up size based on
-// the processor architecture
-// Return:
-// Size of the cache
-size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize)
-{
- throw nullptr;
-}
-
-// Get affinity mask of the current process
-// Parameters:
-// processMask - affinity mask for the specified process
-// systemMask - affinity mask for the system
-// Return:
-// true if it has succeeded, false if it has failed
-// Remarks:
-// A process affinity mask is a bit vector in which each bit represents the processors that
-// a process is allowed to run on. A system affinity mask is a bit vector in which each bit
-// represents the processors that are configured into a system.
-// A process affinity mask is a subset of the system affinity mask. A process is only allowed
-// to run on the processors configured into a system. Therefore, the process affinity mask cannot
-// specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor.
-bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask)
-{
- throw nullptr;
-}
-
-// Get number of processors assigned to the current process
-// Return:
-// The number of processors
-uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
-{
- throw nullptr;
-}
-
-// Return the size of the user-mode portion of the virtual address space of this process.
-// Return:
-// non zero if it has succeeded, 0 if it has failed
-size_t GCToOSInterface::GetVirtualMemoryLimit()
-{
- throw nullptr;
-}
-
-// Get the physical memory that this process can use.
-// Return:
-// non zero if it has succeeded, 0 if it has failed
-// Remarks:
-// If a process runs with a restricted memory limit, it returns the limit. If there's no limit
-// specified, it returns amount of actual physical memory.
-uint64_t GCToOSInterface::GetPhysicalMemoryLimit()
-{
- throw nullptr;
-}
-
-// Get memory status
-// Parameters:
-// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
-// that is in use (0 indicates no memory use and 100 indicates full memory use).
-// available_physical - The amount of physical memory currently available, in bytes.
-// available_page_file - The maximum amount of memory the current process can commit, in bytes.
-void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
-{
- throw nullptr;
-}
-
-// Get a high precision performance counter
-// Return:
-// The counter value
-int64_t GCToOSInterface::QueryPerformanceCounter()
-{
- throw nullptr;
-}
-
-// Get a frequency of the high precision performance counter
-// Return:
-// The counter frequency
-int64_t GCToOSInterface::QueryPerformanceFrequency()
-{
- throw nullptr;
-}
-
-// Get a time stamp with a low precision
-// Return:
-// Time stamp in milliseconds
-uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
-{
- throw nullptr;
-}
-
-
-// Create a new thread for GC use
-// Parameters:
-// function - the function to be executed by the thread
-// param - parameters of the thread
-// affinity - processor affinity of the thread
-// Return:
-// true if it has succeeded, false if it has failed
-bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity)
-{
- throw nullptr;
-}
-
-// Initialize the critical section
-void CLRCriticalSection::Initialize()
-{
- throw nullptr;
-}
-
-// Destroy the critical section
-void CLRCriticalSection::Destroy()
-{
- throw nullptr;
-}
-
-// Enter the critical section. Blocks until the section can be entered.
-void CLRCriticalSection::Enter()
-{
- throw nullptr;
-}
-
-// Leave the critical section
-void CLRCriticalSection::Leave()
-{
- throw nullptr;
-} \ No newline at end of file
diff --git a/src/gc/gcimpl.h b/src/gc/gcimpl.h
index 7e3a13a743..cb91c4dc3e 100644
--- a/src/gc/gcimpl.h
+++ b/src/gc/gcimpl.h
@@ -198,7 +198,6 @@ public:
BOOL FinalizeAppDomain(AppDomain *pDomain, BOOL fRunFinalizers);
BOOL ShouldRestartFinalizerWatchDog();
- void SetCardsAfterBulkCopy( Object**, size_t);
void DiagWalkObject (Object* obj, walk_fn fn, void* context);
public: // FIX
diff --git a/src/gc/gcinterface.ee.h b/src/gc/gcinterface.ee.h
index c5f87ef031..7c0eea2d95 100644
--- a/src/gc/gcinterface.ee.h
+++ b/src/gc/gcinterface.ee.h
@@ -128,6 +128,11 @@ public:
// barrier if it needs to be updated.
virtual
void StompWriteBarrier(WriteBarrierParameters* args) = 0;
+
+ // Signals to the finalizer thread that there are objects ready to
+ // be finalized.
+ virtual
+ void EnableFinalization(bool foundFinalizers) = 0;
};
#endif // _GCINTERFACE_EE_H_
diff --git a/src/gc/gcinterface.h b/src/gc/gcinterface.h
index 1457848992..99d79df633 100644
--- a/src/gc/gcinterface.h
+++ b/src/gc/gcinterface.h
@@ -46,7 +46,9 @@ enum class WriteBarrierOp
{
StompResize,
StompEphemeral,
- Initialize
+ Initialize,
+ SwitchToWriteWatch,
+ SwitchToNonWriteWatch
};
// Arguments to GCToEEInterface::StompWriteBarrier
@@ -85,11 +87,15 @@ struct WriteBarrierParameters
// The new start of the ephemeral generation.
// Used for WriteBarrierOp::StompEphemeral.
- uint8_t* ephemeral_lo;
+ uint8_t* ephemeral_low;
// The new end of the ephemeral generation.
// Used for WriteBarrierOp::StompEphemeral.
- uint8_t* ephemeral_hi;
+ uint8_t* ephemeral_high;
+
+ // The new write watch table, if we are using our own write watch
+ // implementation. Used for WriteBarrierOp::SwitchToWriteWatch only.
+ uint8_t* write_watch_table;
};
#include "gcinterface.ee.h"
@@ -148,6 +154,10 @@ struct segment_info
#define max_generation 2
+// The bit shift used to convert a memory address into an index into the
+// Software Write Watch table.
+#define SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift 0xc
+
class Object;
class IGCHeap;
@@ -303,6 +313,10 @@ public:
// Gets the next finalizable object.
virtual Object* GetNextFinalizable() = 0;
+ // Sets whether or not the GC should report all finalizable objects as
+ // ready to be finalized, instead of only collectable objects.
+ virtual void SetFinalizeRunOnShutdown(bool value) = 0;
+
/*
===========================================================================
BCL routines. These are routines that are directly exposed by mscorlib
@@ -398,9 +412,6 @@ public:
// sanity checks asserting that a GC has not occured.
virtual unsigned GetGcCount() = 0;
- // Sets cards after an object has been memmoved.
- virtual void SetCardsAfterBulkCopy(Object** obj, size_t length) = 0;
-
// Gets whether or not the home heap of this alloc context matches the heap
// associated with this thread.
virtual bool IsThreadUsingAllocationContextHeap(gc_alloc_context* acontext, int thread_number) = 0;
diff --git a/src/gc/gcpriv.h b/src/gc/gcpriv.h
index 3bed8c2cf8..1f97d7f2d5 100644
--- a/src/gc/gcpriv.h
+++ b/src/gc/gcpriv.h
@@ -1671,7 +1671,7 @@ protected:
PER_HEAP
void reset_write_watch (BOOL concurrent_p);
PER_HEAP
- void adjust_ephemeral_limits (bool is_runtime_suspended);
+ void adjust_ephemeral_limits ();
PER_HEAP
void make_generation (generation& gen, heap_segment* seg,
uint8_t* start, uint8_t* pointer);
@@ -2802,13 +2802,11 @@ public:
PER_HEAP
void exit_gc_done_event_lock();
-#ifdef MULTIPLE_HEAPS
PER_HEAP
uint8_t* ephemeral_low; //lowest ephemeral address
PER_HEAP
uint8_t* ephemeral_high; //highest ephemeral address
-#endif //MULTIPLE_HEAPS
PER_HEAP
uint32_t* card_table;
diff --git a/src/gc/gcsvr.cpp b/src/gc/gcsvr.cpp
index cf5fc9335f..70801dd4ee 100644
--- a/src/gc/gcsvr.cpp
+++ b/src/gc/gcsvr.cpp
@@ -13,6 +13,7 @@
#include "gc.h"
#include "gcscan.h"
#include "gcdesc.h"
+#include "softwarewritewatch.h"
#define SERVER_GC 1
diff --git a/src/gc/gcwks.cpp b/src/gc/gcwks.cpp
index 574df8215a..5c489df0e0 100644
--- a/src/gc/gcwks.cpp
+++ b/src/gc/gcwks.cpp
@@ -11,6 +11,7 @@
#include "gc.h"
#include "gcscan.h"
#include "gcdesc.h"
+#include "softwarewritewatch.h"
#ifdef SERVER_GC
#undef SERVER_GC
diff --git a/src/gc/sample/CMakeLists.txt b/src/gc/sample/CMakeLists.txt
index 9552cc51e2..29fd32f2ff 100644
--- a/src/gc/sample/CMakeLists.txt
+++ b/src/gc/sample/CMakeLists.txt
@@ -22,7 +22,7 @@ set(SOURCES
if(WIN32)
list(APPEND SOURCES
- ../gcenv.windows.cpp)
+ ../windows/gcenv.windows.cpp)
add_definitions(-DUNICODE=1)
else()
list(APPEND SOURCES
diff --git a/src/gc/sample/GCSample.cpp b/src/gc/sample/GCSample.cpp
index 664dc38e94..112d291420 100644
--- a/src/gc/sample/GCSample.cpp
+++ b/src/gc/sample/GCSample.cpp
@@ -94,14 +94,11 @@ inline void ErectWriteBarrier(Object ** dst, Object * ref)
if (((uint8_t*)dst < g_gc_lowest_address) || ((uint8_t*)dst >= g_gc_highest_address))
return;
- if((uint8_t*)ref >= g_gc_ephemeral_low && (uint8_t*)ref < g_gc_ephemeral_high)
- {
- // volatile is used here to prevent fetch of g_card_table from being reordered
- // with g_lowest/highest_address check above. See comment in code:gc_heap::grow_brick_card_tables.
- uint8_t* pCardByte = (uint8_t *)*(volatile uint8_t **)(&g_gc_card_table) + card_byte((uint8_t *)dst);
- if(*pCardByte != 0xFF)
- *pCardByte = 0xFF;
- }
+ // volatile is used here to prevent fetch of g_card_table from being reordered
+ // with g_lowest/highest_address check above. See comment in code:gc_heap::grow_brick_card_tables.
+ uint8_t* pCardByte = (uint8_t *)*(volatile uint8_t **)(&g_gc_card_table) + card_byte((uint8_t *)dst);
+ if(*pCardByte != 0xFF)
+ *pCardByte = 0xFF;
}
void WriteBarrier(Object ** dst, Object * ref)
diff --git a/src/gc/sample/GCSample.vcxproj b/src/gc/sample/GCSample.vcxproj
index 1716f462ee..105e289c1a 100644
--- a/src/gc/sample/GCSample.vcxproj
+++ b/src/gc/sample/GCSample.vcxproj
@@ -87,7 +87,7 @@
<ClCompile Include="GCSample.cpp" />
<ClCompile Include="..\gccommon.cpp" />
<ClCompile Include="..\gceewks.cpp" />
- <ClCompile Include="..\gcenv.windows.cpp">
+ <ClCompile Include="..\windows\gcenv.windows.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\gcscan.cpp" />
diff --git a/src/gc/sample/gcenv.ee.cpp b/src/gc/sample/gcenv.ee.cpp
index ac227b4823..e95a78dc48 100644
--- a/src/gc/sample/gcenv.ee.cpp
+++ b/src/gc/sample/gcenv.ee.cpp
@@ -13,8 +13,6 @@ MethodTable * g_pFreeObjectMethodTable;
int32_t g_TrapReturningThreads;
-bool g_fFinalizerRunOnShutDown;
-
EEConfig * g_pConfig;
bool CLREventStatic::CreateManualEventNoThrow(bool bInitialState)
@@ -259,17 +257,12 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
{
}
-void FinalizerThread::EnableFinalization()
+void GCToEEInterface::EnableFinalization(bool foundFinalizers)
{
// Signal to finalizer thread that there are objects to finalize
// TODO: Implement for finalization
}
-bool FinalizerThread::HaveExtraWorkForFinalizer()
-{
- return false;
-}
-
bool IsGCSpecialThread()
{
// TODO: Implement for background GC
diff --git a/src/gc/softwarewritewatch.cpp b/src/gc/softwarewritewatch.cpp
index fa14a04897..b85293857a 100644
--- a/src/gc/softwarewritewatch.cpp
+++ b/src/gc/softwarewritewatch.cpp
@@ -3,10 +3,9 @@
// See the LICENSE file in the project root for more information.
#include "common.h"
-#include "softwarewritewatch.h"
-
#include "gcenv.h"
#include "env/gcenv.os.h"
+#include "softwarewritewatch.h"
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
#ifndef DACCESS_COMPILE
@@ -15,8 +14,8 @@ static_assert((static_cast<size_t>(1) << SOFTWARE_WRITE_WATCH_AddressToTableByte
extern "C"
{
- uint8_t *g_sw_ww_table = nullptr;
- bool g_sw_ww_enabled_for_gc_heap = false;
+ uint8_t *g_gc_sw_ww_table = nullptr;
+ bool g_gc_sw_ww_enabled_for_gc_heap = false;
}
void SoftwareWriteWatch::StaticClose()
@@ -26,8 +25,8 @@ void SoftwareWriteWatch::StaticClose()
return;
}
- g_sw_ww_enabled_for_gc_heap = false;
- g_sw_ww_table = nullptr;
+ g_gc_sw_ww_enabled_for_gc_heap = false;
+ g_gc_sw_ww_table = nullptr;
}
bool SoftwareWriteWatch::GetDirtyFromBlock(
diff --git a/src/gc/softwarewritewatch.h b/src/gc/softwarewritewatch.h
index 3c8491cecb..0e6e6c8191 100644
--- a/src/gc/softwarewritewatch.h
+++ b/src/gc/softwarewritewatch.h
@@ -5,25 +5,20 @@
#ifndef __SOFTWARE_WRITE_WATCH_H__
#define __SOFTWARE_WRITE_WATCH_H__
+#include "gcinterface.h"
+#include "gc.h"
+
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
#ifndef DACCESS_COMPILE
-extern void SwitchToWriteWatchBarrier(bool isRuntimeSuspended);
-extern void SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended);
-
-#define SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift 0xc
-
extern "C"
{
// Table containing the dirty state. This table is translated to exclude the lowest address it represents, see
// TranslateTableToExcludeHeapStartAddress.
- extern uint8_t *g_sw_ww_table;
+ extern uint8_t *g_gc_sw_ww_table;
// Write watch may be disabled when it is not needed (between GCs for instance). This indicates whether it is enabled.
- extern bool g_sw_ww_enabled_for_gc_heap;
-
- extern uint8_t *g_lowest_address; // start address of the GC heap
- extern uint8_t *g_highest_address; // end address of the GC heap
+ extern bool g_gc_sw_ww_enabled_for_gc_heap;
}
class SoftwareWriteWatch
@@ -116,7 +111,7 @@ inline void SoftwareWriteWatch::VerifyMemoryRegion(
inline uint8_t *SoftwareWriteWatch::GetTable()
{
- return g_sw_ww_table;
+ return g_gc_sw_ww_table;
}
inline uint8_t *SoftwareWriteWatch::GetUntranslatedTable()
@@ -163,7 +158,7 @@ inline void SoftwareWriteWatch::SetUntranslatedTable(uint8_t *untranslatedTable,
assert(ALIGN_DOWN(untranslatedTable, sizeof(size_t)) == untranslatedTable);
assert(heapStartAddress != nullptr);
- g_sw_ww_table = TranslateTableToExcludeHeapStartAddress(untranslatedTable, heapStartAddress);
+ g_gc_sw_ww_table = TranslateTableToExcludeHeapStartAddress(untranslatedTable, heapStartAddress);
}
inline void SoftwareWriteWatch::SetResizedUntranslatedTable(
@@ -194,7 +189,7 @@ inline void SoftwareWriteWatch::SetResizedUntranslatedTable(
inline bool SoftwareWriteWatch::IsEnabledForGCHeap()
{
- return g_sw_ww_enabled_for_gc_heap;
+ return g_gc_sw_ww_enabled_for_gc_heap;
}
inline void SoftwareWriteWatch::EnableForGCHeap()
@@ -204,9 +199,13 @@ inline void SoftwareWriteWatch::EnableForGCHeap()
VerifyCreated();
assert(!IsEnabledForGCHeap());
+ g_gc_sw_ww_enabled_for_gc_heap = true;
- g_sw_ww_enabled_for_gc_heap = true;
- SwitchToWriteWatchBarrier(true);
+ WriteBarrierParameters args = {};
+ args.operation = WriteBarrierOp::SwitchToWriteWatch;
+ args.write_watch_table = g_gc_sw_ww_table;
+ args.is_runtime_suspended = true;
+ GCToEEInterface::StompWriteBarrier(&args);
}
inline void SoftwareWriteWatch::DisableForGCHeap()
@@ -216,19 +215,22 @@ inline void SoftwareWriteWatch::DisableForGCHeap()
VerifyCreated();
assert(IsEnabledForGCHeap());
+ g_gc_sw_ww_enabled_for_gc_heap = false;
- g_sw_ww_enabled_for_gc_heap = false;
- SwitchToNonWriteWatchBarrier(true);
+ WriteBarrierParameters args = {};
+ args.operation = WriteBarrierOp::SwitchToNonWriteWatch;
+ args.is_runtime_suspended = true;
+ GCToEEInterface::StompWriteBarrier(&args);
}
inline void *SoftwareWriteWatch::GetHeapStartAddress()
{
- return g_lowest_address;
+ return g_gc_lowest_address;
}
inline void *SoftwareWriteWatch::GetHeapEndAddress()
{
- return g_highest_address;
+ return g_gc_highest_address;
}
inline size_t SoftwareWriteWatch::GetTableByteIndex(void *address)
diff --git a/src/gc/unix/CMakeLists.txt b/src/gc/unix/CMakeLists.txt
new file mode 100644
index 0000000000..ef66abf32a
--- /dev/null
+++ b/src/gc/unix/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+add_compile_options(-fPIC)
+include_directories("../env")
+
+include(configure.cmake)
+
+set(GC_PAL_SOURCES
+ gcenv.unix.cpp)
+
+add_library(gc_unix STATIC ${GC_PAL_SOURCES} ${VERSION_FILE_PATH})
diff --git a/src/gc/unix/config.h.in b/src/gc/unix/config.h.in
new file mode 100644
index 0000000000..7578c74c05
--- /dev/null
+++ b/src/gc/unix/config.h.in
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#ifndef __CONFIG_H__
+#define __CONFIG_H__
+
+#cmakedefine01 HAVE_SYS_TIME_H
+#cmakedefine01 HAVE_SYS_MMAN_H
+#cmakedefine01 HAVE_PTHREAD_THREADID_NP
+#cmakedefine01 HAVE_PTHREAD_GETTHREADID_NP
+#cmakedefine01 HAVE_SCHED_GETCPU
+
+#endif // __CONFIG_H__ \ No newline at end of file
diff --git a/src/gc/unix/configure.cmake b/src/gc/unix/configure.cmake
new file mode 100644
index 0000000000..6e1e8fe27d
--- /dev/null
+++ b/src/gc/unix/configure.cmake
@@ -0,0 +1,40 @@
+check_include_files(sys/time.h HAVE_SYS_TIME_H)
+check_include_files(sys/mman.h HAVE_SYS_MMAN_H)
+check_cxx_source_compiles("
+ #include <pthread.h>
+ #include <stdint.h>
+
+ int main()
+ {
+ uint64_t tid;
+ pthread_threadid_np(pthread_self(), &tid);
+ return (int)tid;
+ }
+ " HAVE_PTHREAD_THREADID_NP)
+
+check_cxx_source_compiles("
+ #include <pthread.h>
+ #include <stdint.h>
+
+ int main()
+ {
+ return (int)pthread_getthreadid_np();
+ }
+ " HAVE_PTHREAD_GETTHREADID_NP)
+
+check_cxx_source_runs("
+ #include <sched.h>
+
+ int main()
+ {
+ int result = sched_getcpu();
+ if (result == -1)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+ " HAVE_SCHED_GETCPU)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) \ No newline at end of file
diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp
new file mode 100644
index 0000000000..34a45b3cc1
--- /dev/null
+++ b/src/gc/unix/gcenv.unix.cpp
@@ -0,0 +1,627 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#include <cstdint>
+#include <cstddef>
+#include <cassert>
+#include <memory>
+
+// The CoreCLR PAL defines _POSIX_C_SOURCE to avoid calling non-posix pthread functions.
+// This isn't something we want, because we're totally fine using non-posix functions.
+#if defined(__APPLE__)
+ #define _DARWIN_C_SOURCE
+#endif // definfed(__APPLE__)
+
+#include <pthread.h>
+#include <signal.h>
+#include "config.h"
+
+// clang typedefs uint64_t to be unsigned long long, which clashes with
+// PAL/MSVC's unsigned long, causing linker errors. This ugly hack
+// will go away once the GC doesn't depend on PAL headers.
+typedef unsigned long uint64_t_hack;
+#define uint64_t uint64_t_hack
+static_assert(sizeof(uint64_t) == 8, "unsigned long isn't 8 bytes");
+
+#ifndef __out_z
+#define __out_z
+#endif // __out_z
+
+#include "gcenv.structs.h"
+#include "gcenv.base.h"
+#include "gcenv.os.h"
+
+#ifndef FEATURE_STANDALONE_GC
+ #error "A GC-private implementation of GCToOSInterface should only be used with FEATURE_STANDALONE_GC"
+#endif // FEATURE_STANDALONE_GC
+
+#ifdef HAVE_SYS_TIME_H
+ #include <sys/time.h>
+#else
+ #error "sys/time.h required by GC PAL for the time being"
+#endif // HAVE_SYS_TIME_
+
+#ifdef HAVE_SYS_MMAN_H
+ #include <sys/mman.h>
+#else
+ #error "sys/mman.h required by GC PAL"
+#endif // HAVE_SYS_MMAN_H
+
+#ifdef __linux__
+ #include <sys/syscall.h>
+#endif // __linux__
+
+#include <time.h> // nanosleep
+#include <sched.h> // sched_yield
+#include <errno.h>
+#include <unistd.h> // sysconf
+
+// The number of milliseconds in a second.
+static const int tccSecondsToMilliSeconds = 1000;
+
+// The number of microseconds in a second.
+static const int tccSecondsToMicroSeconds = 1000000;
+
+// The number of microseconds in a millisecond.
+static const int tccMilliSecondsToMicroSeconds = 1000;
+
+// The number of nanoseconds in a millisecond.
+static const int tccMilliSecondsToNanoSeconds = 1000000;
+
+// The cachced number of logical CPUs observed.
+static uint32_t g_logicalCpuCount = 0;
+
+// Helper memory page used by the FlushProcessWriteBuffers
+static uint8_t g_helperPage[OS_PAGE_SIZE] __attribute__((aligned(OS_PAGE_SIZE)));
+
+// Mutex to make the FlushProcessWriteBuffersMutex thread safe
+static pthread_mutex_t g_flushProcessWriteBuffersMutex;
+
+// Initialize the interface implementation
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::Initialize()
+{
+ // Calculate and cache the number of processors on this machine
+ int cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
+ if (cpuCount == -1)
+ {
+ return false;
+ }
+
+ g_logicalCpuCount = cpuCount;
+
+ // Verify that the s_helperPage is really aligned to the g_SystemInfo.dwPageSize
+ assert((((size_t)g_helperPage) & (OS_PAGE_SIZE - 1)) == 0);
+
+ // Locking the page ensures that it stays in memory during the two mprotect
+ // calls in the FlushProcessWriteBuffers below. If the page was unmapped between
+ // those calls, they would not have the expected effect of generating IPI.
+ int status = mlock(g_helperPage, OS_PAGE_SIZE);
+
+ if (status != 0)
+ {
+ return false;
+ }
+
+ status = pthread_mutex_init(&g_flushProcessWriteBuffersMutex, NULL);
+ if (status != 0)
+ {
+ munlock(g_helperPage, OS_PAGE_SIZE);
+ return false;
+ }
+
+ return true;
+}
+
+// Shutdown the interface implementation
+void GCToOSInterface::Shutdown()
+{
+ int ret = munlock(g_helperPage, OS_PAGE_SIZE);
+ assert(ret == 0);
+ ret = pthread_mutex_destroy(&g_flushProcessWriteBuffersMutex);
+ assert(ret == 0);
+}
+
+// Get numeric id of the current thread if possible on the
+// current platform. It is indended for logging purposes only.
+// Return:
+// Numeric id of the current thread, as best we can retrieve it.
+uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
+{
+#if defined(__linux__)
+ return (uint64_t)syscall(SYS_gettid);
+#elif HAVE_PTHREAD_GETTHREADID_NP
+ return (uint64_t)pthread_getthreadid_np();
+#elif HAVE_PTHREAD_THREADID_NP
+ unsigned long long tid;
+ pthread_threadid_np(pthread_self(), &tid);
+ return (uint64_t)tid;
+#else
+ // Fallback in case we don't know how to get integer thread id on the current platform
+ return (uint64_t)pthread_self();
+#endif
+}
+
+// Get the process ID of the process.
+uint32_t GCToOSInterface::GetCurrentProcessId()
+{
+ return getpid();
+}
+
+// Set ideal affinity for the current thread
+// Parameters:
+// affinity - ideal processor affinity for the thread
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity)
+{
+ // TODO(segilles)
+ return false;
+}
+
+// Get the number of the current processor
+uint32_t GCToOSInterface::GetCurrentProcessorNumber()
+{
+#if HAVE_SCHED_GETCPU
+ int processorNumber = sched_getcpu();
+ assert(processorNumber != -1);
+ return processorNumber;
+#else
+ return 0;
+#endif
+}
+
+// Check if the OS supports getting current processor number
+bool GCToOSInterface::CanGetCurrentProcessorNumber()
+{
+ return HAVE_SCHED_GETCPU;
+}
+
+// Flush write buffers of processors that are executing threads of the current process
+void GCToOSInterface::FlushProcessWriteBuffers()
+{
+ int status = pthread_mutex_lock(&g_flushProcessWriteBuffersMutex);
+ assert(status == 0 && "Failed to lock the flushProcessWriteBuffersMutex lock");
+
+ // Changing a helper memory page protection from read / write to no access
+ // causes the OS to issue IPI to flush TLBs on all processors. This also
+ // results in flushing the processor buffers.
+ status = mprotect(g_helperPage, OS_PAGE_SIZE, PROT_READ | PROT_WRITE);
+ assert(status == 0 && "Failed to change helper page protection to read / write");
+
+ // Ensure that the page is dirty before we change the protection so that
+ // we prevent the OS from skipping the global TLB flush.
+ __sync_add_and_fetch((size_t*)g_helperPage, 1);
+
+ status = mprotect(g_helperPage, OS_PAGE_SIZE, PROT_NONE);
+ assert(status == 0 && "Failed to change helper page protection to no access");
+
+ status = pthread_mutex_unlock(&g_flushProcessWriteBuffersMutex);
+ assert(status == 0 && "Failed to unlock the flushProcessWriteBuffersMutex lock");
+}
+
+// Break into a debugger. Uses a compiler intrinsic if one is available,
+// otherwise raises a SIGTRAP.
+void GCToOSInterface::DebugBreak()
+{
+ // __has_builtin is only defined by clang. GCC doesn't have a debug
+ // trap intrinsic anyway.
+#ifndef __has_builtin
+ #define __has_builtin(x) 0
+#endif // __has_builtin
+
+#if __has_builtin(__builtin_debugtrap)
+ __builtin_debugtrap();
+#else
+ raise(SIGTRAP);
+#endif
+}
+
+// Get number of logical processors
+uint32_t GCToOSInterface::GetLogicalCpuCount()
+{
+ return g_logicalCpuCount;
+}
+
+// Causes the calling thread to sleep for the specified number of milliseconds
+// Parameters:
+// sleepMSec - time to sleep before switching to another thread
+void GCToOSInterface::Sleep(uint32_t sleepMSec)
+{
+ if (sleepMSec == 0)
+ {
+ return;
+ }
+
+ timespec requested;
+ requested.tv_sec = sleepMSec / tccSecondsToMilliSeconds;
+ requested.tv_nsec = (sleepMSec - requested.tv_sec * tccSecondsToMilliSeconds) * tccMilliSecondsToNanoSeconds;
+
+ timespec remaining;
+ while (nanosleep(&requested, &remaining) == EINTR)
+ {
+ requested = remaining;
+ }
+}
+
+// Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
+// Parameters:
+// switchCount - number of times the YieldThread was called in a loop
+void GCToOSInterface::YieldThread(uint32_t switchCount)
+{
+ int ret = sched_yield();
+
+ // sched_yield never fails on Linux, unclear about other OSes
+ assert(ret == 0);
+}
+
+// Reserve virtual memory range.
+// Parameters:
+// size - size of the virtual memory range
+// alignment - requested memory alignment, 0 means no specific alignment requested
+// flags - flags to control special settings like write watching
+// Return:
+// Starting virtual address of the reserved range
+void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
+{
+ assert(!(flags & VirtualReserveFlags::WriteWatch) && "WriteWatch not supported on Unix");
+ if (alignment == 0)
+ {
+ alignment = OS_PAGE_SIZE;
+ }
+
+ size_t alignedSize = size + (alignment - OS_PAGE_SIZE);
+ void * pRetVal = mmap(nullptr, alignedSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
+
+ if (pRetVal != NULL)
+ {
+ void * pAlignedRetVal = (void *)(((size_t)pRetVal + (alignment - 1)) & ~(alignment - 1));
+ size_t startPadding = (size_t)pAlignedRetVal - (size_t)pRetVal;
+ if (startPadding != 0)
+ {
+ int ret = munmap(pRetVal, startPadding);
+ assert(ret == 0);
+ }
+
+ size_t endPadding = alignedSize - (startPadding + size);
+ if (endPadding != 0)
+ {
+ int ret = munmap((void *)((size_t)pAlignedRetVal + size), endPadding);
+ assert(ret == 0);
+ }
+
+ pRetVal = pAlignedRetVal;
+ }
+
+ return pRetVal;
+}
+
+// Release virtual memory range previously reserved using VirtualReserve
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::VirtualRelease(void* address, size_t size)
+{
+ int ret = munmap(address, size);
+
+ return (ret == 0);
+}
+
+// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::VirtualCommit(void* address, size_t size)
+{
+ return mprotect(address, size, PROT_WRITE | PROT_READ) == 0;
+}
+
+// Decomit virtual memory range.
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
+{
+ return mprotect(address, size, PROT_NONE) == 0;
+}
+
+// Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
+// longer of interest, but it should not be decommitted.
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+// unlock - true if the memory range should also be unlocked
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
+{
+ // TODO(CoreCLR#1259) pipe to madvise?
+ return false;
+}
+
+// Check if the OS supports write watching
+bool GCToOSInterface::SupportsWriteWatch()
+{
+ return false;
+}
+
+// Reset the write tracking state for the specified virtual memory range.
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
+{
+ assert(!"should never call ResetWriteWatch on Unix");
+}
+
+// Retrieve addresses of the pages that are written to in a region of virtual memory
+// Parameters:
+// resetState - true indicates to reset the write tracking state
+// address - starting virtual address
+// size - size of the virtual memory range
+// pageAddresses - buffer that receives an array of page addresses in the memory region
+// pageAddressesCount - on input, size of the lpAddresses array, in array elements
+// on output, the number of page addresses that are returned in the array.
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
+{
+ assert(!"should never call GetWriteWatch on Unix");
+ return false;
+}
+
+// Get size of the largest cache on the processor die
+// Parameters:
+// trueSize - true to return true cache size, false to return scaled up size based on
+// the processor architecture
+// Return:
+// Size of the cache
+size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize)
+{
+ // TODO(segilles) processor detection
+ return 0;
+}
+
+// Get affinity mask of the current process
+// Parameters:
+// processMask - affinity mask for the specified process
+// systemMask - affinity mask for the system
+// Return:
+// true if it has succeeded, false if it has failed
+// Remarks:
+// A process affinity mask is a bit vector in which each bit represents the processors that
+// a process is allowed to run on. A system affinity mask is a bit vector in which each bit
+// represents the processors that are configured into a system.
+// A process affinity mask is a subset of the system affinity mask. A process is only allowed
+// to run on the processors configured into a system. Therefore, the process affinity mask cannot
+// specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor.
+bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask)
+{
+ // TODO(segilles) processor detection
+ return false;
+}
+
+// Get number of processors assigned to the current process
+// Return:
+// The number of processors
+uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
+{
+ return g_logicalCpuCount;
+}
+
+// Return the size of the user-mode portion of the virtual address space of this process.
+// Return:
+// non zero if it has succeeded, 0 if it has failed
+size_t GCToOSInterface::GetVirtualMemoryLimit()
+{
+#ifdef BIT64
+ // There is no API to get the total virtual address space size on
+ // Unix, so we use a constant value representing 128TB, which is
+ // the approximate size of total user virtual address space on
+ // the currently supported Unix systems.
+ static const uint64_t _128TB = (1ull << 47);
+ return _128TB;
+#else
+ return (size_t)-1;
+#endif
+}
+
+// Get the physical memory that this process can use.
+// Return:
+// non zero if it has succeeded, 0 if it has failed
+// Remarks:
+// If a process runs with a restricted memory limit, it returns the limit. If there's no limit
+// specified, it returns amount of actual physical memory.
+uint64_t GCToOSInterface::GetPhysicalMemoryLimit()
+{
+ long pages = sysconf(_SC_PHYS_PAGES);
+ if (pages == -1)
+ {
+ return 0;
+ }
+
+ long pageSize = sysconf(_SC_PAGE_SIZE);
+ if (pageSize == -1)
+ {
+ return 0;
+ }
+
+ return pages * pageSize;
+}
+
+// Get memory status
+// Parameters:
+// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
+// that is in use (0 indicates no memory use and 100 indicates full memory use).
+// available_physical - The amount of physical memory currently available, in bytes.
+// available_page_file - The maximum amount of memory the current process can commit, in bytes.
+void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
+{
+ if (memory_load != nullptr || available_physical != nullptr)
+ {
+ uint64_t total = GetPhysicalMemoryLimit();
+
+ uint64_t available = 0;
+ uint32_t load = 0;
+
+ // Get the physical memory in use - from it, we can get the physical memory available.
+ // We do this only when we have the total physical memory available.
+ if (total > 0)
+ {
+ available = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE);
+ uint64_t used = total - available;
+ load = (uint32_t)((used * 100) / total);
+ }
+
+ if (memory_load != nullptr)
+ *memory_load = load;
+ if (available_physical != nullptr)
+ *available_physical = available;
+ }
+
+ if (available_page_file != nullptr)
+ *available_page_file = 0;
+}
+
+// Get a high precision performance counter
+// Return:
+// The counter value
+int64_t GCToOSInterface::QueryPerformanceCounter()
+{
+ // TODO: This is not a particularly efficient implementation - we certainly could
+ // do much more specific platform-dependent versions if we find that this method
+ // runs hot. However, most likely it does not.
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == -1)
+ {
+ assert(!"gettimeofday() failed");
+ // TODO (segilles) unconditional asserts
+ return 0;
+ }
+ return (int64_t) tv.tv_sec * (int64_t) tccSecondsToMicroSeconds + (int64_t) tv.tv_usec;
+}
+
+// Get a frequency of the high precision performance counter
+// Return:
+// The counter frequency
+int64_t GCToOSInterface::QueryPerformanceFrequency()
+{
+ // The counter frequency of gettimeofday is in microseconds.
+ return tccSecondsToMicroSeconds;
+}
+
+// Get a time stamp with a low precision
+// Return:
+// Time stamp in milliseconds
+uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
+{
+ // TODO(segilles) this is pretty naive, we can do better
+ uint64_t retval = 0;
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) == 0)
+ {
+ retval = (tv.tv_sec * tccSecondsToMilliSeconds) + (tv.tv_usec / tccMilliSecondsToMicroSeconds);
+ }
+ else
+ {
+ assert(!"gettimeofday() failed\n");
+ }
+
+ return retval;
+}
+
+// Parameters of the GC thread stub
+struct GCThreadStubParam
+{
+ GCThreadFunction GCThreadFunction;
+ void* GCThreadParam;
+};
+
+// GC thread stub to convert GC thread function to an OS specific thread function
+static void* GCThreadStub(void* param)
+{
+ GCThreadStubParam *stubParam = (GCThreadStubParam*)param;
+ GCThreadFunction function = stubParam->GCThreadFunction;
+ void* threadParam = stubParam->GCThreadParam;
+
+ delete stubParam;
+
+ function(threadParam);
+
+ return NULL;
+}
+
+// Create a new thread for GC use
+// Parameters:
+// function - the function to be executed by the thread
+// param - parameters of the thread
+// affinity - processor affinity of the thread
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity)
+{
+ std::unique_ptr<GCThreadStubParam> stubParam(new (std::nothrow) GCThreadStubParam());
+ if (!stubParam)
+ {
+ return false;
+ }
+
+ stubParam->GCThreadFunction = function;
+ stubParam->GCThreadParam = param;
+
+ pthread_attr_t attrs;
+
+ int st = pthread_attr_init(&attrs);
+ assert(st == 0);
+
+ // Create the thread as detached, that means not joinable
+ st = pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
+ assert(st == 0);
+
+ pthread_t threadId;
+ st = pthread_create(&threadId, &attrs, GCThreadStub, stubParam.get());
+
+ if (st == 0)
+ {
+ stubParam.release();
+ }
+
+ int st2 = pthread_attr_destroy(&attrs);
+ assert(st2 == 0);
+
+ return (st == 0);
+}
+
+// Initialize the critical section
+void CLRCriticalSection::Initialize()
+{
+ int st = pthread_mutex_init(&m_cs.mutex, NULL);
+ assert(st == 0);
+}
+
+// Destroy the critical section
+void CLRCriticalSection::Destroy()
+{
+ int st = pthread_mutex_destroy(&m_cs.mutex);
+ assert(st == 0);
+}
+
+// Enter the critical section. Blocks until the section can be entered.
+void CLRCriticalSection::Enter()
+{
+ pthread_mutex_lock(&m_cs.mutex);
+}
+
+// Leave the critical section
+void CLRCriticalSection::Leave()
+{
+ pthread_mutex_unlock(&m_cs.mutex);
+}
diff --git a/src/gc/gcenv.windows.cpp b/src/gc/windows/gcenv.windows.cpp
index a636478245..a636478245 100644
--- a/src/gc/gcenv.windows.cpp
+++ b/src/gc/windows/gcenv.windows.cpp