Skip to content

Commit 1fa5485

Browse files
EgorBojkotas
andauthored
Allocate static boxes on FOH (#79188)
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent 9166ee4 commit 1fa5485

9 files changed

Lines changed: 154 additions & 76 deletions

File tree

src/coreclr/inc/CrstTypes.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,3 +577,7 @@ End
577577
Crst PgoData
578578
AcquiredBefore LoaderHeap
579579
End
580+
581+
Crst StaticBoxInit
582+
AcquiredBefore LoaderHeap FrozenObjectHeap AssemblyLoader
583+
End

src/coreclr/inc/crsttypes_generated.h

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,29 @@ enum CrstType
112112
CrstSingleUseLock = 94,
113113
CrstSpecialStatics = 95,
114114
CrstStackSampler = 96,
115-
CrstStressLog = 97,
116-
CrstStubCache = 98,
117-
CrstStubDispatchCache = 99,
118-
CrstStubUnwindInfoHeapSegments = 100,
119-
CrstSyncBlockCache = 101,
120-
CrstSyncHashLock = 102,
121-
CrstSystemBaseDomain = 103,
122-
CrstSystemDomain = 104,
123-
CrstSystemDomainDelayedUnloadList = 105,
124-
CrstThreadIdDispenser = 106,
125-
CrstThreadStore = 107,
126-
CrstTieredCompilation = 108,
127-
CrstTypeEquivalenceMap = 109,
128-
CrstTypeIDMap = 110,
129-
CrstUMEntryThunkCache = 111,
130-
CrstUMEntryThunkFreeListLock = 112,
131-
CrstUniqueStack = 113,
132-
CrstUnresolvedClassLock = 114,
133-
CrstUnwindInfoTableLock = 115,
134-
CrstVSDIndirectionCellLock = 116,
135-
CrstWrapperTemplate = 117,
136-
kNumberOfCrstTypes = 118
115+
CrstStaticBoxInit = 97,
116+
CrstStressLog = 98,
117+
CrstStubCache = 99,
118+
CrstStubDispatchCache = 100,
119+
CrstStubUnwindInfoHeapSegments = 101,
120+
CrstSyncBlockCache = 102,
121+
CrstSyncHashLock = 103,
122+
CrstSystemBaseDomain = 104,
123+
CrstSystemDomain = 105,
124+
CrstSystemDomainDelayedUnloadList = 106,
125+
CrstThreadIdDispenser = 107,
126+
CrstThreadStore = 108,
127+
CrstTieredCompilation = 109,
128+
CrstTypeEquivalenceMap = 110,
129+
CrstTypeIDMap = 111,
130+
CrstUMEntryThunkCache = 112,
131+
CrstUMEntryThunkFreeListLock = 113,
132+
CrstUniqueStack = 114,
133+
CrstUnresolvedClassLock = 115,
134+
CrstUnwindInfoTableLock = 116,
135+
CrstVSDIndirectionCellLock = 117,
136+
CrstWrapperTemplate = 118,
137+
kNumberOfCrstTypes = 119
137138
};
138139

139140
#endif // __CRST_TYPES_INCLUDED
@@ -241,6 +242,7 @@ int g_rgCrstLevelMap[] =
241242
5, // CrstSingleUseLock
242243
0, // CrstSpecialStatics
243244
0, // CrstStackSampler
245+
13, // CrstStaticBoxInit
244246
-1, // CrstStressLog
245247
5, // CrstStubCache
246248
0, // CrstStubDispatchCache
@@ -364,6 +366,7 @@ LPCSTR g_rgCrstNameMap[] =
364366
"CrstSingleUseLock",
365367
"CrstSpecialStatics",
366368
"CrstStackSampler",
369+
"CrstStaticBoxInit",
367370
"CrstStressLog",
368371
"CrstStubCache",
369372
"CrstStubDispatchCache",

src/coreclr/vm/appdomain.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ void BaseDomain::Init()
637637
m_NativeTypeLoadLock.Init(CrstInteropData, CrstFlags(CRST_REENTRANCY), TRUE);
638638

639639
m_crstLoaderAllocatorReferences.Init(CrstLoaderAllocatorReferences);
640+
m_crstStaticBoxInitLock.Init(CrstStaticBoxInit);
640641
// Has to switch thread to GC_NOTRIGGER while being held (see code:BaseDomain#AssemblyListLock)
641642
m_crstAssemblyList.Init(CrstAssemblyList, CrstFlags(
642643
CRST_GC_NOTRIGGER_WHEN_TAKEN | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));

src/coreclr/vm/appdomain.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,12 @@ class BaseDomain
10881088
return &m_crstLoaderAllocatorReferences;
10891089
}
10901090

1091+
CrstExplicitInit* GetStaticBoxInitLock()
1092+
{
1093+
LIMITED_METHOD_CONTRACT;
1094+
return &m_crstStaticBoxInitLock;
1095+
}
1096+
10911097
static CrstStatic* GetMethodTableExposedClassObjectLock()
10921098
{
10931099
LIMITED_METHOD_CONTRACT;
@@ -1112,6 +1118,7 @@ class BaseDomain
11121118
CrstExplicitInit m_DomainLocalBlockCrst;
11131119
// Used to protect the reference lists in the collectible loader allocators attached to this appdomain
11141120
CrstExplicitInit m_crstLoaderAllocatorReferences;
1121+
CrstExplicitInit m_crstStaticBoxInitLock;
11151122

11161123
//#AssemblyListLock
11171124
// Used to protect the assembly list. Taken also by GC or debugger thread, therefore we have to avoid

src/coreclr/vm/frozenobjectheap.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ Object* FrozenObjectHeapManager::TryAllocateObject(PTR_MethodTable type, size_t
3737
_ASSERT(type != nullptr);
3838
_ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE);
3939

40+
#ifdef FEATURE_64BIT_ALIGNMENT
41+
_ASSERT(!type->RequiresAlign8());
42+
#endif
43+
4044
// NOTE: objectSize is expected be the full size including header
4145
_ASSERT(objectSize >= MIN_OBJECT_SIZE);
4246

src/coreclr/vm/jitinterface.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,8 +1560,32 @@ void CEEInfo::getFieldInfo (CORINFO_RESOLVED_TOKEN * pResolvedToken,
15601560

15611561
GCX_COOP();
15621562

1563+
_ASSERT(!pFieldMT->Collectible());
1564+
// Field address is expected to be pinned so we don't need to protect it from GC here
15631565
pResult->fieldLookup.addr = pField->GetStaticAddressHandle((void*)pField->GetBase());
15641566
pResult->fieldLookup.accessType = IAT_VALUE;
1567+
if (fieldFlags & CORINFO_FLG_FIELD_STATIC_IN_HEAP)
1568+
{
1569+
Object* frozenObj = VolatileLoad((Object**)pResult->fieldLookup.addr);
1570+
1571+
if (frozenObj == nullptr)
1572+
{
1573+
// Boxed static is not yet set, allocate it
1574+
pFieldMT->AllocateRegularStaticBox(pField, (Object**)pResult->fieldLookup.addr);
1575+
frozenObj = VolatileLoad((Object**)pResult->fieldLookup.addr);
1576+
}
1577+
1578+
_ASSERT(frozenObj != nullptr);
1579+
1580+
// ContainsPointers here is unnecessary but it's cheaper than IsInFrozenSegment
1581+
// for structs containing gc handles
1582+
if (!frozenObj->GetMethodTable()->ContainsPointers() &&
1583+
GCHeapUtilities::GetGCHeap()->IsInFrozenSegment(frozenObj))
1584+
{
1585+
pResult->fieldLookup.addr = frozenObj->GetData();
1586+
fieldFlags &= ~CORINFO_FLG_FIELD_STATIC_IN_HEAP;
1587+
}
1588+
}
15651589
}
15661590
}
15671591

src/coreclr/vm/methodtable.cpp

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "array.h"
5959
#include "castcache.h"
6060
#include "dynamicinterfacecastable.h"
61+
#include "frozenobjectheap.h"
6162

6263
#ifdef FEATURE_INTERPRETER
6364
#include "interpreter.h"
@@ -3495,13 +3496,7 @@ void MethodTable::AllocateRegularStaticBoxes()
34953496

34963497
if (!pField->IsSpecialStatic() && pField->IsByValue())
34973498
{
3498-
TypeHandle th = pField->GetFieldTypeHandleThrowing();
3499-
MethodTable* pFieldMT = th.GetMethodTable();
3500-
3501-
LOG((LF_CLASSLOADER, LL_INFO10000, "\tInstantiating static of type %s\n", pFieldMT->GetDebugClassName()));
3502-
OBJECTREF obj = AllocateStaticBox(pFieldMT, HasFixedAddressVTStatics());
3503-
3504-
SetObjectReference( (OBJECTREF*)(pStaticBase + pField->GetOffset()), obj);
3499+
AllocateRegularStaticBox(pField, (Object**)(pStaticBase + pField->GetOffset()));
35053500
}
35063501

35073502
pField++;
@@ -3510,14 +3505,47 @@ void MethodTable::AllocateRegularStaticBoxes()
35103505
GCPROTECT_END();
35113506
}
35123507

