Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/coreclr/inc/CrstTypes.def
Original file line number Diff line number Diff line change
Expand Up @@ -577,3 +577,7 @@ End
Crst PgoData
AcquiredBefore LoaderHeap
End

Crst StaticBoxInit
AcquiredBefore LoaderHeap FrozenObjectHeap AssemblyLoader
End
47 changes: 25 additions & 22 deletions src/coreclr/inc/crsttypes_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,28 +112,29 @@ enum CrstType
CrstSingleUseLock = 94,
CrstSpecialStatics = 95,
CrstStackSampler = 96,
CrstStressLog = 97,
CrstStubCache = 98,
CrstStubDispatchCache = 99,
CrstStubUnwindInfoHeapSegments = 100,
CrstSyncBlockCache = 101,
CrstSyncHashLock = 102,
CrstSystemBaseDomain = 103,
CrstSystemDomain = 104,
CrstSystemDomainDelayedUnloadList = 105,
CrstThreadIdDispenser = 106,
CrstThreadStore = 107,
CrstTieredCompilation = 108,
CrstTypeEquivalenceMap = 109,
CrstTypeIDMap = 110,
CrstUMEntryThunkCache = 111,
CrstUMEntryThunkFreeListLock = 112,
CrstUniqueStack = 113,
CrstUnresolvedClassLock = 114,
CrstUnwindInfoTableLock = 115,
CrstVSDIndirectionCellLock = 116,
CrstWrapperTemplate = 117,
kNumberOfCrstTypes = 118
CrstStaticBoxInit = 97,
CrstStressLog = 98,
CrstStubCache = 99,
CrstStubDispatchCache = 100,
CrstStubUnwindInfoHeapSegments = 101,
CrstSyncBlockCache = 102,
CrstSyncHashLock = 103,
CrstSystemBaseDomain = 104,
CrstSystemDomain = 105,
CrstSystemDomainDelayedUnloadList = 106,
CrstThreadIdDispenser = 107,
CrstThreadStore = 108,
CrstTieredCompilation = 109,
CrstTypeEquivalenceMap = 110,
CrstTypeIDMap = 111,
CrstUMEntryThunkCache = 112,
CrstUMEntryThunkFreeListLock = 113,
CrstUniqueStack = 114,
CrstUnresolvedClassLock = 115,
CrstUnwindInfoTableLock = 116,
CrstVSDIndirectionCellLock = 117,
CrstWrapperTemplate = 118,
kNumberOfCrstTypes = 119
};

#endif // __CRST_TYPES_INCLUDED
Expand Down Expand Up @@ -241,6 +242,7 @@ int g_rgCrstLevelMap[] =
5, // CrstSingleUseLock
0, // CrstSpecialStatics
0, // CrstStackSampler
13, // CrstStaticBoxInit
-1, // CrstStressLog
5, // CrstStubCache
0, // CrstStubDispatchCache
Expand Down Expand Up @@ -364,6 +366,7 @@ LPCSTR g_rgCrstNameMap[] =
"CrstSingleUseLock",
"CrstSpecialStatics",
"CrstStackSampler",
"CrstStaticBoxInit",
"CrstStressLog",
"CrstStubCache",
"CrstStubDispatchCache",
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/appdomain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ void BaseDomain::Init()
m_NativeTypeLoadLock.Init(CrstInteropData, CrstFlags(CRST_REENTRANCY), TRUE);

m_crstLoaderAllocatorReferences.Init(CrstLoaderAllocatorReferences);
m_crstStaticBoxInitLock.Init(CrstStaticBoxInit);
// Has to switch thread to GC_NOTRIGGER while being held (see code:BaseDomain#AssemblyListLock)
m_crstAssemblyList.Init(CrstAssemblyList, CrstFlags(
CRST_GC_NOTRIGGER_WHEN_TAKEN | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/vm/appdomain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,12 @@ class BaseDomain
return &m_crstLoaderAllocatorReferences;
}

CrstExplicitInit* GetStaticBoxInitLock()
{
LIMITED_METHOD_CONTRACT;
return &m_crstStaticBoxInitLock;
}

static CrstStatic* GetMethodTableExposedClassObjectLock()
{
LIMITED_METHOD_CONTRACT;
Expand All @@ -1112,6 +1118,7 @@ class BaseDomain
CrstExplicitInit m_DomainLocalBlockCrst;
// Used to protect the reference lists in the collectible loader allocators attached to this appdomain
CrstExplicitInit m_crstLoaderAllocatorReferences;
CrstExplicitInit m_crstStaticBoxInitLock;

//#AssemblyListLock
// Used to protect the assembly list. Taken also by GC or debugger thread, therefore we have to avoid
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/vm/frozenobjectheap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t
_ASSERT(type != nullptr);
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);

#ifdef FEATURE_64BIT_ALIGNMENT
_ASSERT(!type->RequiresAlign8());
#endif

// NOTE: objectSize is expected be the full size including header
_ASSERT(objectSize >= MIN_OBJECT_SIZE);

Expand Down
24 changes: 24 additions & 0 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1560,8 +1560,32 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,

GCX_COOP();

_ASSERT(!pFieldMT->Collectible());
// Field address is expected to be pinned so we don't need to protect it from GC here
pResult->fieldLookup.addr = pField->GetStaticAddressHandle((void*)pField->GetBase());
pResult->fieldLookup.accessType = IAT_VALUE;
if (fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP)
{
Object* frozenObj = VolatileLoad((Object**)pResult->fieldLookup.addr);

if (frozenObj == nullptr)
{
// Boxed static is not yet set, allocate it
pFieldMT->AllocateRegularStaticBox(pField, (Object**)pResult->fieldLookup.addr);
frozenObj = VolatileLoad((Object**)pResult->fieldLookup.addr);
}

_ASSERT(frozenObj != nullptr);

// ContainsPointers here is unnecessary but it's cheaper than IsInFrozenSegment
// for structs containing gc handles
if (!frozenObj->GetMethodTable()->ContainsPointers() &&
GCHeapUtilities::GetGCHeap()->IsInFrozenSegment(frozenObj))
{
pResult->fieldLookup.addr = frozenObj->GetData();
fieldFlags &= ~CORINFO_FLG_FIELD_STATIC_IN_HEAP;
}
}
}
}

Expand Down
63 changes: 53 additions & 10 deletions src/coreclr/vm/methodtable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include "array.h"
#include "castcache.h"
#include "dynamicinterfacecastable.h"
#include "frozenobjectheap.h"

#ifdef FEATURE_INTERPRETER
#include "interpreter.h"
Expand Down Expand Up @@ -3495,13 +3496,7 @@ void MethodTable::AllocateRegularStaticBoxes()

if (!pField->IsSpecialStatic() && pField->IsByValue())
{
TypeHandle th = pField->GetFieldTypeHandleThrowing();
MethodTable* pFieldMT = th.GetMethodTable();

LOG((LF_CLASSLOADER, LL_INFO10000, "\tInstantiating static of type %s\n", pFieldMT->GetDebugClassName()));
OBJECTREF obj = AllocateStaticBox(pFieldMT, HasFixedAddressVTStatics());

SetObjectReference( (OBJECTREF*)(pStaticBase + pField->GetOffset()), obj);
AllocateRegularStaticBox(pField, (Object**)(pStaticBase + pField->GetOffset()));
}

pField++;
Expand All @@ -3510,14 +3505,47 @@ void MethodTable::AllocateRegularStaticBoxes()
GCPROTECT_END();
}

void MethodTable::AllocateRegularStaticBox(FieldDesc* pField, Object** boxedStaticHandle)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_COOPERATIVE;
CONTRACTL_END;
}
_ASSERT(pField->IsStatic() && !pField->IsSpecialStatic() && pField->IsByValue());

// Static fields are not pinned in collectible types so we need to protect the address
GCPROTECT_BEGININTERIOR(boxedStaticHandle);
if (VolatileLoad(boxedStaticHandle) == nullptr)
{
// Grab field's type handle before we enter lock
MethodTable* pFieldMT = pField->GetFieldTypeHandleThrowing().GetMethodTable();
bool hasFixedAddr = HasFixedAddressVTStatics();

// Taking a lock since we might come here from multiple threads/places
CrstHolder crst(GetAppDomain()->GetStaticBoxInitLock());

// double-checked locking
if (VolatileLoad(boxedStaticHandle) == nullptr)
{
LOG((LF_CLASSLOADER, LL_INFO10000, "\tInstantiating static of type %s\n", pFieldMT->GetDebugClassName()));
OBJECTREF obj = AllocateStaticBox(pFieldMT, hasFixedAddr, NULL, false);
SetObjectReference((OBJECTREF*)(boxedStaticHandle), obj);
}
}
GCPROTECT_END();
}

