@@ -49,6 +49,100 @@ inline gc_alloc_context* GetThreadAllocContext()
49
49
return & GetThread ()->m_alloc_context ;
50
50
}
51
51
52
+ // When not using per-thread allocation contexts, we (the EE) need to take care that
53
+ // no two threads are concurrently modifying the global allocation context. This lock
54
+ // must be acquired before any sort of operations involving the global allocation context
55
+ // can occur.
56
+ //
57
+ // This lock is acquired by all allocations when not using per-thread allocation contexts.
58
+ // It is acquired in two kinds of places:
59
+ // 1) JIT_TrialAllocFastSP (and related assembly alloc helpers), which attempt to
60
+ // acquire it but move into an alloc slow path if acquiring fails
61
+ // (but does not decrement the lock variable when doing so)
62
+ // 2) Alloc in gchelpers.cpp, which acquire the lock using
63
+ // the Acquire and Release methods below.
64
+ class GlobalAllocLock {
65
+ friend struct AsmOffsets ;
66
+ private:
67
+ // The lock variable. This field must always be first.
68
+ LONG m_lock;
69
+
70
+ public:
71
+ // Creates a new GlobalAllocLock in the unlocked state.
72
+ GlobalAllocLock () : m_lock(-1 ) {}
73
+
74
+ // Copy and copy-assignment operators should never be invoked
75
+ // for this type
76
+ GlobalAllocLock (const GlobalAllocLock&) = delete ;
77
+ GlobalAllocLock& operator =(const GlobalAllocLock&) = delete ;
78
+
79
+ bool TryAcquire ()
80
+ {
81
+ CONTRACTL {
82
+ NOTHROW;
83
+ GC_TRIGGERS; // switch to preemptive mode
84
+ MODE_COOPERATIVE;
85
+ } CONTRACTL_END;
86
+
87
+ return InterlockedCompareExchange (&m_lock, 0 , -1 ) == -1 ;
88
+ }
89
+
90
+ // Acquires the lock, spinning if necessary to do so. When this method
91
+ // returns, m_lock will be zero and the lock will be acquired.
92
+ void Acquire ()
93
+ {
94
+ CONTRACTL {
95
+ NOTHROW;
96
+ GC_TRIGGERS; // switch to preemptive mode
97
+ MODE_COOPERATIVE;
98
+ } CONTRACTL_END;
99
+
100
+ DWORD spinCount = 0 ;
101
+ while (InterlockedExchange (&m_lock, 0 ) != -1 )
102
+ {
103
+ GCX_PREEMP ();
104
+ __SwitchToThread (0 , spinCount++);
105
+ }
106
+
107
+ assert (m_lock == 0 );
108
+ }
109
+
110
+ // Releases the lock.
111
+ void Release ()
112
+ {
113
+ LIMITED_METHOD_CONTRACT;
114
+
115
+ // the lock may not be exactly 0. This is because the
116
+ // assembly alloc routines increment the lock variable and
117
+ // jump if not zero to the slow alloc path, which eventually
118
+ // will try to acquire the lock again. At that point, it will
119
+ // spin in Acquire (since m_lock is some number that's not zero).
120
+ // When the thread that /does/ hold the lock releases it, the spinning
121
+ // thread will continue.
122
+ MemoryBarrier ();
123
+ assert (m_lock >= 0 );
124
+ m_lock = -1 ;
125
+ }
126
+
127
+ // Static helper to acquire a lock, for use with the Holder template.
128
+ static void AcquireLock (GlobalAllocLock *lock)
129
+ {
130
+ WRAPPER_NO_CONTRACT;
131
+ lock->Acquire ();
132
+ }
133
+
134
+ // Static helper to release a lock, for use with the Holder template
135
+ static void ReleaseLock (GlobalAllocLock *lock)
136
+ {
137
+ WRAPPER_NO_CONTRACT;
138
+ lock->Release ();
139
+ }
140
+
141
+ typedef Holder<GlobalAllocLock *, GlobalAllocLock::AcquireLock, GlobalAllocLock::ReleaseLock> Holder;
142
+ };
143
+
144
+ typedef GlobalAllocLock::Holder GlobalAllocLockHolder;
145
+
52
146
struct AsmOffsets {
53
147
static_assert (offsetof(GlobalAllocLock, m_lock) == 0 , " ASM code relies on this property" );
54
148
};
@@ -1437,3 +1531,51 @@ SetCardsAfterBulkCopy(Object **start, size_t len)
1437
1531
#if defined(_MSC_VER) && defined(TARGET_X86)
1438
1532
#pragma optimize("", on) // Go back to command line default optimizations
1439
1533
#endif // _MSC_VER && TARGET_X86
1534
+
1535
+ OBJECTREF TryBoxFromTrialAllocation (MethodTable* pMT, void * unboxedData)
1536
+ {
1537
+ // Use the relevant allocation context to try to do a trail allocation
1538
+ // without calling into the GC.
1539
+ gc_alloc_context* pAllocContext;
1540
+ if (GCHeapUtilities::UseThreadAllocationContexts ())
1541
+ {
1542
+ pAllocContext = GetThreadAllocContext ();
1543
+ }
1544
+ else
1545
+ {
1546
+ pAllocContext = &g_global_alloc_context;
1547
+ }
1548
+
1549
+ uint32_t size = pMT->GetNumInstanceFieldBytes ();
1550
+
1551
+ if (!GCHeapUtilities::UseThreadAllocationContexts ())
1552
+ {
1553
+ // Don't wait on the global alloc lock if we can't acquire it.
1554
+ if (!g_global_alloc_lock.TryAcquire ())
1555
+ {
1556
+ return ObjectToOBJECTREF (nullptr );
1557
+ }
1558
+ }
1559
+
1560
+ if (pAllocContext->alloc_limit - pAllocContext->alloc_ptr < size)
1561
+ {
1562
+ if (!GCHeapUtilities::UseThreadAllocationContexts ())
1563
+ {
1564
+ GlobalAllocLock::ReleaseLock (&g_global_alloc_lock);
1565
+ }
1566
+ // Tail call to the slow helper, we can't allocate from the alloc context.
1567
+ return nullptr ;
1568
+ }
1569
+
1570
+ OBJECTREF objRef = ObjectToOBJECTREF ((Object*)pAllocContext->alloc_ptr );
1571
+ pAllocContext->alloc_ptr += size;
1572
+
1573
+ if (!GCHeapUtilities::UseThreadAllocationContexts ())
1574
+ {
1575
+ GlobalAllocLock::ReleaseLock (&g_global_alloc_lock);
1576
+ }
1577
+
1578
+ CopyValueClass (objRef->UnBox (), unboxedData, pMT);
1579
+
1580
+ return objRef;
1581
+ }
0 commit comments