Skip to content

Commit 27a3d92

Browse files
github-actions[bot]jkotasCopilotAaronRobinsonMSFT
authored
[release/10.0] [NativeAOT] Fix resetting apartment state (#119993)
* Add test for resetting apartment state * Fix * Update src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> * Update src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs * Reuse ComState enum * Update src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs Co-authored-by: Aaron Robinson <[email protected]> * Copy over yet another quirk from CoreCLR --------- Co-authored-by: Jan Kotas <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Aaron Robinson <[email protected]>
1 parent d8e71a4 commit 27a3d92

File tree

2 files changed

+58
-35
lines changed

2 files changed

+58
-35
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Thread.NativeAot.Windows.cs

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ namespace System.Threading
1414
{
1515
public sealed partial class Thread
1616
{
17-
[ThreadStatic]
18-
private static ApartmentType t_apartmentType;
19-
2017
[ThreadStatic]
2118
private static ComState t_comState;
2219

@@ -235,14 +232,15 @@ public ApartmentState GetApartmentState()
235232
return _initialApartmentState;
236233
}
237234

238-
switch (GetCurrentApartmentType())
235+
switch (GetCurrentApartmentState())
239236
{
240-
case ApartmentType.STA:
237+
case ApartmentState.STA:
241238
return ApartmentState.STA;
242-
case ApartmentType.MTA:
239+
case ApartmentState.MTA:
243240
return ApartmentState.MTA;
244241
default:
245-
return ApartmentState.Unknown;
242+
// If COM is uninitialized on the current thread, it is assumed to be implicit MTA.
243+
return ApartmentState.MTA;
246244
}
247245
}
248246

@@ -275,14 +273,29 @@ private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
275273
}
276274
else
277275
{
276+
// Compat: Setting ApartmentState to Unknown uninitializes COM
278277
UninitializeCom();
279278
}
280-
}
281279

282-
// Clear the cache and check whether new state matches the desired state
283-
t_apartmentType = ApartmentType.Unknown;
280+
// Clear the cache and check whether new state matches the desired state
281+
t_comState &= ~(ComState.STA | ComState.MTA);
284282

285-
retState = GetApartmentState();
283+
retState = GetCurrentApartmentState();
284+
}
285+
else
286+
{
287+
Debug.Assert((t_comState & ComState.MTA) != 0);
288+
retState = ApartmentState.MTA;
289+
}
290+
}
291+
292+
// Special case where we pass in Unknown and get back MTA.
293+
// Once we CoUninitialize the thread, the OS will still
294+
// report the thread as implicitly in the MTA if any
295+
// other thread in the process is CoInitialized.
296+
if ((state == ApartmentState.Unknown) && (retState == ApartmentState.MTA))
297+
{
298+
return true;
286299
}
287300

288301
if (retState != state)
@@ -316,7 +329,7 @@ private static void InitializeComForThreadPoolThread()
316329
// Process-wide COM is initialized very early before any managed code can run.
317330
// Assume it is done.
318331
// Prevent re-initialization of COM model on threadpool threads from the default one.
319-
t_comState |= ComState.Locked;
332+
t_comState |= ComState.Locked | ComState.MTA;
320333
}
321334

322335
private static void InitializeCom(ApartmentState state = ApartmentState.MTA)
@@ -396,49 +409,53 @@ internal static Thread EnsureThreadPoolThreadInitialized()
396409
public void Interrupt() { throw new PlatformNotSupportedException(); }
397410

398411
internal static bool ReentrantWaitsEnabled =>
399-
GetCurrentApartmentType() == ApartmentType.STA;
412+
GetCurrentApartmentState() == ApartmentState.STA;
400413

