Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy init of ThreadWaitInfo in Thread.Mono.cs to prevent crash on external attached threads. #51483

Merged

Conversation

lateralusX
Copy link
Member

@lateralusX lateralusX commented Apr 19, 2021

Commit 457f58c added a new managed object into Mono's managed thread object, _waitInfo.

This new object was initialized in Thread managed constructor, problem with that is for all threads that gets their thread object created in native code, create_thread_object, that code did the same thing as constructor before commit, since managed
constructor called down into native code calling the same native method as create_thread_object meaning a thread object created from managed or native would be initialized identical.

After above commit this is no longer true, meaning that all managed thread objects created in native code (like attached external threads) won't get _waitInfo initialized and that leads to a crash when OnThreadExiting gets called from Finalizer thread.

Fix is to make sure _waitInfo get initialize regardless of thread object gets created from managed or native code.

This fix implements a lazy init as part of property access, I tested a native version of the fix, calling back into managed to init managed parts of thread when thread gets created in native, but turns out that such a fix gets a little complicated, since some of the threads are created early when it's not possible to run managed code, so that in turn needed additional changes into the init order, that in turn has bigger impact and could cause other unwanted side effects.

Fixing it in managed using lazy/on demand init is simpler/cleaner approach but might come with a small performance hit for callers of WaitInfo property (will add volatile read and conditional branch), but looking at callers of the property, they appear in functions with rather high "cost", like Sleep, NewMutex, Wait, SignalAndWait, Interrupt and RelinquishOwnership.

//CC @CoffeeFlux

Commit dotnet@457f58c
added a new managed object into Mono's managed thread object, _waitInfo.

This new object was initialized in Thread managed constructor, problem
with that is for all threads that gets their thread object created in
native code, create_thread_object, that code did the same thing as
constructor before commit, since managed constructur called down into
native code calling the same native method as create_thread_object meaning
a thread object created from managed or native would be initialized
indentical.

After above commit this is no longer true, meaning that all managed thread
ojbects created in native code (like attached external threads) won't
get _waitInfo initialized and that leads to a crash when OnThreadExiting
gets called from Finalizer thread.

Fix is to make sure _waitInfo get initialize regardless of thread object
gets created from managed or native code.

This fix implements a lazy init as part of property access, I tested a
native version of the fix, calling back into managed to init managed
parts of thread when tread gets created in native, but turns out that
such a fix gets a little complicated, since some of the threads are
created early when it's not possible to run managed code, so that in turn
needed additional changes into the init order, that in turn has bigger
impact and could cause other unwanted side effects.

Fixing it in managed using lazy/on demand init is simpler/cleaner approach
but might come with a small performance hit for callers of WaitInfo property
(will add volatile read and conditional branch), but looking at callers
of the property, they appear in functions with rather high "cost",
like Sleep, NewMutex, Wait, SignalAndWait, Interrupt and
RelinquishOwnership.
construct under CoreClr on x64 gives the following assembler on property
access:

lea   rax,[rcx+8]
mov   rax,qword ptr [rax]
test  rax,rax
jne   00007FFAD41908C1
call  00007FFAD41904D0
mov   eax,3B9ACA00h

Since its x64 fast path will be a lea/mov and conditional branch on
property access, all inlined in caller.
Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM otherwise

Copy link
Contributor

@CoffeeFlux CoffeeFlux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM—appreciate you looking into this!

@lateralusX lateralusX merged commit 65bcece into dotnet:main Apr 21, 2021
@karelz karelz added this to the 6.0.0 milestone May 20, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Jun 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants