@@ -92,18 +92,6 @@ internal void Reenter(uint previousRecursionCount)
92
92
_recursionCount = previousRecursionCount ;
93
93
}
94
94
95
- private static bool IsFullyInitialized
96
- {
97
- get
98
- {
99
- // If NativeRuntimeEventSource is already being class-constructed by this thread earlier in the stack, Log can
100
- // be null. This property is used to avoid going down the wait path in that case to avoid null checks in several
101
- // other places.
102
- Debug . Assert ( ( StaticsInitializationStage ) s_staticsInitializationStage == StaticsInitializationStage . Complete ) ;
103
- return NativeRuntimeEventSource . Log != null ;
104
- }
105
- }
106
-
107
95
[ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
108
96
private TryLockResult LazyInitializeOrEnter ( )
109
97
{
@@ -113,10 +101,6 @@ private TryLockResult LazyInitializeOrEnter()
113
101
case StaticsInitializationStage . Complete :
114
102
if ( _spinCount == SpinCountNotInitialized )
115
103
{
116
- if ( ! IsFullyInitialized )
117
- {
118
- goto case StaticsInitializationStage . Started ;
119
- }
120
104
_spinCount = s_maxSpinCount ;
121
105
}
122
106
return TryLockResult . Spin ;
@@ -137,7 +121,7 @@ private TryLockResult LazyInitializeOrEnter()
137
121
}
138
122
139
123
stage = ( StaticsInitializationStage ) Volatile . Read ( ref s_staticsInitializationStage ) ;
140
- if ( stage == StaticsInitializationStage . Complete && IsFullyInitialized )
124
+ if ( stage == StaticsInitializationStage . Complete )
141
125
{
142
126
goto case StaticsInitializationStage . Complete ;
143
127
}
@@ -155,7 +139,9 @@ private TryLockResult LazyInitializeOrEnter()
155
139
}
156
140
157
141
default :
158
- Debug . Assert ( stage == StaticsInitializationStage . NotStarted ) ;
142
+ Debug . Assert (
143
+ stage == StaticsInitializationStage . NotStarted ||
144
+ stage == StaticsInitializationStage . PartiallyComplete ) ;
159
145
if ( TryInitializeStatics ( ) )
160
146
{
161
147
goto case StaticsInitializationStage . Complete ;
@@ -169,29 +155,49 @@ private static bool TryInitializeStatics()
169
155
{
170
156
// Since Lock is used to synchronize class construction, and some of the statics initialization may involve class
171
157
// construction, update the stage first to avoid infinite recursion
172
- switch (
173
- ( StaticsInitializationStage )
174
- Interlocked . CompareExchange (
175
- ref s_staticsInitializationStage ,
176
- ( int ) StaticsInitializationStage . Started ,
177
- ( int ) StaticsInitializationStage . NotStarted ) )
158
+ var oldStage = ( StaticsInitializationStage ) s_staticsInitializationStage ;
159
+ while ( true )
178
160
{
179
- case StaticsInitializationStage . Started :
180
- return false ;
181
- case StaticsInitializationStage . Complete :
161
+ if ( oldStage == StaticsInitializationStage . Complete )
162
+ {
182
163
return true ;
164
+ }
165
+
166
+ var stageBeforeUpdate =
167
+ ( StaticsInitializationStage ) Interlocked . CompareExchange (
168
+ ref s_staticsInitializationStage ,
169
+ ( int ) StaticsInitializationStage . Started ,
170
+ ( int ) oldStage ) ;
171
+ if ( stageBeforeUpdate == StaticsInitializationStage . Started )
172
+ {
173
+ return false ;
174
+ }
175
+ if ( stageBeforeUpdate == oldStage )
176
+ {
177
+ Debug . Assert (
178
+ oldStage == StaticsInitializationStage . NotStarted ||
179
+ oldStage == StaticsInitializationStage . PartiallyComplete ) ;
180
+ break ;
181
+ }
182
+
183
+ oldStage = stageBeforeUpdate ;
183
184
}
184
185
185
186
bool isFullyInitialized ;
186
187
try
187
188
{
188
- s_isSingleProcessor = Environment . IsSingleProcessor ;
189
- s_maxSpinCount = DetermineMaxSpinCount ( ) ;
190
- s_minSpinCount = DetermineMinSpinCount ( ) ;
189
+ if ( oldStage == StaticsInitializationStage . NotStarted )
190
+ {
191
+ // If the stage is PartiallyComplete, these will have already been initialized
192
+ s_isSingleProcessor = Environment . IsSingleProcessor ;
193
+ s_maxSpinCount = DetermineMaxSpinCount ( ) ;
194
+ s_minSpinCount = DetermineMinSpinCount ( ) ;
195
+ }
191
196
192
197
// Also initialize some types that are used later to prevent potential class construction cycles. If
193
198
// NativeRuntimeEventSource is already being class-constructed by this thread earlier in the stack, Log can be
194
- // null. Avoid going down the wait path in that case to avoid null checks in several other places.
199
+ // null. Avoid going down the wait path in that case to avoid null checks in several other places. If not fully
200
+ // initialized, the stage will also be set to PartiallyComplete to try again.
195
201
isFullyInitialized = NativeRuntimeEventSource . Log != null ;
196
202
}
197
203
catch
@@ -200,7 +206,11 @@ private static bool TryInitializeStatics()
200
206
throw ;
201
207
}
202
208
203
- Volatile . Write ( ref s_staticsInitializationStage , ( int ) StaticsInitializationStage . Complete ) ;
209
+ Volatile . Write (
210
+ ref s_staticsInitializationStage ,
211
+ isFullyInitialized
212
+ ? ( int ) StaticsInitializationStage . Complete
213
+ : ( int ) StaticsInitializationStage . PartiallyComplete ) ;
204
214
return isFullyInitialized ;
205
215
}
206
216
@@ -242,6 +252,7 @@ private enum StaticsInitializationStage
242
252
{
243
253
NotStarted ,
244
254
Started ,
255
+ PartiallyComplete ,
245
256
Complete
246
257
}
247
258
}
0 commit comments