401-
internal static ApartmentType GetCurrentApartmentType()
414+
// Unlike the public API, this returns ApartmentState.Unknown when COM is uninitialized on the current thread
415+
internal static ApartmentState GetCurrentApartmentState()
402416
{
403-
ApartmentType currentThreadType = t_apartmentType;
404-
if (currentThreadType != ApartmentType.Unknown)
405-
return currentThreadType;
417+
if ((t_comState & (ComState.MTA | ComState.STA)) != 0)
418+
return ((t_comState & ComState.STA) != 0) ? ApartmentState.STA : ApartmentState.MTA;
406419

407420
Interop.APTTYPE aptType;
408421
Interop.APTTYPEQUALIFIER aptTypeQualifier;
409422
int result = Interop.Ole32.CoGetApartmentType(out aptType, out aptTypeQualifier);
410423

411-
ApartmentType type = ApartmentType.Unknown;
424+
ApartmentState state = ApartmentState.Unknown;
412425

413426
switch (result)
414427
{
415428
case HResults.CO_E_NOTINITIALIZED:
416-
type = ApartmentType.None;
429+
Debug.Fail("COM is not initialized");
430+
state = ApartmentState.Unknown;
417431
break;
418432

419433
case HResults.S_OK:
420434
switch (aptType)
421435
{
422436
case Interop.APTTYPE.APTTYPE_STA:
423437
case Interop.APTTYPE.APTTYPE_MAINSTA:
424-
type = ApartmentType.STA;
438+
state = ApartmentState.STA;
425439
break;
426440

427441
case Interop.APTTYPE.APTTYPE_MTA:
428-
type = ApartmentType.MTA;
442+
state = ApartmentState.MTA;
429443
break;
430444

431445
case Interop.APTTYPE.APTTYPE_NA:
432446
switch (aptTypeQualifier)
433447
{
434448
case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MTA:
449+
state = ApartmentState.MTA;
450+
break;
451+
435452
case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA:
436-
type = ApartmentType.MTA;
453+
state = ApartmentState.Unknown;
437454
break;
438455

439456
case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_STA:
440457
case Interop.APTTYPEQUALIFIER.APTTYPEQUALIFIER_NA_ON_MAINSTA:
441-
type = ApartmentType.STA;
458+
state = ApartmentState.STA;
442459
break;
443460

444461
default:
@@ -454,24 +471,18 @@ internal static ApartmentType GetCurrentApartmentType()
454471
break;
455472
}
456473

457-
if (type != ApartmentType.Unknown)
458-
t_apartmentType = type;
459-
return type;
460-
}
461-
462-
internal enum ApartmentType : byte
463-
{
464-
Unknown = 0,
465-
None,
466-
STA,
467-
MTA
474+
if (state != ApartmentState.Unknown)
475+
t_comState |= (state == ApartmentState.STA) ? ComState.STA : ComState.MTA;
476+
return state;
468477
}
469478

470479
[Flags]
471480
internal enum ComState : byte
472481
{
473482
InitializedByUs = 1,
474483
Locked = 2,
484+
MTA = 4,
485+
STA = 8
475486
}
476487
}
477488
}

src/libraries/System.Threading.Thread/tests/ThreadTests.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,20 @@ public static void GetSetApartmentStateTest_ChangeAfterThreadStarted_Windows(
247247
Assert.Equal(ApartmentState.MTA, getApartmentState(t));
248248
Assert.Equal(0, setApartmentState(t, ApartmentState.MTA));
249249
Assert.Equal(ApartmentState.MTA, getApartmentState(t));
250-
Assert.Equal(setType == 0 ? 0 : 2, setApartmentState(t, ApartmentState.STA)); // cannot be changed after thread is started
250+
Assert.Equal(setType == 0 ? 0 : 2, setApartmentState(t, ApartmentState.STA)); // MTA<->STA cannot be changed directly after thread is started
251251
Assert.Equal(ApartmentState.MTA, getApartmentState(t));
252+
253+
if (!PlatformDetection.IsWindowsNanoServer)
254+
{
255+
Assert.Equal(0, setApartmentState(t, ApartmentState.Unknown)); // Compat quirk: MTA<->STA can be changed by going through Unknown
256+
Assert.Equal(ApartmentState.MTA, getApartmentState(t));
257+
Assert.Equal(0, setApartmentState(t, ApartmentState.STA));
258+
Assert.Equal(ApartmentState.STA, getApartmentState(t));
259+
Assert.Equal(0, setApartmentState(t, ApartmentState.Unknown));
260+
Assert.Equal(ApartmentState.MTA, getApartmentState(t));
261+
Assert.Equal(0, setApartmentState(t, ApartmentState.MTA));
262+
Assert.Equal(ApartmentState.MTA, getApartmentState(t));
263+
}
252264
});
253265
}
254266

0 commit comments

Comments
 (0)