3508+
void MethodTable::AllocateRegularStaticBox(FieldDesc* pField, Object** boxedStaticHandle)
3509+
{
3510+
CONTRACTL
3511+
{
3512+
THROWS;
3513+
GC_TRIGGERS;
3514+
MODE_COOPERATIVE;
3515+
CONTRACTL_END;
3516+
}
3517+
_ASSERT(pField->IsStatic() && !pField->IsSpecialStatic() && pField->IsByValue());
3518+
3519+
// Static fields are not pinned in collectible types so we need to protect the address
3520+
GCPROTECT_BEGININTERIOR(boxedStaticHandle);
3521+
if (VolatileLoad(boxedStaticHandle) == nullptr)
3522+
{
3523+
// Grab field's type handle before we enter lock
3524+
MethodTable* pFieldMT = pField->GetFieldTypeHandleThrowing().GetMethodTable();
3525+
bool hasFixedAddr = HasFixedAddressVTStatics();
3526+
3527+
// Taking a lock since we might come here from multiple threads/places
3528+
CrstHolder crst(GetAppDomain()->GetStaticBoxInitLock());
3529+
3530+
// double-checked locking
3531+
if (VolatileLoad(boxedStaticHandle) == nullptr)
3532+
{
3533+
LOG((LF_CLASSLOADER, LL_INFO10000, "\tInstantiating static of type %s\n", pFieldMT->GetDebugClassName()));
3534+
OBJECTREF obj = AllocateStaticBox(pFieldMT, hasFixedAddr, NULL, false);
3535+
SetObjectReference((OBJECTREF*)(boxedStaticHandle), obj);
3536+
}
3537+
}
3538+
GCPROTECT_END();
3539+
}
3540+
35133541
//==========================================================================================
3514-
OBJECTREF MethodTable::AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, OBJECTHANDLE* pHandle)
3542+
OBJECTREF MethodTable::AllocateStaticBox(MethodTable* pFieldMT, BOOL fPinned, OBJECTHANDLE* pHandle, bool canBeFrozen)
35153543
{
35163544
CONTRACTL
35173545
{
35183546
THROWS;
35193547
GC_TRIGGERS;
3520-
MODE_ANY;
3548+
MODE_COOPERATIVE;
35213549
CONTRACTL_END;
35223550
}
35233551

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

3529-
OBJECTREF obj = AllocateObject(pFieldMT);
3557+
OBJECTREF obj = NULL;
3558+
if (canBeFrozen)
3559+
{
3560+
// In case if we don't plan to collect this handle we may try to allocate it on FOH
3561+
_ASSERT(!pFieldMT->ContainsPointers());
3562+
_ASSERT(pHandle == nullptr);
3563+
FrozenObjectHeapManager* foh = SystemDomain::GetFrozenObjectHeapManager();
3564+
obj = ObjectToOBJECTREF(foh->TryAllocateObject(pFieldMT, pFieldMT->GetBaseSize()));
3565+
// obj can be null in case if struct is huge (>64kb)
3566+
if (obj != NULL)
3567+
{
3568+
return obj;
3569+
}
3570+
}
3571+
3572+
obj = AllocateObject(pFieldMT);
35303573

35313574
// Pin the object if necessary
35323575
if (fPinned)

src/coreclr/vm/methodtable.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -831,7 +831,8 @@ class MethodTable
831831
// instantiations of the superclass or interfaces e.g. System.Int32 : IComparable<System.Int32>
832832

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

836837
void CheckRestore();
837838

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,50 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33
//
4+
45
using System;
56
using System.Runtime.CompilerServices;
67

7-
public struct Age {
8-
public int years;
9-
public int months;
10-
}
11-
12-
public class FreeClass
8+
public struct Age
139
{
14-
public static Age FreeAge;
15-
16-
public static unsafe IntPtr AddressOfFreeAge()
17-
{
18-
fixed (Age* pointer = &FreeAge)
19-
{ return (IntPtr) pointer; }
20-
}
10+
public int years;
11+
public int months;
2112
}
2213

2314
public class FixedClass
2415
{
25-
[FixedAddressValueType]
26-
public static Age FixedAge;
27-
28-
public static unsafe IntPtr AddressOfFixedAge()
29-
{
30-
fixed (Age* pointer = &FixedAge)
31-
{ return (IntPtr) pointer; }
32-
}
16+
[FixedAddressValueType]
17+
public static Age FixedAge;
18+
19+
public static unsafe IntPtr AddressOfFixedAge()
20+
{
21+
fixed (Age* pointer = &FixedAge)
22+
{
23+
return (IntPtr)pointer;
24+
}
25+
}
3326
}
3427

3528
public class Example
3629
{
37-
public static int Main()
38-
{
39-
// Get addresses of static Age fields.
40-
IntPtr freePtr1 = FreeClass.AddressOfFreeAge();
41-
42-
IntPtr fixedPtr1 = FixedClass.AddressOfFixedAge();
43-
44-
// Garbage collection.
45-
GC.Collect(3, GCCollectionMode.Forced, true, true);
46-
GC.WaitForPendingFinalizers();
47-
48-
// Get addresses of static Age fields after garbage collection.
49-
IntPtr freePtr2 = FreeClass.AddressOfFreeAge();
50-
IntPtr fixedPtr2 = FixedClass.AddressOfFixedAge();
51-
52-
if(freePtr1 != freePtr2 && fixedPtr1 == fixedPtr2)
53-
{
54-
return 100;
55-
}
56-
57-
return -1;
58-
}
30+
public static int Main()
31+
{
32+
for (int i = 0; i < 1000; i++)
33+
{
34+
IntPtr fixedPtr1 = FixedClass.AddressOfFixedAge();
35+
36+
// Garbage collection.
37+
GC.Collect(3, GCCollectionMode.Forced, true, true);
38+
GC.WaitForPendingFinalizers();
39+
40+
// Get addresses of static Age fields after garbage collection.
41+
IntPtr fixedPtr2 = FixedClass.AddressOfFixedAge();
42+
43+
if (fixedPtr1 != fixedPtr2)
44+
{
45+
return -1;
46+
}
47+
}
48+
return 100;
49+
}
5950
}

0 commit comments

Comments
 (0)