[Local GC] Provide an implementation of GCToOSInterface for Unix-like platforms ...
authorSean Gillespie <sean@swgillespie.me>
Tue, 24 Jan 2017 03:44:10 +0000 (19:44 -0800)
committerJan Kotas <jkotas@microsoft.com>
Tue, 24 Jan 2017 03:44:10 +0000 (19:44 -0800)
* Add way to build with FEATURE_STANDALONE_GC from build.sh
* Make CMake changes to build the GC 'PAL' as its own build target (to avoid -nostdinc).
In addition, introduce a "GC PAL" that provides an implementation of
GCToOSInterface on Unix-like platforms, for use with
FEATURE_STANDALONE_GC.

15 files changed:
CMakeLists.txt
build.cmd
build.sh
clrdefinitions.cmake
src/dlls/mscoree/coreclr/CMakeLists.txt
src/gc/CMakeLists.txt
src/gc/gcenv.ee.standalone.inl
src/gc/gcenv.unix.cpp [deleted file]
src/gc/sample/CMakeLists.txt
src/gc/sample/GCSample.vcxproj
src/gc/unix/CMakeLists.txt [new file with mode: 0644]
src/gc/unix/config.h.in [new file with mode: 0644]
src/gc/unix/configure.cmake [new file with mode: 0644]
src/gc/unix/gcenv.unix.cpp [new file with mode: 0644]
src/gc/windows/gcenv.windows.cpp [moved from src/gc/gcenv.windows.cpp with 100% similarity]

index 2c39780..cdf157e 100644 (file)
@@ -579,6 +579,14 @@ endif(WIN32)
 include_directories("src/pal/prebuilt/inc")
 include_directories("bin/obj")
 
+if(FEATURE_STANDALONE_GC)
+  add_definitions(-DFEATURE_STANDALONE_GC)
+
+  if(CLR_CMAKE_PLATFORM_UNIX)
+    add_subdirectory(src/gc/unix)
+  endif(CLR_CMAKE_PLATFORM_UNIX)
+endif(FEATURE_STANDALONE_GC)
+
 if (CLR_CMAKE_PLATFORM_UNIX)
   include_directories("src/pal/inc")
   include_directories("src/pal/inc/rt")
index f8c49e3..1d95307 100644 (file)
--- a/build.cmd
+++ b/build.cmd
@@ -587,6 +587,7 @@ echo skipmscorlib: skip building System.Private.CoreLib ^(default: System.Privat
 echo skipnative: skip building native components ^(default: native components are built^).
 echo skiptests: skip building tests ^(default: tests are built^).
 echo skipbuildpackages: skip building nuget packages ^(default: packages are built^).
+echo buildstandalonegc: builds the GC in a standalone mode.
 echo -skiprestore: skip restoring packages ^(default: packages are restored during build^).
 echo -disableoss: Disable Open Source Signing for System.Private.CoreLib.
 echo -priority=^<N^> : specify a set of test that will be built and run, with priority N.
index 4302407..8d03121 100755 (executable)
--- a/build.sh
+++ b/build.sh
@@ -47,6 +47,7 @@ usage()
     echo "skipgenerateversion - disable version generation even if MSBuild is supported."
     echo "cmakeargs - user-settable additional arguments passed to CMake."
     echo "bindir - output directory (defaults to $__ProjectRoot/bin)"
+    echo "buildstandalonegc - builds the GC in a standalone mode. Can't be used with \"cmakeargs\"."
 
     exit 1
 }
@@ -724,7 +725,9 @@ while :; do
                 exit 1
             fi
             ;;
-
+        buildstandalonegc)
+            __cmakeargs="-DFEATURE_STANDALONE_GC=1"
+            ;;
         *)
             __UnprocessedBuildArgs="$__UnprocessedBuildArgs $1"
             ;;
index 7719450..a2b920c 100644 (file)
@@ -168,10 +168,6 @@ add_definitions(-DFEATURE_RANDOMIZED_STRING_HASHING)
 add_definitions(-DFEATURE_READYTORUN)
 set(FEATURE_READYTORUN 1)
 
-if (FEATURE_STANDALONE_GC)
-  add_definitions(-DFEATURE_STANDALONE_GC)
-endif(FEATURE_STANDALONE_GC)
-
 if (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386)
   add_definitions(-DFEATURE_REJIT)
 endif(CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_I386)
index aa7bb0d..afd18d6 100644 (file)
@@ -125,6 +125,12 @@ else()
     )
 endif(WIN32)
 
+if(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_STANDALONE_GC)
+    list(APPEND CORECLR_LIBRARIES
+        gc_unix
+    )
+endif(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_STANDALONE_GC)
+
 if(CLR_CMAKE_PLATFORM_UNIX AND FEATURE_EVENT_TRACE)
     list(APPEND CORECLR_LIBRARIES
         eventprovider
index d32d1c2..cba1aa9 100644 (file)
@@ -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})
index e285394..31f3d1d 100644 (file)
 // 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);
 }
 
-inline void GCToEEInterface::EnableFinalization(bool foundFinalizers)
+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 (file)
index 0235952..0000000
+++ /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
index 9552cc5..29fd32f 100644 (file)
@@ -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
index 1716f46..105e289 100644 (file)
@@ -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/unix/CMakeLists.txt b/src/gc/unix/CMakeLists.txt
new file mode 100644 (file)
index 0000000..ef66abf
--- /dev/null
@@ -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 (file)
index 0000000..7578c74
--- /dev/null
@@ -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 (file)
index 0000000..6e1e8fe
--- /dev/null
@@ -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 (file)
index 0000000..34a45b3
--- /dev/null
@@ -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);
+}