Skip to content

Fix AsyncLocal leak in SystemTransactionContext #1596

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

Merged
merged 4 commits into from
Mar 10, 2018

Conversation

pecanw
Copy link
Contributor

@pecanw pecanw commented Mar 7, 2018

Fixes #1594.

@@ -7,7 +7,12 @@ public static class ExecutionContextExtensions
{
public static int LocalValuesCount(this ExecutionContext c)
{
var f = typeof(ExecutionContext).GetField("_localValues", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
#if NETSTANDARD1_3 || NETSTANDARD1_6 || NETSATNDARD2_0 || NETCOREAPP2_0
const string localValuesFieldName = "m_localValues";
Copy link
Member

Choose a reason for hiding this comment

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

We support only 2.0. And we have defined a NETFX variable you can use instead, of course with opposite semantic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to use NETFX.

namespace NHibernate.Test.NHSpecificTest.GH1594
{
[TestFixture]
public class Fixture : BugTestCase
Copy link
Member

Choose a reason for hiding this comment

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

Since it uses system transaction, the fixture must be disabled if the environment does not support them. Add:

protected override bool AppliesTo(ISessionFactoryImplementor factory) =>
	factory.ConnectionProvider.Driver.SupportsSystemTransactions;

(Since currently this is false for all .Net Core / .Standard drivers, the private field fix is in fact not really needed, but it does not harm to have it.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, thanks for describing.

@hazzik
Copy link
Member

hazzik commented Mar 7, 2018

As I understand when one talks about memory leaks what they mean is that the objects survive the GC. The test does not consider any GC at all at the moment.

We have discovered previously (for the other matter) that AsyncLocal<> has heavy memory footprint, but in majority of the cases the memory becomes reclaimed after the GC.

@pecanw
Copy link
Contributor Author

pecanw commented Mar 8, 2018

As for the @hazzik's comment: in this case every time when AsyncLocal<bool> _bypassLock is assigned a value the current ExecutionContext._localValues dictionary gets copied into a new one with a new / updated value of that _bypassLock object instance. The GC can collect the original dictionary, but the new copy (with the additional item in case of the first assignment to the _bypassLock instance) is referenced in the ExecutionContext and thus cannot be released by the GC.

@fredericDelaporte
Copy link
Member

fredericDelaporte commented Mar 8, 2018

This is true in both case, when the async local is static or not. But when it is not static, the current context dictionary references all past instances which have been created and have seen their value affected on the same thread, and grows endlessly till thread end.

The test does not consider any GC at all at the moment.

GC can not change anything in the test, since the multiple created asynclocal stay all at any point in time referenced in a dictionary, itself referenced in a instance referenced as a thread static (without weak references anywhere).

@pecanw
Copy link
Contributor Author

pecanw commented Mar 8, 2018

I have a question not related to this problem - I have the latest version of Resharper installed on my PC and it auto-updates the NHibernate.sln.DotSettings. I have unstaged it not to include it in the commit, but it is quite annoying to keep it it still in mind. May be other contributors have similar problem. Can you tell me if I

  • should keep it it is
  • include the change in the pull request
  • create a new branch + pull request for this specific think
  • just add a new item to NH backlog to let someone do it?

@fredericDelaporte
Copy link
Member

fredericDelaporte commented Mar 8, 2018

Yes we know, and there is the same trouble with Rider. It does that at almost each update, which is quite a nuisance. Sometimes we let it get updated with an unrelated PR. (May be done in #1598.)

@fredericDelaporte fredericDelaporte self-assigned this Mar 8, 2018
@fredericDelaporte fredericDelaporte added this to the 5.1 milestone Mar 8, 2018
@hazzik
Copy link
Member

hazzik commented Mar 9, 2018

GC can not change anything in the test

It can demonstrate and prove that the claims are true.

@hazzik hazzik merged commit 058ecce into nhibernate:master Mar 10, 2018
@hazzik hazzik changed the title GH1594: AsyncLocal leak in SystemTransactionContext Fix AsyncLocal leak in SystemTransactionContext Mar 10, 2018
@pecanw pecanw deleted the GH1594 branch March 12, 2018 08:01
@fredericDelaporte
Copy link
Member

For the record, async re-generation was forgotten is this PR and has been fixed with #1608. (See contributing if you need more information on async generation.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants