diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 48fa93148cd91..768bba0f1001a 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -11488,6 +11488,8 @@ bool gc_heap::is_region_demoted (uint8_t* obj) return demoted_p; } +static GCSpinLock write_barrier_spin_lock; + inline void gc_heap::set_region_gen_num (heap_segment* region, int gen_num) { @@ -11510,8 +11512,6 @@ void gc_heap::set_region_gen_num (heap_segment* region, int gen_num) { if ((region_start < ephemeral_low) || (ephemeral_high < region_end)) { - static GCSpinLock write_barrier_spin_lock; - while (true) { if (Interlocked::CompareExchange(&write_barrier_spin_lock.lock, 0, -1) < 0) diff --git a/src/coreclr/gc/gc.h b/src/coreclr/gc/gc.h index 55e0ed6c29c98..e9c739e3c0d72 100644 --- a/src/coreclr/gc/gc.h +++ b/src/coreclr/gc/gc.h @@ -253,9 +253,9 @@ struct alloc_context : gc_alloc_context class IGCHeapInternal : public IGCHeap { public: - virtual int GetNumberOfHeaps () = 0; - virtual int GetHomeHeapNumber () = 0; - virtual size_t GetPromotedBytes(int heap_index) = 0; + virtual int GetNumberOfHeaps () PURE_VIRTUAL + virtual int GetHomeHeapNumber () PURE_VIRTUAL + virtual size_t GetPromotedBytes(int heap_index) PURE_VIRTUAL unsigned GetMaxGeneration() { diff --git a/src/coreclr/gc/gcinterface.h b/src/coreclr/gc/gcinterface.h index ef2173bd31045..5dddd893c39dd 100644 --- a/src/coreclr/gc/gcinterface.h +++ b/src/coreclr/gc/gcinterface.h @@ -485,22 +485,29 @@ typedef void (* fq_scan_fn)(Object** ppObject, ScanContext *pSC, uint32_t dwFlag typedef void (* handle_scan_fn)(Object** pRef, Object* pSec, uint32_t flags, ScanContext* context, bool isDependent); typedef bool (* async_pin_enum_fn)(Object* object, void* context); - +// Implement pure virtual for NativeAOT Unix (for -p:LinkStandardCPlusPlusLibrary=false the default), +// to avoid linker requiring __cxa_pure_virtual. +#if defined(FEATURE_NATIVEAOT) && !defined(TARGET_WINDOWS) +// `while(true);` is to satisfy the missing `return` statement. It will be optimized away by the compiler. +#define PURE_VIRTUAL { assert(!"pure virtual function called"); while(true); } +#else +#define PURE_VIRTUAL = 0; +#endif class IGCHandleStore { public: - virtual void Uproot() = 0; + virtual void Uproot() PURE_VIRTUAL - virtual bool ContainsHandle(OBJECTHANDLE handle) = 0; + virtual bool ContainsHandle(OBJECTHANDLE handle) PURE_VIRTUAL - virtual OBJECTHANDLE CreateHandleOfType(Object* object, HandleType type) = 0; + virtual OBJECTHANDLE CreateHandleOfType(Object* object, HandleType type) PURE_VIRTUAL - virtual OBJECTHANDLE CreateHandleOfType(Object* object, HandleType type, int heapToAffinitizeTo) = 0; + virtual OBJECTHANDLE CreateHandleOfType(Object* object, HandleType type, int heapToAffinitizeTo) PURE_VIRTUAL - virtual OBJECTHANDLE CreateHandleWithExtraInfo(Object* object, HandleType type, void* pExtraInfo) = 0; + virtual OBJECTHANDLE CreateHandleWithExtraInfo(Object* object, HandleType type, void* pExtraInfo) PURE_VIRTUAL - virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary) = 0; + virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary) PURE_VIRTUAL virtual ~IGCHandleStore() {}; }; @@ -508,41 +515,41 @@ class IGCHandleStore { class IGCHandleManager { public: - virtual bool Initialize() = 0; + virtual bool Initialize() PURE_VIRTUAL - virtual void Shutdown() = 0; + virtual void Shutdown() PURE_VIRTUAL - virtual IGCHandleStore* GetGlobalHandleStore() = 0; + virtual IGCHandleStore* GetGlobalHandleStore() PURE_VIRTUAL - virtual IGCHandleStore* CreateHandleStore() = 0; + virtual IGCHandleStore* CreateHandleStore() PURE_VIRTUAL - virtual void DestroyHandleStore(IGCHandleStore* store) = 0; + virtual void DestroyHandleStore(IGCHandleStore* store) PURE_VIRTUAL - virtual OBJECTHANDLE CreateGlobalHandleOfType(Object* object, HandleType type) = 0; + virtual OBJECTHANDLE CreateGlobalHandleOfType(Object* object, HandleType type) PURE_VIRTUAL - virtual OBJECTHANDLE CreateDuplicateHandle(OBJECTHANDLE handle) = 0; + virtual OBJECTHANDLE CreateDuplicateHandle(OBJECTHANDLE handle) PURE_VIRTUAL - virtual void DestroyHandleOfType(OBJECTHANDLE handle, HandleType type) = 0; + virtual void DestroyHandleOfType(OBJECTHANDLE handle, HandleType type) PURE_VIRTUAL - virtual void DestroyHandleOfUnknownType(OBJECTHANDLE handle) = 0; + virtual void DestroyHandleOfUnknownType(OBJECTHANDLE handle) PURE_VIRTUAL - virtual void SetExtraInfoForHandle(OBJECTHANDLE handle, HandleType type, void* pExtraInfo) = 0; + virtual void SetExtraInfoForHandle(OBJECTHANDLE handle, HandleType type, void* pExtraInfo) PURE_VIRTUAL - virtual void* GetExtraInfoFromHandle(OBJECTHANDLE handle) = 0; + virtual void* GetExtraInfoFromHandle(OBJECTHANDLE handle) PURE_VIRTUAL - virtual void StoreObjectInHandle(OBJECTHANDLE handle, Object* object) = 0; + virtual void StoreObjectInHandle(OBJECTHANDLE handle, Object* object) PURE_VIRTUAL - virtual bool StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object) = 0; + virtual bool StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object) PURE_VIRTUAL - virtual void SetDependentHandleSecondary(OBJECTHANDLE handle, Object* object) = 0; + virtual void SetDependentHandleSecondary(OBJECTHANDLE handle, Object* object) PURE_VIRTUAL - virtual Object* GetDependentHandleSecondary(OBJECTHANDLE handle) = 0; + virtual Object* GetDependentHandleSecondary(OBJECTHANDLE handle) PURE_VIRTUAL - virtual Object* InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE handle, Object* object, Object* comparandObject) = 0; + virtual Object* InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE handle, Object* object, Object* comparandObject) PURE_VIRTUAL - virtual HandleType HandleFetchType(OBJECTHANDLE handle) = 0; + virtual HandleType HandleFetchType(OBJECTHANDLE handle) PURE_VIRTUAL - virtual void TraceRefCountedHandles(HANDLESCANPROC callback, uintptr_t param1, uintptr_t param2) = 0; + virtual void TraceRefCountedHandles(HANDLESCANPROC callback, uintptr_t param1, uintptr_t param2) PURE_VIRTUAL }; // Enum representing the type to be passed to GC.CoreCLR.cs used to deduce the type of configuration. @@ -568,16 +575,16 @@ class IGCHeap { */ // Returns whether or not the given size is a valid segment size. - virtual bool IsValidSegmentSize(size_t size) = 0; + virtual bool IsValidSegmentSize(size_t size) PURE_VIRTUAL // Returns whether or not the given size is a valid gen 0 max size. - virtual bool IsValidGen0MaxSize(size_t size) = 0; + virtual bool IsValidGen0MaxSize(size_t size) PURE_VIRTUAL // Gets a valid segment size. - virtual size_t GetValidSegmentSize(bool large_seg = false) = 0; + virtual size_t GetValidSegmentSize(bool large_seg = false) PURE_VIRTUAL // Sets the limit for reserved virtual memory. - virtual void SetReservedVMLimit(size_t vmlimit) = 0; + virtual void SetReservedVMLimit(size_t vmlimit) PURE_VIRTUAL /* =========================================================================== @@ -590,22 +597,22 @@ class IGCHeap { */ // Blocks until any running concurrent GCs complete. - virtual void WaitUntilConcurrentGCComplete() = 0; + virtual void WaitUntilConcurrentGCComplete() PURE_VIRTUAL // Returns true if a concurrent GC is in progress, false otherwise. - virtual bool IsConcurrentGCInProgress() = 0; + virtual bool IsConcurrentGCInProgress() PURE_VIRTUAL // Temporarily enables concurrent GC, used during profiling. - virtual void TemporaryEnableConcurrentGC() = 0; + virtual void TemporaryEnableConcurrentGC() PURE_VIRTUAL // Temporarily disables concurrent GC, used during profiling. - virtual void TemporaryDisableConcurrentGC() = 0; + virtual void TemporaryDisableConcurrentGC() PURE_VIRTUAL // Returns whether or not Concurrent GC is enabled. - virtual bool IsConcurrentGCEnabled() = 0; + virtual bool IsConcurrentGCEnabled() PURE_VIRTUAL // Wait for a concurrent GC to complete if one is in progress, with the given timeout. - virtual HRESULT WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout) = 0; // Use in native threads. TRUE if succeed. FALSE if failed or timeout + virtual HRESULT WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout) PURE_VIRTUAL // Use in native threads. TRUE if succeed. FALSE if failed or timeout /* @@ -616,10 +623,10 @@ class IGCHeap { */ // Gets the number of finalizable objects. - virtual size_t GetNumberOfFinalizable() = 0; + virtual size_t GetNumberOfFinalizable() PURE_VIRTUAL // Gets the next finalizable object. - virtual Object* GetNextFinalizable() = 0; + virtual Object* GetNextFinalizable() PURE_VIRTUAL /* =========================================================================== @@ -667,77 +674,77 @@ class IGCHeap { bool* isConcurrent, uint64_t* genInfoRaw, uint64_t* pauseInfoRaw, - int kind) = 0; + int kind) PURE_VIRTUAL; // Get the last memory load in percentage observed by the last GC. - virtual uint32_t GetMemoryLoad() = 0; + virtual uint32_t GetMemoryLoad() PURE_VIRTUAL // Gets the current GC latency mode. - virtual int GetGcLatencyMode() = 0; + virtual int GetGcLatencyMode() PURE_VIRTUAL // Sets the current GC latency mode. newLatencyMode has already been // verified by CoreLib to be valid. - virtual int SetGcLatencyMode(int newLatencyMode) = 0; + virtual int SetGcLatencyMode(int newLatencyMode) PURE_VIRTUAL // Gets the current LOH compaction mode. - virtual int GetLOHCompactionMode() = 0; + virtual int GetLOHCompactionMode() PURE_VIRTUAL // Sets the current LOH compaction mode. newLOHCompactionMode has // already been verified by CoreLib to be valid. - virtual void SetLOHCompactionMode(int newLOHCompactionMode) = 0; + virtual void SetLOHCompactionMode(int newLOHCompactionMode) PURE_VIRTUAL // Registers for a full GC notification, raising a notification if the gen 2 or // LOH object heap thresholds are exceeded. - virtual bool RegisterForFullGCNotification(uint32_t gen2Percentage, uint32_t lohPercentage) = 0; + virtual bool RegisterForFullGCNotification(uint32_t gen2Percentage, uint32_t lohPercentage) PURE_VIRTUAL // Cancels a full GC notification that was requested by `RegisterForFullGCNotification`. - virtual bool CancelFullGCNotification() = 0; + virtual bool CancelFullGCNotification() PURE_VIRTUAL // Returns the status of a registered notification for determining whether a blocking // Gen 2 collection is about to be initiated, with the given timeout. - virtual int WaitForFullGCApproach(int millisecondsTimeout) = 0; + virtual int WaitForFullGCApproach(int millisecondsTimeout) PURE_VIRTUAL // Returns the status of a registered notification for determining whether a blocking // Gen 2 collection has completed, with the given timeout. - virtual int WaitForFullGCComplete(int millisecondsTimeout) = 0; + virtual int WaitForFullGCComplete(int millisecondsTimeout) PURE_VIRTUAL // Returns the generation in which obj is found. Also used by the VM // in some places, in particular syncblk code. - virtual unsigned WhichGeneration(Object* obj) = 0; + virtual unsigned WhichGeneration(Object* obj) PURE_VIRTUAL // Returns the number of GCs that have transpired in the given generation // since the beginning of the life of the process. Also used by the VM // for debug code. - virtual int CollectionCount(int generation, int get_bgc_fgc_coutn = 0) = 0; + virtual int CollectionCount(int generation, int get_bgc_fgc_coutn = 0) PURE_VIRTUAL // Begins a no-GC region, returning a code indicating whether entering the no-GC // region was successful. - virtual int StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC) = 0; + virtual int StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC) PURE_VIRTUAL // Exits a no-GC region. - virtual int EndNoGCRegion() = 0; + virtual int EndNoGCRegion() PURE_VIRTUAL // Gets the total number of bytes in use. - virtual size_t GetTotalBytesInUse() = 0; + virtual size_t GetTotalBytesInUse() PURE_VIRTUAL - virtual uint64_t GetTotalAllocatedBytes() = 0; + virtual uint64_t GetTotalAllocatedBytes() PURE_VIRTUAL // Forces a garbage collection of the given generation. Also used extensively // throughout the VM. - virtual HRESULT GarbageCollect(int generation = -1, bool low_memory_p = false, int mode = collection_blocking) = 0; + virtual HRESULT GarbageCollect(int generation = -1, bool low_memory_p = false, int mode = collection_blocking) PURE_VIRTUAL // Gets the largest GC generation. Also used extensively throughout the VM. - virtual unsigned GetMaxGeneration() = 0; + virtual unsigned GetMaxGeneration() PURE_VIRTUAL // Indicates that an object's finalizer should not be run upon the object's collection. - virtual void SetFinalizationRun(Object* obj) = 0; + virtual void SetFinalizationRun(Object* obj) PURE_VIRTUAL // Indicates that an object's finalizer should be run upon the object's collection. - virtual bool RegisterForFinalization(int gen, Object* obj) = 0; + virtual bool RegisterForFinalization(int gen, Object* obj) PURE_VIRTUAL - virtual int GetLastGCPercentTimeInGC() = 0; + virtual int GetLastGCPercentTimeInGC() PURE_VIRTUAL - virtual size_t GetLastGCGenerationSize(int gen) = 0; + virtual size_t GetLastGCGenerationSize(int gen) PURE_VIRTUAL /* =========================================================================== @@ -747,55 +754,55 @@ class IGCHeap { // Initializes the GC heap, returning whether or not the initialization // was successful. - virtual HRESULT Initialize() = 0; + virtual HRESULT Initialize() PURE_VIRTUAL // Returns whether nor this GC was promoted by the last GC. - virtual bool IsPromoted(Object* object) = 0; + virtual bool IsPromoted(Object* object) PURE_VIRTUAL // Returns true if this pointer points into a GC heap, false otherwise. - virtual bool IsHeapPointer(void* object, bool small_heap_only = false) = 0; + virtual bool IsHeapPointer(void* object, bool small_heap_only = false) PURE_VIRTUAL // Return the generation that has been condemned by the current GC. - virtual unsigned GetCondemnedGeneration() = 0; + virtual unsigned GetCondemnedGeneration() PURE_VIRTUAL // Returns whether or not a GC is in progress. - virtual bool IsGCInProgressHelper(bool bConsiderGCStart = false) = 0; + virtual bool IsGCInProgressHelper(bool bConsiderGCStart = false) PURE_VIRTUAL // Returns the number of GCs that have occurred. Mainly used for // sanity checks asserting that a GC has not occurred. - virtual unsigned GetGcCount() = 0; + virtual unsigned GetGcCount() PURE_VIRTUAL // 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; + virtual bool IsThreadUsingAllocationContextHeap(gc_alloc_context* acontext, int thread_number) PURE_VIRTUAL // Returns whether or not this object resides in an ephemeral generation. - virtual bool IsEphemeral(Object* object) = 0; + virtual bool IsEphemeral(Object* object) PURE_VIRTUAL // Blocks until a GC is complete, returning a code indicating the wait was successful. - virtual uint32_t WaitUntilGCComplete(bool bConsiderGCStart = false) = 0; + virtual uint32_t WaitUntilGCComplete(bool bConsiderGCStart = false) PURE_VIRTUAL // "Fixes" an allocation context by binding its allocation pointer to a // location on the heap. - virtual void FixAllocContext(gc_alloc_context* acontext, void* arg, void* heap) = 0; + virtual void FixAllocContext(gc_alloc_context* acontext, void* arg, void* heap) PURE_VIRTUAL // Gets the total survived size plus the total allocated bytes on the heap. - virtual size_t GetCurrentObjSize() = 0; + virtual size_t GetCurrentObjSize() PURE_VIRTUAL // Sets whether or not a GC is in progress. - virtual void SetGCInProgress(bool fInProgress) = 0; + virtual void SetGCInProgress(bool fInProgress) PURE_VIRTUAL // Gets whether or not the GC runtime structures are in a valid state for heap traversal. - virtual bool RuntimeStructuresValid() = 0; + virtual bool RuntimeStructuresValid() PURE_VIRTUAL // Tells the GC when the VM is suspending threads. - virtual void SetSuspensionPending(bool fSuspensionPending) = 0; + virtual void SetSuspensionPending(bool fSuspensionPending) PURE_VIRTUAL // Tells the GC how many YieldProcessor calls are equal to one scaled yield processor call. - virtual void SetYieldProcessorScalingFactor(float yieldProcessorScalingFactor) = 0; + virtual void SetYieldProcessorScalingFactor(float yieldProcessorScalingFactor) PURE_VIRTUAL // Flush the log and close the file if GCLog is turned on. - virtual void Shutdown() = 0; + virtual void Shutdown() PURE_VIRTUAL /* ============================================================================ @@ -807,13 +814,13 @@ class IGCHeap { // Get the timestamp corresponding to the last GC that occurred for the // given generation. - virtual size_t GetLastGCStartTime(int generation) = 0; + virtual size_t GetLastGCStartTime(int generation) PURE_VIRTUAL // Gets the duration of the last GC that occurred for the given generation. - virtual size_t GetLastGCDuration(int generation) = 0; + virtual size_t GetLastGCDuration(int generation) PURE_VIRTUAL // Gets a timestamp for the current moment in time. - virtual size_t GetNow() = 0; + virtual size_t GetNow() PURE_VIRTUAL /* =========================================================================== @@ -828,17 +835,17 @@ class IGCHeap { // owned by the thread that is calling this function. If using per-thread alloc contexts, // no lock is needed; callers not using per-thread alloc contexts will need to acquire // a lock to ensure that the calling thread has unique ownership over this alloc context; - virtual Object* Alloc(gc_alloc_context* acontext, size_t size, uint32_t flags) = 0; + virtual Object* Alloc(gc_alloc_context* acontext, size_t size, uint32_t flags) PURE_VIRTUAL // This is for the allocator to indicate it's done allocating a large object during a // background GC as the BGC threads also need to walk UOH. - virtual void PublishObject(uint8_t* obj) = 0; + virtual void PublishObject(uint8_t* obj) PURE_VIRTUAL // Signals the WaitForGCEvent event, indicating that a GC has completed. - virtual void SetWaitForGCEvent() = 0; + virtual void SetWaitForGCEvent() PURE_VIRTUAL // Resets the state of the WaitForGCEvent back to an unsignalled state. - virtual void ResetWaitForGCEvent() = 0; + virtual void ResetWaitForGCEvent() PURE_VIRTUAL /* =========================================================================== @@ -846,21 +853,21 @@ class IGCHeap { =========================================================================== */ // Returns whether or not this object is too large for SOH. - virtual bool IsLargeObject(Object* pObj) = 0; + virtual bool IsLargeObject(Object* pObj) PURE_VIRTUAL // Walks an object and validates its members. - virtual void ValidateObjectMember(Object* obj) = 0; + virtual void ValidateObjectMember(Object* obj) PURE_VIRTUAL // Retrieves the next object after the given object. When the EE // is not suspended, the result is not accurate - if the input argument // is in Gen0, the function could return zeroed out memory as the next object. - virtual Object* NextObj(Object* object) = 0; + virtual Object* NextObj(Object* object) PURE_VIRTUAL // Given an interior pointer, return a pointer to the object // containing that pointer. This is safe to call only when the EE is suspended. // When fCollectedGenOnly is true, it only returns the object if it's found in // the generation(s) that are being collected. - virtual Object* GetContainingObject(void* pInteriorPtr, bool fCollectedGenOnly) = 0; + virtual Object* GetContainingObject(void* pInteriorPtr, bool fCollectedGenOnly) PURE_VIRTUAL /* =========================================================================== @@ -870,38 +877,38 @@ class IGCHeap { */ // Walks an object, invoking a callback on each member. - virtual void DiagWalkObject(Object* obj, walk_fn fn, void* context) = 0; + virtual void DiagWalkObject(Object* obj, walk_fn fn, void* context) PURE_VIRTUAL // Walks an object, invoking a callback on each member. - virtual void DiagWalkObject2(Object* obj, walk_fn2 fn, void* context) = 0; + virtual void DiagWalkObject2(Object* obj, walk_fn2 fn, void* context) PURE_VIRTUAL // Walk the heap object by object. - virtual void DiagWalkHeap(walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p) = 0; + virtual void DiagWalkHeap(walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p) PURE_VIRTUAL // Walks the survivors and get the relocation information if objects have moved. // gen_number is used when type == walk_for_uoh, otherwise ignored - virtual void DiagWalkSurvivorsWithType(void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type, int gen_number=-1) = 0; + virtual void DiagWalkSurvivorsWithType(void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type, int gen_number=-1) PURE_VIRTUAL // Walks the finalization queue. - virtual void DiagWalkFinalizeQueue(void* gc_context, fq_walk_fn fn) = 0; + virtual void DiagWalkFinalizeQueue(void* gc_context, fq_walk_fn fn) PURE_VIRTUAL // Scan roots on finalizer queue. This is a generic function. - virtual void DiagScanFinalizeQueue(fq_scan_fn fn, ScanContext* context) = 0; + virtual void DiagScanFinalizeQueue(fq_scan_fn fn, ScanContext* context) PURE_VIRTUAL // Scan handles for profiling or ETW. - virtual void DiagScanHandles(handle_scan_fn fn, int gen_number, ScanContext* context) = 0; + virtual void DiagScanHandles(handle_scan_fn fn, int gen_number, ScanContext* context) PURE_VIRTUAL // Scan dependent handles for profiling or ETW. - virtual void DiagScanDependentHandles(handle_scan_fn fn, int gen_number, ScanContext* context) = 0; + virtual void DiagScanDependentHandles(handle_scan_fn fn, int gen_number, ScanContext* context) PURE_VIRTUAL // Describes all generations to the profiler, invoking a callback on each generation. - virtual void DiagDescrGenerations(gen_walk_fn fn, void* context) = 0; + virtual void DiagDescrGenerations(gen_walk_fn fn, void* context) PURE_VIRTUAL // Traces all GC segments and fires ETW events with information on them. - virtual void DiagTraceGCSegments() = 0; + virtual void DiagTraceGCSegments() PURE_VIRTUAL // Get GC settings for tracing purposes. These are settings not obvious from a trace. - virtual void DiagGetGCSettings(EtwGCSettingsInfo* settings) = 0; + virtual void DiagGetGCSettings(EtwGCSettingsInfo* settings) PURE_VIRTUAL /* =========================================================================== @@ -911,7 +918,7 @@ class IGCHeap { // Returns TRUE if GC actually happens, otherwise FALSE. The passed alloc context // must not be null. - virtual bool StressHeap(gc_alloc_context* acontext) = 0; + virtual bool StressHeap(gc_alloc_context* acontext) PURE_VIRTUAL /* =========================================================================== @@ -921,13 +928,13 @@ class IGCHeap { */ // Registers a frozen segment with the GC. - virtual segment_handle RegisterFrozenSegment(segment_info *pseginfo) = 0; + virtual segment_handle RegisterFrozenSegment(segment_info *pseginfo) PURE_VIRTUAL // Unregisters a frozen segment. - virtual void UnregisterFrozenSegment(segment_handle seg) = 0; + virtual void UnregisterFrozenSegment(segment_handle seg) PURE_VIRTUAL // Indicates whether an object is in a frozen segment. - virtual bool IsInFrozenSegment(Object *object) = 0; + virtual bool IsInFrozenSegment(Object *object) PURE_VIRTUAL /* =========================================================================== @@ -936,13 +943,13 @@ class IGCHeap { */ // Enables or disables the given keyword or level on the default event provider. - virtual void ControlEvents(GCEventKeyword keyword, GCEventLevel level) = 0; + virtual void ControlEvents(GCEventKeyword keyword, GCEventLevel level) PURE_VIRTUAL // Enables or disables the given keyword or level on the private event provider. - virtual void ControlPrivateEvents(GCEventKeyword keyword, GCEventLevel level) = 0; + virtual void ControlPrivateEvents(GCEventKeyword keyword, GCEventLevel level) PURE_VIRTUAL // Get the segment/region associated with an address together with its generation for the profiler. - virtual unsigned int GetGenerationWithRange(Object* object, uint8_t** ppStart, uint8_t** ppAllocated, uint8_t** ppReserved) = 0; + virtual unsigned int GetGenerationWithRange(Object* object, uint8_t** ppStart, uint8_t** ppAllocated, uint8_t** ppReserved) PURE_VIRTUAL IGCHeap() {} @@ -951,13 +958,13 @@ class IGCHeap { // version resilience purposes. // Get the total paused duration. - virtual int64_t GetTotalPauseDuration() = 0; + virtual int64_t GetTotalPauseDuration() PURE_VIRTUAL // Gets all the names and values of the GC configurations. - virtual void EnumerateConfigurationValues(void* context, ConfigurationValueFunc configurationValueFunc) = 0; + virtual void EnumerateConfigurationValues(void* context, ConfigurationValueFunc configurationValueFunc) PURE_VIRTUAL // Updates given frozen segment - virtual void UpdateFrozenSegment(segment_handle seg, uint8_t* allocated, uint8_t* committed) = 0; + virtual void UpdateFrozenSegment(segment_handle seg, uint8_t* allocated, uint8_t* committed) PURE_VIRTUAL }; #ifdef WRITE_BARRIER_CHECK diff --git a/src/coreclr/gc/handletablecache.cpp b/src/coreclr/gc/handletablecache.cpp index 19ad231642246..c201e32929c30 100644 --- a/src/coreclr/gc/handletablecache.cpp +++ b/src/coreclr/gc/handletablecache.cpp @@ -56,7 +56,7 @@ void SpinUntil(void *pCond, BOOL fNonZero) #endif //_DEBUG // on MP machines, allow ourselves some spin time before sleeping - static uint32_t uNonSleepSpins = 8 * (GCToEEInterface::GetCurrentProcessCpuCount() - 1); + uint32_t uNonSleepSpins = 8 * (GCToEEInterface::GetCurrentProcessCpuCount() - 1); // spin until the specified condition is met while ((*(uintptr_t *)pCond != 0) != (fNonZero != 0)) diff --git a/src/coreclr/gc/unix/events.cpp b/src/coreclr/gc/unix/events.cpp index 767b414f04c17..d215c764b5f0b 100644 --- a/src/coreclr/gc/unix/events.cpp +++ b/src/coreclr/gc/unix/events.cpp @@ -1,11 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#include -#include -#include -#include -#include #include #include #include "config.gc.h" @@ -280,7 +275,7 @@ bool GCEvent::CreateManualEventNoThrow(bool initialState) bool GCEvent::CreateOSAutoEventNoThrow(bool initialState) { assert(m_impl == nullptr); - std::unique_ptr event(new (std::nothrow) GCEvent::Impl(false, initialState)); + GCEvent::Impl* event(new (nothrow) GCEvent::Impl(false, initialState)); if (!event) { return false; @@ -288,17 +283,18 @@ bool GCEvent::CreateOSAutoEventNoThrow(bool initialState) if (!event->Initialize()) { + delete event; return false; } - m_impl = event.release(); + m_impl = event; return true; } bool GCEvent::CreateOSManualEventNoThrow(bool initialState) { assert(m_impl == nullptr); - std::unique_ptr event(new (std::nothrow) GCEvent::Impl(true, initialState)); + GCEvent::Impl* event(new (nothrow) GCEvent::Impl(true, initialState)); if (!event) { return false; @@ -306,10 +302,10 @@ bool GCEvent::CreateOSManualEventNoThrow(bool initialState) if (!event->Initialize()) { + delete event; return false; } - m_impl = event.release(); + m_impl = event; return true; } - diff --git a/src/coreclr/nativeaot/Bootstrap/base/CMakeLists.txt b/src/coreclr/nativeaot/Bootstrap/base/CMakeLists.txt index 2366cd267bff5..370dfd0712b21 100644 --- a/src/coreclr/nativeaot/Bootstrap/base/CMakeLists.txt +++ b/src/coreclr/nativeaot/Bootstrap/base/CMakeLists.txt @@ -12,4 +12,7 @@ if (CLR_CMAKE_TARGET_WIN32) add_library(bootstrapper.GuardCF STATIC ${SOURCES}) install_static_library(bootstrapper.GuardCF aotsdk nativeaot) target_compile_options(bootstrapper.GuardCF PRIVATE $<$,$>:/guard:cf>) +else() + add_library(stdc++compat STATIC ../stdcppshim.cpp) + install_static_library(stdc++compat aotsdk nativeaot) endif() diff --git a/src/coreclr/nativeaot/Bootstrap/stdcppshim.cpp b/src/coreclr/nativeaot/Bootstrap/stdcppshim.cpp new file mode 100644 index 0000000000000..e8f9a07afbb77 --- /dev/null +++ b/src/coreclr/nativeaot/Bootstrap/stdcppshim.cpp @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include + +namespace std +{ + struct nothrow_t {}; + extern const nothrow_t nothrow = {}; +} + +void* operator new(size_t n, const std::nothrow_t&) noexcept +{ + return malloc(n); +} + +void* operator new[](size_t n, const std::nothrow_t&) noexcept +{ + return malloc(n); +} + +void operator delete(void *p) noexcept +{ + free(p); +} + +void operator delete[](void *p) noexcept +{ + free(p); +} diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index d214d113efece..7710daf3a00d3 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -49,6 +49,7 @@ The .NET Foundation licenses this file to you under the MIT license. + @@ -96,7 +97,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + diff --git a/src/coreclr/nativeaot/CMakeLists.txt b/src/coreclr/nativeaot/CMakeLists.txt index 005d0b9011470..2449ea783eecb 100644 --- a/src/coreclr/nativeaot/CMakeLists.txt +++ b/src/coreclr/nativeaot/CMakeLists.txt @@ -30,6 +30,7 @@ if(CLR_CMAKE_HOST_UNIX) add_compile_options(-fno-rtti) # Native AOT runtime doesn't use RTTI add_compile_options(-fno-exceptions) # Native AOT runtime doesn't use C++ exception handling + add_compile_options(-nostdlib) if(CLR_CMAKE_TARGET_OSX) add_definitions(-D_XOPEN_SOURCE) diff --git a/src/coreclr/nativeaot/Runtime/CMakeLists.txt b/src/coreclr/nativeaot/Runtime/CMakeLists.txt index 2e37b1708d195..d4fcf662ec66b 100644 --- a/src/coreclr/nativeaot/Runtime/CMakeLists.txt +++ b/src/coreclr/nativeaot/Runtime/CMakeLists.txt @@ -105,9 +105,6 @@ if (WIN32) ${GC_DIR}/handletablepriv.h ${GC_DIR}/objecthandle.h ${GC_DIR}/softwarewritewatch.h) -endif(WIN32) - -if(WIN32) include_directories(windows) diff --git a/src/coreclr/nativeaot/Runtime/ICodeManager.h b/src/coreclr/nativeaot/Runtime/ICodeManager.h index 3671b6f683905..30eb4ac6854f2 100644 --- a/src/coreclr/nativeaot/Runtime/ICodeManager.h +++ b/src/coreclr/nativeaot/Runtime/ICodeManager.h @@ -165,50 +165,50 @@ enum class AssociatedDataFlags : unsigned char class ICodeManager { public: - virtual bool IsSafePoint(PTR_VOID pvAddress) = 0; + virtual bool IsSafePoint(PTR_VOID pvAddress) PURE_VIRTUAL virtual bool FindMethodInfo(PTR_VOID ControlPC, - MethodInfo * pMethodInfoOut) = 0; + MethodInfo * pMethodInfoOut) PURE_VIRTUAL - virtual bool IsFunclet(MethodInfo * pMethodInfo) = 0; + virtual bool IsFunclet(MethodInfo * pMethodInfo) PURE_VIRTUAL virtual PTR_VOID GetFramePointer(MethodInfo * pMethodInfo, - REGDISPLAY * pRegisterSet) = 0; + REGDISPLAY * pRegisterSet) PURE_VIRTUAL virtual void EnumGcRefs(MethodInfo * pMethodInfo, PTR_VOID safePointAddress, REGDISPLAY * pRegisterSet, GCEnumContext * hCallback, - bool isActiveStackFrame) = 0; + bool isActiveStackFrame) PURE_VIRTUAL virtual bool UnwindStackFrame(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in/out - PInvokeTransitionFrame** ppPreviousTransitionFrame) = 0; // out + PInvokeTransitionFrame** ppPreviousTransitionFrame) PURE_VIRTUAL // out virtual uintptr_t GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo, - REGDISPLAY * pRegisterSet) = 0; + REGDISPLAY * pRegisterSet) PURE_VIRTUAL - virtual bool IsUnwindable(PTR_VOID pvAddress) = 0; + virtual bool IsUnwindable(PTR_VOID pvAddress) PURE_VIRTUAL virtual bool GetReturnAddressHijackInfo(MethodInfo * pMethodInfo, REGDISPLAY * pRegisterSet, // in PTR_PTR_VOID * ppvRetAddrLocation, // out - GCRefKind * pRetValueKind) = 0; // out + GCRefKind * pRetValueKind) PURE_VIRTUAL // out - virtual PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) = 0; + virtual PTR_VOID RemapHardwareFaultToGCSafePoint(MethodInfo * pMethodInfo, PTR_VOID controlPC) PURE_VIRTUAL - virtual bool EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumState) = 0; + virtual bool EHEnumInit(MethodInfo * pMethodInfo, PTR_VOID * pMethodStartAddress, EHEnumState * pEHEnumState) PURE_VIRTUAL - virtual bool EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClause) = 0; + virtual bool EHEnumNext(EHEnumState * pEHEnumState, EHClause * pEHClause) PURE_VIRTUAL - virtual PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo) = 0; + virtual PTR_VOID GetMethodStartAddress(MethodInfo * pMethodInfo) PURE_VIRTUAL - virtual PTR_VOID GetOsModuleHandle() = 0; + virtual PTR_VOID GetOsModuleHandle() PURE_VIRTUAL - virtual void * GetClasslibFunction(ClasslibFunctionId functionId) = 0; + virtual void * GetClasslibFunction(ClasslibFunctionId functionId) PURE_VIRTUAL // Returns any custom data attached to the method. Format: // AssociatedDataFlags // 1 byte. Flags describing the data stored // Data (stream of bytes) // Variable size (depending on flags). Custom data associated with method - virtual PTR_VOID GetAssociatedData(PTR_VOID ControlPC) = 0; + virtual PTR_VOID GetAssociatedData(PTR_VOID ControlPC) PURE_VIRTUAL }; diff --git a/src/coreclr/nativeaot/Runtime/inc/CommonTypes.h b/src/coreclr/nativeaot/Runtime/inc/CommonTypes.h index 76b55aed711c3..6a35dba3629c1 100644 --- a/src/coreclr/nativeaot/Runtime/inc/CommonTypes.h +++ b/src/coreclr/nativeaot/Runtime/inc/CommonTypes.h @@ -6,9 +6,19 @@ #include #include -#include +#include +#include #include +// Implement pure virtual for Unix (for -p:LinkStandardCPlusPlusLibrary=false the default), +// to avoid linker requiring __cxa_pure_virtual. +#ifdef TARGET_WINDOWS +#define PURE_VIRTUAL = 0; +#else +// `while(true);` is to satisfy the missing `return` statement. It will be optimized away by the compiler. +#define PURE_VIRTUAL { assert(!"pure virtual function called"); while(true); } +#endif + using std::nothrow; using std::size_t; using std::uintptr_t; diff --git a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp index 127217ec32d18..ce7cf86ee7f8d 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalRedhawkUnix.cpp @@ -20,6 +20,7 @@ #include "UnixContext.h" #include "HardwareExceptions.h" #include "cgroupcpu.h" +#include "threadstore.h" #define _T(s) s #include "RhConfig.h" @@ -346,11 +347,6 @@ class EventUnixHandle : public UnixHandle typedef UnixHandle ThreadUnixHandle; -#if !HAVE_THREAD_LOCAL -extern "C" int __cxa_thread_atexit(void (*)(void*), void*, void *); -extern "C" void *__dso_handle; -#endif - // This functions configures behavior of the signals that are not // related to hardware exception handling. void ConfigureSignals() @@ -406,6 +402,10 @@ void InitializeCurrentProcessCpuCount() g_RhNumberOfProcessors = count; } +#ifdef TARGET_LINUX +static pthread_key_t key; +#endif + // The Redhawk PAL must be initialized before any of its exports can be called. Returns true for a successful // initialization and false on failure. REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() @@ -430,11 +430,17 @@ REDHAWK_PALEXPORT bool REDHAWK_PALAPI PalInit() InitializeCurrentProcessCpuCount(); +#ifdef TARGET_LINUX + if (pthread_key_create(&key, RuntimeThreadShutdown) != 0) + { + return false; + } +#endif + return true; } -#if HAVE_THREAD_LOCAL - +#ifndef TARGET_LINUX struct TlsDestructionMonitor { void* m_thread = nullptr; @@ -456,8 +462,7 @@ struct TlsDestructionMonitor // This thread local object is used to detect thread shutdown. Its destructor // is called when a thread is being shut down. thread_local TlsDestructionMonitor tls_destructionMonitor; - -#endif // HAVE_THREAD_LOCAL +#endif // This thread local variable is used for delegate marshalling DECLSPEC_THREAD intptr_t tls_thunkData; @@ -481,10 +486,14 @@ EXTERN_C intptr_t RhGetCurrentThunkContext() // thread - thread to attach extern "C" void PalAttachThread(void* thread) { -#if HAVE_THREAD_LOCAL - tls_destructionMonitor.SetThread(thread); +#ifdef TARGET_LINUX + if (pthread_setspecific(key, thread) != 0) + { + _ASSERTE(!"pthread_setspecific failed"); + RhFailFast(); + } #else - __cxa_thread_atexit(RuntimeThreadShutdown, thread, &__dso_handle); + tls_destructionMonitor.SetThread(thread); #endif } @@ -943,16 +952,13 @@ extern "C" UInt32_BOOL ResetEvent(HANDLE event) extern "C" uint32_t GetEnvironmentVariableA(const char * name, char * buffer, uint32_t size) { - // Using std::getenv instead of getenv since it is guaranteed to be thread safe w.r.t. other - // std::getenv calls in C++11 - const char* value = std::getenv(name); + const char* value = getenv(name); if (value == NULL) { return 0; } size_t valueLen = strlen(value); - if (valueLen < size) { strcpy(buffer, value); diff --git a/src/coreclr/nativeaot/Runtime/unix/config.h.in b/src/coreclr/nativeaot/Runtime/unix/config.h.in index 63d0dd6f7a3f5..5ae3735d9bd2c 100644 --- a/src/coreclr/nativeaot/Runtime/unix/config.h.in +++ b/src/coreclr/nativeaot/Runtime/unix/config.h.in @@ -27,6 +27,4 @@ #cmakedefine01 HAVE_CLOCK_GETTIME_NSEC_NP #cmakedefine01 HAVE_SCHED_GETAFFINITY -#cmakedefine01 HAVE_THREAD_LOCAL - #endif diff --git a/src/coreclr/nativeaot/Runtime/unix/configure.cmake b/src/coreclr/nativeaot/Runtime/unix/configure.cmake index 0b90762c3bfbc..d93449c794021 100644 --- a/src/coreclr/nativeaot/Runtime/unix/configure.cmake +++ b/src/coreclr/nativeaot/Runtime/unix/configure.cmake @@ -105,13 +105,4 @@ check_symbol_exists( check_library_exists(c sched_getaffinity "" HAVE_SCHED_GETAFFINITY) -check_cxx_source_compiles(" -thread_local int x; - -int main(int argc, char **argv) -{ - x = 1; - return 0; -}" HAVE_THREAD_LOCAL) - configure_file(${CMAKE_CURRENT_LIST_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) diff --git a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.csproj b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.csproj index 8f2a5aa349d58..6d2effb8c1f28 100644 --- a/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.csproj +++ b/src/tests/nativeaot/SmokeTests/Preinitialization/Preinitialization.csproj @@ -11,6 +11,12 @@ and this only works if presence of code implies presence of reflection metadata. --> false + + + true