You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After updating dotnet/macios from the .NET 11 runtime (11.0.0-rc.2.26465.108) to the .NET 12 runtime (12.0.0-alpha.1.26471.108), the macOS monotouch-test runner crashes while a worker thread exits. CoreAnimation releases a CALayer during pthread TLS cleanup; its Objective-C destructor calls into managed code to unregister the NSObject. CoreCLR has already destroyed the thread's runtime state and now aborts on the attempted re-entry:
CLR: Assert failure: !"Attempt to execute managed code after the .NET runtime thread state has been destroyed."
src/runtime/src/coreclr/vm/ceemain.cpp:1894
In dotnet/macios, run MonoTouchFixtures.CoreAnimation.LayerTest.TestCALayerDelegateDispose on macOS with the .NET 12 runtime package above. Before the local test workaround was applied, the test created a CALayer subclass on a worker thread, set its delegate, called Dispose(), and returned from the thread. Running the full monotouch-test macOS suite crashed consistently, and running just this test also reproduced the abort. The earlier .NET 11 runtime passed this test.
Adding CATransaction.Flush() immediately after layer.Dispose() on the worker thread lets the native layer be released before that thread's runtime state is destroyed. With that workaround, the complete suite passes. This is a useful test fix, but it does not address other embedding callbacks that might occur during native TLS cleanup.
Suspected regression
dotnet/runtime#132448 introduced CheckThreadStateNotDestroyed() in SetupThread() and, on Apple platforms, a pthread-key marker that persists through later destructor callbacks. I verified that the guard is absent from the prior runtime source and present in the new runtime source. The new detection explains the abort; it does not establish that CoreAnimation's deferred release is itself new.
Mono previously handled this exact class of problem in dotnet/runtime commit 43998aa3cc6: thread_exited_dtor() restores a pthread-key marker so mono_thread_info_is_exiting() detects re-entry from later destructors (explicitly including Objective-C dealloc). Embedding code calls mono_thread_detach_if_exiting() after its managed callback to detach the temporary runtime thread. CoreCLR handles runtime attach/detach automatically; the new CoreCLR guard aborts before any post-callback cleanup can run.
Question
Is reverse P/Invoke from an Objective-C destructor during pthread teardown expected to be unsupported by CoreCLR, or should this scenario be supported with an approach analogous to Mono's attach/detach handling? If it must remain unsupported, is there a supported way for embedding code to detect that thread teardown is underway and avoid invoking managed code, without leaking the managed/native object registration?
Note
This issue was prepared with GitHub Copilot assistance.
Description
After updating dotnet/macios from the .NET 11 runtime (
11.0.0-rc.2.26465.108) to the .NET 12 runtime (12.0.0-alpha.1.26471.108), the macOSmonotouch-testrunner crashes while a worker thread exits. CoreAnimation releases aCALayerduring pthread TLS cleanup; its Objective-C destructor calls into managed code to unregister the NSObject. CoreCLR has already destroyed the thread's runtime state and now aborts on the attempted re-entry:The macOS crash report's relevant stack is:
Reproduction
In dotnet/macios, run
MonoTouchFixtures.CoreAnimation.LayerTest.TestCALayerDelegateDisposeon macOS with the .NET 12 runtime package above. Before the local test workaround was applied, the test created aCALayersubclass on a worker thread, set its delegate, calledDispose(), and returned from the thread. Running the fullmonotouch-testmacOS suite crashed consistently, and running just this test also reproduced the abort. The earlier .NET 11 runtime passed this test.Adding
CATransaction.Flush()immediately afterlayer.Dispose()on the worker thread lets the native layer be released before that thread's runtime state is destroyed. With that workaround, the complete suite passes. This is a useful test fix, but it does not address other embedding callbacks that might occur during native TLS cleanup.Suspected regression
dotnet/runtime#132448 introduced
CheckThreadStateNotDestroyed()inSetupThread()and, on Apple platforms, a pthread-key marker that persists through later destructor callbacks. I verified that the guard is absent from the prior runtime source and present in the new runtime source. The new detection explains the abort; it does not establish that CoreAnimation's deferred release is itself new.Mono previously handled this exact class of problem in dotnet/runtime commit
43998aa3cc6:thread_exited_dtor()restores a pthread-key marker somono_thread_info_is_exiting()detects re-entry from later destructors (explicitly including Objective-Cdealloc). Embedding code callsmono_thread_detach_if_exiting()after its managed callback to detach the temporary runtime thread. CoreCLR handles runtime attach/detach automatically; the new CoreCLR guard aborts before any post-callback cleanup can run.Question
Is reverse P/Invoke from an Objective-C destructor during pthread teardown expected to be unsupported by CoreCLR, or should this scenario be supported with an approach analogous to Mono's attach/detach handling? If it must remain unsupported, is there a supported way for embedding code to detect that thread teardown is underway and avoid invoking managed code, without leaking the managed/native object registration?
Note
This issue was prepared with GitHub Copilot assistance.