//==========================================================================================
OBJECTREF MethodTable::AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, OBJECTHANDLE* pHandle)
OBJECTREF MethodTable::AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, OBJECTHANDLE* pHandle, bool canBeFrozen)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
MODE_COOPERATIVE;
CONTRACTL_END;
}

Expand All @@ -3526,7 +3554,22 @@ OBJECTREF MethodTable::AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, OB
// Activate any dependent modules if necessary
pFieldMT->EnsureInstanceActive();

OBJECTREF obj = AllocateObject(pFieldMT);
OBJECTREF obj = NULL;
if (canBeFrozen)
{
// In case if we don't plan to collect this handle we may try to allocate it on FOH
_ASSERT(!pFieldMT->ContainsPointers());
_ASSERT(pHandle == nullptr);
FrozenObjectHeapManager* foh = SystemDomain::GetFrozenObjectHeapManager();
obj = ObjectToOBJECTREF(foh->TryAllocateObject(pFieldMT, pFieldMT->GetBaseSize()));
// obj can be null in case if struct is huge (>64kb)
if (obj != NULL)
{
return obj;
}
}

obj = AllocateObject(pFieldMT);

// Pin the object if necessary
if (fPinned)
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/vm/methodtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -831,7 +831,8 @@ class MethodTable
// instantiations of the superclass or interfaces e.g. System.Int32 : IComparable<System.Int32>

void AllocateRegularStaticBoxes();
static OBJECTREF AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, OBJECTHANDLE* pHandle = 0);
void AllocateRegularStaticBox(FieldDesc* pField, Object** boxedStaticHandle);
static OBJECTREF AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, OBJECTHANDLE* pHandle = 0, bool canBeFrozen = false);

void CheckRestore();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,50 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//

using System;
using System.Runtime.CompilerServices;

public struct Age {
public int years;
public int months;
}

public class FreeClass
public struct Age
{
public static Age FreeAge;

public static unsafe IntPtr AddressOfFreeAge()
{
fixed (Age* pointer = &FreeAge)
{ return (IntPtr) pointer; }
}
public int years;
public int months;
}

public class FixedClass
{
[FixedAddressValueType]
public static Age FixedAge;

public static unsafe IntPtr AddressOfFixedAge()
{
fixed (Age* pointer = &FixedAge)
{ return (IntPtr) pointer; }
}
[FixedAddressValueType]
public static Age FixedAge;

public static unsafe IntPtr AddressOfFixedAge()
{
fixed (Age* pointer = &FixedAge)
{
return (IntPtr)pointer;
}
}
}

public class Example
{
public static int Main()
{
// Get addresses of static Age fields.
IntPtr freePtr1 = FreeClass.AddressOfFreeAge();

IntPtr fixedPtr1 = FixedClass.AddressOfFixedAge();

// Garbage collection.
GC.Collect(3, GCCollectionMode.Forced, true, true);
GC.WaitForPendingFinalizers();

// Get addresses of static Age fields after garbage collection.
IntPtr freePtr2 = FreeClass.AddressOfFreeAge();
IntPtr fixedPtr2 = FixedClass.AddressOfFixedAge();

if(freePtr1 != freePtr2 && fixedPtr1 == fixedPtr2)
{
return 100;
}

return -1;
}
public static int Main()
{
for (int i = 0; i < 1000; i++)
{
IntPtr fixedPtr1 = FixedClass.AddressOfFixedAge();

// Garbage collection.
GC.Collect(3, GCCollectionMode.Forced, true, true);
GC.WaitForPendingFinalizers();

// Get addresses of static Age fields after garbage collection.
IntPtr fixedPtr2 = FixedClass.AddressOfFixedAge();

if (fixedPtr1 != fixedPtr2)
{
return -1;
}
}
return 100;
}
}